RAG 4 min read

RAG Metadata Prefiltering: Permissions, Dates and Recall

Compare RAG metadata filtering before and after top-k retrieval. Test permissions, missing dates and eligible recall with a labeled synthetic document set.

(Updated: ) • 609 words

RAG metadata prefiltering limits which documents are eligible before ranking. It can enforce a declared source or date policy, but a bad filter can remove the relevant document before similarity search sees it. Compare that loss explicitly instead of promising a fixed accuracy improvement or token saving.

Separate authorization from a relevance preference

A permission rule is mandatory: an unauthorized document must not enter the answer context. A publication-date preference is task-dependent. Do not label extraction time as publication time, and do not treat a missing date as proof that a document is old. The example rejects unknown dates from this narrowly defined recent-source task; a production policy should record them for review.

Metadata Failure mode Handling
Permission list Stale or user-supplied labels allow a forbidden source Enforce trusted authorization at the storage/retrieval boundary
Publication date Missing, malformed or timezone-free value Apply an explicit policy and log unknowns
Source identity Repeated or changed URL Keep a stable record and provenance
Retrieval time Confused with publication time Use a separate UTC field

A synthetic comparison of filter order

The code sorts a small labeled fixture by a supplied score. It is not a vector-database implementation or a production retrieval benchmark. Use it to inspect the difference between filtering the candidate set before top-k and filtering only the already selected top-k. All dates in the fixture are timezone-aware.

from datetime import datetime, timezone

def eligible(document, principal, earliest):
    raw = document.get("published_at")
    if not isinstance(raw, str):
        return False
    try:
        stamp = datetime.fromisoformat(raw.replace("Z", "+00:00"))
    except ValueError:
        return False
    if stamp.tzinfo is None or earliest.tzinfo is None:
        return False
    return principal in document.get("allowed_principals", []) and stamp >= earliest

def compare_filter_order(documents, principal, earliest, k=2):
    ranked = sorted(documents, key=lambda d:d["score"], reverse=True)
    pre = [d for d in ranked if eligible(d, principal, earliest)][:k]
    post = [d for d in ranked[:k] if eligible(d, principal, earliest)]
    eligible_relevant = {d["id"] for d in documents if d["relevant"]
                         and eligible(d, principal, earliest)}
    def report(rows):
        kept = {d["id"] for d in rows}
        return {"kept":sorted(kept), "missed_eligible_relevant":sorted(eligible_relevant-kept)}
    return {"prefilter":report(pre), "postfilter":report(post),
            "observed_at":datetime.now(timezone.utc).isoformat()}

What the failure example should reveal

Construct four records: a high-scoring unauthorized document, a recent permitted relevant document, an older permitted relevant document and a recent permitted irrelevant document. With top-k equal to two, postfiltering can lose a permitted result because an unauthorized candidate consumed a ranking slot. A strict recent-date rule also removes the older relevant document regardless of how good its score is.

Keep two denominators distinct. Recall among eligible relevant documents evaluates the retrieval pipeline under the chosen policy. Recall among all relevant documents reveals what the policy itself excluded. Reporting only the first can hide an over-restrictive date filter. Neither synthetic measure establishes a production precision gain.

Apply the filter where permissions are trusted

The Python predicate is a readable fixture, not an access-control boundary. Apply authorization in the database or service that returns candidates, using trusted identities and metadata. Do not send unauthorized bodies to the model and ask it to ignore them. Use a labeled permitted query set to test the real store’s filtering behavior, including missing and conflicting metadata.

The old SERP collection example is intentionally removed from this filtering article. the ingestion acceptance workflow owns source collection and validation; the search indexing workflow owns indexing mechanics. the live RAG architecture connects those stages. This page owns filter policy and its measurable false exclusions.

Tags:

RAG LLM Tutorial API Development Integration
SearchCans Team

SearchCans Team

SERP API & Reader API Experts

The SearchCans engineering team builds high-performance search APIs serving developers worldwide. We share practical tutorials, best practices, and insights on SERP data, web scraping, RAG pipelines, and AI integration.

Ready to build with SearchCans?

Test SERP API and Reader API with 100 free credits. No credit card required.