Skip to content
This repository has been archived by the owner on Sep 13, 2023. It is now read-only.

Commit

Permalink
LuceneSession/ISession implement unit-of-work pattern where pending c…
Browse files Browse the repository at this point in the history
…hanges are committed when session is disposed.

LuceneSession.Query() tracks modified items and flushes them during commit.
  • Loading branch information
chriseldredge committed Jun 27, 2012
1 parent 9c739c6 commit f946b06
Show file tree
Hide file tree
Showing 25 changed files with 514 additions and 111 deletions.
9 changes: 8 additions & 1 deletion Lucene.Net.Linq.Tests/Integration/IntegrationTestBase.cs
Expand Up @@ -31,11 +31,18 @@ protected virtual Analyzer GetAnalyzer(Version version)

public class SampleDocument
{
private static int count;
public SampleDocument()
{
Key = (count++).ToString();
}

public string Name { get; set; }

[Field(IndexMode.NotAnalyzed)]
public string Id { get; set; }

[Field(Key = true)]
public string Key { get; set; }

public int Scalar { get; set; }
Expand All @@ -53,7 +60,7 @@ public class SampleDocument
public string Alias { get; set; }
}

protected void AddDocument<T>(T document)
protected void AddDocument<T>(T document) where T : new()
{
using (var session = provider.OpenSession<T>())
{
Expand Down
1 change: 0 additions & 1 deletion Lucene.Net.Linq.Tests/Integration/SampleProgram.cs
Expand Up @@ -21,7 +21,6 @@ public static void Main()
using (var session = provider.OpenSession<Article>())
{
session.Add(new Article { Author = "John Doe", BodyText = "some body text", PublishDate = DateTimeOffset.UtcNow });
session.Commit();
}

var articles = provider.AsQueryable<Article>();
Expand Down
38 changes: 38 additions & 0 deletions Lucene.Net.Linq.Tests/Integration/SessionTests.cs
@@ -0,0 +1,38 @@
using System;
using System.Linq;
using NUnit.Framework;

namespace Lucene.Net.Linq.Tests.Integration
{
[TestFixture]
public class SessionTests : IntegrationTestBase
{
protected override Analysis.Analyzer GetAnalyzer(Net.Util.Version version)
{
return new LowercaseKeywordAnalyzer();
}

[SetUp]
public void AddDocuments()
{
AddDocument(new SampleDocument { Name = "c", Scalar = 3, Flag = true, Version = new Version(100, 0, 0) });
AddDocument(new SampleDocument { Name = "a", Scalar = 1, Version = new Version(20, 0, 0) });
AddDocument(new SampleDocument { Name = "b", Scalar = 2, Flag = true, Version = new Version(3, 0, 0) });
}

[Test]
public void Query()
{
var session = provider.OpenSession<SampleDocument>();

using (session)
{
var item = (from d in session.Query() where d.Name == "a" select d).Single();
item.Scalar = 4;
}

var result = (from d in session.Query() where d.Name == "a" select d).Single();
Assert.That(result.Scalar, Is.EqualTo(4));
}
}
}
1 change: 1 addition & 0 deletions Lucene.Net.Linq.Tests/Lucene.Net.Linq.Tests.csproj
Expand Up @@ -73,6 +73,7 @@
<Compile Include="Integration\SelectCollectionTests.cs" />
<Compile Include="Integration\CustomWhereTests.cs" />
<Compile Include="Integration\SelectTests.cs" />
<Compile Include="Integration\SessionTests.cs" />
<Compile Include="Integration\SingleResultTests.cs" />
<Compile Include="Integration\SkipTakeTests.cs" />
<Compile Include="LowercaseKeywordAnalyzer.cs" />
Expand Down
138 changes: 104 additions & 34 deletions Lucene.Net.Linq.Tests/LuceneSessionTests.cs
@@ -1,15 +1,16 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using Lucene.Net.Analysis;
using Lucene.Net.Analysis.Standard;
using Lucene.Net.Documents;
using Lucene.Net.Index;
using Lucene.Net.Linq.Abstractions;
using Lucene.Net.Linq.Mapping;
using Lucene.Net.Search;
using Lucene.Net.Util;
using NUnit.Framework;
using Rhino.Mocks;
using Version = Lucene.Net.Util.Version;

namespace Lucene.Net.Linq.Tests
{
Expand All @@ -27,11 +28,11 @@ public void SetUp()
{
mapper = MockRepository.GenerateStrictMock<IDocumentMapper<Record>>();
writer = MockRepository.GenerateStrictMock<IIndexWriter>();
analyzer = new StandardAnalyzer(Version.LUCENE_29);
analyzer = new LowercaseKeywordAnalyzer();

context = MockRepository.GenerateStub<Context>(null, analyzer, Version.LUCENE_29, writer, new object());

session = new LuceneSession<Record>(mapper, context);
session = new LuceneSession<Record>(mapper, context, null);

mapper.Expect(m => m.ToKey(Arg<Record>.Is.NotNull))
.WhenCalled(mi => mi.ReturnValue = new DocumentKey(new Dictionary<IFieldMappingInfo, object> { { new FakeFieldMappingInfo { FieldName = "Id"}, ((Record)mi.Arguments[0]).Id } }))
Expand All @@ -44,14 +45,14 @@ public void AddWithSameKeyReplaces()
var r1 = new Record { Id = "11", Name = "A" };
var r2 = new Record { Id = "11", Name = "B" };

mapper.Expect(m => m.ToDocument(Arg<Record>.Is.Same(r1), Arg<Document>.Is.NotNull));
mapper.Expect(m => m.ToDocument(Arg<Record>.Is.Same(r2), Arg<Document>.Is.NotNull));

session.Add(r1, r2);
var pendingAdditions = session.ConvertPendingAdditions();

Verify();

Assert.That(session.Additions.Count(), Is.EqualTo(1));
Assert.That(pendingAdditions.Count(), Is.EqualTo(1));
}

[Test]
Expand Down Expand Up @@ -94,51 +95,55 @@ public void Commit_Delete()
}

[Test]
public void Commit_Add()
public void Commit_Add_DeletesKey()
{
var doc1 = new Document();
var doc2 = new Document();
var key = new DocumentKey(new Dictionary<IFieldMappingInfo, object> { { new FakeFieldMappingInfo { FieldName = "Id" }, 1 } });

session.Add(new DocumentKey(), doc1);
session.Add(new DocumentKey(), doc2);
var record = new Record {Id = "1"};

writer.Expect(w => w.AddDocument(doc1));
writer.Expect(w => w.AddDocument(doc2));
session.Add(record);

mapper.Expect(m => m.ToDocument(Arg<Record>.Is.Same(record), Arg<Document>.Is.NotNull));
writer.Expect(w => w.DeleteDocuments(new[] {key.ToQuery(context.Analyzer, context.Version)}));
writer.Expect(w => w.AddDocument(Arg<Document>.Is.NotNull));
writer.Expect(w => w.Commit());

session.Commit();

Verify();

Assert.That(session.Additions, Is.Empty, "Commit should clear pending deletions.");
Assert.That(session.ConvertPendingAdditions, Is.Empty, "Commit should clear pending deletions.");
}

[Test]
public void Commit_Add_DeletesKey()
public void Commit_Add_ConvertsDocumentAndKeyLate()
{
var doc1 = new Document();
var key = new DocumentKey(new Dictionary<IFieldMappingInfo, object> { { new FakeFieldMappingInfo { FieldName = "Id" }, 1 } });

session.Add(key, doc1);
var record = new Record();
var key = new DocumentKey(new Dictionary<IFieldMappingInfo, object> { { new FakeFieldMappingInfo { FieldName = "Id" }, "biully" } });
var deleteQuery = key.ToQuery(context.Analyzer, Version.LUCENE_29);

writer.Expect(w => w.DeleteDocuments(new[] {key.ToQuery(context.Analyzer, context.Version)}));
writer.Expect(w => w.AddDocument(doc1));
mapper.Expect(m => m.ToDocument(Arg<Record>.Is.Same(record), Arg<Document>.Is.NotNull));
writer.Expect(w => w.DeleteDocuments(new[] { deleteQuery }));
writer.Expect(w => w.AddDocument(Arg<Document>.Is.NotNull));//Matches(doc => doc.GetValues("Name")[0] == "a name")));
writer.Expect(w => w.Commit());

session.Add(record);

record.Id = "biully";
record.Name = "a name";

session.Commit();

Verify();

Assert.That(session.Additions, Is.Empty, "Commit should clear pending deletions.");
Assert.That(session.ConvertPendingAdditions, Is.Empty, "Commit should clear pending deletions.");
}

[Test]
public void Commit_ReloadsSearcher()
{
var doc1 = new Document();

session.Add(new DocumentKey(), doc1);

writer.Expect(w => w.AddDocument(doc1));
session.DeleteAll();
writer.Expect(w => w.DeleteAll());
writer.Expect(w => w.Commit());

session.Commit();
Expand Down Expand Up @@ -168,12 +173,11 @@ public void DeleteAll()
public void DeleteAllClearsPendingAdditions()
{
var r1 = new Record();
mapper.Expect(m => m.ToDocument(Arg<Record>.Is.Same(r1), Arg<Document>.Is.NotNull));

session.Add(r1);
session.DeleteAll();

Assert.That(session.Additions, Is.Empty, "Additions");
Assert.That(session.ConvertPendingAdditions, Is.Empty, "Additions");

Verify();
}
Expand All @@ -188,6 +192,17 @@ public void Delete()
Assert.That(session.Deletions.Single().ToString(), Is.EqualTo("+Id:12"));
}

[Test]
public void Delete_RemovesFromPendingAdditions()
{
var r1 = new Record { Id = "12" };
session.Add(r1);
session.Delete(r1);

Assert.That(session.Additions, Is.Empty);
Assert.That(session.Deletions.Single().ToString(), Is.EqualTo("+Id:12"));
}

[Test]
public void Delete_SetsPendingChangesFlag()
{
Expand All @@ -197,8 +212,7 @@ public void Delete_SetsPendingChangesFlag()

Assert.That(session.PendingChanges, Is.True, "PendingChanges");
}



[Test]
public void Delete_ThrowsOnEmptyKey()
{
Expand All @@ -212,6 +226,62 @@ public void Delete_ThrowsOnEmptyKey()
Assert.That(call, Throws.InvalidOperationException);
}

[Test]
public void Query_Attaches()
{
var records = new Record[0].AsQueryable();
var provider = MockRepository.GenerateStrictMock<IQueryProvider>();
var queryable = MockRepository.GenerateStrictMock<IQueryable<Record>>();
queryable.Expect(q => q.Provider).Return(provider);
queryable.Expect(q => q.Expression).Return(Expression.Constant(records));
provider.Expect(p => p.CreateQuery<Record>(Arg<Expression>.Is.NotNull)).Return(records);
session = new LuceneSession<Record>(mapper, context, queryable);

session.Query();

queryable.VerifyAllExpectations();
provider.VerifyAllExpectations();
}

[Test]
public void PendingChanges_DirtyDocuments()
{
var record = new Record();
var copy = new Record();
mapper.Expect(m => m.Equals(record, copy)).Return(false);
mapper.Expect(m => m.ToDocument(Arg<Record>.Is.Same(record), Arg<Document>.Is.NotNull));
session.DocumentTracker.TrackDocument(record, copy);
record.Id = "1";

session.StageModifiedDocuments();

Assert.That(session.PendingChanges, Is.True, "Should detect modified document.");
}

[Test]
public void Dispose_Commits()
{
writer.Expect(w => w.DeleteAll());
writer.Expect(w => w.Commit());

session.DeleteAll();
session.Dispose();
}

[Test]
public void Commit_RollbackException_ThrowsAggregateException()
{
var ex1 = new Exception("ex1");
var ex2 = new Exception("ex2");
writer.Expect(w => w.DeleteAll()).Throw(ex1);
writer.Expect(w => w.Rollback()).Throw(ex2);

session.DeleteAll();

var thrown = Assert.Throws<AggregateException>(session.Commit);
Assert.That(thrown.InnerExceptions, Is.EquivalentTo(new[] {ex1, ex2}));
}

private void Verify()
{
mapper.VerifyAllExpectations();
Expand Down
34 changes: 34 additions & 0 deletions Lucene.Net.Linq.Tests/Mapping/ReflectionDocumentMapperTests.cs
Expand Up @@ -8,6 +8,9 @@ namespace Lucene.Net.Linq.Tests.Mapping
[TestFixture]
public class ReflectionDocumentMapperTests
{
private ReflectedDocument item1 = new ReflectedDocument { Id = "1", Version = new Version("1.2.3.4"), Location = "New York", Name = "Fun things", Number = 12 };
private ReflectedDocument item2 = new ReflectedDocument { Id = "1", Version = new Version("1.2.3.4"), Location = "New York", Name = "Fun things", Number = 12 };

[Test]
public void CtrFindsKeyFields()
{
Expand Down Expand Up @@ -50,6 +53,34 @@ public void ToKey_NotEqual()
Assert.That(key1, Is.Not.EqualTo(key2));
}

[Test]
public void Documents_Equal()
{
var mapper = new ReflectionDocumentMapper<ReflectedDocument>();

Assert.That(mapper.Equals(item1, item2), Is.True);
}

[Test]
public void Documents_Equal_IgnoredField()
{
var mapper = new ReflectionDocumentMapper<ReflectedDocument>();

item1.IgnoreMe = "different";

Assert.That(mapper.Equals(item1, item2), Is.True);
}

[Test]
public void Documents_Equal_Not()
{
var mapper = new ReflectionDocumentMapper<ReflectedDocument>();

item1.Version = new Version("5.6.7.8");

Assert.That(mapper.Equals(item1, item2), Is.False);
}

public class ReflectedDocument
{
[Field(Key = true)]
Expand All @@ -65,6 +96,9 @@ public class ReflectedDocument

[NumericField(Key = true)]
public int Number { get; set; }

[IgnoreField]
public string IgnoreMe { get; set; }
}
}

Expand Down

0 comments on commit f946b06

Please sign in to comment.