blob: cd02ee768af1a713367e70a44f372b0dfccb98eb [file] [log] [blame]
using Lucene.Net.Index.Extensions;
using Lucene.Net.Support;
using NUnit.Framework;
using System;
using System.Collections.Generic;
using Assert = Lucene.Net.TestFramework.Assert;
using Console = Lucene.Net.Util.SystemConsole;
namespace Lucene.Net.Index
{
/*
* 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 ArrayUtil = Lucene.Net.Util.ArrayUtil;
using IBits = Lucene.Net.Util.IBits;
using BytesRef = Lucene.Net.Util.BytesRef;
using Directory = Lucene.Net.Store.Directory;
using DocIdSetIterator = Lucene.Net.Search.DocIdSetIterator;
using LuceneTestCase = Lucene.Net.Util.LuceneTestCase;
using MockAnalyzer = Lucene.Net.Analysis.MockAnalyzer;
using MockDirectoryWrapper = Lucene.Net.Store.MockDirectoryWrapper;
using RAMDirectory = Lucene.Net.Store.RAMDirectory;
using TestUtil = Lucene.Net.Util.TestUtil;
[TestFixture]
public class TestPerSegmentDeletes : LuceneTestCase
{
[Test]
public virtual void TestDeletes1()
{
//IndexWriter.debug2 = System.out;
Directory dir = new MockDirectoryWrapper(new Random(Random.Next()), new RAMDirectory());
IndexWriterConfig iwc = new IndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(Random));
iwc.SetMergeScheduler(new SerialMergeScheduler());
iwc.SetMaxBufferedDocs(5000);
iwc.SetRAMBufferSizeMB(100);
RangeMergePolicy fsmp = new RangeMergePolicy(this, false);
iwc.SetMergePolicy(fsmp);
IndexWriter writer = new IndexWriter(dir, iwc);
for (int x = 0; x < 5; x++)
{
writer.AddDocument(DocHelper.CreateDocument(x, "1", 2));
//System.out.println("numRamDocs(" + x + ")" + writer.numRamDocs());
}
//System.out.println("commit1");
writer.Commit();
Assert.AreEqual(1, writer.SegmentCount);
for (int x = 5; x < 10; x++)
{
writer.AddDocument(DocHelper.CreateDocument(x, "2", 2));
//System.out.println("numRamDocs(" + x + ")" + writer.numRamDocs());
}
//System.out.println("commit2");
writer.Commit();
Assert.AreEqual(2, writer.SegmentCount);
for (int x = 10; x < 15; x++)
{
writer.AddDocument(DocHelper.CreateDocument(x, "3", 2));
//System.out.println("numRamDocs(" + x + ")" + writer.numRamDocs());
}
writer.DeleteDocuments(new Term("id", "1"));
writer.DeleteDocuments(new Term("id", "11"));
// flushing without applying deletes means
// there will still be deletes in the segment infos
writer.Flush(false, false);
Assert.IsTrue(writer.bufferedUpdatesStream.Any());
// get reader flushes pending deletes
// so there should not be anymore
IndexReader r1 = writer.GetReader();
Assert.IsFalse(writer.bufferedUpdatesStream.Any());
r1.Dispose();
// delete id:2 from the first segment
// merge segments 0 and 1
// which should apply the delete id:2
writer.DeleteDocuments(new Term("id", "2"));
writer.Flush(false, false);
fsmp = (RangeMergePolicy)writer.Config.MergePolicy;
fsmp.doMerge = true;
fsmp.start = 0;
fsmp.length = 2;
writer.MaybeMerge();
Assert.AreEqual(2, writer.SegmentCount);
// id:2 shouldn't exist anymore because
// it's been applied in the merge and now it's gone
IndexReader r2 = writer.GetReader();
int[] id2docs = ToDocsArray(new Term("id", "2"), null, r2);
Assert.IsTrue(id2docs == null);
r2.Dispose();
/*
/// // added docs are in the ram buffer
/// for (int x = 15; x < 20; x++) {
/// writer.AddDocument(TestIndexWriterReader.CreateDocument(x, "4", 2));
/// System.out.println("numRamDocs(" + x + ")" + writer.numRamDocs());
/// }
/// Assert.IsTrue(writer.numRamDocs() > 0);
/// // delete from the ram buffer
/// writer.DeleteDocuments(new Term("id", Integer.toString(13)));
///
/// Term id3 = new Term("id", Integer.toString(3));
///
/// // delete from the 1st segment
/// writer.DeleteDocuments(id3);
///
/// Assert.IsTrue(writer.numRamDocs() > 0);
///
/// //System.out
/// // .println("segdels1:" + writer.docWriter.deletesToString());
///
/// //Assert.IsTrue(writer.docWriter.segmentDeletes.Size() > 0);
///
/// // we cause a merge to happen
/// fsmp.doMerge = true;
/// fsmp.start = 0;
/// fsmp.length = 2;
/// System.out.println("maybeMerge "+writer.SegmentInfos);
///
/// SegmentInfo info0 = writer.SegmentInfos.Info(0);
/// SegmentInfo info1 = writer.SegmentInfos.Info(1);
///
/// writer.MaybeMerge();
/// System.out.println("maybeMerge after "+writer.SegmentInfos);
/// // there should be docs in RAM
/// Assert.IsTrue(writer.numRamDocs() > 0);
///
/// // assert we've merged the 1 and 2 segments
/// // and still have a segment leftover == 2
/// Assert.AreEqual(2, writer.SegmentInfos.Size());
/// Assert.IsFalse(segThere(info0, writer.SegmentInfos));
/// Assert.IsFalse(segThere(info1, writer.SegmentInfos));
///
/// //System.out.println("segdels2:" + writer.docWriter.deletesToString());
///
/// //Assert.IsTrue(writer.docWriter.segmentDeletes.Size() > 0);
///
/// IndexReader r = writer.GetReader();
/// IndexReader r1 = r.getSequentialSubReaders()[0];
/// printDelDocs(r1.GetLiveDocs());
/// int[] docs = toDocsArray(id3, null, r);
/// System.out.println("id3 docs:"+Arrays.toString(docs));
/// // there shouldn't be any docs for id:3
/// Assert.IsTrue(docs == null);
/// r.Dispose();
///
/// part2(writer, fsmp);
///
*/
// System.out.println("segdels2:"+writer.docWriter.segmentDeletes.toString());
//System.out.println("close");
writer.Dispose();
dir.Dispose();
}
/// <summary>
/// static boolean hasPendingDeletes(SegmentInfos infos) {
/// for (SegmentInfo info : infos) {
/// if (info.deletes.Any()) {
/// return true;
/// }
/// }
/// return false;
/// }
///
/// </summary>
internal virtual void Part2(IndexWriter writer, RangeMergePolicy fsmp)
{
for (int x = 20; x < 25; x++)
{
writer.AddDocument(DocHelper.CreateDocument(x, "5", 2));
//System.out.println("numRamDocs(" + x + ")" + writer.numRamDocs());
}
writer.Flush(false, false);
for (int x = 25; x < 30; x++)
{
writer.AddDocument(DocHelper.CreateDocument(x, "5", 2));
//System.out.println("numRamDocs(" + x + ")" + writer.numRamDocs());
}
writer.Flush(false, false);
//System.out.println("infos3:"+writer.SegmentInfos);
Term delterm = new Term("id", "8");
writer.DeleteDocuments(delterm);
//System.out.println("segdels3:" + writer.docWriter.deletesToString());
fsmp.doMerge = true;
fsmp.start = 1;
fsmp.length = 2;
writer.MaybeMerge();
// deletes for info1, the newly created segment from the
// merge should have no deletes because they were applied in
// the merge
//SegmentInfo info1 = writer.SegmentInfos.Info(1);
//Assert.IsFalse(exists(info1, writer.docWriter.segmentDeletes));
//System.out.println("infos4:"+writer.SegmentInfos);
//System.out.println("segdels4:" + writer.docWriter.deletesToString());
}
internal virtual bool SegThere(SegmentCommitInfo info, SegmentInfos infos)
{
foreach (SegmentCommitInfo si in infos.Segments)
{
if (si.Info.Name.Equals(info.Info.Name, StringComparison.Ordinal))
{
return true;
}
}
return false;
}
public static void PrintDelDocs(IBits bits)
{
if (bits == null)
{
return;
}
for (int x = 0; x < bits.Length; x++)
{
Console.WriteLine(x + ":" + bits.Get(x));
}
}
public virtual int[] ToDocsArray(Term term, IBits bits, IndexReader reader)
{
Fields fields = MultiFields.GetFields(reader);
Terms cterms = fields.GetTerms(term.Field);
TermsEnum ctermsEnum = cterms.GetIterator(null);
if (ctermsEnum.SeekExact(new BytesRef(term.Text())))
{
DocsEnum docsEnum = TestUtil.Docs(Random, ctermsEnum, bits, null, DocsFlags.NONE);
return ToArray(docsEnum);
}
return null;
}
public static int[] ToArray(DocsEnum docsEnum)
{
IList<int?> docs = new List<int?>();
while (docsEnum.NextDoc() != DocIdSetIterator.NO_MORE_DOCS)
{
int docID = docsEnum.DocID;
docs.Add(docID);
}
return ArrayUtil.ToInt32Array(docs);
}
public class RangeMergePolicy : MergePolicy
{
private readonly TestPerSegmentDeletes outerInstance;
internal bool doMerge = false;
internal int start;
internal int length;
internal readonly bool useCompoundFile;
internal RangeMergePolicy(TestPerSegmentDeletes outerInstance, bool useCompoundFile)
{
this.outerInstance = outerInstance;
this.useCompoundFile = useCompoundFile;
}
protected override void Dispose(bool disposing)
{
}
public override MergeSpecification FindMerges(MergeTrigger mergeTrigger, SegmentInfos segmentInfos)
{
MergeSpecification ms = new MergeSpecification();
if (doMerge)
{
OneMerge om = new OneMerge(segmentInfos.AsList().SubList(start, start + length));
ms.Add(om);
doMerge = false;
return ms;
}
return null;
}
public override MergeSpecification FindForcedMerges(SegmentInfos segmentInfos, int maxSegmentCount, IDictionary<SegmentCommitInfo, bool?> segmentsToMerge)
{
return null;
}
public override MergeSpecification FindForcedDeletesMerges(SegmentInfos segmentInfos)
{
return null;
}
public override bool UseCompoundFile(SegmentInfos segments, SegmentCommitInfo newSegment)
{
return useCompoundFile;
}
}
}
}