Full-Text Search with Index
-
Prior to reading this article, it is recommended to take a look at the Full-Text search with dynamic queries article to learn about the
Search
method. -
All capabilities provided by
Search
with a dynamic query can also be used when querying a static-index. -
However, as opposed to making a dynamic search query where an auto-index is created for you,
when using a static-index:-
You must configure the index-field in which you want to search.
See examples below. -
You can configure which analyzer will be used to tokenize this field.
See selecting an analyzer.
-
Indexing single field for FTS
The index:
public class Employees_ByNotes :
AbstractIndexCreationTask<Employee, Employees_ByNotes.IndexEntry>
{
// The IndexEntry class defines the index-fields
public class IndexEntry
{
public string EmployeeNotes { get; set; }
}
public Employees_ByNotes()
{
// The 'Map' function defines the content of the index-fields
Map = employees => from employee in employees
select new IndexEntry()
{
EmployeeNotes = employee.Notes[0]
};
// Configure the index-field for FTS:
// Set 'FieldIndexing.Search' on index-field 'EmployeeNotes'
Index(x => x.EmployeeNotes, FieldIndexing.Search);
// Optionally: Set your choice of analyzer for the index-field.
// Here the text from index-field 'EmployeeNotes' will be tokenized by 'WhitespaceAnalyzer'.
Analyze(x => x.EmployeeNotes, "WhitespaceAnalyzer");
// Note:
// If no analyzer is set then the default 'RavenStandardAnalyzer' is used.
}
}
Query with Search:
-
Use
Search
to make a full-text search when querying the index. -
Refer to Full-Text search with dynamic queries for all available Search options,
such as using wildcards, searching for multiple terms, etc.
List<Employee> employees = session
// Query the index
.Query<Employees_ByNotes.IndexEntry, Employees_ByNotes>()
// Call 'Search':
// pass the index field that was configured for FTS and the term to search for.
.Search(x => x.EmployeeNotes, "French")
.OfType<Employee>()
.ToList();
// * Results will contain all Employee documents that have 'French' in their 'Notes' field.
//
// * Search is case-sensitive since field was indexed using the 'WhitespaceAnalyzer'
// which preserves casing.
List<Employee> employees = await asyncSession
// Query the index
.Query<Employees_ByNotes.IndexEntry, Employees_ByNotes>()
// Call 'Search':
// pass the index field that was configured for FTS and the term to search for.
.Search(x => x.EmployeeNotes, "French")
.OfType<Employee>()
.ToListAsync();
// * Results will contain all Employee documents that have 'French' in their 'Notes' field.
//
// * Search is case-sensitive since field was indexed using the 'WhitespaceAnalyzer'
// which preserves casing.
List<Employee> employees = session.Advanced
// Query the index
.DocumentQuery<Employees_ByNotes.IndexEntry, Employees_ByNotes>()
// Call 'Search':
// pass the index field that was configured for FTS and the term to search for.
.Search(x => x.EmployeeNotes, "French")
.OfType<Employee>()
.ToList();
// * Results will contain all Employee documents that have 'French' in their 'Notes' field.
//
// * Search is case-sensitive since field was indexed using the 'WhitespaceAnalyzer'
// which preserves casing.
from index "Employees/ByNotes"
where search(EmployeeNotes, "French")
Indexing multiple fields for FTS
The index:
public class Employees_ByEmployeeData :
AbstractIndexCreationTask<Employee, Employees_ByEmployeeData.IndexEntry>
{
public class IndexEntry
{
public object[] EmployeeData { get; set; }
}
public Employees_ByEmployeeData()
{
Map = employees => from employee in employees
select new IndexEntry()
{
EmployeeData = new object[]
{
// Multiple document-fields can be indexed
// into the single index-field 'EmployeeData'
employee.FirstName,
employee.LastName,
employee.Title,
employee.Notes
}
};
// Configure the index-field for FTS:
// Set 'FieldIndexing.Search' on index-field 'EmployeeData'
Index(x => x.EmployeeData, FieldIndexing.Search);
// Note:
// Since no analyzer is set then the default 'RavenStandardAnalyzer' is used.
}
}
Sample query:
List<Employee> employees = session
// Query the static-index
.Query<Employees_ByEmployeeData.IndexEntry, Employees_ByEmployeeData>()
// A logical OR is applied between the following two Search calls:
.Search(x => x.EmployeeData, "Manager")
// A logical AND is applied between the following two terms:
.Search(x => x.EmployeeData, "French Spanish", @operator: SearchOperator.And)
.OfType<Employee>()
.ToList();
// * Results will contain all Employee documents that have:
// ('Manager' in any of the 4 document-fields that were indexed)
// OR
// ('French' AND 'Spanish' in any of the 4 document-fields that were indexed)
//
// * Search is case-insensitive since the default analyzer is used
List<Employee> employees = await asyncSession
// Query the static-index
.Query<Employees_ByEmployeeData.IndexEntry, Employees_ByEmployeeData>()
// A logical OR is applied between the following two Search calls:
.Search(x => x.EmployeeData, "Manager")
// A logical AND is applied between the following two terms:
.Search(x => x.EmployeeData, "French Spanish", @operator: SearchOperator.And)
.OfType<Employee>()
.ToListAsync();
// * Results will contain all Employee documents that have:
// ('Manager' in any of the 4 document-fields that were indexed)
// OR
// ('French' AND 'Spanish' in any of the 4 document-fields that were indexed)
//
// * Search is case-insensitive since the default analyzer is used
List<Employee> employees = session.Advanced
// Query the static-index
.DocumentQuery<Employees_ByEmployeeData.IndexEntry, Employees_ByEmployeeData>()
.OpenSubclause()
// A logical OR is applied between the following two Search calls:
.Search(x => x.EmployeeData, "Manager")
// A logical AND is applied between the following two terms:
.Search(x => x.EmployeeData, "French Spanish", @operator: SearchOperator.And)
.CloseSubclause()
.OfType<Employee>()
.ToList();
// * Results will contain all Employee documents that have:
// ('Manager' in any of the 4 document-fields that were indexed)
// OR
// ('French' AND 'Spanish' in any of the 4 document-fields that were indexed)
//
// * Search is case-insensitive since the default analyzer is used
from index "Employees/ByEmployeeData"
where (search(EmployeeData, "Manager") or search(EmployeeData, "French Spanish", and))
Boosting search results
-
In order to prioritize results, you can provide a boost value to the searched terms.
This can be applied by either of the following:-
Add a boost value to the relevant index-field inside the index definition.
Refer to article indexes - boosting. -
Add a boost value to the queried terms at query time.
Refer to article Boost search results.
-