/* | |
* 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 Document = Lucene.Net.Documents.Document; | |
using Field = Lucene.Net.Documents.Field; | |
using IndexInput = Lucene.Net.Store.IndexInput; | |
using RAMDirectory = Lucene.Net.Store.RAMDirectory; | |
using LuceneTestCase = Lucene.Net.Util.LuceneTestCase; | |
using Analyzer = Lucene.Net.Analysis.Analyzer; | |
using LowerCaseTokenizer = Lucene.Net.Analysis.LowerCaseTokenizer; | |
using Token = Lucene.Net.Analysis.Token; | |
using TokenFilter = Lucene.Net.Analysis.TokenFilter; | |
using TokenStream = Lucene.Net.Analysis.TokenStream; | |
namespace Lucene.Net.Index | |
{ | |
/// <summary> This testcase tests whether multi-level skipping is being used | |
/// to reduce I/O while skipping through posting lists. | |
/// | |
/// Skipping in general is already covered by several other | |
/// testcases. | |
/// | |
/// </summary> | |
[TestFixture] | |
public class TestMultiLevelSkipList : LuceneTestCase | |
{ | |
[Test] | |
public virtual void TestSimpleSkip() | |
{ | |
RAMDirectory dir = new RAMDirectory(); | |
IndexWriter writer = new IndexWriter(dir, new PayloadAnalyzer(), true); | |
Term term = new Term("test", "a"); | |
for (int i = 0; i < 5000; i++) | |
{ | |
Document d1 = new Document(); | |
d1.Add(new Field(term.Field(), term.Text(), Field.Store.NO, Field.Index.TOKENIZED)); | |
writer.AddDocument(d1); | |
} | |
writer.Flush(); | |
writer.Optimize(); | |
writer.Close(); | |
IndexReader reader = IndexReader.Open(dir); | |
SegmentTermPositions tp = (SegmentTermPositions) reader.TermPositions(); | |
tp.FreqStream_ForNUnitTest = new CountingStream(this, tp.FreqStream_ForNUnitTest); | |
for (int i = 0; i < 2; i++) | |
{ | |
counter = 0; | |
tp.Seek(term); | |
CheckSkipTo(tp, 14, 185); // no skips | |
CheckSkipTo(tp, 17, 190); // one skip on level 0 | |
CheckSkipTo(tp, 287, 200); // one skip on level 1, two on level 0 | |
// this test would fail if we had only one skip level, | |
// because than more bytes would be read from the freqStream | |
CheckSkipTo(tp, 4800, 250); // one skip on level 2 | |
} | |
} | |
public virtual void CheckSkipTo(TermPositions tp, int target, int maxCounter) | |
{ | |
tp.SkipTo(target); | |
if (maxCounter < counter) | |
{ | |
Assert.Fail("Too many bytes read: " + counter); | |
} | |
Assert.AreEqual(target, tp.Doc(), "Wrong document " + tp.Doc() + " after skipTo target " + target); | |
Assert.AreEqual(1, tp.Freq(), "Frequency is not 1: " + tp.Freq()); | |
tp.NextPosition(); | |
byte[] b = new byte[1]; | |
tp.GetPayload(b, 0); | |
Assert.AreEqual((byte) target, b[0], "Wrong payload for the target " + target + ": " + b[0]); | |
} | |
private class PayloadAnalyzer : Analyzer | |
{ | |
public override TokenStream TokenStream(System.String fieldName, System.IO.TextReader reader) | |
{ | |
return new PayloadFilter(new LowerCaseTokenizer(reader)); | |
} | |
} | |
private class PayloadFilter : TokenFilter | |
{ | |
internal static int count = 0; | |
protected internal PayloadFilter(TokenStream input):base(input) | |
{ | |
} | |
public override Token Next() | |
{ | |
Token t = input.Next(); | |
if (t != null) | |
{ | |
t.SetPayload(new Payload(new byte[]{(byte) count++})); | |
} | |
return t; | |
} | |
} | |
private int counter = 0; | |
// Simply extends IndexInput in a way that we are able to count the number | |
// of bytes read | |
internal class CountingStream : IndexInput, System.ICloneable | |
{ | |
private void InitBlock(TestMultiLevelSkipList enclosingInstance) | |
{ | |
this.enclosingInstance = enclosingInstance; | |
} | |
private TestMultiLevelSkipList enclosingInstance; | |
public TestMultiLevelSkipList Enclosing_Instance | |
{ | |
get | |
{ | |
return enclosingInstance; | |
} | |
} | |
private IndexInput input; | |
internal CountingStream(TestMultiLevelSkipList enclosingInstance, IndexInput input) | |
{ | |
InitBlock(enclosingInstance); | |
this.input = input; | |
} | |
public override byte ReadByte() | |
{ | |
Enclosing_Instance.counter++; | |
return this.input.ReadByte(); | |
} | |
public override void ReadBytes(byte[] b, int offset, int len) | |
{ | |
Enclosing_Instance.counter += len; | |
this.input.ReadBytes(b, offset, len); | |
} | |
public override void Close() | |
{ | |
this.input.Close(); | |
} | |
public override long GetFilePointer() | |
{ | |
return this.input.GetFilePointer(); | |
} | |
public override void Seek(long pos) | |
{ | |
this.input.Seek(pos); | |
} | |
public override long Length() | |
{ | |
return this.input.Length(); | |
} | |
public override System.Object Clone() | |
{ | |
return new CountingStream(enclosingInstance, (IndexInput) this.input.Clone()); | |
} | |
} | |
} | |
} |