Filter with Lucene Syntax
-
Use the Lucene query syntax when you want direct access to Lucene's query-parser syntax,
for example, to use Lucene-specific wildcard syntax such as?, or to reuse an existing Lucene query string. -
In the client API, use
WhereLucenefromDocumentQuery/AsyncDocumentQuery.
In raw RQL, use the correspondinglucene()method. -
Filtering with
WhereLucene/lucene()is supported only by indexes that use the Lucene search engine
(this applies to both static-indexes and auto-indexes). If the target index uses Corax, the query throws an exception. -
In this article:
Basic usage
-
WhereLucene(string fieldName, string whereClause)takes 2 parameters:-
Field name:
The field name tells Lucene which field to search when the clause does not specify a field explicitly.
In.WhereLucene("Name", "bistro"), the termbistrois searched in theNamefield. -
Where clause:
The where clause is a string written in Lucene query syntax.
RavenDB parses this string on the server with Lucene's query parser.
-
-
When making a dynamic query on a collection:
RavenDB creates or reuses an auto-index with a full-text search field for the requested field.
This field uses the default search analyzer. By default, this isRavenStandardAnalyzer, which lowercases and tokenizes the text, so matching is case-insensitive (e.g. bistro matches "Bistro du Centre"). -
When querying a static index:
The Lucene query clause is parsed using the analyzer configured for the target index field.
Configure the analyzer in the index definition, for example withAnalyze(...).
If aSearchfield has no analyzer configured, RavenDB uses the default search analyzer.
The following query returns all companies whose Name field contains the term bistro:
- DocumentQuery
- DocumentQuery_async
- RawQuery
- RawQuery_async
- RQL
List<Company> companies = session.Advanced
.DocumentQuery<Company>()
.WhereLucene("Name", "bistro")
.ToList();
List<Company> companies = await asyncSession.Advanced
.AsyncDocumentQuery<Company>()
.WhereLucene("Name", "bistro")
.ToListAsync();
List<Company> companies = session.Advanced
.RawQuery<Company>("from Companies where lucene(Name, $clause)")
.AddParameter("clause", "bistro")
.ToList();
List<Company> companies = await asyncSession.Advanced
.AsyncRawQuery<Company>("from Companies where lucene(Name, $clause)")
.AddParameter("clause", "bistro")
.ToListAsync();
from Companies
where lucene(Name, "bistro")
If no suitable index already exists for this dynamic query,
RavenDB creates an auto-index for Companies with Name indexed as a full-text search field, for example, Auto/Companies/BySearch(Name).
Common Lucene clauses
The whereClause argument specifies the filtering predicate in Lucene query syntax.
Common clause types include:
| Clause type | Example clause | Meaning |
|---|---|---|
| Single term | bistro | Match documents whose field contains the term bistro. |
| Wildcards | bist* | Match terms that start with bist. |
| Single-character wildcard | b?stro | Match one character at the ? position. |
| Boolean operators | (bistro OR cafe) AND NOT grill | Combine terms with AND, OR, NOT, and parentheses. |
| Phrase | "sales representative" | Match the exact phrase, with terms in order. |
| Proximity | "fluent french"~5 | Match both terms within 5 words of each other. |
| Fuzzy | bistor~ | Match terms similar to bistor, such as minor typos. |
| Term boost | bistro^2 OR cafe | Match both terms, but rank bistro matches higher when ordering by score. |
| Term range | [apple TO banana] | Match terms in the inclusive term range. Use {...} for exclusive bounds. |
Wildcards
Use * to match any number of characters and ? to match exactly one character:
- DocumentQuery
- DocumentQuery_async
- RQL
// Matches "bistro", "bistros", "bistrot", ...
List<Company> companies = session.Advanced
.DocumentQuery<Company>()
.WhereLucene("Name", "bist*")
.ToList();
// Matches terms such as "bistro" and "bystro"
List<Company> companiesWithSingleCharacterWildcard = session.Advanced
.DocumentQuery<Company>()
.WhereLucene("Name", "b?stro")
.ToList();
// Matches "bistro", "bistros", "bistrot", ...
List<Company> companies = await asyncSession.Advanced
.AsyncDocumentQuery<Company>()
.WhereLucene("Name", "bist*")
.ToListAsync();
// Matches terms such as "bistro" and "bystro"
List<Company> companiesWithSingleCharacterWildcard = await asyncSession.Advanced
.AsyncDocumentQuery<Company>()
.WhereLucene("Name", "b?stro")
.ToListAsync();
// Matches "bistro", "bistros", "bistrot", ...
from Companies
where lucene(Name, "bist*")
// Matches terms such as "bistro" and "bystro"
from Companies
where lucene(Name, "b?stro")
Leading wildcards
Leading wildcards (e.g. *stro) are allowed as well.
Note that a leading wildcard forces the server to scan all terms of the field, which can be slow on large indexes.
Boolean operators
Use AND, OR, NOT, and parentheses to combine terms inside the Lucene clause.
Lucene operator keywords are case-sensitive, so they must be written in UPPERCASE.
The following query matches companies whose Name field contains bistro OR cafe,
but excludes names that contain grill:
- DocumentQuery
- DocumentQuery_async
- RQL
List<Company> companies = session.Advanced
.DocumentQuery<Company>()
.WhereLucene("Name", "(bistro OR cafe) AND NOT grill")
.ToList();
List<Company> companies = await asyncSession.Advanced
.AsyncDocumentQuery<Company>()
.WhereLucene("Name", "(bistro OR cafe) AND NOT grill")
.ToListAsync();
from Companies
where lucene(Name, '(bistro OR cafe) AND NOT grill')
Phrase and proximity search
Surround multiple terms with double quotes to match a phrase.
In C# string literals, escape those quote characters with \".
Add ~<number> after the closing quote to perform a proximity search:
the terms must appear near each other within the specified proximity distance.
- DocumentQuery
- DocumentQuery_async
- RQL
// Exact phrase:
List<Employee> employees = session.Advanced
.DocumentQuery<Employee>()
.WhereLucene("Notes", "\"sales representative\"")
.ToList();
// Proximity - "sales" and "french" within a distance of 10 words:
List<Employee> employeesProx = session.Advanced
.DocumentQuery<Employee>()
.WhereLucene("Notes", "\"sales french\"~10")
.ToList();
// Exact phrase:
List<Employee> employees = await asyncSession.Advanced
.AsyncDocumentQuery<Employee>()
.WhereLucene("Notes", "\"sales representative\"")
.ToListAsync();
// Proximity - "sales" and "french" within a distance of 10 words:
List<Employee> employeesProx = await asyncSession.Advanced
.AsyncDocumentQuery<Employee>()
.WhereLucene("Notes", "\"sales french\"~10")
.ToListAsync();
// Exact phrase:
from Employees
where lucene(Notes, '"sales representative"')
// Proximity - "sales" and "french" within a distance of 10 words:
from Employees
where lucene(Notes, '"sales french"~10')
Fuzzy search
Append ~ to a single term to match indexed terms that are similar to it, for example, to tolerate minor typos.
You can add a minimum similarity value after ~, such as bistor~0.7.
The value is between 0 and 1 (default: 0.5); higher values require closer similarity:
- DocumentQuery
- DocumentQuery_async
- RQL
// Matches "bistro" although the searched term is misspelled
List<Company> companies = session.Advanced
.DocumentQuery<Company>()
.WhereLucene("Name", "bistor~")
.ToList();
// Matches "bistro" although the searched term is misspelled
List<Company> companies = await asyncSession.Advanced
.AsyncDocumentQuery<Company>()
.WhereLucene("Name", "bistor~")
.ToListAsync();
// Matches "bistro" although the searched term is misspelled
from Companies
where lucene(Name, 'bistor~')
Boosting terms
Append ^<number> to a term to increase its relevance score.
Boosting does not change which documents match the clause.
It only affects ranking when results are ordered by score:
- DocumentQuery
- DocumentQuery_async
- RQL
// When ordered by score,
// companies matching "bistro" rank higher than those matching "cafe"
List<Company> companies = session.Advanced
.DocumentQuery<Company>()
.WhereLucene("Name", "bistro^2 OR cafe")
.OrderByScore()
.ToList();
// When ordered by score,
// companies matching "bistro" rank higher than those matching "cafe"
List<Company> companies = await asyncSession.Advanced
.AsyncDocumentQuery<Company>()
.WhereLucene("Name", "bistro^2 OR cafe")
.OrderByScore()
.ToListAsync();
// When ordered by score,
// companies matching "bistro" rank higher than those matching "cafe"
from Companies
where lucene(Name, 'bistro^2 OR cafe')
order by score()
Range clauses
Use square brackets for an inclusive range and curly braces for an exclusive range.
Lucene range clauses compare indexed terms lexicographically:
- DocumentQuery
- DocumentQuery_async
- RQL
// Match company names from "bistro" through "cafe", inclusive
List<Company> companies = session.Advanced
.DocumentQuery<Company>()
.WhereLucene("Name", "[bistro TO cafe]")
.ToList();
// Match company names from "bistro" through "cafe", inclusive
List<Company> companies = await asyncSession.Advanced
.AsyncDocumentQuery<Company>()
.WhereLucene("Name", "[bistro TO cafe]")
.ToListAsync();
// Match company names from "bistro" through "cafe", inclusive
from Companies
where lucene(Name, '[bistro TO cafe]')
Numeric and date ranges
For range queries over numeric or date fields, prefer strongly typed methods:
use comparison operators such as WhereGreaterThan / WhereLessThan,
or WhereBetween.
A Lucene range clause compares string terms lexicographically,
so numeric and date ranges can produce unexpected results.
Filter on multiple fields
-
In
WhereLucene(fieldName, whereClause), thefieldNameargument is Lucene's default field:
terms in the clause that are not prefixed with another field name are checked against this field. -
Inside the Lucene
whereClause, prefix a term with<FieldName>:to search another indexed field. -
Use this pattern when querying a static index that defines the fields referenced in the Lucene clause.
RavenDB creates or chooses an auto-index from thefieldNameargument;
it does not infer additional fields from inside the raw Lucene clause.
Assume the following static index exists:
public class Companies_ByNameAndPhone : AbstractIndexCreationTask<Company, Companies_ByNameAndPhone.IndexEntry>
{
public class IndexEntry
{
public string Name { get; set; }
public string Phone { get; set; }
}
public Companies_ByNameAndPhone()
{
Map = companies => from company in companies
select new IndexEntry
{
Name = company.Name,
Phone = company.Phone
};
Index(x => x.Name, FieldIndexing.Search);
Index(x => x.Phone, FieldIndexing.Search);
}
}
The following query targets both the Name and Phone fields:
bistrois not prefixed, so it is checked against the default field,Name.Phone:981*is prefixed withPhone:, so it is checked against thePhonefield.
The query matches companies whose Name contains bistro
and whose Phone field contains a term that starts with 981.
- DocumentQuery
- DocumentQuery_async
- RQL
List<Company> companies = session.Advanced
.DocumentQuery<Company>("Companies/ByNameAndPhone")
.WhereLucene("Name", "bistro AND Phone:981*")
.ToList();
List<Company> companies = await asyncSession.Advanced
.AsyncDocumentQuery<Company>("Companies/ByNameAndPhone")
.WhereLucene("Name", "bistro AND Phone:981*")
.ToListAsync();
from index 'Companies/ByNameAndPhone'
where lucene(Name, 'bistro AND Phone:981*')
Case-sensitive matching
To keep query terms unchanged, use exact: true in the client API, or wrap lucene() with exact(...) in RQL.
RavenDB then parses the Lucene clause with Lucene's KeywordAnalyzer.
For case-sensitive matching, the indexed field must also preserve casing.
The example below uses a static index that defines Name as an exact field.
public class Companies_ByNameExact : AbstractIndexCreationTask<Company, Companies_ByNameExact.IndexEntry>
{
public class IndexEntry
{
public string Name { get; set; }
}
public Companies_ByNameExact()
{
Map = companies => from company in companies
select new IndexEntry
{
Name = company.Name
};
Index(x => x.Name, FieldIndexing.Exact);
}
}
Query the index with:
- DocumentQuery
- DocumentQuery_async
- RQL
// Matches "Bistro...", but not "bistro..." or "BISTRO..."
List<Company> companies = session.Advanced
.DocumentQuery<Company>("Companies/ByNameExact")
.WhereLucene("Name", "Bistro*", exact: true)
.ToList();
// Matches "Bistro...", but not "bistro..." or "BISTRO..."
List<Company> companies = await asyncSession.Advanced
.AsyncDocumentQuery<Company>("Companies/ByNameExact")
.WhereLucene("Name", "Bistro*", exact: true)
.ToListAsync();
// Matches "Bistro...", but not "bistro..." or "BISTRO..."
from index 'Companies/ByNameExact'
where exact(lucene(Name, 'Bistro*'))
Exact analyzer
FieldIndexing.Exact indexes the field with the default exact analyzer.
The default is KeywordAnalyzer.
WhereLucene(..., exact: true) and exact(lucene(...)) use the KeywordAnalyzer at query time.
If you configure a different exact analyzer for indexing, make sure the query terms can still match the indexed terms.
Combine with other filtering methods
You can combine a Lucene clause with other DocumentQuery filters.
Use AndAlso(), OrElse(), and the Not modifier to compose filters outside the Lucene clause:
The following query matches companies whose Name contains bistro or cafe, and whose country is France.
- DocumentQuery
- DocumentQuery_async
- RQL
List<Company> companies = session.Advanced
.DocumentQuery<Company>()
.WhereLucene("Name", "bistro OR cafe")
.AndAlso()
.WhereEquals("Address.Country", "France")
.ToList();
List<Company> companies = await asyncSession.Advanced
.AsyncDocumentQuery<Company>()
.WhereLucene("Name", "bistro OR cafe")
.AndAlso()
.WhereEquals("Address.Country", "France")
.ToListAsync();
from Companies
where lucene(Name, 'bistro OR cafe') and Address.Country = 'France'
Escaping special characters
The following characters are part of Lucene query syntax.
To match them as literal characters, escape them with a backslash (\):
+ - && || ! ( ) { } [ ] ^ " ~ * ? : \
For example, the following clause searches for the literal text (171) in the Phone field:
- DocumentQuery
- DocumentQuery_async
- RQL
// The C# string "\\(" becomes \( in the Lucene clause
List<Company> companies = session.Advanced
.DocumentQuery<Company>()
.WhereLucene("Phone", "\\(171\\)")
.ToList();
// The C# string "\\(" becomes \( in the Lucene clause
List<Company> companies = await asyncSession.Advanced
.AsyncDocumentQuery<Company>()
.WhereLucene("Phone", "\\(171\\)")
.ToListAsync();
from Companies
where lucene(Phone, '\\(171\\)')
Escape user input
The whereClause is parsed as Lucene query syntax on the server.
If you build the clause from user input, escape or validate the user-provided value before embedding it.
Otherwise, characters such as *, ?, :, or operators such as OR can change the meaning of the query.
For example, a user value that contains OR Name:* could turn a literal search value into a broader Lucene query.
Restrictions
-
Lucene search engine only
WhereLucene/lucene()is supported only by indexes that use the Lucene search engine.
Running the query against an index that uses Corax throws an exception.The search engine in use is determined by the Indexing.Auto.SearchEngineType and Indexing.Static.SearchEngineType configuration keys, or per index (static indexes).
- DocumentQuery only
In the client API,WhereLuceneis available inDocumentQuery/AsyncDocumentQueryand has no LINQ (session.Query) equivalent. In raw RQL, uselucene().
- The clause must be a string or
null
ThewhereClauseargument, or the value passed tolucene()in RQL, must be a string ornull.
Passing any other value type throws an exception.
Anullclause is treated as a null-value query for the specified field.
- The clause must be valid Lucene syntax
The clause is parsed by Lucene's query parser when the query executes.
Invalid Lucene syntax fails the query with a parse error.
-
Use static indexes for multi-field Lucene clauses
In a dynamic query, RavenDB creates or chooses an auto-index based on the field passed as the first argument.
Field prefixes inside the raw Lucene clause, such asPhone:981*, are not used to infer additional auto-index fields.
Use a static index when the Lucene clause references fields other than the first-argument field.from Companies
where lucene(Name, 'bistro AND Phone:981*')
In this dynamic query, Name is the field RavenDB can infer for the auto-index.
Phone appears only inside the Lucene clause string, so define a static index that maps both Name and Phone.
Syntax
- DocumentQuery
- DocumentQuery_async
- RQL
IDocumentQuery<T> WhereLucene(string fieldName, string whereClause);
IDocumentQuery<T> WhereLucene(string fieldName, string whereClause, bool exact);
IAsyncDocumentQuery<T> WhereLucene(string fieldName, string whereClause);
IAsyncDocumentQuery<T> WhereLucene(string fieldName, string whereClause, bool exact);
lucene(fieldName, whereClause)
exact(lucene(fieldName, whereClause))
| Parameter | Type | Description |
|---|---|---|
| fieldName | string | Name of the field the clause is checked against (the Lucene default field). Terms inside the clause can target other fields using the <FieldName>: prefix. |
| whereClause | string | A predicate written in Lucene query syntax. |
| exact | bool | true - parse the clause with KeywordAnalyzer for exact matching.Default: false. |