> ## Documentation Index
> Fetch the complete documentation index at: https://cortex-e852fafe-t3code-rewrite-docs-declutter.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Semantic Search & Retrieval

> How semantic, BM25 keyword, graph, and metadata signals work together in a HydraDB query.

Semantic search matches by meaning: "time off" can find a document about "annual leave." Keyword search matches the words themselves, which helps with an error code such as `E_AUTH_429`. HydraDB combines both by default; this is called **hybrid search**. BM25 is the keyword-ranking method used in that combination.

***

## Semantic vs keyword search

| Query Type | Finds | Best For |
| - | - | - |
| Semantic | Text with similar meaning | Natural-language questions, paraphrases, conceptual lookup |
| BM25 keyword | Text with matching tokens | Error codes, identifiers, names, SKUs, exact phrases |
| Graph | Related entities and relationships | Multi-hop questions, dependencies, ownership, project context |

`POST /query` is the unified retrieval endpoint. Two parameters decide what runs:

* **`type`** picks the store: `"knowledge"` (Knowledge), `"memory"` (user-scoped Memories), or `"all"` (both from the same scope, merged and re-ranked together).
* **`query_by`** picks the retrieval method: `"hybrid"` (semantic + BM25, the default) or `"text"` (BM25 only, with `operator: "or" | "and" | "phrase"`, default `"or"`).

***

## Why pure semantic search breaks

Pure vector search can miss important production constraints:

* Exact identifiers such as `E_AUTH_429` or `payments-worker-v4` may be generalized away.
* A project name can collide with a normal word, like `strawberry` the project vs strawberry the fruit.
* Old and new documents can look equally relevant without recency or metadata signals.
* Different users can need different context for the same query.
* Relationship questions need graph context, not only similar text chunks.

That is why HydraDB exposes semantic retrieval through `query_by: "hybrid"` inside the unified `/query` endpoint rather than as a separate pure-vector mode.

***

## The `alpha` parameter

`alpha` controls the semantic versus BM25 keyword blend when `query_by: "hybrid"`. Higher values lean semantic; lower values lean on keywords.

| `alpha` | Behavior | Use When |
| - | - | - |
| `1.0` | Semantic-heavy | Conceptual questions and paraphrases |
| `0.8` | Default semantic-leaning hybrid | Most agent query workflows |
| `0.5` | Balanced | Mixed natural language and exact terms |
| `0.2` | Keyword-leaning | Queries with product names, error strings, or IDs |
| `0.0` | Keyword only | Debugging exact-match behavior |

Start with the API default (`0.8`; `"auto"` also resolves to `0.8`) and tune from observed results. If users query for exact IDs and get loosely related content, lower `alpha`. If they ask broad conceptual questions and get sparse results, raise it. `alpha` applies only to `query_by: "hybrid"`; it is ignored for `"text"`.

***

## Query request example

<CodeGroup>
  ```bash cURL theme={"dark"}
  curl -X POST 'https://api.hydradb.com/query' \
    -H "Authorization: Bearer $HYDRA_DB_API_KEY" \
    -H "API-Version: 2" \
    -H "Content-Type: application/json" \
    -d '{
      "database": "acme",
      "collection": "team-mobile",
      "query": "How do we rotate API keys?",
      "type": "knowledge",
      "query_by": "hybrid",
      "max_results": 8,
      "alpha": 0.8,
      "recency_bias": 0.2,
      "graph_context": true,
      "metadata_filters": {
        "project": "phoenix"
      }
    }'
  ```

  ```typescript TypeScript SDK theme={"dark"}
  const result = await client.query({
    database: "acme",
    collection: "team-mobile",
    query: "How do we rotate API keys?",
    type: "knowledge",
    queryBy: "hybrid",
    maxResults: 8,
    alpha: 0.8,
    recencyBias: 0.2,
    graphContext: true,
    metadataFilters: { project: "phoenix" },
  });
  ```

  ```python Python SDK theme={"dark"}
  result = client.query(
      database="acme",
      collection="team-mobile",
      query="How do we rotate API keys?",
      type="knowledge",
      query_by="hybrid",
      max_results=8,
      alpha=0.8,
      recency_bias=0.2,
      graph_context=True,
      metadata_filters={"project": "phoenix"},
  )
  ```
</CodeGroup>

`metadata_filters` are exact constraints that run before ranking and are re-checked after the matching passages are loaded. Use them whenever the query has a scope that should not be violated. Top-level keys match `metadata` and support `equals`, `contains`, and `contains_any` (no range or fuzzy match); nest under `additional_metadata` to filter free-form per-document fields. The example above uses one to keep retrieval inside the `phoenix` project.

***

## More recipes

### Technical lookup

Lower `alpha` when names, IDs, and literal strings matter.

<CodeGroup>
  ```bash cURL theme={"dark"}
  curl -X POST 'https://api.hydradb.com/query' \
    -H "Authorization: Bearer $HYDRA_DB_API_KEY" \
    -H "API-Version: 2" \
    -H "Content-Type: application/json" \
    -d '{
      "database": "acme",
      "query": "TimeoutError in payments-worker v4.2.1",
      "type": "knowledge",
      "query_by": "hybrid",
      "max_results": 5,
      "alpha": 0.3,
      "recency_bias": 0.4
    }'
  ```

  ```typescript TypeScript SDK theme={"dark"}
  const result = await client.query({
    database: "acme",
    query: "TimeoutError in payments-worker v4.2.1",
    type: "knowledge",
    queryBy: "hybrid",
    maxResults: 5,
    alpha: 0.3,
    recencyBias: 0.4,
  });
  ```

  ```python Python SDK theme={"dark"}
  result = client.query(
      database="acme",
      query="TimeoutError in payments-worker v4.2.1",
      type="knowledge",
      query_by="hybrid",
      max_results=5,
      alpha=0.3,
      recency_bias=0.4,
  )
  ```
</CodeGroup>

### Exact phrase query

Switch to `query_by: "text"` with `operator: "phrase"` when a literal match is the point of the query.

<CodeGroup>
  ```bash cURL theme={"dark"}
  curl -X POST 'https://api.hydradb.com/query' \
    -H "Authorization: Bearer $HYDRA_DB_API_KEY" \
    -H "API-Version: 2" \
    -H "Content-Type: application/json" \
    -d '{
      "database": "acme",
      "query": "mechanical engineer",
      "type": "knowledge",
      "query_by": "text",
      "operator": "phrase",
      "max_results": 10
    }'
  ```

  ```typescript TypeScript SDK theme={"dark"}
  const result = await client.query({
    database: "acme",
    query: "mechanical engineer",
    type: "knowledge",
    queryBy: "text",
    operator: "phrase",
    maxResults: 10,
  });
  ```

  ```python Python SDK theme={"dark"}
  result = client.query(
      database="acme",
      query="mechanical engineer",
      type="knowledge",
      query_by="text",
      operator="phrase",
      max_results=10,
  )
  ```
</CodeGroup>

***

## Reading the response

`POST /query` returns ranked chunks and source metadata, not an answer. A typical application flow is:

1. Call `POST /query` with the right `type` and `query_by` for the query.
2. Keep the chunks that are relevant enough for your use case.
3. Format `chunk_content`, source titles, and graph context into a prompt.
4. Ask your LLM to answer using only that context.

See [How to Use API Results](/essentials/v2/api-results) for complete context-building examples.

***

## Related

* [Query](/essentials/v2/query): full parameter reference and parallel query patterns
* [Context Graphs](/essentials/v2/context-graphs): how graph context enriches retrieval
* [Metadata](/essentials/v2/metadata): designing filterable schemas
* [How to Use API Results](/essentials/v2/api-results): turning the response into an LLM prompt


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.