Manually Constructing Queries
Query
object and execute it against the index.There are two approaches you can take to constructing a Query
, using the fluent builder syntax and manually creating the relevant query parts.
The simplest is the fluent builder syntax as it takes care of automatically normalizing your search text to be in line with that expected in the index.
Fluent query building
All fluent query building starts from the index:
index.Query().ExactMatch("text");
You can immediately execute a built query:
var searchResults = index.Query().ExactMatch("text").Execute();
Or just build an IQuery
representing it:
var query = index.Query().ExactMatch("text").Build();
You can exact match a search term, as in the above examples, fuzzy match or wildcard match:
// Fuzzy match
var searchResults = index.Query().FuzzyMatch("text").Execute();
// Wildcard match, parsing from text
var searchResults = index.Query().WildcardMatch("te%*").Execute();
// Wildcard match, building explicitly
var searchResults = index.Query().WildcardMatch(w => w
.Text("te")
.SingleCharacter()
.MultipleCharacters())
.Execute();
Fluently combining search terms
Terms can be combined with and (&
)/or (|
):
// Combine with And (&)
index.Query()
.ExactMatch("west")
.And.ExactMatch("wing")
.Execute()
// Combine with Or (|)
index.Query()
.ExactMatch("west")
.Or.ExactMatch("wing")
.Execute()
All other LIFTI query operators are also supported:
// Combine with Preceding (>)
index.Query()
.ExactMatch("west")
.Preceding.ExactMatch("wing")
.Execute()
// Combine with Near (~)
index.Query()
.ExactMatch("west")
.Near(3).ExactMatch("wing")
.Execute()
// Combine with Preceding Near (~>)
index.Query()
.ExactMatch("west")
.CloselyPreceding(3).ExactMatch("wing")
.Execute()
Adjacent words
You can build a series of search terms that must follow each other sequentially (i.e. “one two three”):
index.Query()
.Adjacent(a => a.ExactMatch("west").ExactMatch("wing"))
.Execute()
Within an adjacent search you can also use fuzzy match or wildcard searches as well:
index.Query()
.Adjacent(a => a.FuzzyMatch("wst").WildcardMatch("wi*"))
.Execute()
Searching in a specific field
Like in the LIFTI query syntax to restrict a search to a specific field (e.g. [fieldName]=text
) you can do the same fluently:
index.Query()
.InField("description", f => f.FuzzyMatch("west"))
.Execute()
Multiple search terms in an InField
call will automatically be placed in parenthesis:
var query = index.Query()
.InField("description", f => f.ExactMatch("west").Or.ExactMatch("wing"))
.Build();
Console.WriteLine(query.ToString()); // [description]=(west | wing)
Bracketing parts of the query
To logically group a section of a query together you can place them in a bracketed section:
var query = index.Query()
.ExactMatch("one")
.Or
.Bracketed(b => b
.ExactMatch("two")
.And.ExactMatch("three"))
.Build();
Console.WriteLine(query.ToString()); // one | (two & three)
Manual query construction
You can manually construct a Query
using any combination of IQueryPart
s, but take care to normalize any text to match in the same way that it has been indexed. You can do this using either the IIndexTokenizer
for the index, or if specific tokenization rules have been configured for a field, then the tokenizer for that field:
var tokenizer = index.DefaultTokenizer;
var query = new Query(
new AndQueryOperator(
new ExactWordQueryPart(tokenizer.Normalize("hello")),
new ExactWordQueryPart(tokenizer.Normalize("there"))));
IQueryPart
s come in two flavors:
- Operators - these contain other
IQueryParts
, combining the results that they return according to certain rules, e.g.AndQueryPart
andOrQueryPart
- Textual - these work the
IIndexNavigator
to query the index for matches appropriate to them, e.g.ExactWordQueryPart
,FuzzyMatchQueryPart
.
Textual query parts
ExactWordQueryPart(string word)
Searches the index for words that exactly match word
.
FuzzyMatchQueryPart(string word, ushort maxEditDistance = 4, ushort maxSequentialEdits = 1)
?
in LIFTI query syntax
Performs a fuzzy match against the index.
maxEditDistance
: The maximum of edits allowed for any given match. The higher this value, the more divergent matches will be.maxSequentialEdits
The maximum number of edits that are allowed to appear sequentially. By default this is 1, which forces matches to be more similar to the search criteria
WildcardQueryPart(IReadOnlyList<WildcardQueryFragment> fragments)
*
and%
in LIFTI query syntax
A WildcardQueryPart
consists of multiple WildcardQueryFragment
s that are processed sequentially to match tokens in the index. They can be constructed using the following methods:
WildcardQueryFragment.MultiCharacter()
- matches zero or more characters in the index.WildcardQueryFragment.SingleCharacter()
- matches any single character in the index.WildcardQueryFragment.CreateText(string text)
- exact matches a fragment of text at whatever point has been reached in the index.
For example:
var wildcard = new WildcardQueryPart(new[] {
WildcardQueryFragment.SingleCharacter(),
WildcardQueryFragment.SingleCharacter(),
WildcardQueryFragment.CreateText("d")
})
Would translate to the LIFTI query %%d
, matching any words that start with any two letters followed by a d
.
Structural query parts
AndQueryOperator(IQueryPart left, IQueryPart right)
&
in LIFTI query syntax
Intersects the results of two query parts. In other words, only matches that appear on both sides will be returned.
OrQueryOperator(IQueryPart left, IQueryPart right)
|
in LIFTI query syntax
Unions the results of two query parts. In other words, a deduplicated set of matches on both sides will be returned.
BracketedQueryPart(IQueryPart statement)
(
and)
in LIFTI query syntax
This can be used to group other query parts together, ensure they are executed in the right order.
AdjacentWordsQueryPart(IReadOnlyList<IQueryPart> matches)
"
in LIFTI query syntax
A query part requiring that a series of matches must appear in a document in sequence.
FieldFilterQueryOperator(string fieldName, byte fieldId, IQueryPart statement)
field=
in LIFTI query syntax
Restricts the resulting document matches to only those that include matching tokens in a specific field.
NearQueryOperator(IQueryPart left, IQueryPart right, int tolerance = 5)
~n
in LIFTI query syntax
Produces an intersection of two IQueryPart
s, restricting an document’s field matches such that the locations are close to one another.
Documents that result in no field matches are filtered out.
PrecedingNearQueryOperator(IQueryPart left, IQueryPart right, int tolerance = 5)
~n>
in LIFTI query syntax
Produces an intersection of two IQueryPart
s, restricting an document’s field matches such that the locations of the first appear before the locations of the second and within a specified tolerance.
Documents that result in no field matches are filtered out.
PrecedingQueryOperator(IQueryPart left, IQueryPart right)
>
in LIFTI query syntax
Produces an intersection of two IQueryPart
s, restricting an document’s field matches such that the locations of the first appear before the locations of the second.
Documents that result in no field matches are filtered out.