I started using RavenDB since a couple days and I'm already stuck in something, I think, should be quite easy to perform.
What I would like to do is a search to obtain a list of products that have in the property Title
all the words typed by an user.
An example:
product/1 -> title: "my awesome product"
product/2 -> title: "super product asd"
If I search "prod per" I would expect only the second product to appear as the result.
In my head, I would do something like this
public IList<Product> GetBySearchTerms(string searchTerms, int pageIndex, int pageSize, out int totalItems)
{
pageIndex--;
if (pageIndex < 0)
pageIndex = 0;
IList<Product> result = new List<Product>();
var query = var query = session.Query<Product>().Statistics(out stats);
var termsList = searchTerms.Split(' ', StringSplitOptions.RemoveEmptyEntries);
foreach (var term in termsList)
query = query.Where(x => x.Title.Contains(term));
if (pageSize > 0)
result = query.Skip(pageIndex * pageSize).Take(pageSize).ToList();
else
result = query.ToList();
totalItems = stats.TotalResults;
return result;
}
After some digging I found out that the first problem is in the Contains
method. It is not implemented/supported due to how the search behave in RavenDB.
I should instead use the Search
method, but I also read that using *term*
should not be used due to performance issues.
So I ended up creating an Index in RavenDB like this one
Name: ProductSearchByName
Map: from doc in docs.Products select new { Title = doc.Title }
And the code
public IList<Product> GetBySearchTerms(string searchTerms, int pageIndex, int pageSize, out int totalItems)
{
pageIndex--;
if (pageIndex < 0)
pageIndex = 0;
IList<Product> result = new List<Product>();
RavenQueryStatistics stats;
var query = session.Query<Product>("ProductSearchByName").Statistics(out stats);
query = searchTerms
.Split(new[] { " " }, StringSplitOptions.RemoveEmptyEntries)
.Aggregate(query, (q, term) => q.Search(x => x.Title, "*" + term + "*", options: SearchOptions.And, escapeQueryOptions: EscapeQueryOptions.AllowAllWildcards));
if (pageSize > 0)
result = query.Skip(pageIndex * pageSize).Take(pageSize).ToList();
else
result = query.ToList();
totalItems = stats.TotalResults;
return result;
}
This search does what I need, but I'm concerned about all the warnings about using the wildcards.
Is there a way to obtain the Contains
result without using *term*
?
What should be a correct approach / solution to this problem?