> ## Documentation Index
> Fetch the complete documentation index at: https://docs.helix-db.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Filtering

> Narrow traversal streams with predicates and exact search candidate sets

<div className="flex flex-wrap gap-2"><Badge color="blue" size="sm">Guide</Badge></div>

Use a source predicate to select the initial candidates and `.where(...)` for
expression-based filtering later in a traversal. Vector and full-text search
prefiltering rank only an exact traversal-defined candidate set.

## Common predicates

| Intent              | Builder                      |
| ------------------- | ---------------------------- |
| Equality            | `eq(property, value)`        |
| Comparison          | `gt`, `gte`, `lt`, `lte`     |
| Inclusive range     | `between`                    |
| Set membership      | `isIn` / `is_in`             |
| Property exists     | `hasKey` / `has_key`         |
| Prefix              | `startsWith` / `starts_with` |
| Boolean composition | `and`, `or`, `not`           |

Values can be literals or typed parameter expressions.

## Filter a stream

<CodeGroup>
  ```rust Rust theme={"languages":{"custom":["languages/helixql.json"]}}
  g()
      .n_with_label("User")
      .where_(Predicate::gte("score", 100))
      .value_map(Some(vec!["$id", "name", "score"]))
  ```

  ```ts TypeScript theme={"languages":{"custom":["languages/helixql.json"]}}
  import { Predicate, g } from "@helix-db/helix-db";

  g()
    .nWithLabel("User")
    .where(Predicate.gte("score", 100))
    .valueMap(["$id", "name", "score"])
  ```

  ```go Go theme={"languages":{"custom":["languages/helixql.json"]}}
  helix.G().
  	NWithLabel("User").
  	Where(helix.PredGte("score", int64(100))).
  	ValueMap("$id", "name", "score")
  ```

  ```python Python theme={"languages":{"custom":["languages/helixql.json"]}}
  (
      g()
      .n_with_label("User")
      .where(Predicate.gte("score", 100))
      .value_map(["$id", "name", "score"])
  )
  ```

  ```json JSON theme={"languages":{"custom":["languages/helixql.json"]}}
  {
    "request_type": "read",
    "query_name": "ranked_users",
    "query": {
      "read": {
        "entries": [{
          "query": {
            "name": "users",
            "root": {
              "value_map": {
                "input": {
                  "where": {
                    "input": {
                      "nodes_where": {
                        "predicate": {
                          "eq": {
                            "left": { "property": "$label" },
                            "right": { "constant": { "string": "User" } }
                          }
                        }
                      }
                    },
                    "predicate": {
                      "gte": {
                        "left": { "property": "score" },
                        "right": { "constant": { "i64": 100 } }
                      }
                    }
                  }
                },
                "properties": ["$id", "name", "score"]
              }
            }
          }
        }],
        "returns": ["users"]
      }
    }
  }
  ```
</CodeGroup>

## Vector prefiltering

Vector prefiltering starts with a node or edge traversal, then ranks only the exact
members of that stream. Use it when graph membership is a correctness boundary, such
as “documents this user may access” or “products reachable from this category.”

The execution order is **graph traversal → exact candidate membership → vector
ranking → top k**. The traversal membership is authoritative. Approximate index
structures may accelerate ranking, but a result outside the candidate set cannot be
returned.

### Rank a node stream

This request finds projects owned by the current user, ranks that exact set by
embedding distance, and returns the top five.

<Note>
  This query requires an active three-dimensional cosine vector index on
  `Project.embedding`. Create and activate that index before running the request.
</Note>

<CodeGroup>
  ```rust Rust theme={"languages":{"custom":["languages/helixql.json"]}}
  use helix_db::dsl::prelude::*;

  #[query]
  fn owned_project_matches(
      username: String,
      query_vector: Vec<f32>,
      limit: i64,
  ) -> ReadBatch {
      read_batch()
          .var_as(
              "matches",
              g()
                  .n_with_label_where("User", SourcePredicate::eq("username", username))
                  .out(Some("OWNS"))
                  .vector_search_with("Project", "embedding", query_vector, limit, None)
                  .value_map(Some(vec!["$id", "name", "$distance"])),
          )
          .returning(["matches"])
  }

  let request = owned_project_matches(
      "alice".to_string(),
      vec![1.0f32, 0.0, 0.0],
      5,
  )?;
  ```

  ```ts TypeScript theme={"languages":{"custom":["languages/helixql.json"]}}
  import {
    SourcePredicate,
    defineParams,
    g,
    param,
    readBatch,
  } from "@helix-db/helix-db";

  const params = defineParams({
    username: param.string(),
    query_vector: param.array(param.f32()),
    limit: param.i64(),
  });

  const query = readBatch()
    .varAs(
      "matches",
      g()
        .nWithLabelWhere("User", SourcePredicate.eq("username", params.username))
        .out("OWNS")
        .vectorSearchWith("Project", "embedding", params.query_vector, params.limit)
        .valueMap(["$id", "name", "$distance"]),
    )
    .returning(["matches"]);

  const request = query.toQueryRequest(
    params,
    { username: "alice", query_vector: [1, 0, 0], limit: 5n },
    { queryName: "owned_project_matches" },
  );
  ```

  ```go Go theme={"languages":{"custom":["languages/helixql.json"]}}
  q := helix.ReadQuery("owned_project_matches")
  username := q.ParamString("username", "alice")
  queryVector := q.ParamArray(
  	"query_vector",
  	[]float32{1, 0, 0},
  	helix.ParamTypeF32(),
  )
  limit := q.ParamI64("limit", 5)

  request := q.
  	VarAs(
  		"matches",
  		helix.G().
  			NWithLabelWhere("User", helix.SourceEq("username", username)).
  			Out("OWNS").
  			VectorSearchNodesWithin("Project", "embedding", queryVector, limit).
  			ValueMap("$id", "name", "$distance"),
  	).
  	Returning("matches")
  ```

  ```python Python theme={"languages":{"custom":["languages/helixql.json"]}}
  from helixdb import SourcePredicate, define_params, g, param, read_batch

  params = define_params({
      "username": param.string(),
      "query_vector": param.array(param.f32()),
      "limit": param.i64(),
  })

  query = (
      read_batch()
      .var_as(
          "matches",
          g()
          .n_with_label_where(
              "User", SourcePredicate.eq("username", params.username)
          )
          .out("OWNS")
          .vector_search_with(
              "Project", "embedding", params.query_vector, params.limit
          )
          .value_map(["$id", "name", "$distance"]),
      )
      .returning(["matches"])
  )

  request = query.to_query_request(
      params,
      {
          "username": "alice",
          "query_vector": [1.0, 0.0, 0.0],
          "limit": 5,
      },
      query_name="owned_project_matches",
  )
  ```

  ```json JSON theme={"languages":{"custom":["languages/helixql.json"]}}
  {
    "request_type": "read",
    "query_name": "owned_project_matches",
    "query": {
      "read": {
        "entries": [{
          "query": {
            "name": "matches",
            "root": {
              "value_map": {
                "input": {
                  "vector_search_nodes_within": {
                    "input": {
                      "out": {
                        "input": {
                          "nodes_where": {
                            "predicate": {
                              "and": {
                                "predicates": [
                                  {
                                    "eq": {
                                      "left": { "property": "$label" },
                                      "right": { "constant": { "string": "User" } }
                                    }
                                  },
                                  {
                                    "eq": {
                                      "left": { "property": "username" },
                                      "right": { "param": "username" }
                                    }
                                  }
                                ]
                              }
                            }
                          }
                        },
                        "label": "OWNS"
                      }
                    },
                    "label": "Project",
                    "property": "embedding",
                    "query_vector": { "expr": { "param": "query_vector" } },
                    "k": { "expr": { "param": "limit" } }
                  }
                },
                "properties": ["$id", "name", "$distance"]
              }
            }
          }
        }],
        "returns": ["matches"]
      }
    },
    "parameters": {
      "username": "alice",
      "query_vector": [1, 0, 0],
      "limit": 5
    },
    "parameter_types": {
      "username": "string",
      "query_vector": { "array": "f32" },
      "limit": "i64"
    }
  }
  ```
</CodeGroup>

Use `VectorSearchEdgesWithin` in Go after an edge traversal. Rust
`vector_search_with`, TypeScript `vectorSearchWith`, and Python
`vector_search_with` select the node or edge wire operation from the current stream.
Their literal forms are `vector_search` and `vectorSearch`.

### Requirements

* Create a compatible vector index for the candidate label and property.
* Match the index dimension exactly.
* Use the same tenant partition value as the index when it is tenant-partitioned.
* Preserve `$distance` in a projection before traversing away from a ranked hit.
* Bound the candidate traversal when its size can grow without application limits.

<Note>
  Exact membership does not mean the vector engine exhaustively compares every
  candidate embedding. It means the final result is checked against the exact traversal
  set.
</Note>

### When to search without a prefilter

Use a source vector search when the whole indexed label and optional tenant partition
is the intended candidate set. Use vector prefiltering when relationships,
permissions, or earlier filters define membership.

## Full Text Search prefiltering

Full-text search (FTS) prefiltering starts with a node or edge traversal, then ranks
only the unique IDs in that stream by BM25 score. Use it when relationships,
permissions, or earlier predicates define which records are eligible for text search.

The execution order is **graph traversal → exact candidate membership → BM25 ranking
→ top k**. Results are identical to an exhaustive BM25 search of the selected tenant
partition, intersected with the candidate IDs, followed by deterministic top-k
selection. BM25 statistics still come from the full tenant partition.

### Rank a node stream

This request finds documents the current user can read, ranks that exact set for
`"graph databases"`, and returns the top five.

<Note>
  This query requires an active text index on `Document.body`. Create and activate that
  index before running the request.
</Note>

<CodeGroup>
  ```rust Rust theme={"languages":{"custom":["languages/helixql.json"]}}
  use helix_db::dsl::prelude::*;

  #[query]
  fn readable_document_matches(
      username: String,
      query_text: String,
      limit: i64,
  ) -> ReadBatch {
      read_batch()
          .var_as(
              "matches",
              g()
                  .n_with_label_where("User", SourcePredicate::eq("username", username))
                  .out(Some("CAN_READ"))
                  .text_search_with("Document", "body", query_text, limit, None)
                  .value_map(Some(vec!["$id", "title", "$score"])),
          )
          .returning(["matches"])
  }

  let request = readable_document_matches(
      "alice".to_string(),
      "graph databases".to_string(),
      5,
  )?;
  ```

  ```ts TypeScript theme={"languages":{"custom":["languages/helixql.json"]}}
  import {
    SourcePredicate,
    defineParams,
    g,
    param,
    readBatch,
  } from "@helix-db/helix-db";

  const params = defineParams({
    username: param.string(),
    query_text: param.string(),
    limit: param.i64(),
  });

  const query = readBatch()
    .varAs(
      "matches",
      g()
        .nWithLabelWhere("User", SourcePredicate.eq("username", params.username))
        .out("CAN_READ")
        .textSearchWith("Document", "body", params.query_text, params.limit)
        .valueMap(["$id", "title", "$score"]),
    )
    .returning(["matches"]);

  const request = query.toQueryRequest(
    params,
    { username: "alice", query_text: "graph databases", limit: 5n },
    { queryName: "readable_document_matches" },
  );
  ```

  ```go Go theme={"languages":{"custom":["languages/helixql.json"]}}
  q := helix.ReadQuery("readable_document_matches")
  username := q.ParamString("username", "alice")
  queryText := q.ParamString("query_text", "graph databases")
  limit := q.ParamI64("limit", 5)

  request := q.
  	VarAs(
  		"matches",
  		helix.G().
  			NWithLabelWhere("User", helix.SourceEq("username", username)).
  			Out("CAN_READ").
  			TextSearchNodesWithin("Document", "body", queryText, limit).
  			ValueMap("$id", "title", "$score"),
  	).
  	Returning("matches")
  ```

  ```python Python theme={"languages":{"custom":["languages/helixql.json"]}}
  from helixdb import SourcePredicate, define_params, g, param, read_batch

  params = define_params({
      "username": param.string(),
      "query_text": param.string(),
      "limit": param.i64(),
  })

  query = (
      read_batch()
      .var_as(
          "matches",
          g()
          .n_with_label_where(
              "User", SourcePredicate.eq("username", params.username)
          )
          .out("CAN_READ")
          .text_search_with(
              "Document", "body", params.query_text, params.limit
          )
          .value_map(["$id", "title", "$score"]),
      )
      .returning(["matches"])
  )

  request = query.to_query_request(
      params,
      {
          "username": "alice",
          "query_text": "graph databases",
          "limit": 5,
      },
      query_name="readable_document_matches",
  )
  ```

  ```json JSON theme={"languages":{"custom":["languages/helixql.json"]}}
  {
    "request_type": "read",
    "query_name": "readable_document_matches",
    "query": {
      "read": {
        "entries": [{
          "query": {
            "name": "matches",
            "root": {
              "value_map": {
                "input": {
                  "text_search_nodes_within": {
                    "input": {
                      "out": {
                        "input": {
                          "nodes_where": {
                            "predicate": {
                              "and": {
                                "predicates": [
                                  {
                                    "eq": {
                                      "left": { "property": "$label" },
                                      "right": { "constant": { "string": "User" } }
                                    }
                                  },
                                  {
                                    "eq": {
                                      "left": { "property": "username" },
                                      "right": { "param": "username" }
                                    }
                                  }
                                ]
                              }
                            }
                          }
                        },
                        "label": "CAN_READ"
                      }
                    },
                    "label": "Document",
                    "property": "body",
                    "query_text": { "expr": { "param": "query_text" } },
                    "k": { "expr": { "param": "limit" } }
                  }
                },
                "properties": ["$id", "title", "$score"]
              }
            }
          }
        }],
        "returns": ["matches"]
      }
    },
    "parameters": {
      "username": "alice",
      "query_text": "graph databases",
      "limit": 5
    },
    "parameter_types": {
      "username": "string",
      "query_text": "string",
      "limit": "i64"
    }
  }
  ```
</CodeGroup>

Use `TextSearchEdgesWithin` in Go after an edge traversal. Rust
`text_search_with`, TypeScript `textSearchWith`, and Python `text_search_with`
select the node or edge wire operation from the current stream. Their literal forms
are `text_search` and `textSearch`.

### Result contract

* Output IDs are a deduplicated subset of the input IDs.
* The result contains at most `min(unique candidates, k)` rows.
* Rows are ordered by BM25 score descending, then entity ID ascending.
* The selected input row keeps its bindings, path, and sack; `$score` is attached.
* Empty input returns without opening the text index.
* A wrong-kind input or more than 1,000,000 unique candidates is a query error.
* A tenant-partitioned index requires the same tenant value used to build the
  candidate stream.

### When to search without a prefilter

Use a source text search when the whole indexed label and optional tenant partition
is the intended candidate set. Do not implement exact FTS filtering as source text
search followed by `.where(...)`: excluded high-scoring hits can consume the source
top-k and leave fewer than `k` eligible results. Build the candidate stream first and
use FTS prefiltering when membership is authoritative.

## Next steps

<CardGroup cols={2}>
  <Card title="Typed parameters" icon="sliders" href="/database/helix-db/query-guides/parameters">
    Move request-specific filter values out of the AST.
  </Card>

  <Card title="Indexes" icon="gauge-high" href="/database/helix-db/query-guides/secondary-indexes">
    Back equality and range predicates with an index.
  </Card>

  <Card title="Vector indexes" icon="circle-nodes" href="/database/helix-db/query-guides/vector-indexes">
    Create the dimensioned index used for ranking.
  </Card>

  <Card title="Text indexes" icon="align-left" href="/database/helix-db/query-guides/text-indexes">
    Create the BM25 index used for full-text ranking.
  </Card>

  <Card title="Project search results" icon="table-columns" href="/database/helix-db/query-guides/projections">
    Preserve ranked hit metadata before continuing a traversal.
  </Card>
</CardGroup>
