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

# Search Guide

> Vector graph search in Nebula

Nebula searches over a vector graph of entities and relationships, not raw text chunks. You send a natural language query; Nebula traverses the graph and returns structured memory: **semantics** (facts and inferences), **procedures** (preferences and habits), and **episodes** (temporal event clusters). Set `include_sources` to return the original text grounding those results.

## Basic Search

<CodeGroup>
  ```python Python theme={null}
  from nebula import Nebula

  nebula = Nebula()

  results = nebula.memories.search(
      query="What has Sarah worked on?",
      collection_ids=["engineering"],
      include_sources=True,
  ).results

  # Semantic: facts, inferences, and tasks (compact-mode default)
  for fact in results.semantic or []:
      print(f"[{fact['category']}] {fact['description']}")
      print(f"  Score: {fact['activation_score']:.2f}")

  # Procedural: user preferences and habits
  for proc in results.procedural or []:
      print(f"{proc['statement']} (confidence: {proc['confidence']:.2f})")

  # Sources: original source text grounding the semantics
  for source in results.sources or []:
      print(f"[{source.get('speaker', 'unknown')}]: {source['text']}")
  ```

  ```javascript JavaScript theme={null}
  import Nebula from '@nebula-ai/sdk';
  const client = new Nebula({ apiKey: process.env.NEBULA_API_KEY });

  const { results } = await client.memories.search({
    query: 'What has Sarah worked on?',
    collection_ids: ['engineering'],
    include_sources: true,
  });

  // Semantic (compact-mode default)
  results.semantic?.forEach(f =>
    console.log(`[${f.category}] ${f.description}`)
  );

  // Procedural
  results.procedural?.forEach(p =>
    console.log(`${p.statement} (confidence: ${p.confidence})`)
  );

  // Sources
  results.sources?.forEach(u =>
    console.log(`[${u.speaker ?? u.source_role ?? 'unknown'}]: ${u.text}`)
  );
  ```

  ```bash cURL theme={null}
  curl -X POST "https://api.zeroset.com/v1/memories/search" \
    -H "Content-Type: application/json" \
    -H "Authorization: Bearer YOUR_API_KEY" \
    -d '{
      "query": "What has Sarah worked on?",
      "collection_ids": ["engineering"],
      "include_sources": true
    }'
  ```
</CodeGroup>

## Understanding Results

Search returns a `MemoryResponse` with three memory layers plus optional sources:

| Layer          | Contains                                                                          | Example                                                            |
| -------------- | --------------------------------------------------------------------------------- | ------------------------------------------------------------------ |
| **Semantics**  | Subject-predicate-value triples (facts, inferences, tasks)                        | `Sarah Chen → led → Aurora migration`                              |
| **Procedures** | User preferences and behavioral patterns                                          | `Prefers PostgreSQL over MySQL`                                    |
| **Episodes**   | Temporally clustered events                                                       | `Q4 database migration project`                                    |
| **Sources**    | Original source text that grounds the semantics when `include_sources` is enabled | `"Sarah led the migration from PostgreSQL to Aurora last quarter"` |

Each item has an `activation_score` (0-1) reflecting its relevance to the query. With `include_sources` enabled, semantics link back to source text for a full provenance chain from structured assertions to original content.

Semantic and procedural results may include `memory_class`, such as `preference`, `identity`, `strategy`, `trace`, or `workflow_pattern`, to distinguish the kind of memory returned.

<Info>The vector graph is built automatically when you store memories. You don't need to define entities or relationships - Nebula extracts them.</Info>

## Search Effort

The `effort` parameter is the primary control for search. It determines how deeply Nebula traverses the vector graph.

| Effort   | Depth           | Use Case                                      |
| -------- | --------------- | --------------------------------------------- |
| `auto`   | Adapts to query | Default - good for most queries               |
| `low`    | 2 hops, narrow  | Fast lookups, simple factual queries          |
| `medium` | 2 hops, wider   | Broader exploration across more relationships |
| `high`   | 3 hops, widest  | Deep multi-hop reasoning across the graph     |

<CodeGroup>
  ```python Python theme={null}
  # Quick factual lookup
  results = nebula.memories.search(query="Sarah's role", effort="low").results

  # Deep exploration
  results = nebula.memories.search(
      query="How are the Q4 projects connected?",
      effort="high",
  ).results
  ```

  ```javascript JavaScript theme={null}
  // Quick factual lookup
  const { results } = await client.memories.search({
    query: "Sarah's role",
    effort: 'low',
  });

  // Deep exploration
  const { results: deep } = await client.memories.search({
    query: 'How are the Q4 projects connected?',
    effort: 'high',
  });
  ```

  ```bash cURL theme={null}
  curl -X POST "https://api.zeroset.com/v1/memories/search" \
    -H "Content-Type: application/json" \
    -H "Authorization: Bearer YOUR_API_KEY" \
    -d '{
      "query": "How are the Q4 projects connected?",
      "effort": "high"
    }'
  ```
</CodeGroup>

<Tip>Start with the default (`auto`). Only increase effort if you need the graph to discover connections further from the query.</Tip>

## Scoping with Collections

Use `collection_ids` to scope which part of the vector graph is searched.

<CodeGroup>
  ```python Python theme={null}
  # Search specific collections
  results = nebula.memories.search(
      query="customer feedback",
      collection_ids=["support-2024", "support-2023", "surveys"],
  ).results

  # Search all accessible collections (omit collection_ids)
  results = nebula.memories.search(query="product roadmap").results
  ```

  ```javascript JavaScript theme={null}
  const { results } = await client.memories.search({
    query: 'customer feedback',
    collection_ids: ['support-2024', 'support-2023', 'surveys'],
  });

  // Search all accessible collections
  const { results: all } = await client.memories.search({ query: 'product roadmap' });
  ```

  ```bash cURL theme={null}
  curl -X POST "https://api.zeroset.com/v1/memories/search" \
    -H "Content-Type: application/json" \
    -H "Authorization: Bearer YOUR_API_KEY" \
    -d '{
      "query": "customer feedback",
      "collection_ids": ["support-2024", "support-2023", "surveys"]
    }'
  ```
</CodeGroup>

<Warning>Searching all collections is slower than targeting specific ones.</Warning>

## Authority Scores

Authority is a store-time parameter on conversation messages that tells Nebula how much to trust a piece of content. It's stored per-message, not per-memory.

```python theme={null}
support = nebula.collections.retrieve_by_name("support").results

# Verified answer from support agent
conv = nebula.memories.create(
    collection_id=support.id,
    kind="conversation",
    messages=[{
        "content": "Here is the verified answer...",
        "role": "assistant",
        "authority": 0.9,
    }],
).results
conv_id = conv.id

# Uncertain user message
nebula.memories.append(
    conv_id,
    collection_id=support.id,
    messages=[{
        "content": "I'm not sure, maybe try this?",
        "role": "user",
        "authority": 0.4,
    }],
)
```

| Score     | Use Case                         |
| --------- | -------------------------------- |
| `0.9-1.0` | Verified facts, official sources |
| `0.5-0.7` | General content (default: 0.5)   |
| `0.0-0.3` | Low confidence or uncertain      |

## Advanced Options

### Hybrid Search Weights

Control the balance between semantic and full-text matching for seed discovery (how the graph traversal finds its starting points):

<CodeGroup>
  ```python Python theme={null}
  results = nebula.memories.search(
      query="neural networks",
      collection_ids=["research"],
      search_settings={
          "semantic_weight": 0.7,
          "fulltext_weight": 0.3,
      },
  ).results
  ```

  ```javascript JavaScript theme={null}
  const { results } = await client.memories.search({
    query: 'neural networks',
    collection_ids: ['research'],
    search_settings: {
      semantic_weight: 0.7,
      fulltext_weight: 0.3,
    },
  });
  ```

  ```bash cURL theme={null}
  curl -X POST "https://api.zeroset.com/v1/memories/search" \
    -H "Content-Type: application/json" \
    -H "Authorization: Bearer YOUR_API_KEY" \
    -d '{
      "query": "neural networks",
      "collection_ids": ["research"],
      "search_settings": {
        "semantic_weight": 0.7,
        "fulltext_weight": 0.3
      }
    }'
  ```
</CodeGroup>

Defaults (0.8 semantic, 0.2 fulltext) work well for most cases. Lower semantic weight if you need exact keyword matching.

### Metadata Filters

Optionally narrow the search scope using metadata constraints. Filters restrict which part of the graph is entered - the graph still handles discovery within that scope.

```python theme={null}
results = nebula.memories.search(
    query="project updates",
    collection_ids=["work"],
    filters={
        "status": "active",
        "team": "engineering",
    },
).results
```

**Operators:** `$eq`, `$ne`, `$in`, `$nin`, `$gt`, `$gte`, `$lt`, `$lte`, `$like`, `$ilike`, `$overlap`, `$contains`, `$and`, `$or`

See [Metadata Filtering](/guides/metadata-filtering) for the full reference.

## Next Steps

* [Metadata Filtering](/guides/metadata-filtering) - Filter operator reference
* [Memory Operations](/guides/memory-operations) - Store and manage memories
