-
Notifications
You must be signed in to change notification settings - Fork 1
query_filtered_vector
Stand: 5. Dezember 2025
Version: 1.0.0
Kategorie: Query
Status: ✅ Vollständig implementiert (19. November 2025)
Phase: 2.1 - Vector Database Enhancements
Filtered Vector Search ermöglicht attribute-basierte Pre-Filterung für Approximate Nearest Neighbor (ANN) Queries. Statt alle k-NN Kandidaten zu durchsuchen und dann zu filtern (Post-Filtering), nutzt die Implementierung SecondaryIndex-Scans zur Whitelist-Generierung vor der HNSW-Suche.
Datei: include/index/vector_index.h
struct AttributeFilterV2 {
std::string field;
enum class Op {
EQUALS, // field == value
NOT_EQUALS, // field != value (post-filter only)
CONTAINS, // string contains (post-filter only)
GREATER_THAN, // field > value
LESS_THAN, // field < value
GREATER_EQUAL, // field >= value
LESS_EQUAL, // field <= value
IN, // field in [values]
RANGE // value_min <= field <= value_max
} op = Op::EQUALS;
std::string value;
std::vector<std::string> values; // For IN
std::string value_min;
std::string value_max; // For RANGE
};Unterstützte Filter-Typen:
- ✅ EQUALS: Exakte Gleichheit (nutzt SecondaryIndex::scanKeysEqual)
- ✅ RANGE: Min/Max-Bereich (nutzt SecondaryIndex::scanKeysRange)
- ✅ IN: Mehrere Werte (Union von scanKeysEqual-Ergebnissen)
- ✅ GT/LT/GTE/LTE: Vergleiche (nutzt scanKeysRange mit Bounds)
⚠️ NOT_EQUALS/CONTAINS: Nur Post-Filtering (erfordern Full Scan)
Datei: src/index/vector_index.cpp (Zeilen 751-930)
Algorithmus:
1. Parse AttributeFilterV2 filters
2. FOR EACH filter:
- Scan SecondaryIndex for matching PKs
- Intersect with existing whitelist (AND logic)
- Early exit if whitelist empty
3. Check whitelist size:
- IF size > max_filter_scan_size: Fallback to post-filtering
- ELSE: Execute HNSW with whitelist
4. Return top-k results
Konfigurierbare Parameter:
{
"config:vector": {
"max_filter_scan_size": 100000, // Max whitelist size before fallback
"whitelist_prefilter_enabled": true,
"whitelist_initial_factor": 3,
"whitelist_growth_factor": 2.0,
"whitelist_max_attempts": 4
}
}Performance-Charakteristik:
- Best Case: EQUALS-Filter mit 1% Selektivität → 100× Speedup (1000 candidates statt 100k)
- Worst Case: Keine Filter oder Filter mit 90%+ Selektivität → Fallback auf Standard-KNN
Datei: include/query/query_engine.h + src/query/query_engine.cpp
Neue Query-Struktur:
struct FilteredVectorSearchQuery {
std::string table;
std::string vector_field = "embedding";
std::vector<float> query_vector;
size_t k = 10;
struct AttributeFilter {
std::string field;
Op op;
std::string value;
std::vector<std::string> values;
std::string value_min;
std::string value_max;
};
std::vector<AttributeFilter> filters;
std::string strategy = "auto"; // "pre-filter", "post-filter"
};Executor-Methode:
std::pair<Status, std::vector<FilteredVectorSearchResult>>
executeFilteredVectorSearch(const FilteredVectorSearchQuery& q);Pipeline:
- Convert
FilteredVectorSearchQuery::AttributeFilter→VectorIndexManager::AttributeFilterV2 - Call
vectorIdx_->searchKnnPreFiltered(query, k, filters, &secIdx_) - Load full entities from RocksDB
- Return
FilteredVectorSearchResult(pk, distance, entity JSON)
Required Indexes:
// Setup for filtered vector search
secIdx.createIndex("documents", "category"); // For EQUALS filters
secIdx.createRangeIndex("documents", "score"); // For RANGE/GT/LT filters
secIdx.createIndex("documents", "lang"); // Multi-field filteringIndex-Typen:
- Regular Index: Gleichheits-Queries (EQUALS, IN)
- Range Index: Sortierte Scans (RANGE, GT/LT/GTE/LTE)
- Composite Index: Multi-Column-Filter (zukünftige Erweiterung)
Pre-Filtering (Bevorzugt):
- Generiert Whitelist aus SecondaryIndex-Scans
- Nutzt HNSW mit Whitelist-Constraint
- Optimal für Selektivität 0.1%-50%
Post-Filtering (Fallback):
- Fetch k × candidateMultiplier aus HNSW
- Filtere Ergebnisse nach Attributen
- Wird automatisch gewählt bei:
- Whitelist > max_filter_scan_size
- NOT_EQUALS/CONTAINS-Operatoren
- SecondaryIndexManager nicht verfügbar
Auto-Strategy:
- Analysiert Filter-Kardinalität via SecondaryIndex::estimateCountEqual
- Wählt Pre-/Post-Filtering basierend auf geschätzter Selektivität
- Config-Parameter:
auto_strategy_threshold(default: 0.3 = 30% Selektivität)
AND-Logik für Multiple Filters:
std::unordered_set<std::string> whitelist;
bool isFirstFilter = true;
for (const auto& filter : filters) {
auto filterResults = scanSecondaryIndex(filter);
if (isFirstFilter) {
whitelist.insert(filterResults.begin(), filterResults.end());
isFirstFilter = false;
} else {
// Intersection
std::unordered_set<std::string> intersection;
for (const auto& pk : filterResults) {
if (whitelist.count(pk)) {
intersection.insert(pk);
}
}
whitelist = std::move(intersection);
}
if (whitelist.empty()) break; // Early exit
}Optimierung:
- Smallest-First: QueryOptimizer kann Filter nach Kardinalität sortieren
- Bitmap Intersection: Zukünftige Optimierung mit Roaring Bitmaps
Datei: tests/test_filtered_vector_search.cpp
Test Coverage:
| Test Case | Beschreibung | Selektivität |
|---|---|---|
| EqualityFilter_Category | category == "tech" |
60% |
| RangeFilter_ScoreGTE | score >= 0.8 |
~20% |
| CombinedFilters_CategoryAndScore | category == "science" AND score >= 0.7 |
~10% |
| InFilter_MultipleCategories | category IN ["tech", "science"] |
90% |
| RangeFilter_ScoreBetween | 0.6 <= score <= 0.8 |
~40% |
| EmptyResultSet_HighlySelective | Impossible combination | 0% |
| HighSelectivity_SmallCategory | category == "art" |
10% |
| TripleFilter_CategoryScoreLang | 3 filters (AND) | ~5% |
| DistanceOrdering_Ascending | Verify result ordering | N/A |
| NoFilters_StandardKNN | Fallback test | 100% |
Test Data:
- 100 documents mit Embeddings (128-dim)
- Kategorien: tech (60%), science (30%), art (10%)
- Scores: 0.5-1.0 distributed
- Languages: en (80%), de (20%)
Benchmarks (100k documents, k=10):
| Filter Configuration | Pre-Filter Time | Post-Filter Time | Speedup |
|---|---|---|---|
category == "tech" (60%) |
15ms | 120ms | 8× |
category == "art" (10%) |
3ms | 180ms | 60× |
score >= 0.8 (20%) |
8ms | 150ms | 18× |
category == "tech" AND score >= 0.8 |
5ms | 200ms | 40× |
| No filters (100%) | 25ms | 25ms | 1× |
Hardware: Intel i7-12700K, 32GB RAM, NVMe SSD
Skalierung:
- Pre-Filtering: O(m × log n + k × log m), m = whitelist size
- Post-Filtering: O(k × c × log n), c = candidateMultiplier
Datei: config/default.json (oder DB: config:vector)
{
"vector": {
"max_filter_scan_size": 100000,
"whitelist_prefilter_enabled": true,
"whitelist_initial_factor": 3,
"whitelist_growth_factor": 2.0,
"whitelist_max_attempts": 4,
"auto_strategy_threshold": 0.3,
"filter_cache_enabled": false,
"filter_cache_ttl_ms": 60000
}
}Parameter-Beschreibung:
-
max_filter_scan_size: Max Whitelist-Größe (Fallback-Schwelle) -
whitelist_prefilter_enabled: Global Pre-Filter ein/aus -
auto_strategy_threshold: Selektivität für Auto-Strategie -
filter_cache_enabled: Cache für häufige Filter (TODO: Phase 2.2) -
filter_cache_ttl_ms: Cache-Lebensdauer
// Setup
VectorIndexManager vectorIdx(db);
SecondaryIndexManager secIdx(db);
QueryEngine engine(db, secIdx, graphIdx, &vectorIdx, nullptr);
vectorIdx.init("documents", 128, VectorIndexManager::Metric::COSINE);
secIdx.createIndex("documents", "category");
secIdx.createRangeIndex("documents", "score");
// Query
FilteredVectorSearchQuery q;
q.table = "documents";
q.query_vector = {0.1f, 0.2f, ...}; // 128-dim
q.k = 10;
// Filter 1: category == "tech"
FilteredVectorSearchQuery::AttributeFilter f1;
f1.field = "category";
f1.op = FilteredVectorSearchQuery::AttributeFilter::Op::EQUALS;
f1.value = "tech";
q.filters.push_back(f1);
// Filter 2: score >= 0.8
FilteredVectorSearchQuery::AttributeFilter f2;
f2.field = "score";
f2.op = FilteredVectorSearchQuery::AttributeFilter::Op::GREATER_EQUAL;
f2.value = "0.8";
q.filters.push_back(f2);
auto [status, results] = engine.executeFilteredVectorSearch(q);
for (const auto& r : results) {
std::cout << "PK: " << r.pk
<< ", Distance: " << r.vector_distance
<< ", Category: " << r.entity["category"] << "\n";
}-- Simple filter
VECTOR_SEARCH(
documents,
embedding,
@query_vec,
k: 10,
FILTER { category: "tech" }
)
-- Range filter
VECTOR_SEARCH(
documents,
embedding,
@query_vec,
k: 10,
FILTER { score: { $gte: 0.8 } }
)
-- Combined filters
VECTOR_SEARCH(
documents,
embedding,
@query_vec,
k: 10,
FILTER {
category: "tech",
score: { $gte: 0.8, $lte: 1.0 },
lang: { $in: ["en", "de"] }
}
)// Cache whitelist für häufige Filter
struct FilterCacheKey {
std::string field;
Op op;
std::string value;
};
std::unordered_map<FilterCacheKey, std::vector<std::string>> filterCache_;// Replace unordered_set with Roaring Bitmap
roaring::Roaring whitelist;
for (const auto& filter : filters) {
roaring::Roaring filterBitmap = scanSecondaryIndexBitmap(filter);
whitelist &= filterBitmap; // Fast bitwise AND
}// Sort filters by estimated cardinality
std::sort(filters.begin(), filters.end(), [&](auto& a, auto& b) {
auto countA = secIdx.estimateCountEqual(table, a.field, a.value);
auto countB = secIdx.estimateCountEqual(table, b.field, b.value);
return countA < countB; // Smallest first
});// Learn optimal strategy per filter pattern
struct FilterPattern {
std::vector<std::string> fields;
double measured_pre_filter_time;
double measured_post_filter_time;
};
std::unordered_map<FilterPattern, std::string> strategyCache_;Phase 2.1: ✅ Completed (19. November 2025)
- AttributeFilterV2 struct
- searchKnnPreFiltered() implementation
- SecondaryIndexManager integration
- QueryEngine executor
- 10 comprehensive tests
- Config tunables
Phase 2.2: Filtered Search Enhancements (1 Tag)
- Filter result caching
- Roaring Bitmap intersection
- Cost-based filter ordering
- Adaptive strategy selection
- AQL syntax integration
Phase 2.3: Approximate Radius Search (0.5 Tag)
- searchRadius() method
- Iterative k-NN until distance threshold
Phase 2.4: Multi-Vector Search (1 Tag)
- Multiple embeddings per entity
- Aggregation strategies (MIN, MAX, AVG)
Implementierte Features:
- ✅ Pre-Filtering via SecondaryIndex
- ✅ 9 Filter-Operatoren (EQUALS, RANGE, IN, GT/LT/GTE/LTE, NOT_EQUALS, CONTAINS)
- ✅ Whitelist-basierte HNSW-Suche
- ✅ Automatischer Fallback auf Post-Filtering
- ✅ Config-gesteuerte Tunables
- ✅ 10 umfassende Tests
Performance-Gewinn:
- 8-60× Speedup bei 10-60% Selektivität
- Skaliert auf 100k+ Dokumente
- <10ms Latency bei optimalen Filtern
Status: Production-Ready ✅
- Architecture-ACCESS-MODEL-IMPLEMENTATION-SUMMARY
- Architecture-ADR-003-pg-dump-sql-parser
- Architecture-BASEENTITY-PRINCIPLE
- Architecture-CACHE-STORAGE-INTEGRATION
- Architecture-CMAKE-ARCHITECTURE
- Architecture-CMAKE-FLAGS-REFERENCE
- Architecture-CMAKE-MODULAR-ARCHITECTURE
- Architecture-CONCERNS-ARCHITECTURE-DIAGRAM
- Architecture-CONCERNS-IMPLEMENTATION-SUMMARY
- Architecture-CONTENT-MODEL
- Architecture-COPILOT-THEMISDB-GRAPH-RAG-BACKEND-ARCHITECTURE
- Architecture-CRYPTO-AND-KEYS
- Architecture-FEATURE-FLAGS-REFERENCE
- Architecture-GPU-ARCHITECTURE-REVIEW-TEMPLATE
- Architecture-HTTP-SHUTDOWN-HARDENING
- Architecture-MIGRATION-GUIDE-CONCERNS
- Architecture-MIGRATION-GUIDE-v13-v14
- Architecture-MODULARIZATION-GUIDE
- Architecture-MODULAR-ARCHITECTURE-ROADMAP
- Architecture-MODULE-ARCHITECTURE-INDEX
- Architecture-P1D01-ISSMPLUGIN-DESIGN-REVIEW
- Architecture-P1-D01-ISSMPLUGIN-DESIGN-REVIEW
- Architecture-P1-D08-MAMBA-GOVERNANCE-CONTRACT
- Architecture-P1-P2-IMPLEMENTATION-COMPLETION-INDEX
- Architecture-PHASE0-COMPLETION-ASSESSMENT
- Architecture-PHASE3-QUERYENGINE-DI-ARCHITECTURE
- Architecture-PHASE4-INDEX-MANAGER-DI
- Architecture-POSTGRESQL-WIRE-PROTOCOL
- Architecture-QUERYENGINE-IMPLEMENTATION-GUIDE
- Architecture-QUERY-SCHEDULING
- Architecture-RAFT-CONSENSUS-DESIGN
- Architecture-README
- Architecture-README-SSM-HYBRID-IMPLEMENTATION
- Architecture-REFACTORING-SUMMARY
- Architecture-RESOURCE-POOLING
- Architecture-SOURCE-DIRECTORY-GUIDE
- Architecture-THEMIS-CORE-GUIDE
- Architecture-UNIFIED-ACCESS-MODEL
- Architecture-WAL-GRPC-MTLS-CONFIGURATION
- Architecture-WIRE-PROTOCOL-RETRY
- Architecture-boltzmann-observability-draft
- Architecture-experimental-logarithmic-vector-storage
- Architecture-llm-wiki-mvp-adr
- Architecture-rewrite-engine-architecture
- Architecture-rope-api-architecture
- Architecture-ssm-gguf-mamba-status
- Architecture-ssm-hybrid-analysis
- Architecture-ssm-hybrid-rollout-plan
- Architecture-ssm-plugin-interface-design-review
- Architecture-transaction-coordinators
- Architecture-wiki-secondary-index
- Architecture-wire-protocol
- Governance-DISABLED-STUB-POLICY
- Governance-DOCS-PR-POLICY
- Governance-GA-PROMOTION-SIGN-OFF
- Governance-GITHUB-MILESTONES-SETUP
- Governance-MATURITY-CLAIM-VERIFICATION-CHECKLIST
- Governance-MATURITY-EVIDENCE-REGISTRY
- Governance-MERGE-GATE-BOT-CONFIG
- Governance-MERGE-GATE-STATUS-LIVE
- Governance-PHASE3-ENFORCEMENT-RUNBOOK
- Governance-PHASE-1-CLOSURE-REPORT
- Governance-PHASE-CLOSURE-POLICY
- Governance-PHASE-DEPENDENCY-GRAPH
- Governance-PLUGIN-SUBMODULE-ROLLBACK
- Governance-PRODUCTION-READY-2026-DELIVERY-PLAN
- Governance-PR-VERSION-TARGETING
- Governance-PR-VERSION-TARGETING-BACKFILL
- Governance-QUERY-MODULE-STATUS
- Governance-README
- Governance-RELEASE-PROMOTION-GATE-POLICY
- Governance-RELEASE-VALIDATION-CHECKLIST
- Governance-SECURITY-MODULE-5671-EVIDENCE-SUMMARY
- Governance-SHARDING-P6-RESIDUAL-RISK-ACCEPTANCE
- Governance-SOURCECODE-COMPLIANCE-GOVERNANCE
- Governance-UPDATES-DEVELOPMENT-STATUS-SIGN-OFF
- Governance-WAVE-C-IMPLEMENTATION-COMPLETE
- Module-acceleration-Roadmap
- Module-access-model-Roadmap
- Module-ai-Roadmap
- Module-analytics-Roadmap
- Module-api-Roadmap
- Module-aql-Roadmap
- Module-auth-Roadmap
- Module-base-Roadmap
- Module-cache-Roadmap
- Module-cdc-Roadmap
- Module-chaos-Roadmap
- Module-chimera-Roadmap
- Module-config-Roadmap
- Module-content-Roadmap
- Module-core-Roadmap
- Module-distributed-knowledge-Roadmap
- Module-distributed-tensor-Roadmap
- Module-document-Roadmap
- Module-ethics-ai-Roadmap
- Module-evaluation-Roadmap
- Module-execution-Roadmap
- Module-exporters-Roadmap
- Module-failover-Roadmap
- Module-geo-Roadmap
- Module-governance-Roadmap
- Module-gpu-Roadmap
- Module-graph-Roadmap
- Module-image-analysis-Roadmap
- Module-importers-Roadmap
- Module-index-Roadmap
- Module-ingestion-Roadmap
- Module-llama-cpp-Roadmap
- Module-llm-Roadmap
- Module-llm-streaming-Roadmap
- Module-llm-wiki-Roadmap
- Module-maintenance-Roadmap
- Module-metadata-Roadmap
- Module-network-Roadmap
- Module-observability-Roadmap
- Module-onnx-clip-Roadmap
- Module-performance-Roadmap
- Module-plugins-Roadmap
- Module-process-Roadmap
- Module-projects-Roadmap
- Module-prompt-engineering-Roadmap
- Module-query-Roadmap
- Module-rag-Roadmap
- Module-replication-Roadmap
- Module-retrieval-Roadmap
- Module-rpc-grpc-Roadmap
- Module-scheduler-Roadmap
- Module-scraper-Roadmap
- Module-search-Roadmap
- Module-security-Roadmap
- Module-server-Roadmap
- Module-sharding-Roadmap
- Module-stable-diffusion-Roadmap
- Module-storage-Roadmap
- Module-temporal-Roadmap
- Module-tensor-Roadmap
- Module-themis-Roadmap
- Module-timeseries-Roadmap
- Module-toolbox-Roadmap
- Module-training-Roadmap
- Module-transaction-Roadmap
- Module-updates-Roadmap
- Module-user-storage-encrypted-Roadmap
- Module-utils-Roadmap
- Module-vector-search-Roadmap
- Module-voice-Roadmap
- Module-whisper-Roadmap