From 836c00fb37f0facf9c2bd917ce9ee3e80558bca0 Mon Sep 17 00:00:00 2001 From: Sebastien Ros Date: Sun, 10 Feb 2013 07:37:34 -0800 Subject: [PATCH] Adding faceted search capabilities --HG-- branch : 1.x --- .../Indexing/LuceneSearchBuilderTests.cs | 19 ++++++++ src/Orchard.Web/Modules/Lucene/Lucene.csproj | 5 +++ .../Lucene/Services/LuceneSearchBuilder.cs | 26 +++++++++++ .../Modules/Lucene/Services/SearchBits.cs | 43 +++++++++++++++++++ src/Orchard/Indexing/ISearchBuilder.cs | 1 + src/Orchard/Indexing/NullSearchBuilder.cs | 5 +++ src/Orchard/Orchard.Framework.csproj | 1 + 7 files changed, 100 insertions(+) create mode 100644 src/Orchard.Web/Modules/Lucene/Services/SearchBits.cs diff --git a/src/Orchard.Tests.Modules/Indexing/LuceneSearchBuilderTests.cs b/src/Orchard.Tests.Modules/Indexing/LuceneSearchBuilderTests.cs index cd76eef81..7259f6b30 100644 --- a/src/Orchard.Tests.Modules/Indexing/LuceneSearchBuilderTests.cs +++ b/src/Orchard.Tests.Modules/Indexing/LuceneSearchBuilderTests.cs @@ -639,5 +639,24 @@ namespace Orchard.Tests.Modules.Indexing { .WithField("field3", 3).Mandatory().AsFilter() .Count(), Is.EqualTo(1)); } + + [Test] + public void ShouldReturnFacetedResults() { + _provider.CreateIndex("default"); + _provider.Store("default", _provider.New(1).Add("body", "michael is in the kitchen").Analyze()); + _provider.Store("default", _provider.New(2).Add("body", "michael has a cousin named michel").Analyze()); + _provider.Store("default", _provider.New(3).Add("body", "speak inside the mic").Analyze()); + _provider.Store("default", _provider.New(4).Add("body", "a dog is pursuing a cat").Analyze()); + _provider.Store("default", _provider.New(5).Add("body", "michael speaks to elephants").Analyze()); + + var michael = SearchBuilder.WithField("body", "michael").GetBits(); + var speak = SearchBuilder.WithField("body", "speak").GetBits(); + Assert.That(michael.Count(), Is.EqualTo(3)); + Assert.That(speak.Count(), Is.EqualTo(2)); + + Assert.That(speak.And(michael).Count(), Is.EqualTo(1)); + Assert.That(speak.Or(michael).Count(), Is.EqualTo(4)); + Assert.That(speak.Xor(michael).Count(), Is.EqualTo(3)); + } } } diff --git a/src/Orchard.Web/Modules/Lucene/Lucene.csproj b/src/Orchard.Web/Modules/Lucene/Lucene.csproj index ebd498216..1b8b6bf21 100644 --- a/src/Orchard.Web/Modules/Lucene/Lucene.csproj +++ b/src/Orchard.Web/Modules/Lucene/Lucene.csproj @@ -21,6 +21,10 @@ 4.0 + + + + true @@ -66,6 +70,7 @@ + diff --git a/src/Orchard.Web/Modules/Lucene/Services/LuceneSearchBuilder.cs b/src/Orchard.Web/Modules/Lucene/Services/LuceneSearchBuilder.cs index 482a03152..7634d6101 100644 --- a/src/Orchard.Web/Modules/Lucene/Services/LuceneSearchBuilder.cs +++ b/src/Orchard.Web/Modules/Lucene/Services/LuceneSearchBuilder.cs @@ -1,4 +1,5 @@ using System; +using System.Collections; using System.Collections.Generic; using System.Globalization; using System.Linq; @@ -6,6 +7,7 @@ using Lucene.Models; using Lucene.Net.Index; using Lucene.Net.Search; using Lucene.Net.Store; +using Lucene.Net.Util; using Orchard.Indexing; using Orchard.Logging; using Lucene.Net.Documents; @@ -355,6 +357,30 @@ namespace Lucene.Services { } + public ISearchBits GetBits() { + var query = CreateQuery(); + IndexSearcher searcher; + + try { + searcher = new IndexSearcher(_directory, true); + } + catch { + // index might not exist if it has been rebuilt + Logger.Information("Attempt to read a none existing index"); + return null; + } + + try { + var filter = new QueryWrapperFilter(query); + var bits = filter.GetDocIdSet(searcher.GetIndexReader()); + var disi = new OpenBitSetDISI(bits.Iterator(), searcher.MaxDoc()); + return new SearchBits(disi); + } + finally { + searcher.Close(); + } + } + public ISearchHit Get(int documentId) { var query = new TermQuery(new Term("id", documentId.ToString(CultureInfo.InvariantCulture))); diff --git a/src/Orchard.Web/Modules/Lucene/Services/SearchBits.cs b/src/Orchard.Web/Modules/Lucene/Services/SearchBits.cs new file mode 100644 index 000000000..01adb981b --- /dev/null +++ b/src/Orchard.Web/Modules/Lucene/Services/SearchBits.cs @@ -0,0 +1,43 @@ +using System; +using Lucene.Net.Util; +using Orchard.Indexing; + +namespace Lucene.Services { + public class SearchBits : ISearchBits { + internal readonly OpenBitSet _openBitSet; + + public SearchBits(OpenBitSet openBitSet) { + _openBitSet = openBitSet; + } + + public ISearchBits And(ISearchBits other) { + return Apply(other, (x, y) => x.And(y)); + } + + public ISearchBits Or(ISearchBits other) { + return Apply(other, (x, y) => x.Or(y)); + } + + public ISearchBits Xor(ISearchBits other) { + return Apply(other, (x, y) => x.Xor(y)); + } + + public long Count() { + return _openBitSet.Cardinality(); + } + + private ISearchBits Apply(ISearchBits other, Action operation) { + var bitset = (OpenBitSet)_openBitSet.Clone(); + var otherBitSet = other as SearchBits; + + if (otherBitSet == null) { + throw new InvalidOperationException("The other bitset must be of type OpenBitSet"); + } + + operation(bitset, otherBitSet._openBitSet); + + return new SearchBits(bitset); + + } + } +} \ No newline at end of file diff --git a/src/Orchard/Indexing/ISearchBuilder.cs b/src/Orchard/Indexing/ISearchBuilder.cs index e2f73280a..de372a7eb 100644 --- a/src/Orchard/Indexing/ISearchBuilder.cs +++ b/src/Orchard/Indexing/ISearchBuilder.cs @@ -57,6 +57,7 @@ namespace Orchard.Indexing { ISearchBuilder Slice(int skip, int count); IEnumerable Search(); ISearchHit Get(int documentId); + ISearchBits GetBits(); int Count(); } diff --git a/src/Orchard/Indexing/NullSearchBuilder.cs b/src/Orchard/Indexing/NullSearchBuilder.cs index 521de9856..003510ad7 100644 --- a/src/Orchard/Indexing/NullSearchBuilder.cs +++ b/src/Orchard/Indexing/NullSearchBuilder.cs @@ -127,6 +127,11 @@ namespace Orchard.Indexing { public ISearchHit Get(int documentId) { return null; } + + public ISearchBits GetBits() { + throw new NotImplementedException(); + } + public int Count() { return 0; } diff --git a/src/Orchard/Orchard.Framework.csproj b/src/Orchard/Orchard.Framework.csproj index c9a10dc31..6e59cbfec 100644 --- a/src/Orchard/Orchard.Framework.csproj +++ b/src/Orchard/Orchard.Framework.csproj @@ -238,6 +238,7 @@ +