Recently I had a need for fuzzy search in one of my projects. Since I was using NHibernate, NHibernate.Search (Lucene.NET-based) seemed like a good choice. However, there was one limitation — NHSearch required custom attributes for its mapping.

That was suboptimal. I had to reference NHSearch from domain entities class, I had to add Id property to my entities to map NHSearch [DocumentId]. Fortunately, it was a free time project, so I decided to take a break from it and fix the NHSearch mapping.

Some days later, the patch was in NHSearch trunk.

So, while there are no new built-in mappings except the attribute one, it is now possible to create a mapping manually. Here is a simple sample mapping that indexes Title property of Book entity:

using NHibernate.Cfg;
using NHibernate.Engine;
using NHibernate.Properties;
using NHibernate.Search.Bridge;
using NHibernate.Search.Mapping;

internal class CustomSearchMapping : ISearchMapping {
    public ICollection<DocumentMapping> Build(Configuration cfg) {
        var bookMapping = new DocumentMapping(typeof(Book)) {
            DocumentId = new DocumentIdMapping("Id", BridgeFactory.INTEGER, null),
            Fields = { MapField<Book>(book => book.Title) }
        };

        return new List<DocumentMapping> { bookMapping };
    }

    private FieldMapping MapField<T>(Expression<Func<T, object>> propertyReference) {
        var property = (PropertyInfo)((MemberExpression)propertyReference.Body).Member;

        var getter = new BasicPropertyAccessor.BasicGetter(typeof(T), property, property.Name);
        var bridge = BridgeFactory.GuessType(property.Name, property.PropertyType, null, null);

        return new FieldMapping(property.Name, bridge, getter);
    }
}

That’s something that will hopefully get better in time (notice required call to BridgeFactory.GuessType with nulls, that one thing I haven’t yet got to fix).
But this works, and this does not require attributes (and this does not require reflection at all, actually, you can write your own IGetter with any kind of logic).

One more thing — the “Id” parameter to DocumentIdMapping is how the Id field will be named in index, not your identifier property name.
There is also additional parameter to DocumentIdMapping that specifies property name, but in most scenarios it should just work, even if the identifier is not mapped.

To enable this custom mapping, add the following to your nhsearch.cfg.xml or web.config:

<property name="hibernate.search.mapping">Sample.Repositories.CustomSearchMapping, Sample.Repositories</property>
  • regisbsb

    Hi, do you know where can I find some recent demo or tutorial using NHibernate.Search? Its all out dated in my searches.

  • http://blog.ashmind.com Andrey Shchekin

    You mean the search syntax itself? I think it is pretty much unchanged from the Java Lucene.
    In general Lucene tutorials + Ayende samples were fresh enough for me, do not know if there is something better.

  • regisbsb

    Which samples? They are all very old I think.

  • http://blog.ashmind.com Andrey Shchekin

    It is not like NHibernate.Search changed a lot, except for my mapping change.
    Everything described here http://ayende.com/Blog/archive/2009/05/03/nhibe… is still relevant.
    Also, there are query syntax rules: http://lucene.apache.org/java/2_3_2/queryparser….

  • regisbsb

    Thank you very much.

  • regisbsb

    Thank you very much.

  • que0x

    perfect ! for me i had to modify the code a little bit:

    private FieldMapping MapField(Expression<Func> propertyReference)
    {
    var property = (PropertyInfo)((MemberExpression)propertyReference.Body).Member;
    var getter = new BasicPropertyAccessor.BasicGetter(typeof(T), property, property.Name);
    var bridge = BridgeFactory.GuessType(property.Name, property.PropertyType, null, null);

    return new FieldMapping(property.Name, bridge, getter);
    }

    otherwise the property will be UnaryExpression if i used object.