├── .gitattributes ├── .gitignore ├── LICENSE.md ├── README.md └── src ├── .nuget ├── NuGet.Config ├── NuGet.exe └── NuGet.targets ├── SimpleLucene.Tests ├── Data │ └── Repository.cs ├── Definitions │ ├── ProductIndexDefinition.cs │ └── ProductResultDefinition.cs ├── Entities │ └── Product.cs ├── IndexOptionsTestFixture.cs ├── IndexQueueTestFixture.cs ├── IndexServiceTestFixture.cs ├── MemoryIndexSearcher.cs ├── MemoryIndexWriter.cs ├── Properties │ └── AssemblyInfo.cs ├── Queries │ └── ProductQuery.cs ├── SearchServiceTestFixture.cs ├── SimpleLucene.Tests.csproj ├── Tasks │ ├── ProductUpdateTask.cs │ └── TaskEqualityTestFixture.cs ├── TestIndexLocation.cs └── packages.config ├── SimpleLucene.sln └── SimpleLucene ├── BaseIndexLocation.cs ├── DelegateSearchResultDefinition.cs ├── DirectoryIndexSearcher.cs ├── DirectoryIndexWriter.cs ├── DocumentExtensions.cs ├── DocumentResultDefinition.cs ├── FileSystemIndexLocation.cs ├── IIndexDefinition.cs ├── IIndexLocation.cs ├── IIndexSearcher.cs ├── IIndexService.cs ├── IIndexWriter.cs ├── IResultDefinition.cs ├── ISearchService.cs ├── IndexManagement ├── IIndexTask.cs ├── Impl │ ├── EntityDeleteTask.cs │ └── EntityUpdateTask.cs └── IndexQueue.cs ├── IndexOptions.cs ├── IndexResult.cs ├── IndexService.cs ├── Properties └── AssemblyInfo.cs ├── QueryBase.cs ├── SearchResult.cs ├── SearchService.cs ├── SimpleLucene.csproj ├── SimpleLuceneExtensions.cs └── packages.config /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | ############################################################################### 7 | # Set default behavior for command prompt diff. 8 | # 9 | # This is need for earlier builds of msysgit that does not have it on by 10 | # default for csharp files. 11 | # Note: This is only used by command line 12 | ############################################################################### 13 | #*.cs diff=csharp 14 | 15 | ############################################################################### 16 | # Set the merge driver for project and solution files 17 | # 18 | # Merging from the command prompt will add diff markers to the files if there 19 | # are conflicts (Merging from VS is not affected by the settings below, in VS 20 | # the diff markers are never inserted). Diff markers may cause the following 21 | # file extensions to fail to load in VS. An alternative would be to treat 22 | # these files as binary and thus will always conflict and require user 23 | # intervention with every merge. To do so, just uncomment the entries below 24 | ############################################################################### 25 | #*.sln merge=binary 26 | #*.csproj merge=binary 27 | #*.vbproj merge=binary 28 | #*.vcxproj merge=binary 29 | #*.vcproj merge=binary 30 | #*.dbproj merge=binary 31 | #*.fsproj merge=binary 32 | #*.lsproj merge=binary 33 | #*.wixproj merge=binary 34 | #*.modelproj merge=binary 35 | #*.sqlproj merge=binary 36 | #*.wwaproj merge=binary 37 | 38 | ############################################################################### 39 | # behavior for image files 40 | # 41 | # image files are treated as binary by default. 42 | ############################################################################### 43 | #*.jpg binary 44 | #*.png binary 45 | #*.gif binary 46 | 47 | ############################################################################### 48 | # diff behavior for common document formats 49 | # 50 | # Convert binary document formats to text before diffing them. This feature 51 | # is only available from the command line. Turn it on by uncommenting the 52 | # entries below. 53 | ############################################################################### 54 | #*.doc diff=astextplain 55 | #*.DOC diff=astextplain 56 | #*.docx diff=astextplain 57 | #*.DOCX diff=astextplain 58 | #*.dot diff=astextplain 59 | #*.DOT diff=astextplain 60 | #*.pdf diff=astextplain 61 | #*.PDF diff=astextplain 62 | #*.rtf diff=astextplain 63 | #*.RTF diff=astextplain 64 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | [Oo]bj 2 | [Bb]in 3 | *.user 4 | *.suo 5 | *.[Cc]ache 6 | *.bak 7 | *.ncb 8 | *.log 9 | *.DS_Store 10 | [Tt]humbs.db 11 | _ReSharper.* 12 | *.resharper 13 | Ankh.NoLoad 14 | tools 15 | build 16 | packages 17 | artifacts -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License 2 | 3 | Copyright (c) 2014 Ben Foster, United Kingdom 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 6 | 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | SimpleLucene 2 | ============ 3 | 4 | SimpleLucene is a wrapper for the popular Lucene.NET search engine. -------------------------------------------------------------------------------- /src/.nuget/NuGet.Config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/.nuget/NuGet.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/benfoster/SimpleLucene/a0fdd8fc86a723d06d148e8ed4b1cc33bbdad107/src/.nuget/NuGet.exe -------------------------------------------------------------------------------- /src/.nuget/NuGet.targets: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | $(MSBuildProjectDirectory)\..\ 5 | 6 | 7 | false 8 | 9 | 10 | false 11 | 12 | 13 | true 14 | 15 | 16 | false 17 | 18 | 19 | 20 | 21 | 22 | 26 | 27 | 28 | 29 | 30 | $([System.IO.Path]::Combine($(SolutionDir), ".nuget")) 31 | 32 | 33 | 34 | 35 | $(SolutionDir).nuget 36 | 37 | 38 | 39 | packages.$(MSBuildProjectName.Replace(' ', '_')).config 40 | 41 | 42 | 43 | 44 | 45 | $(PackagesProjectConfig) 46 | 47 | 48 | 49 | 50 | packages.config 51 | 52 | 53 | 54 | 55 | 56 | 57 | $(NuGetToolsPath)\NuGet.exe 58 | @(PackageSource) 59 | 60 | "$(NuGetExePath)" 61 | mono --runtime=v4.0.30319 $(NuGetExePath) 62 | 63 | $(TargetDir.Trim('\\')) 64 | 65 | -RequireConsent 66 | -NonInteractive 67 | 68 | "$(SolutionDir) " 69 | "$(SolutionDir)" 70 | 71 | 72 | $(NuGetCommand) install "$(PackagesConfig)" -source "$(PackageSources)" $(NonInteractiveSwitch) $(RequireConsentSwitch) -solutionDir $(PaddedSolutionDir) 73 | $(NuGetCommand) pack "$(ProjectPath)" -Properties "Configuration=$(Configuration);Platform=$(Platform)" $(NonInteractiveSwitch) -OutputDirectory "$(PackageOutputDir)" -symbols 74 | 75 | 76 | 77 | RestorePackages; 78 | $(BuildDependsOn); 79 | 80 | 81 | 82 | 83 | $(BuildDependsOn); 84 | BuildPackage; 85 | 86 | 87 | 88 | 89 | 90 | 91 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 106 | 107 | 110 | 111 | 112 | 113 | 115 | 116 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 148 | 149 | 150 | 151 | 152 | -------------------------------------------------------------------------------- /src/SimpleLucene.Tests/Data/Repository.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using SimpleLucene.Tests.Entities; 6 | 7 | namespace SimpleLucene.Tests.Data 8 | { 9 | public class Repository 10 | { 11 | public IList Products { 12 | get { 13 | return new List { 14 | new Product { Id = 1, Name = "Football" }, 15 | new Product { Id = 2, Name = "Coffee Cup"}, 16 | new Product { Id = 3, Name = "Nike Trainers"}, 17 | new Product { Id = 4, Name = "Apple iPod Nano"}, 18 | new Product { Id = 5, Name = "Asus eeePC"}, 19 | new Product { Id = 6, Name = "Pack of cards"}, 20 | new Product { Id = 7, Name = "Funky TShirt"}, 21 | new Product { Id = 8, Name = "Blackberry 9800"}, 22 | new Product { Id = 9, Name = "Guitar Hero"}, 23 | new Product { Id = 10, Name = "House Plant"}, 24 | }; 25 | } 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/SimpleLucene.Tests/Definitions/ProductIndexDefinition.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using SimpleLucene.Tests.Entities; 6 | using Lucene.Net.Documents; 7 | using Lucene.Net.Index; 8 | 9 | namespace SimpleLucene.Tests.Definitions 10 | { 11 | public class ProductIndexDefinition : IIndexDefinition 12 | { 13 | public Document Convert(Product p) { 14 | var document = new Document(); 15 | document.Add(new Field("type", "product", Field.Store.NO, Field.Index.NOT_ANALYZED)); 16 | document.Add(new Field("id", p.Id.ToString(), Field.Store.YES, Field.Index.NOT_ANALYZED)); 17 | document.Add(new Field("name", p.Name, Field.Store.YES, Field.Index.ANALYZED)); 18 | return document; 19 | } 20 | 21 | public Term GetIndex(Product p) { 22 | return new Term("id", p.Id.ToString()); 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/SimpleLucene.Tests/Definitions/ProductResultDefinition.cs: -------------------------------------------------------------------------------- 1 | using SimpleLucene.Tests.Entities; 2 | 3 | namespace SimpleLucene.Tests.Definitions 4 | { 5 | public class ProductResultDefinition : IResultDefinition 6 | { 7 | public Product Convert(Lucene.Net.Documents.Document document) 8 | { 9 | return new Product 10 | { 11 | Id = document.GetValue("id"), 12 | Name = document.GetValue("name") 13 | }; 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/SimpleLucene.Tests/Entities/Product.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | namespace SimpleLucene.Tests.Entities 7 | { 8 | public class Product 9 | { 10 | public int Id { get; set; } 11 | public string Name { get; set; } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/SimpleLucene.Tests/IndexOptionsTestFixture.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using NUnit.Framework; 6 | using Lucene.Net.Analysis.Standard; 7 | using Lucene.Net.Analysis; 8 | 9 | namespace SimpleLucene.Tests 10 | { 11 | [TestFixture] 12 | public class IndexOptionsTestFixture 13 | { 14 | [Test] 15 | public void Two_default_instances_are_equal() 16 | { 17 | var io1 = new IndexOptions(); 18 | var io2 = new IndexOptions(); 19 | 20 | Assert.AreEqual(io1, io2); 21 | } 22 | 23 | [Test] 24 | public void Two_references_to_the_same_object_are_equal() 25 | { 26 | var io1 = new IndexOptions { IndexLocation = new TestIndexLocation("products") }; 27 | var io2 = io1; 28 | Assert.AreEqual(io1, io2); 29 | } 30 | 31 | [Test] 32 | public void Two_index_options_with_same_locations_are_equal() 33 | { 34 | var io1 = new IndexOptions { IndexLocation = new TestIndexLocation("products") }; 35 | var io2 = new IndexOptions { IndexLocation = new TestIndexLocation("products") }; 36 | Assert.AreEqual(io1, io2); 37 | } 38 | 39 | [Test] 40 | public void Two_index_options_with_different_locations_are_not_equal() { 41 | var io1 = new IndexOptions { IndexLocation = new TestIndexLocation("products") }; 42 | var io2 = new IndexOptions { IndexLocation = new TestIndexLocation("products2") }; 43 | Assert.AreNotEqual(io1, io2); 44 | } 45 | 46 | [Test] 47 | public void Two_index_options_with_different_recreate_index_are_not_equal() 48 | { 49 | var io1 = new IndexOptions { IndexLocation = new TestIndexLocation("products"), RecreateIndex = true }; 50 | var io2 = new IndexOptions { IndexLocation = new TestIndexLocation("products"), RecreateIndex = false }; 51 | Assert.AreNotEqual(io1, io2); 52 | } 53 | 54 | [Test] 55 | public void Two_index_options_with_different_optimize_index_are_not_equal() 56 | { 57 | var io1 = new IndexOptions { IndexLocation = new TestIndexLocation("products"), OptimizeIndex = true }; 58 | var io2 = new IndexOptions { IndexLocation = new TestIndexLocation("products"), OptimizeIndex = false }; 59 | Assert.AreNotEqual(io1, io2); 60 | } 61 | 62 | [Test] 63 | public void Two_index_options_with_different_analyzers_are_not_equal() 64 | { 65 | var io1 = new IndexOptions { IndexLocation = new TestIndexLocation("products") }; // default Standard Analyzer 66 | var io2 = new IndexOptions { IndexLocation = new TestIndexLocation("products"), Analyzer = new SimpleAnalyzer() }; 67 | Assert.AreNotEqual(io1, io2); 68 | } 69 | 70 | [Test] 71 | public void Two_index_options_with_same_attributes_are_equal() 72 | { 73 | var io1 = new IndexOptions(); 74 | io1.Attributes.Add("somekey", "somevalue"); 75 | io1.Attributes.Add("somecount", 100); 76 | var io2 = new IndexOptions(); 77 | io2.Attributes.Add("somekey", "somevalue"); 78 | io2.Attributes.Add("somecount", 100); 79 | Assert.AreEqual(io1, io2); 80 | } 81 | 82 | [Test] 83 | public void Two_index_options_with_different_attributes_are_not_equal() 84 | { 85 | var io1 = new IndexOptions(); 86 | io1.Attributes.Add("somekey", "somevalue"); 87 | var io2 = new IndexOptions(); 88 | io2.Attributes.Add("someotherkey", "someothervalue"); 89 | Assert.AreNotEqual(io1, io2); 90 | } 91 | 92 | [Test] 93 | public void Two_index_options_with_same_attributes_but_diff_values_are_not_equal() 94 | { 95 | var io1 = new IndexOptions(); 96 | io1.Attributes.Add("somekey", "somevalue"); 97 | io1.Attributes.Add("somecount", 100); 98 | var io2 = new IndexOptions(); 99 | io2.Attributes.Add("somekey", "somevalue2"); 100 | io2.Attributes.Add("somecount", 150); 101 | Assert.AreNotEqual(io1, io2); 102 | } 103 | } 104 | } 105 | 106 | -------------------------------------------------------------------------------- /src/SimpleLucene.Tests/IndexQueueTestFixture.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using NUnit.Framework; 4 | using SimpleLucene.IndexManagement; 5 | using SimpleLucene.Tests.Definitions; 6 | using SimpleLucene.Tests.Entities; 7 | 8 | namespace SimpleLucene.Tests 9 | { 10 | [TestFixture] 11 | public class IndexQueueTestFixture 12 | { 13 | [Test] 14 | public void Can_add_index_task_to_queue() 15 | { 16 | var task = new EntityUpdateTask(new Product(), 17 | new ProductIndexDefinition(), new TestIndexLocation()); 18 | 19 | IndexQueue.Instance.Queue(task); 20 | 21 | Assert.IsTrue(IndexQueue.Instance.Contains(task)); 22 | 23 | IIndexTask fromQueue; 24 | IndexQueue.Instance.TryDequeue(out fromQueue); 25 | 26 | Assert.AreEqual(task, fromQueue); 27 | } 28 | 29 | 30 | [Test] 31 | public void Can_resolve_index_services_when_processing_index_queue() 32 | { 33 | var t1 = new EntityUpdateTask(new Product(), new ProductIndexDefinition(), new TestIndexLocation("products")); 34 | var t2 = new EntityUpdateTask(new Product(), new ProductIndexDefinition(), new TestIndexLocation("products")); 35 | var t3 = new EntityUpdateTask(new Product(), new ProductIndexDefinition(), new TestIndexLocation("products2")); 36 | 37 | Action queue = t => IndexQueue.Instance.Queue(t); 38 | 39 | queue(t1); 40 | queue(t2); 41 | queue(t3); 42 | 43 | var services = new HashSet(); 44 | 45 | IIndexTask task; 46 | while (IndexQueue.Instance.TryDequeue(out task)) 47 | { 48 | var service = services.FindWithOptions(task.IndexOptions); 49 | 50 | if (service == null) { 51 | service = new IndexService(new DirectoryIndexWriter(task.IndexOptions.IndexLocation.GetDirectory(), task.IndexOptions.RecreateIndex)); 52 | services.Add(service); 53 | } 54 | 55 | // process the task 56 | } 57 | 58 | Assert.IsTrue(services.Count == 2); 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/SimpleLucene.Tests/IndexServiceTestFixture.cs: -------------------------------------------------------------------------------- 1 | using Lucene.Net.Index; 2 | using Lucene.Net.Search; 3 | using NUnit.Framework; 4 | using SimpleLucene.Tests.Data; 5 | using SimpleLucene.Tests.Definitions; 6 | using System.Linq; 7 | 8 | namespace SimpleLucene.Tests 9 | { 10 | [TestFixture] 11 | public class IndexServiceTestFixture 12 | { 13 | [Test] 14 | public void Can_index_a_single_entity() 15 | { 16 | var repo = new Repository(); 17 | var product = repo.Products.First(); 18 | 19 | var writer = new MemoryIndexWriter(true); 20 | 21 | using (var indexService = new IndexService(writer)) 22 | { 23 | var result = indexService.IndexEntity(product, new ProductIndexDefinition()); 24 | Assert.AreEqual(1, result.Count); 25 | Assert.AreEqual(0, result.Errors.Count); 26 | } 27 | } 28 | 29 | [Test] 30 | public void Can_index_a_collection_of_entities() 31 | { 32 | var repo = new Repository(); 33 | 34 | var writer = new MemoryIndexWriter(true); 35 | using (var indexService = new IndexService(writer)) 36 | { 37 | var result = indexService.IndexEntities(repo.Products, new ProductIndexDefinition()); 38 | Assert.AreEqual(10, result.Count); 39 | Assert.AreEqual(0, result.Errors.Count); 40 | } 41 | } 42 | 43 | [Test] 44 | public void Can_use_the_same_index_writer_for_multiple_index_operations() 45 | { 46 | var repo = new Repository(); 47 | 48 | var writer = new MemoryIndexWriter(true); 49 | var indexService = new IndexService(writer); 50 | 51 | var p1p5 = repo.Products.Skip(0).Take(5); 52 | var result = indexService.IndexEntities(p1p5, new ProductIndexDefinition()); 53 | Assert.AreEqual(5, result.Count); 54 | 55 | var p6p10 = repo.Products.Skip(5).Take(5); 56 | var result2 = indexService.IndexEntities(p6p10, new ProductIndexDefinition()); 57 | Assert.AreEqual(5,result2.Count); 58 | 59 | var searcher = new MemoryIndexSearcher(writer.Directory, true); 60 | 61 | var searchResult = new SearchService(searcher).SearchIndex(new TermQuery(new Term("type", "product"))); 62 | 63 | Assert.AreEqual(0, searchResult.Documents.Count(), "Index writer has not yet been committed so should return 0"); 64 | // commits writer 65 | indexService.Dispose(); 66 | 67 | searchResult = new SearchService(searcher).SearchIndex(new TermQuery(new Term("type", "product"))); 68 | Assert.AreEqual(10, searchResult.Documents.Count()); 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/SimpleLucene.Tests/MemoryIndexSearcher.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.IO; 6 | using Lucene.Net.Search; 7 | using Lucene.Net.Store; 8 | 9 | namespace SimpleLucene.Tests 10 | { 11 | public class MemoryIndexSearcher : IIndexSearcher 12 | { 13 | private readonly bool readOnly; 14 | private readonly RAMDirectory directory; 15 | 16 | public MemoryIndexSearcher(RAMDirectory directory, bool readOnly) { 17 | this.readOnly = readOnly; 18 | this.directory = directory; 19 | } 20 | 21 | public Searcher Create() { 22 | return new IndexSearcher(directory, readOnly); 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/SimpleLucene.Tests/MemoryIndexWriter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using Lucene.Net.Index; 6 | using System.IO; 7 | using Lucene.Net.Store; 8 | using Lucene.Net.Analysis.Standard; 9 | 10 | namespace SimpleLucene.Tests 11 | { 12 | public class MemoryIndexWriter : IIndexWriter 13 | { 14 | public bool CreateIndex {get; private set;} 15 | 16 | public RAMDirectory Directory { get; set; } 17 | 18 | public MemoryIndexWriter(bool createIndex) 19 | { 20 | this.Directory = new RAMDirectory(); 21 | this.CreateIndex = createIndex; 22 | this.IndexOptions = new IndexOptions(); 23 | } 24 | 25 | public IndexOptions IndexOptions { get; set; } 26 | 27 | public IndexWriter Create() 28 | { 29 | var ramDirectory = new RAMDirectory(); 30 | this.Directory = ramDirectory; 31 | return new IndexWriter(ramDirectory, 32 | new StandardAnalyzer(Lucene.Net.Util.Version.LUCENE_30), 33 | CreateIndex, 34 | IndexWriter.MaxFieldLength.UNLIMITED); 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/SimpleLucene.Tests/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("SimpleLucene.Tests")] 9 | [assembly: AssemblyDescription("Test project for SimpleLucene")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("Ben Foster")] 12 | [assembly: AssemblyProduct("SimpleLucene.Tests")] 13 | [assembly: AssemblyCopyright("Copyright © Ben Foster 2011")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("2ed8faad-e36a-437b-a217-5b0c59ae0132")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /src/SimpleLucene.Tests/Queries/ProductQuery.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using Lucene.Net.Search; 6 | using Lucene.Net.Index; 7 | 8 | namespace SimpleLucene.Tests.Queries 9 | { 10 | public class ProductQuery : QueryBase 11 | { 12 | public ProductQuery(Query query) : base(query) { } 13 | public ProductQuery() { } 14 | 15 | public ProductQuery WithId(int id) 16 | { 17 | if (id > 0) 18 | { 19 | var query = new TermQuery(new Term("id", id.ToString())); 20 | this.AddQuery(query); 21 | } 22 | return this; 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/SimpleLucene.Tests/SearchServiceTestFixture.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using Lucene.Net.Index; 3 | using Lucene.Net.Search; 4 | using Lucene.Net.Store; 5 | using NUnit.Framework; 6 | using SimpleLucene.Tests.Data; 7 | using SimpleLucene.Tests.Definitions; 8 | using SimpleLucene.Tests.Queries; 9 | using SimpleLucene.Tests.Entities; 10 | 11 | namespace SimpleLucene.Tests 12 | { 13 | [TestFixture] 14 | public class SearchServiceTestFixture 15 | { 16 | RAMDirectory directory; 17 | 18 | [TestFixtureSetUp] 19 | public void SetUp() 20 | { 21 | var repo = new Repository(); 22 | 23 | var writer = new MemoryIndexWriter(true); 24 | using (var indexService = new IndexService(writer)) 25 | { 26 | indexService.IndexEntities(repo.Products, new ProductIndexDefinition()); 27 | } 28 | directory = writer.Directory; 29 | } 30 | 31 | [TestFixtureTearDown] 32 | public void TearDown() 33 | { 34 | directory.Close(); 35 | } 36 | 37 | [Test] 38 | public void Can_search_index() 39 | { 40 | var indexSearcher = new MemoryIndexSearcher(directory, true); 41 | var searchService = new SearchService(indexSearcher); 42 | 43 | var result = searchService.SearchIndex(new ProductQuery().WithId(5).Query); 44 | Assert.IsNotNull(result); 45 | Assert.AreEqual(1, result.Documents.Count()); 46 | } 47 | 48 | [Test] 49 | public void Can_transform_results_with_result_definition() 50 | { 51 | var indexSearcher = new MemoryIndexSearcher(directory, true); 52 | var searchService = new SearchService(indexSearcher); 53 | 54 | var result = searchService.SearchIndex( 55 | new TermQuery(new Term("type", "product")), 56 | new ProductResultDefinition() 57 | ); 58 | 59 | Assert.IsNotNull(result); 60 | Assert.AreEqual(10, result.Results.Count()); 61 | } 62 | 63 | [Test] 64 | public void Can_transform_results_with_delegate_definition() 65 | { 66 | var indexSearcher = new MemoryIndexSearcher(directory, true); 67 | var searchService = new SearchService(indexSearcher); 68 | 69 | var result = searchService.SearchIndex( 70 | new TermQuery(new Term("id", "1")), 71 | doc => { 72 | return new Product { Name = doc.Get("name") }; 73 | }); 74 | 75 | Assert.AreEqual(result.Results.Count(), 1); 76 | Assert.AreEqual(result.Results.First().Name, "Football"); 77 | } 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/SimpleLucene.Tests/SimpleLucene.Tests.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Debug 5 | AnyCPU 6 | 8.0.30703 7 | 2.0 8 | {C9FF8CA5-7D30-44A6-9D26-20F2C5213251} 9 | Library 10 | Properties 11 | SimpleLucene.Tests 12 | SimpleLucene.Tests 13 | v4.0 14 | 512 15 | ..\ 16 | true 17 | 18 | 19 | true 20 | full 21 | false 22 | bin\Debug\ 23 | DEBUG;TRACE 24 | prompt 25 | 4 26 | 27 | 28 | pdbonly 29 | true 30 | bin\Release\ 31 | TRACE 32 | prompt 33 | 4 34 | 35 | 36 | 37 | False 38 | ..\packages\SharpZipLib.0.86.0\lib\20\ICSharpCode.SharpZipLib.dll 39 | 40 | 41 | False 42 | ..\packages\Lucene.Net.3.0.3\lib\NET40\Lucene.Net.dll 43 | 44 | 45 | False 46 | ..\packages\NUnit.2.5.9.10348\lib\nunit.framework.dll 47 | 48 | 49 | False 50 | ..\packages\NUnit.2.5.9.10348\lib\nunit.mocks.dll 51 | 52 | 53 | False 54 | ..\packages\NUnit.2.5.9.10348\lib\pnunit.framework.dll 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | {72761871-5706-4ADD-A464-8935CC45FC57} 84 | SimpleLucene 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. 95 | 96 | 97 | 98 | 105 | -------------------------------------------------------------------------------- /src/SimpleLucene.Tests/Tasks/ProductUpdateTask.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using SimpleLucene.Tests.Entities; 6 | using SimpleLucene.IndexManagement; 7 | using System.IO; 8 | 9 | namespace SimpleLucene.Tests.Tasks 10 | { 11 | public class ProductUpdateTask : IIndexTask 12 | { 13 | protected readonly Product entity; 14 | 15 | public ProductUpdateTask(Product entity) 16 | { 17 | this.entity = entity; 18 | } 19 | 20 | public override bool Equals(object obj) 21 | { 22 | return (Equals(obj as ProductUpdateTask)); 23 | } 24 | 25 | public override int GetHashCode() { 26 | if (Equals(entity.Id, default(int))) 27 | return base.GetHashCode(); 28 | return entity.Id.GetHashCode(); 29 | } 30 | 31 | private static bool IsTransient(Product p) 32 | { 33 | return p != null && 34 | Equals(p.Id, default(int)); 35 | } 36 | 37 | public virtual bool Equals(ProductUpdateTask other) 38 | { 39 | if (other == null) 40 | return false; 41 | 42 | if (ReferenceEquals(this, other)) 43 | return true; 44 | 45 | if (!IsTransient(this.entity) && !IsTransient(other.entity)) 46 | return Equals(entity.Id, other.entity.Id); 47 | 48 | 49 | return false; 50 | } 51 | 52 | #region IIndexTask Members 53 | 54 | public void Execute(IIndexService indexService) 55 | { 56 | throw new NotImplementedException(); 57 | } 58 | 59 | public IndexOptions IndexOptions { get; set; } 60 | 61 | #endregion 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/SimpleLucene.Tests/Tasks/TaskEqualityTestFixture.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using NUnit.Framework; 6 | using SimpleLucene.Tests.Entities; 7 | using SimpleLucene.IndexManagement; 8 | using SimpleLucene.Tests.Definitions; 9 | using System.IO; 10 | 11 | namespace SimpleLucene.Tests.Tasks 12 | { 13 | [TestFixture] 14 | public class TaskEqualityTestFixture 15 | { 16 | [Test] 17 | public void Two_of_same_task_type_with_different_entities_should_not_be_equal() 18 | { 19 | var p1 = new Product { Id = 1 }; 20 | var p2 = new Product { Id = 2 }; 21 | 22 | var task1 = new ProductUpdateTask(p1); 23 | var task2 = new ProductUpdateTask(p2); 24 | 25 | Assert.AreNotEqual(task1, task2); 26 | } 27 | 28 | [Test] 29 | public void Two_tasks_with_same_entity_should_be_equal() 30 | { 31 | var p1 = new Product { Id = 1 }; 32 | 33 | var task1 = new ProductUpdateTask(p1); 34 | var task2 = new ProductUpdateTask(p1); 35 | 36 | Assert.AreEqual(task1, task2); 37 | } 38 | 39 | [Test] 40 | public void Two_different_task_types_with_same_entity_should_not_be_equal() 41 | { 42 | var p1 = new Product { Id = 1 }; 43 | 44 | var task1 = new ProductUpdateTask(p1); 45 | var task2 = new EntityUpdateTask(p1, new ProductIndexDefinition(), new TestIndexLocation()); 46 | 47 | Assert.AreNotEqual(task1, task2); 48 | } 49 | 50 | [Test] 51 | public void Index_queue_resolves_equality() 52 | { 53 | var p1 = new Product { Id = 1 }; 54 | 55 | var task1 = new ProductUpdateTask(p1); 56 | 57 | IndexQueue.Instance.Queue(task1); 58 | 59 | var task2 = new ProductUpdateTask(p1); 60 | 61 | Assert.IsTrue(IndexQueue.Instance.Contains(task2)); 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/SimpleLucene.Tests/TestIndexLocation.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.IO; 6 | 7 | namespace SimpleLucene.Tests 8 | { 9 | public class TestIndexLocation : BaseIndexLocation { 10 | 11 | private string indexLocation; 12 | 13 | public TestIndexLocation(string indexDirectory = "") 14 | { 15 | indexLocation = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "indexes"); 16 | 17 | if (!string.IsNullOrEmpty(indexDirectory)) 18 | indexLocation = Path.Combine(indexLocation, indexDirectory); 19 | } 20 | 21 | public override string GetPath() { 22 | return indexLocation; 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/SimpleLucene.Tests/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /src/SimpleLucene.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 2013 4 | VisualStudioVersion = 12.0.30110.0 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nuget", ".nuget", "{1CDA62F0-38CE-477E-A2F4-047ABBAA79C8}" 7 | ProjectSection(SolutionItems) = preProject 8 | .nuget\NuGet.Config = .nuget\NuGet.Config 9 | .nuget\NuGet.exe = .nuget\NuGet.exe 10 | .nuget\NuGet.targets = .nuget\NuGet.targets 11 | EndProjectSection 12 | EndProject 13 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SimpleLucene", "SimpleLucene\SimpleLucene.csproj", "{72761871-5706-4ADD-A464-8935CC45FC57}" 14 | EndProject 15 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SimpleLucene.Tests", "SimpleLucene.Tests\SimpleLucene.Tests.csproj", "{C9FF8CA5-7D30-44A6-9D26-20F2C5213251}" 16 | EndProject 17 | Global 18 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 19 | Debug|Any CPU = Debug|Any CPU 20 | Release|Any CPU = Release|Any CPU 21 | EndGlobalSection 22 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 23 | {72761871-5706-4ADD-A464-8935CC45FC57}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 24 | {72761871-5706-4ADD-A464-8935CC45FC57}.Debug|Any CPU.Build.0 = Debug|Any CPU 25 | {72761871-5706-4ADD-A464-8935CC45FC57}.Release|Any CPU.ActiveCfg = Release|Any CPU 26 | {72761871-5706-4ADD-A464-8935CC45FC57}.Release|Any CPU.Build.0 = Release|Any CPU 27 | {C9FF8CA5-7D30-44A6-9D26-20F2C5213251}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 28 | {C9FF8CA5-7D30-44A6-9D26-20F2C5213251}.Debug|Any CPU.Build.0 = Debug|Any CPU 29 | {C9FF8CA5-7D30-44A6-9D26-20F2C5213251}.Release|Any CPU.ActiveCfg = Release|Any CPU 30 | {C9FF8CA5-7D30-44A6-9D26-20F2C5213251}.Release|Any CPU.Build.0 = Release|Any CPU 31 | EndGlobalSection 32 | GlobalSection(SolutionProperties) = preSolution 33 | HideSolutionNode = FALSE 34 | EndGlobalSection 35 | EndGlobal 36 | -------------------------------------------------------------------------------- /src/SimpleLucene/BaseIndexLocation.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace SimpleLucene 4 | { 5 | /// 6 | /// A base class for index locations. 7 | /// 8 | public abstract class BaseIndexLocation : IIndexLocation 9 | { 10 | /// 11 | /// Compares the index location based on it's path 12 | /// 13 | public virtual bool Equals(IIndexLocation other) 14 | { 15 | if (other == null) 16 | { 17 | return false; 18 | } 19 | return other.GetPath().Equals(this.GetPath(), StringComparison.InvariantCultureIgnoreCase); 20 | } 21 | 22 | public override bool Equals(object obj) 23 | { 24 | return Equals(obj as IIndexLocation); 25 | } 26 | 27 | public override int GetHashCode() 28 | { 29 | return base.GetHashCode(); 30 | } 31 | 32 | public abstract string GetPath(); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/SimpleLucene/DelegateSearchResultDefinition.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Lucene.Net.Documents; 3 | 4 | namespace SimpleLucene 5 | { 6 | public class DelegateSearchResultDefinition : IResultDefinition 7 | { 8 | private readonly Func converter; 9 | public DelegateSearchResultDefinition(Func converter) 10 | { 11 | if (converter == null) 12 | { 13 | throw new ArgumentNullException("converter"); 14 | } 15 | 16 | this.converter = converter; 17 | } 18 | 19 | public T Convert(Document document) 20 | { 21 | if (document == null) 22 | { 23 | throw new ArgumentNullException("document"); 24 | } 25 | 26 | return converter(document); 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/SimpleLucene/DirectoryIndexSearcher.cs: -------------------------------------------------------------------------------- 1 | using Lucene.Net.Search; 2 | using Lucene.Net.Store; 3 | using System; 4 | using System.IO; 5 | 6 | namespace SimpleLucene 7 | { 8 | /// 9 | /// A searcher for directory based indexes 10 | /// 11 | public class DirectoryIndexSearcher : IIndexSearcher 12 | { 13 | private readonly DirectoryInfo indexLocation; 14 | private readonly bool readOnly; 15 | 16 | public DirectoryIndexSearcher(DirectoryInfo indexLocation, bool readOnly = true) 17 | { 18 | if (indexLocation == null) 19 | { 20 | throw new ArgumentNullException("indexLocation"); 21 | } 22 | 23 | this.indexLocation = indexLocation; 24 | this.readOnly = readOnly; 25 | } 26 | 27 | /// 28 | /// Creates the underlying Lucene searcher. 29 | /// 30 | /// A Lucene Searcher 31 | public Searcher Create() 32 | { 33 | var fsDirectory = FSDirectory.Open(indexLocation); 34 | return new IndexSearcher(fsDirectory, true); 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/SimpleLucene/DirectoryIndexWriter.cs: -------------------------------------------------------------------------------- 1 | using Lucene.Net.Analysis; 2 | using Lucene.Net.Analysis.Standard; 3 | using Lucene.Net.Index; 4 | using Lucene.Net.Store; 5 | using System; 6 | using System.IO; 7 | 8 | namespace SimpleLucene 9 | { 10 | /// 11 | /// A writer for directory based indexes 12 | /// 13 | public class DirectoryIndexWriter : IIndexWriter 14 | { 15 | public IndexOptions IndexOptions { get; set; } 16 | 17 | public DirectoryIndexWriter(DirectoryInfo indexLocation, bool recreateIndex = false) 18 | : this(indexLocation, new StandardAnalyzer(Lucene.Net.Util.Version.LUCENE_30), recreateIndex) 19 | { } 20 | 21 | public DirectoryIndexWriter(DirectoryInfo indexLocation, Analyzer analyzer, bool recreateIndex = false) 22 | { 23 | if (indexLocation == null) 24 | { 25 | throw new ArgumentNullException("indexLocation"); 26 | } 27 | 28 | if (analyzer == null) 29 | { 30 | throw new ArgumentNullException("analyzer"); 31 | } 32 | 33 | IndexOptions = new IndexOptions 34 | { 35 | IndexLocation = new FileSystemIndexLocation(indexLocation), 36 | RecreateIndex = recreateIndex, 37 | Analyzer = analyzer 38 | }; 39 | } 40 | 41 | /// 42 | /// Creates the underlying Lucene index writer. 43 | /// 44 | /// A Lucene Index Writer 45 | public IndexWriter Create() 46 | { 47 | var fsDirectory = FSDirectory.Open(IndexOptions.IndexLocation.GetDirectory()); 48 | 49 | var recreateIndex = this.IndexOptions.RecreateIndex; 50 | 51 | if (!recreateIndex) // then we should create anyway 52 | recreateIndex = !(IndexReader.IndexExists(fsDirectory)); 53 | 54 | return new IndexWriter(fsDirectory, 55 | IndexOptions.Analyzer, 56 | recreateIndex, 57 | IndexWriter.MaxFieldLength.UNLIMITED); 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/SimpleLucene/DocumentExtensions.cs: -------------------------------------------------------------------------------- 1 | using Lucene.Net.Documents; 2 | using System; 3 | 4 | namespace SimpleLucene 5 | { 6 | /// 7 | /// Extension methods for . 8 | /// 9 | public static class DocumentExtensions 10 | { 11 | public static T GetValue(this Document document, string field) 12 | { 13 | if (document == null) 14 | { 15 | throw new ArgumentNullException("document"); 16 | } 17 | 18 | if (string.IsNullOrEmpty("field")) 19 | { 20 | throw new ArgumentNullException("field"); 21 | } 22 | 23 | var value = document.Get(field); 24 | 25 | if (value != null) 26 | { 27 | return (T)Convert.ChangeType(value, typeof(T)); 28 | } 29 | 30 | return default(T); 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/SimpleLucene/DocumentResultDefinition.cs: -------------------------------------------------------------------------------- 1 | using Lucene.Net.Documents; 2 | 3 | namespace SimpleLucene 4 | { 5 | /// 6 | /// A simple result definition for untyped searches (just returns the Lucene document) 7 | /// 8 | public class DocumentResultDefinition : IResultDefinition 9 | { 10 | public Document Convert(Document document) 11 | { 12 | return document; 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/SimpleLucene/FileSystemIndexLocation.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | 4 | namespace SimpleLucene 5 | { 6 | /// 7 | /// Represents a file sytem based index location 8 | /// 9 | public class FileSystemIndexLocation : BaseIndexLocation 10 | { 11 | private readonly DirectoryInfo directoryInfo; 12 | 13 | public FileSystemIndexLocation(DirectoryInfo directoryInfo) 14 | { 15 | if (directoryInfo == null) 16 | { 17 | throw new ArgumentNullException("directoryInfo"); 18 | } 19 | 20 | this.directoryInfo = directoryInfo; 21 | } 22 | 23 | public override string GetPath() 24 | { 25 | return directoryInfo.FullName; 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/SimpleLucene/IIndexDefinition.cs: -------------------------------------------------------------------------------- 1 | using Lucene.Net.Documents; 2 | using Lucene.Net.Index; 3 | 4 | namespace SimpleLucene 5 | { 6 | public interface IIndexDefinition where T : class 7 | { 8 | Document Convert(T entity); 9 | Term GetIndex(T entity); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/SimpleLucene/IIndexLocation.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace SimpleLucene 4 | { 5 | public interface IIndexLocation : IEquatable 6 | { 7 | string GetPath(); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/SimpleLucene/IIndexSearcher.cs: -------------------------------------------------------------------------------- 1 | using Lucene.Net.Search; 2 | 3 | namespace SimpleLucene 4 | { 5 | public interface IIndexSearcher 6 | { 7 | Searcher Create(); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/SimpleLucene/IIndexService.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using Lucene.Net.Documents; 4 | using Lucene.Net.Index; 5 | 6 | namespace SimpleLucene 7 | { 8 | public interface IIndexService : IDisposable 9 | { 10 | IIndexWriter IndexWriter { get; } 11 | IndexResult IndexEntities(IEnumerable entities, IIndexDefinition definition) where TEntity : class; 12 | IndexResult IndexEntities(IEnumerable entities, Func converter); 13 | IndexResult IndexEntity(TEntity entity, IIndexDefinition definition) where TEntity : class; 14 | void Remove(Term searchTerm); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/SimpleLucene/IIndexWriter.cs: -------------------------------------------------------------------------------- 1 | using Lucene.Net.Index; 2 | 3 | namespace SimpleLucene 4 | { 5 | public interface IIndexWriter 6 | { 7 | IndexWriter Create(); 8 | IndexOptions IndexOptions { get; set; } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/SimpleLucene/IResultDefinition.cs: -------------------------------------------------------------------------------- 1 | using Lucene.Net.Documents; 2 | 3 | namespace SimpleLucene 4 | { 5 | public interface IResultDefinition 6 | { 7 | T Convert(Document document); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/SimpleLucene/ISearchService.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Lucene.Net.Documents; 3 | using Lucene.Net.Search; 4 | namespace SimpleLucene 5 | { 6 | public interface ISearchService : IDisposable 7 | { 8 | SearchResult SearchIndex(Query query); 9 | SearchResult SearchIndex(Query query, IResultDefinition definition); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/SimpleLucene/IndexManagement/IIndexTask.cs: -------------------------------------------------------------------------------- 1 |  2 | namespace SimpleLucene.IndexManagement 3 | { 4 | /// 5 | /// Interface for index tasks 6 | /// 7 | public interface IIndexTask 8 | { 9 | void Execute(IIndexService indexService); 10 | IndexOptions IndexOptions { get; set; } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/SimpleLucene/IndexManagement/Impl/EntityDeleteTask.cs: -------------------------------------------------------------------------------- 1 | using Lucene.Net.Index; 2 | using System; 3 | 4 | namespace SimpleLucene.IndexManagement 5 | { 6 | /// 7 | /// A task for removing an entity from an index 8 | /// 9 | /// The type of entity to remove 10 | public class EntityDeleteTask : IIndexTask where TEntity : class 11 | { 12 | protected readonly Term term; 13 | 14 | public IndexOptions IndexOptions { get; set; } 15 | 16 | public EntityDeleteTask(IIndexLocation indexLocation, string fieldName, string value) 17 | { 18 | if (indexLocation == null) 19 | { 20 | throw new ArgumentNullException("indexLocation"); 21 | } 22 | 23 | if (string.IsNullOrEmpty("fieldName")) 24 | { 25 | throw new ArgumentNullException("fieldName"); 26 | } 27 | 28 | if (string.IsNullOrEmpty("value")) 29 | { 30 | throw new ArgumentNullException("value"); 31 | } 32 | 33 | term = new Term(fieldName, value); 34 | IndexOptions = new IndexOptions { IndexLocation = indexLocation }; 35 | } 36 | 37 | public void Execute(IIndexService indexService) 38 | { 39 | if (indexService == null) 40 | { 41 | throw new ArgumentNullException("indexService"); 42 | } 43 | 44 | indexService.Remove(term); 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/SimpleLucene/IndexManagement/Impl/EntityUpdateTask.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace SimpleLucene.IndexManagement 4 | { 5 | /// 6 | /// An task for reindexing an entity 7 | /// 8 | /// The type of entity to index 9 | public class EntityUpdateTask : IIndexTask where TEntity : class 10 | { 11 | protected readonly TEntity entity; 12 | protected readonly IIndexDefinition definition; 13 | 14 | public IndexOptions IndexOptions { get; set; } 15 | 16 | public EntityUpdateTask(TEntity entity, IIndexDefinition definition, IIndexLocation indexLocation) 17 | { 18 | if (entity == null) 19 | { 20 | throw new ArgumentNullException("entity"); 21 | } 22 | 23 | if (definition == null) 24 | { 25 | throw new ArgumentNullException("definition"); 26 | } 27 | 28 | if (indexLocation == null) 29 | { 30 | throw new ArgumentNullException("indexLocation"); 31 | } 32 | 33 | this.entity = entity; 34 | this.definition = definition; 35 | this.IndexOptions = new IndexOptions { IndexLocation = indexLocation }; 36 | } 37 | 38 | public void Execute(IIndexService indexService) 39 | { 40 | if (indexService == null) 41 | { 42 | throw new ArgumentNullException("indexService"); 43 | } 44 | 45 | var result = indexService.IndexEntity(entity, definition); 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/SimpleLucene/IndexManagement/IndexQueue.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Concurrent; 2 | using System.Linq; 3 | 4 | namespace SimpleLucene.IndexManagement 5 | { 6 | /// 7 | /// Provides a simple queue for index tasks 8 | /// 9 | public class IndexQueue 10 | { 11 | static readonly IndexQueue instance = new IndexQueue(); 12 | static readonly object padlock = new object(); 13 | 14 | private ConcurrentQueue cq = new ConcurrentQueue(); 15 | 16 | private IndexQueue() { } 17 | 18 | public static IndexQueue Instance 19 | { 20 | get { return instance; } 21 | } 22 | 23 | public void Queue(IIndexTask task) 24 | { 25 | cq.Enqueue(task); 26 | } 27 | 28 | public bool TryDequeue(out IIndexTask task) 29 | { 30 | return cq.TryDequeue(out task); 31 | } 32 | 33 | public bool Contains(IIndexTask instance) 34 | { 35 | lock (padlock) 36 | { 37 | return cq.Contains(instance); 38 | } 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/SimpleLucene/IndexOptions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using Lucene.Net.Analysis; 4 | using Lucene.Net.Analysis.Standard; 5 | 6 | namespace SimpleLucene 7 | { 8 | /// 9 | /// Used as a definition for resolving indexwriters 10 | /// 11 | public class IndexOptions : IEquatable 12 | { 13 | public IndexOptions() 14 | { 15 | this.Analyzer = new StandardAnalyzer(Lucene.Net.Util.Version.LUCENE_30); 16 | this.Attributes = new Dictionary(); 17 | } 18 | 19 | public bool OptimizeIndex { get; set; } 20 | public bool RecreateIndex { get; set; } 21 | public IIndexLocation IndexLocation { get; set; } 22 | public Analyzer Analyzer { get; set; } 23 | public IDictionary Attributes { get; set; } 24 | 25 | public bool Equals(IndexOptions other) 26 | { 27 | if (other == null) 28 | return false; 29 | 30 | return (AreEqual(other.OptimizeIndex, this.OptimizeIndex)) 31 | && (AreEqual(other.RecreateIndex, this.RecreateIndex)) 32 | && (AreEqual(other.IndexLocation, this.IndexLocation)) 33 | && (AreEqual(other.Analyzer.GetType(), this.Analyzer.GetType())) 34 | && (CompareAttributes(other.Attributes)); 35 | } 36 | 37 | public override bool Equals(object obj) 38 | { 39 | return Equals(obj as IndexOptions); 40 | } 41 | 42 | public override int GetHashCode() 43 | { 44 | return base.GetHashCode(); 45 | } 46 | 47 | protected bool AreEqual(object obj1, object obj2) 48 | { 49 | if (obj1 == null && obj2 == null) 50 | return true; 51 | 52 | return (obj1.Equals(obj2)); 53 | } 54 | 55 | protected bool CompareAttributes(IDictionary other) 56 | { 57 | foreach (var item in this.Attributes) 58 | { 59 | object obj; 60 | if (other.TryGetValue(item.Key, out obj)) 61 | { 62 | if (!AreEqual(obj, item.Value)) 63 | return false; 64 | } 65 | else 66 | { 67 | return false; 68 | } 69 | } 70 | return true; 71 | } 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/SimpleLucene/IndexResult.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Collections.ObjectModel; 3 | 4 | namespace SimpleLucene 5 | { 6 | public class IndexResult 7 | { 8 | private List errors; 9 | 10 | public IndexResult() 11 | { 12 | errors = new List(); 13 | } 14 | 15 | public bool Success { get; set; } 16 | public int Count { get; set; } 17 | public long ExecutionTime { get; set; } 18 | 19 | public void AddError(string error) 20 | { 21 | errors.Add(error); 22 | } 23 | 24 | public ReadOnlyCollection Errors { get { return errors.AsReadOnly(); } } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/SimpleLucene/IndexService.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using Lucene.Net.Documents; 4 | using Lucene.Net.Index; 5 | using System.Diagnostics; 6 | 7 | namespace SimpleLucene 8 | { 9 | /// 10 | /// Default index service implementation 11 | /// 12 | public class IndexService : IIndexService 13 | { 14 | private bool isDisposed; 15 | private readonly IIndexWriter indexWriter; 16 | private IndexWriter luceneWriter; 17 | 18 | public IndexService(IIndexWriter indexWriter) 19 | { 20 | if (indexWriter == null) 21 | { 22 | throw new ArgumentNullException("indexWriter"); 23 | } 24 | 25 | this.indexWriter = indexWriter; 26 | } 27 | 28 | public IIndexWriter IndexWriter 29 | { 30 | get 31 | { 32 | return indexWriter; 33 | } 34 | } 35 | 36 | /// 37 | /// Indexes a collection of entities using the provided index definition 38 | /// 39 | /// The type of entity to index 40 | /// A list of entities 41 | /// The index definition 42 | /// An index result with the outcome of the indexing operation 43 | public IndexResult IndexEntities(IEnumerable entities, IIndexDefinition definition) where TEntity : class 44 | { 45 | return IndexEntities(entities, definition.Convert); 46 | } 47 | 48 | /// 49 | /// Indexes a collection of entities using a delegate index definition 50 | /// 51 | /// The type of entity to index 52 | /// A list of entities 53 | /// The delegate converter 54 | /// An index result with the outcome of the indexing operation 55 | public IndexResult IndexEntities(IEnumerable entities, Func converter) 56 | { 57 | var result = new IndexResult(); 58 | result.ExecutionTime = Time(() => 59 | { 60 | foreach (TEntity entity in entities) 61 | { 62 | this.GetWriter().AddDocument(converter(entity)); 63 | result.Count++; 64 | } 65 | result.Success = true; 66 | }); 67 | 68 | return result; 69 | } 70 | 71 | /// 72 | /// Indexes a single entity with the provided definition 73 | /// 74 | /// The type of entity to index 75 | /// The entity to index 76 | /// The index definition 77 | /// An index result with the outcome of the indexing operation 78 | public IndexResult IndexEntity(TEntity entity, IIndexDefinition definition) where TEntity : class 79 | { 80 | var result = new IndexResult(); 81 | result.ExecutionTime = Time(() => 82 | { 83 | this.GetWriter().UpdateDocument(definition.GetIndex(entity), definition.Convert(entity)); 84 | result.Count++; 85 | result.Success = true; 86 | }); 87 | 88 | return result; 89 | } 90 | 91 | /// 92 | /// Removes all documents that match the given term in the index 93 | /// 94 | /// A search term to match 95 | public void Remove(Term searchTerm) 96 | { 97 | this.GetWriter().DeleteDocuments(searchTerm); 98 | } 99 | 100 | public void Dispose() 101 | { 102 | if (!isDisposed && luceneWriter != null) 103 | { 104 | if (indexWriter.IndexOptions.OptimizeIndex) 105 | luceneWriter.Optimize(); 106 | 107 | luceneWriter.Commit(); 108 | luceneWriter.Dispose(); 109 | isDisposed = true; 110 | } 111 | luceneWriter = null; 112 | } 113 | 114 | protected IndexWriter GetWriter() 115 | { 116 | if (isDisposed) 117 | isDisposed = false; 118 | 119 | if (luceneWriter == null) 120 | luceneWriter = indexWriter.Create(); 121 | 122 | return luceneWriter; 123 | } 124 | 125 | private static long Time(Action action) 126 | { 127 | var sw = Stopwatch.StartNew(); 128 | action.Invoke(); 129 | return sw.ElapsedMilliseconds; 130 | } 131 | } 132 | } 133 | -------------------------------------------------------------------------------- /src/SimpleLucene/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("SimpleLucene")] 9 | [assembly: AssemblyDescription("SimpleLucene is a wrapper for the popular Lucene.NET search engine.")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("Ben Foster")] 12 | [assembly: AssemblyProduct("SimpleLucene")] 13 | [assembly: AssemblyCopyright("Copyright © Ben Foster 2014")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("8ebb4eca-8479-4e58-bc67-64557a3c3013")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.2")] 36 | [assembly: AssemblyFileVersion("1.0.0.2")] 37 | [assembly: AssemblyInformationalVersion("1.0.0.2")] 38 | -------------------------------------------------------------------------------- /src/SimpleLucene/QueryBase.cs: -------------------------------------------------------------------------------- 1 | using Lucene.Net.Index; 2 | using Lucene.Net.Search; 3 | using System; 4 | using System.Collections.Generic; 5 | 6 | namespace SimpleLucene 7 | { 8 | /// 9 | /// A base class for deriving custom query objects 10 | /// 11 | public abstract class QueryBase 12 | { 13 | private readonly BooleanQuery baseQuery = new BooleanQuery(); 14 | 15 | protected QueryBase() { } 16 | 17 | protected QueryBase(Query query) 18 | { 19 | AddQuery(query); 20 | } 21 | 22 | public Query Query 23 | { 24 | get { return baseQuery; } 25 | } 26 | 27 | protected void AddQuery(Query query) 28 | { 29 | AddQuery(query, Occur.MUST); 30 | } 31 | 32 | protected void AddQuery(Query query, Occur occur) 33 | { 34 | if (query != null) 35 | { 36 | baseQuery.Add(query, occur); 37 | } 38 | } 39 | 40 | protected Query GetQueryFromList(IList list, string key) 41 | { 42 | return GetQueryFromList(list, (i) => key, i => i.ToString()); 43 | } 44 | 45 | protected Query GetQueryFromList(IList list, Func key, Func value) 46 | { 47 | if (list.Count > 1) 48 | { 49 | var query = new BooleanQuery(); 50 | foreach (var id in list) 51 | { 52 | query.Add(TermQuery(key(id), value(id)), Occur.MUST); 53 | } 54 | return query; 55 | } 56 | if (list.Count == 1) 57 | { 58 | return TermQuery(key(list[0]), value(list[0])); 59 | } 60 | return null; 61 | } 62 | 63 | protected Query TermQuery(string key, string value) 64 | { 65 | return new TermQuery(new Term(key, value)); 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/SimpleLucene/SearchResult.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using Lucene.Net.Documents; 4 | 5 | namespace SimpleLucene 6 | { 7 | public class SearchResult 8 | { 9 | private readonly IEnumerable documents; 10 | private readonly IResultDefinition definition; 11 | 12 | private IEnumerable entities; 13 | 14 | public SearchResult(IEnumerable documents, IResultDefinition definition) 15 | { 16 | this.documents = documents; 17 | this.definition = definition; 18 | } 19 | 20 | public IEnumerable Documents 21 | { 22 | get { return documents; } 23 | } 24 | 25 | public IEnumerable Results 26 | { 27 | get 28 | { 29 | if (entities == null && definition != null) 30 | { 31 | entities = documents.Select(doc => definition.Convert(doc)).ToList(); 32 | } 33 | return entities; 34 | } 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/SimpleLucene/SearchService.cs: -------------------------------------------------------------------------------- 1 | using Lucene.Net.Documents; 2 | using Lucene.Net.Search; 3 | using System; 4 | using System.Linq; 5 | 6 | namespace SimpleLucene 7 | { 8 | /// 9 | /// Default search service implementation 10 | /// 11 | public class SearchService : ISearchService 12 | { 13 | private readonly IIndexSearcher indexSearcher; 14 | private Searcher luceneSearcher; 15 | private bool isDisposed; 16 | 17 | public SearchService(IIndexSearcher indexSearcher) 18 | { 19 | if (indexSearcher == null) 20 | { 21 | throw new ArgumentNullException("indexSearcher"); 22 | } 23 | 24 | this.indexSearcher = indexSearcher; 25 | } 26 | 27 | /// 28 | /// Searches an index using the provided query 29 | /// 30 | /// A Lucene query to use for the search 31 | /// A search result containing Lucene documents 32 | public SearchResult SearchIndex(Query query) 33 | { 34 | return SearchIndex(query, new DocumentResultDefinition()); 35 | } 36 | 37 | /// 38 | /// Searches an index using the provided query and returns a strongly typed result object 39 | /// 40 | /// The type of result object to return 41 | /// A Lucene query to use for the search 42 | /// A search definition used to transform the returned Lucene documents 43 | /// A search result object containing both Lucene documents and typed objects based on the definition 44 | public SearchResult SearchIndex(Query query, IResultDefinition definition) 45 | { 46 | var searcher = this.GetSearcher(); 47 | TopDocs hits = null; 48 | hits = searcher.Search(query, 25000); 49 | var results = hits.ScoreDocs.Select(h => searcher.Doc(h.Doc)); 50 | return new SearchResult(results, definition); 51 | } 52 | 53 | public void Dispose() 54 | { 55 | if (!isDisposed && luceneSearcher != null) 56 | { 57 | luceneSearcher.Dispose(); 58 | } 59 | luceneSearcher = null; 60 | } 61 | 62 | protected Searcher GetSearcher() 63 | { 64 | if (isDisposed) 65 | { 66 | isDisposed = false; 67 | } 68 | 69 | if (luceneSearcher == null) 70 | { 71 | luceneSearcher = indexSearcher.Create(); 72 | } 73 | 74 | return luceneSearcher; 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/SimpleLucene/SimpleLucene.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Debug 5 | AnyCPU 6 | 8.0.30703 7 | 2.0 8 | {72761871-5706-4ADD-A464-8935CC45FC57} 9 | Library 10 | Properties 11 | SimpleLucene 12 | SimpleLucene 13 | v4.0 14 | 512 15 | ..\ 16 | true 17 | 18 | 19 | true 20 | full 21 | false 22 | bin\Debug\ 23 | DEBUG;TRACE 24 | prompt 25 | 4 26 | 27 | 28 | pdbonly 29 | true 30 | bin\Release\ 31 | TRACE 32 | prompt 33 | 4 34 | 35 | 36 | 37 | False 38 | ..\packages\SharpZipLib.0.86.0\lib\20\ICSharpCode.SharpZipLib.dll 39 | 40 | 41 | False 42 | ..\packages\Lucene.Net.3.0.3\lib\NET40\Lucene.Net.dll 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. 88 | 89 | 90 | 91 | 98 | -------------------------------------------------------------------------------- /src/SimpleLucene/SimpleLuceneExtensions.cs: -------------------------------------------------------------------------------- 1 | using Lucene.Net.Documents; 2 | using Lucene.Net.Search; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.IO; 6 | using System.Linq; 7 | 8 | namespace SimpleLucene 9 | { 10 | /// 11 | /// Extension methods for SimpleLucene components 12 | /// 13 | public static class SimpleLuceneExtensions 14 | { 15 | /// 16 | /// Finds a valid index service that has a writer with matching index options 17 | /// 18 | /// The services collection to search 19 | /// The index options to match 20 | /// Index Service 21 | public static IIndexService FindWithOptions(this IEnumerable services, IndexOptions options) 22 | { 23 | if (services == null) 24 | { 25 | throw new ArgumentNullException("services"); 26 | } 27 | 28 | return services.SingleOrDefault(s => s.IndexWriter.IndexOptions.Equals(options)); 29 | } 30 | 31 | /// 32 | /// Creates a DirectoryInfo object from the index location's path 33 | /// 34 | /// The index location 35 | /// DirectoryInfo 36 | public static DirectoryInfo GetDirectory(this IIndexLocation location) 37 | { 38 | if (location == null) 39 | { 40 | throw new ArgumentNullException("location"); 41 | } 42 | 43 | return new DirectoryInfo(location.GetPath()); 44 | } 45 | 46 | /// 47 | /// Searches an index using the provided query and returns a strongly typed result object 48 | /// 49 | /// The type of result object to return 50 | /// A Lucene query to use for the search 51 | /// A delegate definition to transform the returned Lucene documents 52 | /// A search result object containing both Lucene documents and typed objects based on the delegate definition 53 | public static SearchResult SearchIndex(this ISearchService searchService, 54 | Query query, Func definition) 55 | { 56 | if (searchService == null) 57 | { 58 | throw new ArgumentNullException("searchService"); 59 | } 60 | 61 | return searchService.SearchIndex(query, new DelegateSearchResultDefinition(definition)); 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/SimpleLucene/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | --------------------------------------------------------------------------------