Knowledge Base Metadata Filtering#
Overview#
Metadata filtering is a pre-retrieval stage that narrows which documents are eligible before any vector, keyword, or full-text search is performed. It operates in three modes β disabled, manual, and automatic β and uses a two-stage architecture:
- Stage 1 β SQL filter: evaluate metadata conditions against
DatasetDocument.doc_metadatato produce ametadata_filter_document_idsmap (keyed by dataset ID). - Stage 2 β Constrained search: pass those document IDs as
document_ids_filterintoRetrievalService.retrieve(), restricting vector/keyword search to matching documents only.
The central orchestration method is DatasetRetrieval.get_metadata_filter_condition(), called before both single-dataset and multi-dataset retrieval paths. Its return value β a (metadata_filter_document_ids, metadata_condition) tuple β is then threaded through single_retrieve() and multiple_retrieve().
Filtering Modes#
disabled (default)#
Returns (None, None) immediately with no database queries . No overhead is added to retrieval.
manual#
Conditions are defined statically in the node or app configuration as a MetadataFilteringCondition. At runtime:
- Variable interpolation:
{{variable}}placeholders in string condition values are resolved against workflow inputs by_replace_metadata_filter_value(). In the workflow node, the variable pool performs this resolution earlier viaKnowledgeRetrievalNode._resolve_metadata_filtering_conditions(), which handles both scalar and sequence values. - Logical combination: conditions are combined with
ANDorORbased onlogical_operator.
automatic#
Conditions are derived at request time by an LLM:
- All
DatasetMetadatafield names for the target datasets are queried from the database . - A few-shot prompt (see
template_prompts.py) instructs the LLM to return a{"metadata_map": [...]}JSON structure containingmetadata_field_name,metadata_field_value, andcomparison_operatorfor each extracted filter. - The LLM response is parsed with
parse_and_check_json_markdown(), and only recognized field names are kept . - Auto mode defaults the logical operator to
"or".
The LLM to use for automatic mode is configured separately via metadata_model_config on the node/request.
Data Model#
Core types live in api/core/rag/entities/metadata_entities.py:
| Type | Key fields | Notes |
|---|---|---|
Condition | name, comparison_operator, value | Represents a single filter predicate |
MetadataFilteringCondition | logical_operator ("and" | "or"), conditions |
SupportedComparisonOperator | β | Literal union of all allowed operators |
ConditionValue | β | str | Sequence[str] | int | float | None |
Operator groups :
- String/array:
contains,not contains,start with,end with,is,is not,empty,not empty,in,not in - Numeric:
=,β,>,<,β₯,β€ - Time:
before,after
At the workflow node level, KnowledgeRetrievalNodeData (in entities.py) holds metadata_filtering_mode ("disabled" | "automatic" | "manual"), metadata_model_config, and metadata_filtering_conditions. Note: the conditions field on MetadataFilteringCondition is marked deprecated=True in the Pydantic model but is still actively used at runtime.
SQL Condition Evaluation#
DatasetRetrieval.process_metadata_filter_func() translates each Condition into a SQLAlchemy filter expression against DatasetDocument.doc_metadata, a JSON column. Key behaviors:
- JSON access:
DatasetDocument.doc_metadata[metadata_name].as_string()for text comparisons;.as_float()for numeric and time operators . - String ops (
contains,start with,end with,not contains): useLIKE/NOT LIKEwithescape_like_pattern()to prevent injection . - Equality (
is/=): dispatches on value type β string uses==, numeric uses.as_float() ==. in/not in: accepts a comma-separated string, a list, or a tuple. An empty list short-circuits toliteral(False)(in) orliteral(True)(not in) .empty/not empty: maps toIS NULL/IS NOT NULLon the JSON key .
After all filter expressions are built, the final SQL query restricts to documents that are indexed (indexing_status == "completed"), enabled, and not archived, then applies the combined AND/OR filter . Results are grouped into metadata_filter_document_ids: dict[str, list[str]] keyed by dataset ID.
Two-Stage Retrieval Architecture#
Query / Attachment
β
βΌ
βββββββββββββββββββββββββββββββββββββββββββββββ
β Stage 1 β SQL Metadata Filter β
β get_metadata_filter_condition() β
β β SELECT doc_id FROM DatasetDocument β
β WHERE doc_metadata conditions... β
β β metadata_filter_document_ids β
ββββββββββββββββββββ¬βββββββββββββββββββββββββββ
β
βββββββββββββ΄ββββββββββββββ
β No matching docs? β Has docs?
β Return [] immediately β
βββββββββββββββββββββββββββ
β
βΌ
βββββββββββββββββββββββββββββββββββββββββββββββ
β Stage 2 β Constrained Vector/Keyword Searchβ
β RetrievalService.retrieve( β
β document_ids_filter=doc_ids β
β ) β
β β embedding_search() / keyword_search() β
β / full_text_index_search() β
βββββββββββββββββββββββββββββββββββββββββββββββ
Short-circuit behavior: if any metadata condition is active but produces zero matching document IDs, retrieval returns an empty result immediately β no search query is issued .
External knowledge bases: the metadata_condition object is forwarded verbatim to external dataset providers via ExternalDatasetService.fetch_external_knowledge_retrieval(), letting external backends apply their own filtering logic.
Multi-dataset parallelism: in _multiple_retrieve_thread(), each dataset's allowed document IDs are looked up from metadata_filter_document_ids before spawning per-dataset retrieval threads, so datasets with zero matching documents are skipped entirely .
Key Files and Entry Points#
| File | Purpose |
|---|---|
api/core/rag/entities/metadata_entities.py | Condition, MetadataFilteringCondition, SupportedComparisonOperator, ConditionValue β the core data model |
api/core/rag/retrieval/dataset_retrieval.py | DatasetRetrieval β orchestration: get_metadata_filter_condition(), process_metadata_filter_func(), _automatic_metadata_filter_func() |
api/core/rag/retrieval/template_prompts.py | Few-shot prompt templates for automatic LLM-based metadata extraction |
api/core/rag/datasource/retrieval_service.py | RetrievalService β executes constrained vector/keyword search using document_ids_filter |
api/core/workflow/nodes/knowledge_retrieval/knowledge_retrieval_node.py | KnowledgeRetrievalNode β workflow node that resolves variable references in conditions and dispatches to DatasetRetrieval |
api/core/workflow/nodes/knowledge_retrieval/entities.py | KnowledgeRetrievalNodeData β node config including metadata_filtering_mode, metadata_model_config, metadata_filtering_conditions |
Call path (workflow):
KnowledgeRetrievalNode._run() β _fetch_dataset_retriever() β DatasetRetrieval.knowledge_retrieval() β get_metadata_filter_condition() β single_retrieve() or multiple_retrieve() β RetrievalService.retrieve().