Full-Text Search
-
This article is about running a full-text search with a dynamic query.
To learn how to run a full-text search using a static-index, see full-text search with index. -
Use the
Search()
method to query for documents that contain specified term/s within the text of the specified document field/s. -
When running a full-text search with a dynamic query, the auto-index created by the server breaks down the text of the searched document field using the default search analyzer.
All generated terms are lower-cased, so the search is case-insensitive. -
Gain additional control over term tokenization by running a full-text search using a static-index, where the used analyzer is configurable.
-
A boost value can be set for each search to prioritize results. Learn more in boost search results.
-
User experience can be enhanced by requesting text fragments that highlight the searched terms in the results. Learn more in highlight search results.
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 ore 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 ore 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 ore 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 |