Skip to main content

SearchType

Enum defining the available search modes for cognee.search().

Values

The Retrieval source column shows what each type reads from: Vector (semantic similarity over embeddings), Graph (knowledge-graph traversal / Cypher), Vector + Graph (semantic seeds plus graph context — the “semantics + graph” combination), or Lexical (keyword matching, no embeddings). Types marked Varies pick or combine sources at runtime.

Speed, cost, and recall depth

Two things drive the cost of a search type: how many LLM calls it makes and how much retrieved context it puts into each prompt. The tabs below give measured reference numbers from one benchmark run: Search speed & recall depth compares every type on latency, call counts, and recall depth, and Session speed and overhead shows what the default session settings add on top, including the prompt-token cost of each type.
Methodology. Measured on cognee 1.4 (dev) with openai/gpt-5-mini, LanceDB and Kuzu, against a small knowledge base (10 short documents → 99 nodes / 191 edges), 3 questions × 2 runs per type (1 run for the iterative modes), default top_k=15. Token counts are provider-reported and summed over all LLM calls in the query. Absolute values scale with your corpus, model, and hardware — read them as ratios and orders of magnitude, not guarantees. On larger corpora token costs grow with chunk size: a single chunk can hold up to ~8,191 tokens, and no layer truncates the assembled context by default.
The single-call types (upper block) were measured with session memory disabled (AUTO_FEEDBACK=false): a query costs exactly its retrieval plus at most one completion. The iterative multi-call modes (lower block) were measured with default session settings — subtract one LLM call for their sessions-off cost, small relative to these totals. Prompt-token costs are in the Session speed and overhead tab.Rows marked (estimated) were not measured — they are derived from how the retriever is built (CYPHER, CODING_RULES, and CODE are direct graph reads with no LLM or embedding call — CODE loads the dataset’s code subgraph once and caches the parsed snapshot for 60 seconds, so only the first call scales with graph size; TRIPLET_COMPLETION has the same shape as RAG_COMPLETION; GRAPH_REPORT reads every node and edge via get_graph_data(), so its latency grows with the graph rather than with top_k; AGENTIC_COMPLETION is bounded by the measured iterative modes; BROAD makes one decision call, one more if its query fails, and one answer call unless the decision already answered, and embeds only on the reading route (one search per question phrasing) and when the dataset exceeds store_mb). Embedding-call counts for the multi-call modes are likewise derived rather than measured: each retrieval sweep embeds the query once, and FEELING_LUCKY inherits whatever the chosen type embeds — anywhere from 0 for the direct graph readers up to 5 if the selector picks GRAPH_COMPLETION_COT.The ordering to remember: latency is nearly identical for the three single-call completion types (the single LLM call dominates; retrieval differs by tens of milliseconds), while token cost and recall depth grow together — on this corpus roughly 1 : 4.7 : 7.5 for RAG : GRAPH : HYBRID (measured sessions-off). GRAPH_COMPLETION does the most retrieval work per query (it fans out one vector search per index collection — 5 collections by default, repeated for each searched dataset and each capped at wide_search_top_k=100 results — plus a graph projection), but that stays cheap next to the completion call.
To go faster: prefer CHUNKS, SUMMARIES, or CHUNKS_LEXICAL (no completion call), pass only_context=True, or disable auto-feedback. To go deeper: start with GRAPH_COMPLETION, escalate to GRAPH_COMPLETION_DECOMPOSITION for multi-part questions or GRAPH_COMPLETION_COT for multi-hop reasoning — the tables above show what that escalation costs (roughly 3× and 10× the latency, 2.5× and 8× the prompt tokens of GRAPH_COMPLETION on this corpus). Lowering max_iter / context_extension_rounds via retriever_specific_config reduces cost proportionally. See Search Basics — Advanced Parameters.

Choosing a Search Type

Use HYBRID_COMPLETION (default) for the best balance of accuracy and context. Use GRAPH_COMPLETION for graph-only context, or RAG_COMPLETION for a simpler chunk-based approach.
Use CHUNKS for semantic chunk retrieval or CHUNKS_LEXICAL for keyword-based. Use SUMMARIES for pre-generated summaries.
Use CYPHER for raw Cypher queries or NATURAL_LANGUAGE to have cognee translate your question to Cypher.
Just call recall() without query_type: its rule-based router checks two patterns and defaults to HYBRID_COMPLETION, so plain questions get a generated answer. A query that is entirely wrapped in double quotes runs CHUNKS_LEXICAL, and an explicit coding-rules or code-review phrase runs CODING_RULES; both return payloads rather than an answer. CYPHER is never auto-routed — it takes an explicit query_type — and so does every other type. See Auto-routing behavior for the rule table.FEELING_LUCKY is a different, LLM-based selector. It can choose retrieval-only types such as SUMMARIES or CHUNKS, which return payloads instead of a generated answer, so avoid it when you need an answer on every call.
All four graph-completion modes retrieve graph triplets and generate an LLM answer, but they differ in depth and latency:See Search Basics — Advanced Parameters and Retrievers for full details.

Per-search-type parameters

Every type accepts the common parameters (query_text, top_k, system_prompt/system_prompt_path, only_context, verbose, include_references, datasets/dataset_ids, user, session_id) documented in Search Basics — Parameters Reference. The graph-completion family additionally honors the graph-ranking knobs (wide_search_top_k, triplet_distance_penalty, feedback_influence, neighborhood_depth, neighborhood_seed_top_k) and node-set filters. wide_search_top_k and triplet_distance_penalty are the two knobs HYBRID_COMPLETION rejects rather than ignores — passing either with the default search type raises InvalidHybridSearchConfig; see Search Basics — Advanced Parameters. With recall(), use node_name and node_name_filter_operator; node_type is only exposed on lower-level search(). The accordions below list each search type’s type-specific parameters. Most entries are passed through retriever_specific_config; when a search type uses a common parameter in a special way, that is noted.
Deterministic and non-generative — it makes no LLM call and opts out of the session-turn preparation step, so query_text reaches the vector search verbatim and the call always returns summary records rather than a conversational acknowledgement.
Deterministic and non-generative — it makes no LLM call and opts out of the session-turn preparation step, so query_text reaches the vector search verbatim and the call always returns chunk records rather than a conversational acknowledgement.
Deterministic and non-generative — it makes no LLM call and opts out of the session-turn preparation step, so the keywords you pass are the keywords that are scored, and the call always returns chunk records rather than a conversational acknowledgement.
Without node_name, the query is searched directly against the whole Triplet_text collection. With node_name, the candidate triplets are resolved on the graph instead: the requested node sets are expanded into a subgraph, a triplet is in scope only when both endpoints of its edge are in that subgraph — the same rule the graph-completion search types use — and only those triplets are then ranked against the query.
Scoped triplet search ranks the in-scope triplets through the vector store’s optional id-bounded scoring (score_by_ids), which LanceDB, PGVector, and Turso implement. On an adapter without it — Neptune Analytics, for example — passing node_name to TRIPLET_COMPLETION raises NotImplementedError. Unscoped TRIPLET_COMPLETION works on every adapter.
Retrieval runs as vector seeds → 1-hop graph traversal → triplet ranking (top_k triplets) → context assembly → LLM completion. See the concrete lookup process for each stage and how top_k and wide_search_top_k shape it.
Hybrid retrieval reranked by the Timestamp nodes the default pipeline extracts; works on every built-in graph store (a community adapter needs get_graph_data and get_neighborhood). See Temporal Knowledge Graph.
Requires a Cypher-capable graph backend (Kuzu or Neo4j); on a backend that cannot run Cypher, such as the Postgres graph demo backend, the search raises SearchTypeNotSupported.CYPHER opts out of the session-turn preparation step, so query_text is executed exactly as written, with no pre-retrieval LLM call, even under the default CACHING=true / AUTO_FEEDBACK=true. (Before v1.6.3 the turn analysis could rewrite the statement into prose that failed to parse, or gate the turn into an acknowledgement instead of rows.) See Sessions and Caching.
Generates and executes Cypher, so it requires a Cypher-capable graph backend (Kuzu or Neo4j); on a backend that cannot run Cypher, such as the Postgres graph demo backend, the search raises SearchTypeNotSupported.Like CYPHER, it opts out of the session-turn preparation step, so the question is not rewritten or gated before Cypher generation. It still makes its own LLM calls to generate the Cypher (up to max_attempts). The rows the generated query returns are the search result, the same shape CYPHER returns.
Returns the stored rules and ignores query_text, but does not opt out of the session-turn preparation step: under the default session settings it still pays a pre-retrieval LLM call, and the analysis can gate the turn into a short acknowledgement instead of the rules. Set AUTO_FEEDBACK=false to avoid both.
Runs one deterministic operation over the code graph the code-graph pipeline built — no LLM, embedding, or vector call. Its arguments arrive through the dedicated code_query dict on search() and recall() rather than retriever_specific_config; passing code_query with any other search type raises InvalidCodeSearchConfig. top_k and the prompt parameters have no effect, query_text is read only as a seed or name filter when the dict carries no other filter, and batched queries are not supported. Searching several datasets at once treats a seed that one dataset cannot resolve as an empty result for that dataset rather than a failed call, with the reason on that entry’s error key.operation selects exactly one graph operation; anything else in the dict is that operation’s arguments. Omitting operation runs explore. An unknown name raises CodeSearchValidationError. max_depth, max_nodes and limit are clamped to the maximum shown, and a non-positive value falls back to the default.diagram works on every operation ("mermaid", "dot", or true for Mermaid); architecture sets it to Mermaid by itself. Seeds resolve by exact name first, then by short name and substring — an ambiguous name raises, so pass an id or narrow with repo. The rollup flags (type_rollup on a reverse traverse, target_rollup on find_path, both on by default) widen a type seed to its methods and constructor, so a dependent that only calls a method still counts; set them to false for the literal node. impact_analysis always rolls up its targets and has no flag.Fact kinds. Every extracted fact carries a kind. query_facts filters on it with kind (one) or kinds (several); explore, traverse, impact_analysis and architecture restrict the nodes they return with node_types. Both fields accept either the kind below or the node type it maps to ("symbol" and "CodeSymbol" are the same filter). find_path and delta take neither, and insights always returns insight facts.Which kinds a repository actually produces depends on its languages and on what enola’s extractors found, so a filter on a kind the snapshot has none of returns an empty page rather than an error. Repositories themselves (CodeRepository) are deliberately not facts — they appear only in delta.HTTP call relations. Two relation names connect an HTTP call to the code on either side of it, and both are valid relation_types values wherever that argument is accepted:Both resolve their target by fact id first, then by name inside the repository the reference names. A reference that resolves to neither is skipped and included in the skipped N count the run logs. With handled_by (route → handler), unchanged, these let impact_analysis cross a client→server call: a handler’s dependents chain back through handled_by → reaches_route → makes_request to the calling symbol in the other repository, and that repository then appears in cross_repo_impact. The chain is three hops, the default max_depth, so a lower max_depth stops before the caller.
Requires the resolved scope to contain exactly one dataset.
Ignores query_text — the report always covers the whole graph — and opts out of the session-turn preparation step, since there is no query to analyse.For the file-writing equivalent with its own top_n argument, see cognee.report().
Deterministic and non-generative — it makes no LLM call and opts out of the session-turn preparation step. Scoped to exactly one dataset: pass a single entry in datasets/dataset_ids, or the call raises CogneeValidationError (InvalidSkillsDatasetScope).Only skills that are active and whose dataset_scope contains the requested dataset are returned; a skill with an empty dataset_scope never matches. Each result carries the skill’s metadata — id, name, description, maintainer, maintainer_url, version, tags, license, declared_tools, dataset_scope, is_active, source_repo_url, source_dir — and the raw vector score, a backend distance where lower is better. The procedure body is deliberately withheld to preserve progressive disclosure; load it with the load_skill tool or GET /api/v1/skills/{skill_id}.When no skills have been ingested yet the collection does not exist, and the search returns an empty result rather than raising NoDataError — unlike SUMMARIES.
Takes no top_k: it reads every chunk and dlt row of the dataset being searched (up to store_mb), and rebuilds a temporary SQLite record store for each question.context_tokens, reading_tokens, batch_mb and store_mb must be positive, or the retriever raises ValueError.
Answer format. After the answer comes a note saying how it was reached: (Answered from the whole dataset.), (Computed by a SQL query over the whole dataset: <sql>), or (Answered from the N parts of the dataset most similar to the question; …). When a SQL result has more than one row, the full result table is appended too.Limits. The model-written query is limited to one read-only SELECT (an SQLite authorizer denies anything else), 1,000 result rows, and 120 seconds. Only the first 200 rows are shown to the answer model. A failing query is sent back to the model once, with the error. BROAD returns no include_references evidence. When the dataset is larger than context_tokens and the question needs the text read, a dataset without chunk embeddings raises BroadLimitError naming context_tokens as the setting to raise.only_context=True returns the user prompt built over the query result or the parts read, but it is not free: the decision call (and a retry, if the query fails) and the query itself run during retrieval. When the decision answers directly from the whole dataset, the prompt is built around that answer.
See Search Basics — Advanced Parameters for retriever_specific_config usage and Retrievers for per-retriever behavior.