Full-text Search
Keyword search, fuzzy matching, phrase search, and BM25 relevance ranking.
Full-text search lets you find nodes by the words in their string properties. Omnigraph provides four text search expressions: search() for keyword matching, fuzzy() for typo-tolerant matching, match_text() for exact phrase matching, and bm25() for relevance ranking.
Schema setup
Text search requires an @index annotation on a String property. This causes Omnigraph to build an inverted index over that field when data is loaded.
node Article {
slug: String @key
title: String @index
body: String @index
}
node Person {
name: String @key
bio: String @index
}Without @index, queries that reference search(), fuzzy(), match_text(), or bm25() on that field are rejected at compile time.
search() -- keyword matching
search(field, term) appears in the match block and filters to nodes whose indexed field contains the search term. It uses the inverted index for fast lookup.
query find_articles($term: String) {
match {
$a: Article
search($a.title, $term)
}
return { $a.slug, $a.title }
}omnigraph read ./my-graph \
--query queries.gq \
--name find_articles \
--params '{"term": "graph database"}' \
--json[
{ "slug": "intro-graphs", "title": "Introduction to Graph Databases" },
{ "slug": "graph-perf", "title": "Graph Database Performance" }
]fuzzy() -- typo-tolerant matching
fuzzy(field, term, distance) works like search() but allows matches within a given edit distance. This catches typos and minor spelling variations.
query fuzzy_search($term: String) {
match {
$p: Person
fuzzy($p.name, $term, 2)
}
return { $p.name }
}The third argument is the maximum Levenshtein edit distance. A distance of 1 catches single-character typos; 2 is a reasonable default for most use cases.
omnigraph read ./my-graph \
--query queries.gq \
--name fuzzy_search \
--params '{"term": "Alic"}'This matches "Alice" despite the missing final character.
match_text() -- phrase matching
match_text(field, phrase) filters to nodes whose field contains the exact phrase as a contiguous sequence of words. Unlike search(), word order and adjacency matter.
query phrase_search($phrase: String) {
match {
$a: Article
match_text($a.body, $phrase)
}
return { $a.slug, $a.title }
}omnigraph read ./my-graph \
--query queries.gq \
--name phrase_search \
--params '{"phrase": "copy on write"}'This returns only articles that contain the exact phrase "copy on write" in their body, not articles that merely contain those three words in different positions.
bm25() -- relevance ranking
bm25(field, term) appears in the order clause and ranks results by their BM25 relevance score. BM25 accounts for term frequency, document length, and corpus statistics to produce a relevance score.
query ranked_articles($term: String) {
match {
$a: Article
search($a.title, $term)
}
order bm25($a.title, $term)
return { $a.slug, $a.title }
}You can use search() in match to filter and bm25() in order to rank the filtered results:
omnigraph read ./my-graph \
--query queries.gq \
--name ranked_articles \
--params '{"term": "graph"}'[
{ "slug": "graph-perf", "title": "Graph Database Performance" },
{ "slug": "intro-graphs", "title": "Introduction to Graph Databases" }
]Results are sorted by BM25 score in descending order.
Combining search with traversal
Search expressions compose naturally with graph traversal. You can filter by text, traverse edges, and return properties from connected nodes:
query articles_by_author($term: String, $author: String) {
match {
$a: Article
search($a.title, $term)
$a writtenBy $p: Person { name: $author }
}
order bm25($a.title, $term)
return { $a.slug, $a.title, $p.name }
}This finds articles matching a keyword, then narrows to only those written by a specific author, ranked by BM25 relevance.