Skip to main content
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), episodes (temporal event clusters), and sources (the original text).
from nebula import Nebula, Memory

nebula = Nebula()

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

# Semantics: structured subject-predicate-value triples
for fact in results.semantics:
    print(f"{fact['subject']}{fact['predicate']}{fact['value']}")
    print(f"  Score: {fact['activation_score']:.2f}")

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

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

Understanding Results

Search returns a MemoryResponse with four layers of memory:
LayerContainsExample
SemanticsSubject-predicate-value triples (facts, inferences, tasks)Sarah Chen → led → Aurora migration
ProceduresUser preferences and behavioral patternsPrefers PostgreSQL over MySQL
EpisodesTemporally clustered eventsQ4 database migration project
SourcesOriginal source text that grounds the semantics"Sarah led the migration from PostgreSQL to Aurora last quarter"
Each item has an activation_score (0-1) reflecting its relevance to the query. Semantics items link back to their source text, giving you a full provenance chain from structured assertions down to original content.
The vector graph is built automatically when you store memories. You don’t need to define entities or relationships - Nebula extracts them.

Search Effort

The effort parameter is the primary control for search. It determines how deeply Nebula traverses the vector graph.
EffortDepthUse Case
autoAdapts to queryDefault - good for most queries
low2 hops, narrowFast lookups, simple factual queries
medium2 hops, widerBroader exploration across more relationships
high3 hops, widestDeep multi-hop reasoning across the graph
# Quick factual lookup
results = nebula.search(query="Sarah's role", effort="low")

# Deep exploration
results = nebula.search(query="How are the Q4 projects connected?", effort="high")
Start with the default (auto). Only increase effort if you need the graph to discover connections further from the query.

Scoping with Collections

Use collection_ids to scope which part of the vector graph is searched.
# Search specific collections
results = nebula.search(
    query="customer feedback",
    collection_ids=["support-2024", "support-2023", "surveys"]
)

# Search all accessible collections (omit collection_ids)
results = nebula.search(query="product roadmap")
Searching all collections is slower than targeting specific ones.

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.
# Verified answer from support agent
conv_id = nebula.store_memory(
    Memory(
        collection_id="support",
        content="Here is the verified answer...",
        role="assistant",
        authority=0.9
    )
)

# Uncertain user message
nebula.store_memory(
    Memory(
        memory_id=conv_id,
        collection_id="support",
        content="I'm not sure, maybe try this?",
        role="user",
        authority=0.4
    )
)
ScoreUse Case
0.9-1.0Verified facts, official sources
0.5-0.7General content (default: 0.5)
0.0-0.3Low 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):
results = nebula.search(
    query="neural networks",
    collection_ids=["research"],
    search_settings={
        "semantic_weight": 0.7,
        "fulltext_weight": 0.3
    }
)
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.
results = nebula.search(
    query="project updates",
    collection_ids=["work"],
    filters={
        "status": "active",
        "team": "engineering"
    }
)
Operators: $eq, $ne, $in, $nin, $gt, $gte, $lt, $lte, $like, $ilike, $overlap, $contains, $and, $or See Metadata Filtering for the full reference.

Next Steps