539 lines
18 KiB
C#
539 lines
18 KiB
C#
/*
|
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
|
* contributor license agreements. See the NOTICE file distributed with
|
|
* this work for additional information regarding copyright ownership.
|
|
* The ASF licenses this file to You under the Apache License, Version 2.0
|
|
* (the "License"); you may not use this file except in compliance with
|
|
* the License. You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
using System;
|
|
|
|
using NUnit.Framework;
|
|
|
|
using SimpleAnalyzer = Lucene.Net.Analysis.SimpleAnalyzer;
|
|
using Document = Lucene.Net.Documents.Document;
|
|
using Field = Lucene.Net.Documents.Field;
|
|
using Norm = Lucene.Net.Index.SegmentReader.Norm;
|
|
using AlreadyClosedException = Lucene.Net.Store.AlreadyClosedException;
|
|
using Directory = Lucene.Net.Store.Directory;
|
|
using FSDirectory = Lucene.Net.Store.FSDirectory;
|
|
using LockObtainFailedException = Lucene.Net.Store.LockObtainFailedException;
|
|
using MockRAMDirectory = Lucene.Net.Store.MockRAMDirectory;
|
|
using Similarity = Lucene.Net.Search.Similarity;
|
|
using LuceneTestCase = Lucene.Net.Util.LuceneTestCase;
|
|
|
|
namespace Lucene.Net.Index
|
|
{
|
|
|
|
/// <summary> Tests cloning multiple types of readers, modifying the deletedDocs and norms
|
|
/// and verifies copy on write semantics of the deletedDocs and norms is
|
|
/// implemented properly
|
|
/// </summary>
|
|
[TestFixture]
|
|
public class TestIndexReaderClone:LuceneTestCase
|
|
{
|
|
|
|
[Test]
|
|
public virtual void TestCloneReadOnlySegmentReader()
|
|
{
|
|
Directory dir1 = new MockRAMDirectory();
|
|
|
|
TestIndexReaderReopen.CreateIndex(dir1, false);
|
|
IndexReader reader = IndexReader.Open(dir1, false);
|
|
IndexReader readOnlyReader = reader.Clone(true);
|
|
|
|
Assert.IsTrue(IsReadOnly(readOnlyReader), "reader isn't read only");
|
|
Assert.IsFalse(DeleteWorked(1, readOnlyReader), "deleting from the original should not have worked");
|
|
|
|
reader.Close();
|
|
readOnlyReader.Close();
|
|
dir1.Close();
|
|
}
|
|
|
|
// open non-readOnly reader1, clone to non-readOnly
|
|
// reader2, make sure we can change reader2
|
|
[Test]
|
|
public virtual void TestCloneNoChangesStillReadOnly()
|
|
{
|
|
Directory dir1 = new MockRAMDirectory();
|
|
|
|
TestIndexReaderReopen.CreateIndex(dir1, true);
|
|
IndexReader r1 = IndexReader.Open(dir1, false);
|
|
IndexReader r2 = r1.Clone(false);
|
|
|
|
Assert.IsTrue(DeleteWorked(1, r2), "deleting from the cloned should have worked");
|
|
|
|
r1.Close();
|
|
r2.Close();
|
|
dir1.Close();
|
|
}
|
|
|
|
// open non-readOnly reader1, clone to non-readOnly
|
|
// reader2, make sure we can change reader1
|
|
[Test]
|
|
public virtual void TestCloneWriteToOrig()
|
|
{
|
|
Directory dir1 = new MockRAMDirectory();
|
|
|
|
TestIndexReaderReopen.CreateIndex(dir1, true);
|
|
IndexReader r1 = IndexReader.Open(dir1, false);
|
|
IndexReader r2 = r1.Clone(false);
|
|
|
|
Assert.IsTrue(DeleteWorked(1, r1), "deleting from the original should have worked");
|
|
|
|
r1.Close();
|
|
r2.Close();
|
|
dir1.Close();
|
|
}
|
|
|
|
// open non-readOnly reader1, clone to non-readOnly
|
|
// reader2, make sure we can change reader2
|
|
[Test]
|
|
public virtual void TestCloneWriteToClone()
|
|
{
|
|
Directory dir1 = new MockRAMDirectory();
|
|
|
|
TestIndexReaderReopen.CreateIndex(dir1, true);
|
|
IndexReader r1 = IndexReader.Open(dir1, false);
|
|
IndexReader r2 = r1.Clone(false);
|
|
|
|
Assert.IsTrue(DeleteWorked(1, r2), "deleting from the original should have worked");
|
|
|
|
// should fail because reader1 holds the write lock
|
|
Assert.IsTrue(!DeleteWorked(1, r1), "first reader should not be able to delete");
|
|
r2.Close();
|
|
// should fail because we are now stale (reader1
|
|
// committed changes)
|
|
Assert.IsTrue(!DeleteWorked(1, r1), "first reader should not be able to delete");
|
|
r1.Close();
|
|
|
|
dir1.Close();
|
|
}
|
|
|
|
// create single-segment index, open non-readOnly
|
|
// SegmentReader, add docs, reopen to multireader, then do
|
|
// delete
|
|
[Test]
|
|
public virtual void TestReopenSegmentReaderToMultiReader()
|
|
{
|
|
Directory dir1 = new MockRAMDirectory();
|
|
|
|
TestIndexReaderReopen.CreateIndex(dir1, false);
|
|
IndexReader reader1 = IndexReader.Open(dir1, false);
|
|
|
|
TestIndexReaderReopen.ModifyIndex(5, dir1);
|
|
|
|
IndexReader reader2 = reader1.Reopen();
|
|
Assert.IsTrue(reader1 != reader2);
|
|
|
|
Assert.IsTrue(DeleteWorked(1, reader2));
|
|
reader1.Close();
|
|
reader2.Close();
|
|
dir1.Close();
|
|
}
|
|
|
|
// open non-readOnly reader1, clone to readOnly reader2
|
|
[Test]
|
|
public virtual void TestCloneWriteableToReadOnly()
|
|
{
|
|
Directory dir1 = new MockRAMDirectory();
|
|
|
|
TestIndexReaderReopen.CreateIndex(dir1, true);
|
|
IndexReader reader = IndexReader.Open(dir1, false);
|
|
IndexReader readOnlyReader = reader.Clone(true);
|
|
|
|
Assert.IsTrue(IsReadOnly(readOnlyReader), "reader isn't read only");
|
|
Assert.IsFalse(DeleteWorked(1, readOnlyReader), "deleting from the original should not have worked");
|
|
|
|
// this readonly reader shouldn't have a write lock
|
|
Assert.IsFalse(readOnlyReader.hasChanges, "readOnlyReader has a write lock");
|
|
|
|
reader.Close();
|
|
readOnlyReader.Close();
|
|
dir1.Close();
|
|
}
|
|
|
|
// open non-readOnly reader1, reopen to readOnly reader2
|
|
[Test]
|
|
public virtual void TestReopenWriteableToReadOnly()
|
|
{
|
|
Directory dir1 = new MockRAMDirectory();
|
|
|
|
TestIndexReaderReopen.CreateIndex(dir1, true);
|
|
IndexReader reader = IndexReader.Open(dir1, false);
|
|
int docCount = reader.NumDocs();
|
|
Assert.IsTrue(DeleteWorked(1, reader));
|
|
Assert.AreEqual(docCount - 1, reader.NumDocs());
|
|
|
|
IndexReader readOnlyReader = reader.Reopen(true);
|
|
Assert.IsTrue(IsReadOnly(readOnlyReader), "reader isn't read only");
|
|
|
|
Assert.IsFalse(DeleteWorked(1, readOnlyReader));
|
|
Assert.AreEqual(docCount - 1, readOnlyReader.NumDocs());
|
|
reader.Close();
|
|
readOnlyReader.Close();
|
|
dir1.Close();
|
|
}
|
|
|
|
// open readOnly reader1, clone to non-readOnly reader2
|
|
[Test]
|
|
public virtual void TestCloneReadOnlyToWriteable()
|
|
{
|
|
Directory dir1 = new MockRAMDirectory();
|
|
|
|
TestIndexReaderReopen.CreateIndex(dir1, true);
|
|
IndexReader reader1 = IndexReader.Open(dir1, true);
|
|
|
|
IndexReader reader2 = reader1.Clone(false);
|
|
|
|
Assert.IsFalse(IsReadOnly(reader2), "reader should not be read only");
|
|
|
|
Assert.IsFalse(DeleteWorked(1, reader1), "deleting from the original reader should not have worked");
|
|
// this readonly reader shouldn't yet have a write lock
|
|
Assert.IsFalse(reader2.hasChanges, "cloned reader should not have write lock");
|
|
|
|
Assert.IsTrue(DeleteWorked(1, reader2), "deleting from the cloned reader should have worked");
|
|
reader1.Close();
|
|
reader2.Close();
|
|
dir1.Close();
|
|
}
|
|
|
|
// open non-readOnly reader1 on multi-segment index, then
|
|
// optimize the index, then clone to readOnly reader2
|
|
[Test]
|
|
public virtual void TestReadOnlyCloneAfterOptimize()
|
|
{
|
|
Directory dir1 = new MockRAMDirectory();
|
|
|
|
TestIndexReaderReopen.CreateIndex(dir1, true);
|
|
IndexReader reader1 = IndexReader.Open(dir1, false);
|
|
IndexWriter w = new IndexWriter(dir1, new SimpleAnalyzer(), IndexWriter.MaxFieldLength.LIMITED);
|
|
w.Optimize();
|
|
w.Close();
|
|
IndexReader reader2 = reader1.Clone(true);
|
|
Assert.IsTrue(IsReadOnly(reader2));
|
|
reader1.Close();
|
|
reader2.Close();
|
|
dir1.Close();
|
|
}
|
|
|
|
private static bool DeleteWorked(int doc, IndexReader r)
|
|
{
|
|
bool exception = false;
|
|
try
|
|
{
|
|
// trying to delete from the original reader should throw an exception
|
|
r.DeleteDocument(doc);
|
|
}
|
|
catch (System.Exception ex)
|
|
{
|
|
exception = true;
|
|
}
|
|
return !exception;
|
|
}
|
|
|
|
[Test]
|
|
public virtual void TestCloneReadOnlyDirectoryReader()
|
|
{
|
|
Directory dir1 = new MockRAMDirectory();
|
|
|
|
TestIndexReaderReopen.CreateIndex(dir1, true);
|
|
IndexReader reader = IndexReader.Open(dir1, false);
|
|
IndexReader readOnlyReader = reader.Clone(true);
|
|
|
|
Assert.IsTrue(IsReadOnly(readOnlyReader), "reader isn't read only");
|
|
|
|
reader.Close();
|
|
readOnlyReader.Close();
|
|
dir1.Close();
|
|
}
|
|
|
|
public static bool IsReadOnly(IndexReader r)
|
|
{
|
|
if (r is ReadOnlySegmentReader || r is ReadOnlyDirectoryReader)
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
[Test]
|
|
public virtual void TestParallelReader()
|
|
{
|
|
Directory dir1 = new MockRAMDirectory();
|
|
TestIndexReaderReopen.CreateIndex(dir1, true);
|
|
Directory dir2 = new MockRAMDirectory();
|
|
TestIndexReaderReopen.CreateIndex(dir2, true);
|
|
IndexReader r1 = IndexReader.Open(dir1, false);
|
|
IndexReader r2 = IndexReader.Open(dir2, false);
|
|
|
|
ParallelReader pr1 = new ParallelReader();
|
|
pr1.Add(r1);
|
|
pr1.Add(r2);
|
|
|
|
PerformDefaultTests(pr1);
|
|
pr1.Close();
|
|
dir1.Close();
|
|
dir2.Close();
|
|
}
|
|
|
|
/// <summary> 1. Get a norm from the original reader 2. Clone the original reader 3.
|
|
/// Delete a document and set the norm of the cloned reader 4. Verify the norms
|
|
/// are not the same on each reader 5. Verify the doc deleted is only in the
|
|
/// cloned reader 6. Try to delete a document in the original reader, an
|
|
/// exception should be thrown
|
|
///
|
|
/// </summary>
|
|
/// <param name="r1">IndexReader to perform tests on
|
|
/// </param>
|
|
/// <throws> Exception </throws>
|
|
private void PerformDefaultTests(IndexReader r1)
|
|
{
|
|
float norm1 = Similarity.DecodeNorm(r1.Norms("field1")[4]);
|
|
|
|
IndexReader pr1Clone = (IndexReader) r1.Clone();
|
|
pr1Clone.DeleteDocument(10);
|
|
pr1Clone.SetNorm(4, "field1", 0.5f);
|
|
Assert.IsTrue(Similarity.DecodeNorm(r1.Norms("field1")[4]) == norm1);
|
|
Assert.IsTrue(Similarity.DecodeNorm(pr1Clone.Norms("field1")[4]) != norm1);
|
|
|
|
Assert.IsTrue(!r1.IsDeleted(10));
|
|
Assert.IsTrue(pr1Clone.IsDeleted(10));
|
|
|
|
// try to update the original reader, which should throw an exception
|
|
Assert.Throws<LockObtainFailedException>(() => r1.DeleteDocument(11),
|
|
"Tried to delete doc 11 and an exception should have been thrown");
|
|
pr1Clone.Close();
|
|
}
|
|
|
|
[Test]
|
|
public virtual void TestMixedReaders()
|
|
{
|
|
Directory dir1 = new MockRAMDirectory();
|
|
TestIndexReaderReopen.CreateIndex(dir1, true);
|
|
Directory dir2 = new MockRAMDirectory();
|
|
TestIndexReaderReopen.CreateIndex(dir2, true);
|
|
IndexReader r1 = IndexReader.Open(dir1, false);
|
|
IndexReader r2 = IndexReader.Open(dir2, false);
|
|
|
|
MultiReader multiReader = new MultiReader(new IndexReader[]{r1, r2});
|
|
PerformDefaultTests(multiReader);
|
|
multiReader.Close();
|
|
dir1.Close();
|
|
dir2.Close();
|
|
}
|
|
|
|
[Test]
|
|
public virtual void TestSegmentReaderUndeleteall()
|
|
{
|
|
Directory dir1 = new MockRAMDirectory();
|
|
TestIndexReaderReopen.CreateIndex(dir1, false);
|
|
SegmentReader origSegmentReader = SegmentReader.GetOnlySegmentReader(dir1);
|
|
origSegmentReader.DeleteDocument(10);
|
|
AssertDelDocsRefCountEquals(1, origSegmentReader);
|
|
origSegmentReader.UndeleteAll();
|
|
Assert.IsNull(origSegmentReader.deletedDocsRef_ForNUnit);
|
|
origSegmentReader.Close();
|
|
// need to test norms?
|
|
dir1.Close();
|
|
}
|
|
|
|
[Test]
|
|
public virtual void TestSegmentReaderCloseReferencing()
|
|
{
|
|
Directory dir1 = new MockRAMDirectory();
|
|
TestIndexReaderReopen.CreateIndex(dir1, false);
|
|
SegmentReader origSegmentReader = SegmentReader.GetOnlySegmentReader(dir1);
|
|
origSegmentReader.DeleteDocument(1);
|
|
origSegmentReader.SetNorm(4, "field1", 0.5f);
|
|
|
|
SegmentReader clonedSegmentReader = (SegmentReader) origSegmentReader.Clone();
|
|
AssertDelDocsRefCountEquals(2, origSegmentReader);
|
|
origSegmentReader.Close();
|
|
AssertDelDocsRefCountEquals(1, origSegmentReader);
|
|
// check the norm refs
|
|
Norm norm = clonedSegmentReader.norms_ForNUnit["field1"];
|
|
Assert.AreEqual(1, norm.BytesRef().RefCount());
|
|
clonedSegmentReader.Close();
|
|
dir1.Close();
|
|
}
|
|
|
|
[Test]
|
|
public virtual void TestSegmentReaderDelDocsReferenceCounting()
|
|
{
|
|
Directory dir1 = new MockRAMDirectory();
|
|
TestIndexReaderReopen.CreateIndex(dir1, false);
|
|
|
|
IndexReader origReader = IndexReader.Open(dir1, false);
|
|
SegmentReader origSegmentReader = SegmentReader.GetOnlySegmentReader(origReader);
|
|
// deletedDocsRef should be null because nothing has updated yet
|
|
Assert.IsNull(origSegmentReader.deletedDocsRef_ForNUnit);
|
|
|
|
// we deleted a document, so there is now a deletedDocs bitvector and a
|
|
// reference to it
|
|
origReader.DeleteDocument(1);
|
|
AssertDelDocsRefCountEquals(1, origSegmentReader);
|
|
|
|
// the cloned segmentreader should have 2 references, 1 to itself, and 1 to
|
|
// the original segmentreader
|
|
IndexReader clonedReader = (IndexReader) origReader.Clone();
|
|
SegmentReader clonedSegmentReader = SegmentReader.GetOnlySegmentReader(clonedReader);
|
|
AssertDelDocsRefCountEquals(2, origSegmentReader);
|
|
// deleting a document creates a new deletedDocs bitvector, the refs goes to
|
|
// 1
|
|
clonedReader.DeleteDocument(2);
|
|
AssertDelDocsRefCountEquals(1, origSegmentReader);
|
|
AssertDelDocsRefCountEquals(1, clonedSegmentReader);
|
|
|
|
// make sure the deletedocs objects are different (copy
|
|
// on write)
|
|
Assert.IsTrue(origSegmentReader.deletedDocs_ForNUnit != clonedSegmentReader.deletedDocs_ForNUnit);
|
|
|
|
AssertDocDeleted(origSegmentReader, clonedSegmentReader, 1);
|
|
Assert.IsTrue(!origSegmentReader.IsDeleted(2)); // doc 2 should not be deleted
|
|
// in original segmentreader
|
|
Assert.IsTrue(clonedSegmentReader.IsDeleted(2)); // doc 2 should be deleted in
|
|
// cloned segmentreader
|
|
|
|
// deleting a doc from the original segmentreader should throw an exception
|
|
Assert.Throws<LockObtainFailedException>(() => origReader.DeleteDocument(4), "expected exception");
|
|
|
|
origReader.Close();
|
|
// try closing the original segment reader to see if it affects the
|
|
// clonedSegmentReader
|
|
clonedReader.DeleteDocument(3);
|
|
clonedReader.Flush();
|
|
AssertDelDocsRefCountEquals(1, clonedSegmentReader);
|
|
|
|
// test a reopened reader
|
|
IndexReader reopenedReader = clonedReader.Reopen();
|
|
IndexReader cloneReader2 = (IndexReader) reopenedReader.Clone();
|
|
SegmentReader cloneSegmentReader2 = SegmentReader.GetOnlySegmentReader(cloneReader2);
|
|
AssertDelDocsRefCountEquals(2, cloneSegmentReader2);
|
|
clonedReader.Close();
|
|
reopenedReader.Close();
|
|
cloneReader2.Close();
|
|
|
|
dir1.Close();
|
|
}
|
|
|
|
// LUCENE-1648
|
|
[Test]
|
|
public virtual void TestCloneWithDeletes()
|
|
{
|
|
Directory dir1 = new MockRAMDirectory();
|
|
TestIndexReaderReopen.CreateIndex(dir1, false);
|
|
IndexReader origReader = IndexReader.Open(dir1, false);
|
|
origReader.DeleteDocument(1);
|
|
|
|
IndexReader clonedReader = (IndexReader) origReader.Clone();
|
|
origReader.Close();
|
|
clonedReader.Close();
|
|
|
|
IndexReader r = IndexReader.Open(dir1, false);
|
|
Assert.IsTrue(r.IsDeleted(1));
|
|
r.Close();
|
|
dir1.Close();
|
|
}
|
|
|
|
// LUCENE-1648
|
|
[Test]
|
|
public virtual void TestCloneWithSetNorm()
|
|
{
|
|
Directory dir1 = new MockRAMDirectory();
|
|
TestIndexReaderReopen.CreateIndex(dir1, false);
|
|
IndexReader orig = IndexReader.Open(dir1, false);
|
|
orig.SetNorm(1, "field1", 17.0f);
|
|
byte encoded = Similarity.EncodeNorm(17.0f);
|
|
Assert.AreEqual(encoded, orig.Norms("field1")[1]);
|
|
|
|
// the cloned segmentreader should have 2 references, 1 to itself, and 1 to
|
|
// the original segmentreader
|
|
IndexReader clonedReader = (IndexReader) orig.Clone();
|
|
orig.Close();
|
|
clonedReader.Close();
|
|
|
|
IndexReader r = IndexReader.Open(dir1, false);
|
|
Assert.AreEqual(encoded, r.Norms("field1")[1]);
|
|
r.Close();
|
|
dir1.Close();
|
|
}
|
|
|
|
private void AssertDocDeleted(SegmentReader reader, SegmentReader reader2, int doc)
|
|
{
|
|
Assert.AreEqual(reader.IsDeleted(doc), reader2.IsDeleted(doc));
|
|
}
|
|
|
|
private void AssertDelDocsRefCountEquals(int refCount, SegmentReader reader)
|
|
{
|
|
Assert.AreEqual(refCount, reader.deletedDocsRef_ForNUnit.RefCount());
|
|
}
|
|
|
|
[Test]
|
|
public virtual void TestCloneSubreaders()
|
|
{
|
|
Directory dir1 = new MockRAMDirectory();
|
|
|
|
TestIndexReaderReopen.CreateIndex(dir1, true);
|
|
IndexReader reader = IndexReader.Open(dir1, false);
|
|
reader.DeleteDocument(1); // acquire write lock
|
|
IndexReader[] subs = reader.GetSequentialSubReaders();
|
|
System.Diagnostics.Debug.Assert(subs.Length > 1);
|
|
|
|
IndexReader[] clones = new IndexReader[subs.Length];
|
|
for (int x = 0; x < subs.Length; x++)
|
|
{
|
|
clones[x] = (IndexReader) subs[x].Clone();
|
|
}
|
|
reader.Close();
|
|
for (int x = 0; x < subs.Length; x++)
|
|
{
|
|
clones[x].Close();
|
|
}
|
|
dir1.Close();
|
|
}
|
|
|
|
[Test]
|
|
public virtual void TestLucene1516Bug()
|
|
{
|
|
Directory dir1 = new MockRAMDirectory();
|
|
TestIndexReaderReopen.CreateIndex(dir1, false);
|
|
IndexReader r1 = IndexReader.Open(dir1, false);
|
|
r1.IncRef();
|
|
IndexReader r2 = r1.Clone(false);
|
|
r1.DeleteDocument(5);
|
|
r1.DecRef();
|
|
|
|
r1.IncRef();
|
|
|
|
r2.Close();
|
|
r1.DecRef();
|
|
r1.Close();
|
|
dir1.Close();
|
|
}
|
|
|
|
[Test]
|
|
public virtual void TestCloseStoredFields()
|
|
{
|
|
Directory dir = new MockRAMDirectory();
|
|
IndexWriter w = new IndexWriter(dir, new SimpleAnalyzer(), IndexWriter.MaxFieldLength.UNLIMITED);
|
|
w.UseCompoundFile = false;
|
|
Document doc = new Document();
|
|
doc.Add(new Field("field", "yes it's stored", Field.Store.YES, Field.Index.ANALYZED));
|
|
w.AddDocument(doc);
|
|
w.Close();
|
|
IndexReader r1 = IndexReader.Open(dir, false);
|
|
IndexReader r2 = r1.Clone(false);
|
|
r1.Close();
|
|
r2.Close();
|
|
dir.Close();
|
|
}
|
|
}
|
|
} |