NHibernate-search Sort with Analyzer

Hello everyone,
I’m trying to sort a field with lowercase and with their full content in NHibernate-Search.
I downloaded the code from NHibernate-Search github directory, to update our codebase to latest NHibernate version to prevent vulnerabilities.

Anyway, I’m unable sort a text as lowercase and with full content. It was working before but I guess the tokenization changed and now sorting doesnt work like before.

Here is the code:

using System; using FluentNHibernate.Cfg; using FluentNHibernate.Cfg.Db; using FluentNHibernate.Mapping; using NHibernate; using System.Linq; using NHibernate.Search; using NHibernate.Search.Attributes; using NHibernate.Search.Store; using NHibernate.Search.Event; using Lucene.Net.Analysis.Standard; using Lucene.Net.Search; using Lucene.Net.Analysis.Core; using Lucene.Net.Analysis; using Lucene.Net.Util; using System.IO; namespace MyNHibernate.LuceneSort { internal class BasicStringSort { static void Main() { Console.WriteLine("Testing NHibernate Sort"); Console.WriteLine($"NHibernate Version: {typeof(ISession).Assembly.GetName().Version}"); Account A1, A2, A3, A4, A5, A6; var sessionFactory = CreateSessionFactory(); DeleteAccounts(sessionFactory); var allAccounts = new Account[] { }; using (var session = sessionFactory.OpenSession()) { using (var transaction = session.BeginTransaction()) { A1 = new Account("mgn"); A2 = new Account("MGN"); A3 = new Account("Third"); A4 = new Account("MGN"); A5 = new Account("qwewqewq"); A6 = new Account("EUROPE"); allAccounts = [A1, A2, A3, A4, A5, A6]; session.SaveOrUpdate(A1); session.SaveOrUpdate(A2); session.SaveOrUpdate(A3); session.SaveOrUpdate(A4); session.SaveOrUpdate(A5); session.SaveOrUpdate(A6); transaction.Commit(); } } using (var session = sessionFactory.OpenSession()) { var fullTextSession = Search.CreateFullTextSession(session); var fullTextQuery = fullTextSession.CreateFullTextQuery<Account>("*:*") .SetSort(new Sort(new SortField("Name_sort", SortFieldType.STRING, false))); var nhSearchResults = fullTextQuery.List<Account>(); foreach (var result in nhSearchResults) { Console.WriteLine($"Found Account: {result.Id}-{result.Name}"); } var sortedAccounts = allAccounts.OrderBy(w => w.Name.ToLower()).ToArray(); if (sortedAccounts.Select(a => a.Name).SequenceEqual(nhSearchResults.Select(a => a.Name))) { Console.WriteLine("Sort worked properly."); } else { Console.WriteLine("Error! Sort didn't work properly!"); } } DeleteAccounts(sessionFactory); } private static void DeleteAccounts(ISessionFactory sessionFactory) { using var session = sessionFactory.OpenSession(); using var transaction = session.BeginTransaction(); session.CreateQuery("DELETE FROM Account").ExecuteUpdate(); transaction.Commit(); } public static ISessionFactory CreateSessionFactory() { var factory = Fluently.Configure() .Database(MsSqlConfiguration.MsSql2005 .ShowSql() .ConnectionString("Data Source=.;Initial Catalog=test_db;User Id=id;Password=pw")) .Mappings(m => m.FluentMappings .Add<AccountMap>() ) .ExposeConfiguration(cfg => { cfg.SetProperty("hibernate.search.default.directory_provider", typeof(RAMDirectoryProvider).AssemblyQualifiedName); cfg.SetProperty("hibernate.search.default.indexBase.create", "true"); cfg.SetProperty("hibernate.search.analyzer", typeof(StandardAnalyzer).AssemblyQualifiedName); cfg.SetProperty("hibernate.search.indexing_strategy", "event"); cfg.SetListener(NHibernate.Event.ListenerType.PostUpdate, new FullTextIndexEventListener()); cfg.SetListener(NHibernate.Event.ListenerType.PostInsert, new FullTextIndexEventListener()); cfg.SetListener(NHibernate.Event.ListenerType.PostDelete, new FullTextIndexEventListener()); }) .BuildSessionFactory(); using (var session = factory.OpenSession()) { var searchFactory = Search.CreateFullTextSession(session).SearchFactory; searchFactory.Optimize(); } return factory; } } [Indexed] public class Account { [DocumentId] public virtual int Id { get; protected set; } public virtual int LastDataVersion { get; set; } [Field(Name = "Name_sort", Index = Index.UnTokenized)] [Analyzer(typeof(LowerKeywordAnalyzer))] public virtual string Name { get; set; } public Account() { Name = string.Empty; } public Account(string name) { Name = name; } public override string ToString() => $"Name:{Name}"; } public class AccountMap : ClassMap<Account> { public AccountMap() { Schema("accounts"); Table("Accounts"); Id(x => x.Id); Map(x => x.LastDataVersion); Map(x => x.Name, "Name"); } } public class LowerKeywordAnalyzer : Analyzer { protected override TokenStreamComponents CreateComponents(string fieldName, TextReader reader) { Tokenizer tokenizer = new KeywordTokenizer(reader); TokenStream tokenStream = new LowerCaseFilter(LuceneVersion.LUCENE_48, tokenizer); return new TokenStreamComponents(tokenizer, tokenStream); } } } 

I looked at the docs and I found that I need to use Normalizer, but I couldnt find the Normalizer in NHibernate: Hibernate Search 5.8.2.Final: Reference Guide

All I want is something works similar to this:

var sortedAccounts = allAccounts.OrderBy(w => w.Name.ToLower()); 

Hey @kasap

This forum is for the Hibernate Search, and we do not actually maintain the port NHibernate-search…
With that in mind … if you want to get a behavior similar to a normalizer from an analyzer try using the KeywordTokenizer [1] (link to Lucene 4.8 as you have this version mentioned in your code). This keyword tokenizer returns the entire input as a single output token.

[1] KeywordTokenizer (Lucene 4.8.0 API)