Orchard and Lucene: getting started with custom search – problems with booleans

by Oliver 12. September 2011 01:46

Update: I filed a bug in the Orchard issue tracker and it’s already been fixed. Nice work!


We’ve just started to build our own customized search module. The goal is to have checkboxes for all searchable boolean properties of a given ContentPart and search by checking or unchecking them.

In Orchard, one can add contents to the search index using the following code (this is an example for adding a boolean):

   1: public class MyPartHandler : ContentHandler
   2: {
   3:     public MyPartHandler(IRepository<MyPartRecord> repository)
   4:     {
   5:         Filters.Add(StorageFilter.For(repository));
   6:  
   7:         OnIndexing<MyPart>(
   8:             (context, myPart) => context.DocumentIndex
   9:                                      .Add("DogsAllowed", myPart.DogsAllowed == true ? "true" : "false").Store());
  10:     }
  11: }
Now, when a user checks the checkbox for “DogsAllowed” we want to ask the search index for all documents (Lucene term for what I would call an entity) with the DogsAllowed property set to true. The way we would do that is something along the following lines:
   1: ...
   2: var searchBuilder = _indexProvider().CreateSearchBuilder("Search")
   3: searchBuilder.WithField("DogsAllowed", true);
   4: ...

The problem with this is: it gives me zero results! Of course, to get something into the index you have to go and save some document/entity for the OnIndexing handler to run, but I’d done that. Somehow I had the feeling that my search was not doing what it was supposed to…

So I wanted to know more about the index Lucene had generated so far. Searching for lucene index visualizer lead me to Luke, the Lucene index toolbox which I downloaded v 0.9.9.1 of because it was built against Lucene 2.9.1 and in Orchard the Lucene.NET version is only 2.9.2. Maybe a newer version would have also worked but I got what I wanted anyways.

After providing the path to the index files, Luke showed me some interesting stuff, among other things that the boolean was stored as string “True” (mind the upper case!):

image

This behavior is actually no surprise when we look at the source code for the Add(string name, bool value) method, it just calls the Add(string name, string value) method with the bool converted to a string:

   1: public IDocumentIndex Add(string name, bool value) {
   2:     return Add(name, value.ToString());
   3: }
It’s good to know that true.ToString() == “True” btw.

So, now when I search inside Luke using the query DogsAllowed:True I do get a result – but using DogsAllowed:true I don’t! (The syntax for searches is field:value.)

image               image

So now, why does my search (in Orchard) for all documents that have the DogsAllowed property set to true return no results when there obviously is a document in the index with exactly that?

Well, let’s look at the implementation of .WithField():

   1: public ISearchBuilder WithField(string field, bool value) {
   2:     CreatePendingClause();
   3:     _query = new TermQuery(new Term(field, value.ToString().ToLower()));
   4:     return this;
   5: }
When I debugged through the search I saw that all of a sudden we were searching for DogsAllowed==true instead of True which to Lucene are two completely different things (at least in this scenario). Actually, turning on logging for the Lucene.Services namespace, we get some debug logging telling us what the search is currently looking for:
   1: 2011-09-10 00:44:33,026 [6] Lucene.Services.LuceneIndexProvider - Searching: DogsAllowed:true* 

Now I’m not surprised anymore that I’m not getting any results!

This is obviously a bug in Orchard’s Lucene module, but for now I can easily get around it using simply passing the lower case strings to the Add() method myself like this:

   1: OnIndexing<MyPart>(
   2:     (context, myPart) => context.DocumentIndex
   3:                             .Add("DogsAllowed", myPart.DogsAllowed == true ? "true" : "false").Store());

Happy coding!

Comments are closed

About Oliver

shades-of-orange.com code blog logo I build web applications using ASP.NET and have a passion for javascript. Enjoy MVC 4 and Orchard CMS, and I do TDD whenever I can. I like clean code. Love to spend time with my wife and our children. My profile on Stack Exchange, a network of free, community-driven Q&A sites

About Anton

shades-of-orange.com code blog logo I'm a software developer at teamaton. I code in C# and work with MVC, Orchard, SpecFlow, Coypu and NHibernate. I enjoy beach volleyball, board games and Coke.