Full-Text Search
-
This article is about making a full-text search with a dynamic query.
-
For making a full-text search using a static-index see full-text search with index.
-
Use method
Search()
to query for documents that contain the specified term(s)
within the text of the specified document field(s). -
You can provide a boost value to each search in order to prioritize results.
Learn more in boost search results. -
In addition to retrieving matching documents, in order to enhance user experience,
you can request to get text fragments that highlight the searched terms in the results.
Learn more in highlight search results.
-
When making a full-text search with a dynamic query, the auto-index created by the server
breaks down the text of the document field in which you search using the default search analyzer.
All terms generated will be lower-cased so search will be case-insensitive. -
To have more control over how terms are tokenized, perform a full-text search using a static-index,
where you can configure which analyzer to use.
Search for single term
List<Employee> employees = session
// Make a dynamic query on Employees collection
.Query<Employee>()
// * Call 'Search' to make a Full-Text search
// * Search is case-insensitive
// * Look for documents containing the term 'University' within their 'Notes' field
.Search(x => x.Notes, "University")
.ToList();
// Results will contain Employee documents that have
// any case variation of the term 'university' in their 'Notes' field.
List<Employee> employees = await asyncSession
// Make a dynamic query on Employees collection
.Query<Employee>()
// * Call 'Search' to make a Full-Text search
// * Search is case-insensitive
// * Look for documents containing the term 'University' within their 'Notes' field
.Search(x => x.Notes, "University")
.ToListAsync();
// Results will contain Employee documents that have
// any case variation of the term 'university' in their 'Notes' field.
List<Employee> employees = session.Advanced
// Make a dynamic DocumentQuery on Employees collection
.DocumentQuery<Employee>()
// * Call 'Search' to make a Full-Text search
// * Search is case-insensitive
// * Look for documents containing the term 'University' within their 'Notes' field
.Search(x => x.Notes, "University")
.ToList();
// Results will contain Employee documents that have
// any case variation of the term 'university' in their 'Notes' field.
from "Employees"
where search(Notes, "University")
-
Executing the above query will generate the auto-index
Auto/Employees/BySearch(Notes)
. -
This auto-index will contain the following two index-fields:
-
Notes
Contains terms with the original text from the indexed document field 'Notes'.
Text is lower-cased and Not tokenized. -
search(Notes)
Contains lower-cased terms that were tokenized from the 'Notes' field by the default search analyzer (RavenStandardAnalyzer). Calling theSearch()
method targets these terms to find matching documents.
-
Search for multiple terms
-
You can search for multiple terms in the same field in a single search method.
-
By default, the logical operator between these terms is 'OR'.
-
This behavior can be modified. See section Search operators.
Pass terms in a string:
List<Employee> employees = session
.Query<Employee>()
// * Pass multiple terms in a single string, separated by spaces.
// * Look for documents containing either 'University' OR 'Sales' OR 'Japanese'
// within their 'Notes' field
.Search(x => x.Notes, "University Sales Japanese")
.ToList();
// * Results will contain Employee documents that have at least one of the specified terms.
// * Search is case-insensitive.
List<Employee> employees = await asyncSession
.Query<Employee>()
// * Pass multiple terms in a single string, separated by spaces.
// * Look for documents containing either 'University' OR 'Sales' OR 'Japanese'
// within their 'Notes' field
.Search(x => x.Notes, "University Sales Japanese")
.ToListAsync();
// * Results will contain Employee documents that have at least one of the specified terms.
// * Search is case-insensitive.
List<Employee> employees = session.Advanced
.DocumentQuery<Employee>()
// * Pass multiple terms in a single string, separated by spaces.
// * Look for documents containing either 'University' OR 'Sales' OR 'Japanese'
// within their 'Notes' field
.Search(x => x.Notes, "University Sales Japanese")
.ToList();
// * Results will contain Employee documents that have at least one of the specified terms.
// * Search is case-insensitive.
from "Employees"
where search(Notes, "University Sales Japanese")
Pass terms in a list:
List<Employee> employees = session
.Query<Employee>()
// * Pass terms in IEnumerable<string>.
// * Look for documents containing either 'University' OR 'Sales' OR 'Japanese'
// within their 'Notes' field
.Search(x => x.Notes, new[] { "University", "Sales", "Japanese" })
.ToList();
// * Results will contain Employee documents that have at least one of the specified terms.
// * Search is case-insensitive.
List<Employee> employees = await asyncSession
.Query<Employee>()
// * Pass terms in IEnumerable<string>.
// * Look for documents containing either 'University' OR 'Sales' OR 'Japanese'
// within their 'Notes' field
.Search(x => x.Notes, new[] { "University", "Sales", "Japanese" })
.ToListAsync();
// * Results will contain Employee documents that have at least one of the specified terms.
// * Search is case-insensitive.
from "Employees"
where search(Notes, "University Sales Japanese")
Search in multiple fields
-
You can search for terms in different fields by making multiple search calls.
-
By default, the logical operator between consecutive search methods is 'OR'.
-
This behavior can be modified. See section Search options.
List<Employee> employees = session
.Query<Employee>()
// * Look for documents containing:
// 'French' in their 'Notes' field OR 'President' in their 'Title' field
.Search(x => x.Notes, "French")
.Search(x => x.Title, "President")
.ToList();
// * Results will contain Employee documents that have
// at least one of the specified fields with the specified terms.
// * Search is case-insensitive.
List<Employee> employees = await asyncSession
.Query<Employee>()
// * Look for documents containing:
// 'French' in their 'Notes' field OR 'President' in their 'Title' field
.Search(x => x.Notes, "French")
.Search(x => x.Title, "President")
.ToListAsync();
// * Results will contain Employee documents that have
// at least one of the specified fields with the specified terms.
// * Search is case-insensitive.
List<Employee> employees = session.Advanced
.DocumentQuery<Employee>()
// * Look for documents containing:
// 'French' in their 'Notes' field OR 'President' in their 'Title' field
.Search(x => x.Notes, "French")
.Search(x => x.Title, "President")
.ToList();
// * Results will contain Employee documents that have
// at least one of the specified fields with the specified terms.
// * Search is case-insensitive.
from "Employees"
where (search(Notes, "French") or search(Title, "President"))
Search in complex object
-
You can search for terms within a complex object.
-
Any nested text field within the object is searchable.
List<Company> companies = session
.Query<Company>()
// * Look for documents that contain:
// the term 'USA' OR 'London' in any field within the complex 'Address' object
.Search(x => x.Address, "USA London")
.ToList();
// * Results will contain Company documents that are located either in 'USA' OR in 'London'.
// * Search is case-insensitive.
List<Company> companies = await asyncSession
.Query<Company>()
// * Look for documents that contain:
// the term 'USA' OR 'London' in any field within the complex 'Address' object
.Search(x => x.Address, "USA London")
.ToListAsync();
// * Results will contain Company documents that are located either in 'USA' OR in 'London'.
// * Search is case-insensitive.
List<Company> companies = session.Advanced
.DocumentQuery<Company>()
// * Look for documents that contain:
// the term 'USA' OR 'London' in any field within the complex 'Address' object
.Search(x => x.Address, "USA London")
.ToList();
// * Results will contain Company documents that are located either in 'USA' OR in 'London'.
// * Search is case-insensitive.
from "Companies"
where search(Address, "USA London")
Search operators
-
By default, the logical operator between multiple terms within the same field in a search call is OR.
-
This can be modified using the
@operator
parameter as follows:
AND:
List<Employee> employees = session
.Query<Employee>()
// * Pass `@operator` with 'SearchOperator.And'
.Search(x => x.Notes, "College German", @operator: SearchOperator.And)
.ToList();
// * Results will contain Employee documents that have BOTH 'College' AND 'German'
// in their 'Notes' field.
// * Search is case-insensitive.
List<Employee> employees = await asyncSession
.Query<Employee>()
// * Pass `@operator` with 'SearchOperator.And'
.Search(x => x.Notes, "College German", @operator: SearchOperator.And)
.ToListAsync();
// * Results will contain Employee documents that have BOTH 'College' AND 'German'
// in their 'Notes' field.
// * Search is case-insensitive.
List<Employee> employees = session.Advanced
.DocumentQuery<Employee>()
// * Pass `@operator` with 'SearchOperator.And'
.Search(x => x.Notes, "College German", @operator: SearchOperator.And)
.ToList();
// * Results will contain Employee documents that have BOTH 'College' AND 'German'
// in their 'Notes' field.
// * Search is case-insensitive.
from "Employees"
where search(Notes, "College German", and)
OR:
List<Employee> employees = session
.Query<Employee>()
// * Pass `@operator` with 'SearchOperator.Or' (or don't pass this param at all)
.Search(x => x.Notes, "College German", @operator: SearchOperator.Or)
.ToList();
// * Results will contain Employee documents that have EITHER 'College' OR 'German'
// in their 'Notes' field.
// * Search is case-insensitive.
List<Employee> employees = await asyncSession
.Query<Employee>()
// * Pass `@operator` with 'SearchOperator.Or' (or don't pass this param at all)
.Search(x => x.Notes, "College German", @operator: SearchOperator.Or)
.ToListAsync();
// * Results will contain Employee documents that have EITHER 'College' OR 'German'
// in their 'Notes' field.
// * Search is case-insensitive.
List<Employee> employees = session.Advanced
.DocumentQuery<Employee>()
// * Pass `@operator` with 'SearchOperator.Or' (or don't pass this param at all)
.Search(x => x.Notes, "College German", @operator: SearchOperator.Or)
.ToList();
// * Results will contain Employee documents that have EITHER 'College' OR 'German'
// in their 'Notes' field.
// * Search is case-insensitive.
from "Employees"
where search(Notes, "College German")
Search options
-
Search options allow to:
- Negate a search criteria.
- Specify the logical operator used between consecutive search calls.
-
When using
Query
: use theoptions
parameter.
When usingDocumentQuery
: follow the specific syntax in each example below.
Negate search:
List<Company> companies = session
.Query<Company>()
// Pass 'options' with 'SearchOptions.Not'
.Search(x => x.Address, "USA", options: SearchOptions.Not)
.ToList();
// * Results will contain Company documents are NOT located in 'USA'
// * Search is case-insensitive
List<Company> companies = await asyncSession
.Query<Company>()
// Pass 'options' with 'SearchOptions.Not'
.Search(x => x.Address, "USA", options: SearchOptions.Not)
.ToListAsync();
// * Results will contain Company documents are NOT located in 'USA'
// * Search is case-insensitive
List<Company> companies = session.Advanced
.DocumentQuery<Company>()
.OpenSubclause()
// Call 'Not' to negate the next search call
.Not
.Search(x => x.Address, "USA")
.CloseSubclause()
.ToList();
// * Results will contain Company documents are NOT located in 'USA'
// * Search is case-insensitive
from "Companies"
where (exists(Address) and not search(Address, "USA"))
Default behavior between search calls:
- By default, the logical operator between consecutive search methods is OR.
List<Company> companies = session
.Query<Company>()
.Where(x => x.Contact.Title == "Owner")
// Operator AND will be used with previous 'Where' predicate
.Search(x => x.Address.Country, "France")
// Operator OR will be used between the two 'Search' calls by default
.Search(x => x.Name, "Markets")
.ToList();
// * Results will contain Company documents that have:
// ('Owner' as the 'Contact.Title')
// AND
// (are located in 'France' OR have 'Markets' in their 'Name' field)
//
// * Search is case-insensitive
List<Company> companies = await asyncSession
.Query<Company>()
.Where(x => x.Contact.Title == "Owner")
// Operator AND will be used with previous 'Where' predicate
.Search(x => x.Address.Country, "France")
// Operator OR will be used between the two 'Search' calls by default
.Search(x => x.Name, "Markets")
.ToListAsync();
// * Results will contain Company documents that have:
// ('Owner' as the 'Contact.Title')
// AND
// (are located in 'France' OR have 'Markets' in their 'Name' field)
//
// * Search is case-insensitive
List<Company> companies = session.Advanced
.DocumentQuery<Company>()
.WhereEquals(x => x.Contact.Title, "Owner")
// Operator AND will be used with previous 'Where' predicate
// Call 'OpenSubclause' to open predicate block
.OpenSubclause()
.Search(x => x.Address.Country, "France")
// Operator OR will be used between the two 'Search' calls by default
.Search(x => x.Name, "Markets")
// Call 'CloseSubclause' to close predicate block
.CloseSubclause()
.ToList();
// * Results will contain Company documents that have:
// ('Owner' as the 'Contact.Title')
// AND
// (are located in 'France' OR have 'Markets' in their 'Name' field)
//
// * Search is case-insensitive
from "Companies"
where Contact.Title == "Owner" and
(search(Address.Country, "France") or search(Name, "Markets"))
AND search calls:
List<Employee> employees = session
.Query<Employee>()
.Search(x => x.Notes, "French")
// * Pass 'options' with 'SearchOptions.And' to the second 'Search'
// * Operator AND will be used with previous the 'Search' call
.Search(x => x.Title, "Manager", options: SearchOptions.And)
.ToList();
// * Results will contain Employee documents that have:
// ('French' in their 'Notes' field)
// AND
// ('Manager' in their 'Title' field)
//
// * Search is case-insensitive
List<Employee> employees = await asyncSession
.Query<Employee>()
.Search(x => x.Notes, "French")
// * Pass 'options' with 'SearchOptions.And' to this second 'Search'
// * Operator AND will be used with previous the 'Search' call
.Search(x => x.Title, "Manager", options: SearchOptions.And)
.ToListAsync();
// * Results will contain Employee documents that have:
// ('French' in their 'Notes' field)
// AND
// ('Manager' in their 'Title' field)
//
// * Search is case-insensitive
List<Employee> employees = session.Advanced
.DocumentQuery<Employee>()
.Search(x => x.Notes, "French")
// Call 'AndAlso' so that operator AND will be used with previous 'Search' call
.AndAlso()
.Search(x => x.Title, "Manger")
.ToList();
// * Results will contain Employee documents that have:
// ('French' in their 'Notes' field)
// AND
// ('Manager' in their 'Title' field)
//
// * Search is case-insensitive
from "Employees"
where search(Notes, "French") and search(Title, "Manager")
Use options as bit flags:
List<Employee> employees = session
.Query<Employee>()
.Search(x => x.Notes, "French")
// Pass logical operators as flags in the 'options' parameter
.Search(x => x.Title, "Manager", options: SearchOptions.Not | SearchOptions.And)
.ToList();
// * Results will contain Employee documents that have:
// ('French' in their 'Notes' field)
// AND
// (do NOT have 'Manager' in their 'Title' field)
//
// * Search is case-insensitive
List<Employee> employees = await asyncSession
.Query<Employee>()
.Search(x => x.Notes, "French")
// Pass logical operators as flags in the 'options' parameter
.Search(x => x.Title, "Manager", options: SearchOptions.Not | SearchOptions.And)
.ToListAsync();
// * Results will contain Employee documents that have:
// ('French' in their 'Notes' field)
// AND
// (do NOT have 'Manager' in their 'Title' field)
//
// * Search is case-insensitive
List<Employee> employees = session.Advanced
.DocumentQuery<Employee>()
.Search(x => x.Notes, "French")
// Call 'AndAlso' so that operator AND will be used with previous 'Search' call
.AndAlso()
.OpenSubclause()
// Call 'Not' to negate the next search call
.Not
.Search(x => x.Title, "Manager")
.CloseSubclause()
.ToList();
// * Results will contain Employee documents that have:
// ('French' in their 'Notes' field)
// AND
// (do NOT have 'Manager' in their 'Title' field)
//
// * Search is case-insensitive
from "Employees"
where search(Notes, "French") and
(exists(Title) and not search(Title, "Manager"))
Using wildcards
-
Wildcards can be used to replace:
- Prefix of a searched term
- Postfix of a searched term
- Both prefix & postfix
-
Note:
-
Searching with a wildcard as the prefix of the term (e.g.
*text
) is less recommended,
as it will cause the server to perform a full index scan. -
Instead, consider using a static-index that indexes the field in reverse order
and then query with a wildcard as the postfix, which is much faster.
-
List<Employee> employees = session
.Query<Employee>()
// Use '*' to replace one or more characters
.Search(x => x.Notes, "art*")
.Search(x => x.Notes, "*logy")
.Search(x => x.Notes, "*mark*")
.ToList();
// Results will contain Employee documents that have in their 'Notes' field:
// (terms that start with 'art') OR
// (terms that end with 'logy') OR
// (terms that have the text 'mark' in the middle)
//
// * Search is case-insensitive
List<Employee> employees = await asyncSession
.Query<Employee>()
// Use '*' to replace one or more characters
.Search(x => x.Notes, "art*")
.Search(x => x.Notes, "*logy")
.Search(x => x.Notes, "*mark*")
.ToListAsync();
// Results will contain Employee documents that have in their 'Notes' field:
// (terms that start with 'art') OR
// (terms that end with 'logy') OR
// (terms that have the text 'mark' in the middle)
//
// * Search is case-insensitive
List<Employee> employees = session.Advanced
.DocumentQuery<Employee>()
// Use '*' to replace one or more characters
.Search(x => x.Notes, "art*")
.Search(x => x.Notes, "*logy")
.Search(x => x.Notes, "*mark*")
.ToList();
// Results will contain Employee documents that have in their 'Notes' field:
// (terms that start with 'art') OR
// (terms that end with 'logy') OR
// (terms that have the text 'mark' in the middle)
//
// * Search is case-insensitive
from "Employees" where
search(Notes, "art*") or
search(Notes, "*logy") or
search(Notes, "*mark*")
Syntax
// Query overloads:
// ================
IRavenQueryable<T> Search<T>(
Expression<Func<T, object>> fieldSelector,
string searchTerms,
decimal boost,
SearchOptions options,
SearchOperator @operator);
IRavenQueryable<T> Search<T>(
Expression<Func<T, object>> fieldSelector,
IEnumerable<string> searchTerms,
decimal boost,
SearchOptions options,
SearchOperator @operator);
// DocumentQuery overloads:
// ========================
IDocumentQueryBase<T> Search(
string fieldName,
string searchTerms,
SearchOperator @operator);
IDocumentQueryBase<T> Search<TValue>(
Expression<Func<T, TValue>> propertySelector,
string searchTerms,
SearchOperator @operator);
Parameter | Type | Description |
---|---|---|
fieldSelector | Expression<Func<TResult>> |
Points to the field in which you search. |
fieldName | string | Name of the field in which you search. |
searchTerms | string / IEnumerable<string> |
A string containing the term or terms (separated by spaces) to search for. Or, can pass an array (or other IEnumerable ) with terms to search for. |
boost | decimal | The boost value. Learn more in boost search results. Default is 1.0 |
options | SearchOptions enum |
Logical operator to use between consecutive Search methods. Can be Or , And , Not , or Guess .Default is SearchOptions.Guess |
@operator | SearchOperator enum |
Logical operator to use between multiple terms in the same Search method. Can be Or or And .Default is SearchOperation.Or |