← All Vector search lessons

Vector search / HANDS-ON LESSON

Filter the corpus before choosing Top K

Keep tenant and version boundaries while retrieving a useful current document.

IntermediateAbout 20 min5 practice questionsReviewed 2026-10-05SaveMyToken editorial

WHAT YOU WILL BUILD

A counterexample showing how filtering only after Top K can leave an empty result.

Ranking and eligibility answer different questions. A passage can score highly and still belong to another tenant or an obsolete version. The exercise compares selecting from eligible records with filtering a globally truncated result.

Before you start

Use a text editor and Node.js 22 or newer ↗. Check your version with node --version. Save the downloaded file in an empty folder, then open a terminal in that folder.

All data is included. No packages, account, or API key are required. This is exact dot-product sorting of three handwritten vectors. It does not implement a vector database, HNSW, or a production authorization system.

Download the lab (.mjs)

FOLLOW ALONG

Work through the example.

  1. 01

    Inspect the candidate pool

    The highest-scoring record belongs to tenant B. Tenant A's old policy is second; its current policy is third. The request is for tenant A's current content only.

  2. 02

    Run the post-filter baseline

    Take the global top result and then check eligibility. The only candidate is rejected, leaving zero results even though a valid current policy exists.

  3. 03

    Run the eligible-corpus search

    Filter by tenant and current version first, then rank. The valid current-policy record is returned. The printed results show why these two orders are not equivalent.

  4. 04

    Carry the boundary into an integration

    When using a database, apply trusted access constraints in its query and check its filtered-search behavior. Test strict filters, no eligible results, deleted records, and current-version changes with representative data.

THE COMPLETE LAB

Run it locally.

Run this command from the folder containing your downloaded file:

node vector-filtering.mjs

View or copy the complete JavaScript
// SaveMyToken local lab. Run with Node.js 22 or newer.
import assert from "node:assert/strict";

// Exact dot-product ranking over toy vectors, not an ANN index.
const query = [1, 0];
const records = [
  { id: "other-tenant", tenant: "B", current: true, vector: [1, 0] },
  { id: "old-policy", tenant: "A", current: false, vector: [0.99, 0.01] },
  { id: "current-policy", tenant: "A", current: true, vector: [0.8, 0.2] },
];
const score = row => row.vector.reduce((sum, x, i) => sum + x * query[i], 0);
const rank = rows => [...rows].sort((a, b) => score(b) - score(a));
const eligible = row => row.tenant === "A" && row.current;
const postFilter = rank(records).slice(0, 1).filter(eligible);
const preFilter = rank(records.filter(eligible)).slice(0, 1);
assert.equal(postFilter.length, 0);
assert.equal(preFilter[0].id, "current-policy");
console.log("Filter after top 1:", postFilter.length, "results");
console.log("Filter before top 1:", preFilter[0].id);

Expected output for the unchanged example

Filter after top 1: 0 results
Filter before top 1: current-policy

The assertions also check the baseline behavior. After changing an input, predict the result and update the relevant assertion.

NOW CHANGE ONE THING

Make the example your own.

Add a second current tenant-A document and test K=2. Then mark both current records false and verify that no result is returned instead of relaxing the access filter.

You are done when…

Every returned record is eligible, and you can explain the empty post-filter result without blaming the similarity metric.

If something goes wrong

Increasing global K may find an eligible record but does not replace access control. Approximate indexes may also have different recall behavior under restrictive filters; measure it.

EXPLAIN WHAT YOU LEARNED

Interview practice

Try answering aloud before opening the reference answer. These are original learning questions, not a record of any employer's interviews.

01Why keep metadata alongside vectors?

Metadata identifies the source, version, tenant or access scope, and other eligibility conditions. The vector alone cannot reliably encode those rules.

Watch for: Semantic similarity is not a permission check.

02Why can filtering after Top K miss valid evidence?

The global shortlist may contain only ineligible records. Filtering that truncated list cannot recover eligible records excluded before filtering.

Watch for: An empty shortlist does not prove the eligible corpus contains no answer.

03How does exact search differ from approximate search?

Exact search considers the full eligible set under the defined metric. Approximate search trades some retrieval accuracy for efficiency through an index or candidate-selection method.

Watch for: This three-record sorting example is not evidence of approximate-index performance.

04How should updated documents be handled?

Associate chunks with a document version, publish a consistent updated set, and remove or exclude superseded chunks. Test retrieval immediately after the change.

Watch for: Adding new vectors while keeping old conflicting passages active can mix policy versions.

05What should you measure when selecting an index?

Measure recall against an exact baseline together with latency, memory, build time, updates, and filtering behavior on representative data.

Watch for: A latency number without corpus size and recall conditions is hard to interpret.

Practice more vector search questions

Sources and further reading

The explanation and local exercises were written for SaveMyToken. These references support the underlying concepts; the sample outputs describe only the supplied examples.

Qdrant: filtering ↗Sentence Transformers: semantic textual similarity ↗