Sorting
Tip: There are many examples of sorting in the FluentApiTests
source code to use as examples/reference.
Score
By default search results are ordered by Score descending so there's nothing specific that needs to be done to support this. If you use a different sorting operation then the Score
value will be 0 for all results.
Custom sorting
Any field that is a numerical or date based is automatically sortable. To make text based fields sortable you need to explicitly opt-in for that behavior. By default all fields are FieldDefinitionTypes.FullText
which are not sortable. To make a text field sortable it needs to be FieldDefinitionTypes.FullTextSortable
.
You cannot sort on both the score and a custom field.
Sorting is done by either the OrderBy
or OrderByDescending
methods using a SortableField
and a SortType
. The SortType
should typically match the field definition type (i.e. Int, Long, Double, etc...)
- For
FieldDefinitionTypes.FullTextSortable
useSortType.String
- For
FieldDefinitionTypes.DateTime
useSortType.Long
.
Example:
var searcher = indexer.GetSearcher();
var orderedResults = searcher
.CreateQuery("content")
.Field("writerName", "administrator")
.OrderBy(new SortableField("name", SortType.String))
.Execute();
var orderedDescendingResults = searcher
.CreateQuery("content")
.Field("writerName", "administrator")
.OrderByDescending(new SortableField("name", SortType.String))
.Execute();
Limiting results
To limit results we can use the QueryOptions
class when executing a search query. The QueryOptions
class provides the ability to skip and take.
Examples:
var searcher = indexer.GetSearcher();
var takeFirstTenInIndex = searcher
.CreateQuery()
.All()
.Execute(QueryOptions.SkipTake(0, 10))
var skipFiveAndTakeFirstTenInIndex = searcher
.CreateQuery()
.All()
.Execute(QueryOptions.SkipTake(5, 10))
var takeThreeResults = searcher
.CreateQuery("content")
.Field("writerName", "administrator")
.OrderBy(new SortableField("name", SortType.String))
.Execute(QueryOptions.SkipTake(0, 3));
var takeSevenHundredResults = searcher
.CreateQuery("content")
.Field("writerName", "administrator")
.OrderByDescending(new SortableField("name", SortType.String))
.Execute(QueryOptions.SkipTake(0, 700));
By default when using Execute()
or Execute(QueryOptions.SkipTake(0))
where no take parameter is provided the take of the search will be set to QueryOptions.DefaultMaxResults
(500).
Paging
There's a blog post writeup here on how to properly page with Examine (and Lucene).
There are 2 important parts to this:
- The Skip method on the
ISearchResults
object - The Search overload on the
BaseSearchProvider
where you can specifymaxResults
ISearchResults.Skip
is very different from the Linq Skip method so you need to be sure you are using the Skip
method on the ISearchResults
object. This tells Lucene to skip over a specific number of results without allocating the result objects. If you use Linq’s Skip method on the underlying IEnumerable<SearchResult>
of ISearchResults
, this will allocate all of the result objects and then filter them in memory which is what you don’t want to do.
Lucene isn't perfect for paging because it doesn’t natively support the Linq equivalent to "Skip/Take" (UPDATE: In an upcoming Examine version, it can natively support this!). It understands Skip (as above) but doesn't understand Take, instead it only knows how to limit the max results so that it doesn’t allocate every result, most of which you would probably not need when paging.
With the combination of ISearchResult.Skip
and maxResults
, we can tell Lucene to:
- Skip over a certain number of results without allocating them and tell Lucene
- only allocate a certain number of results after skipping
Example
//for example purposes, we want to show page #4 (which is pageIndex of 3)
var pageIndex = 3;
//for this example, the page size is 10 items
var pageSize = 10;
var searchResult = searchProvider.Search(criteria,
//don't return more results than we need for the paging
//this is the 'trick' - we need to load enough search results to fill
//all pages from 1 to the current page of 4
maxResults: pageSize*(pageIndex + 1));
//then we use the Skip method to tell Lucene to not allocate search results
//for the first 3 pages
var pagedResults = searchResult.Skip(pageIndex*pageSize);
var totalResults = searchResult.TotalItemCount;