/* * 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 Lucene.Net.Analysis.Tokenattributes; using NUnit.Framework; using Analyzer = Lucene.Net.Analysis.Analyzer; using LowerCaseTokenizer = Lucene.Net.Analysis.LowerCaseTokenizer; using TokenFilter = Lucene.Net.Analysis.TokenFilter; using TokenStream = Lucene.Net.Analysis.TokenStream; using Document = Lucene.Net.Documents.Document; using Field = Lucene.Net.Documents.Field; using Index = Lucene.Net.Documents.Field.Index; using Store = Lucene.Net.Documents.Field.Store; using IndexInput = Lucene.Net.Store.IndexInput; using RAMDirectory = Lucene.Net.Store.RAMDirectory; using LuceneTestCase = Lucene.Net.Util.LuceneTestCase; namespace Lucene.Net.Index { /// 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. /// /// [TestFixture] public class TestMultiLevelSkipList:LuceneTestCase { [Test] public virtual void TestSimpleSkip() { RAMDirectory dir = new RAMDirectory(); IndexWriter writer = new IndexWriter(dir, new PayloadAnalyzer(), true, IndexWriter.MaxFieldLength.LIMITED); 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.ANALYZED)); writer.AddDocument(d1); } writer.Commit(); writer.Optimize(); writer.Close(); IndexReader reader = SegmentReader.GetOnlySegmentReader(dir); SegmentTermPositions tp = (SegmentTermPositions) reader.TermPositions(); tp.freqStream = new CountingStream(this, tp.freqStream); 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); Assert.Greater(maxCounter, counter, "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; internal IPayloadAttribute payloadAtt; protected internal PayloadFilter(TokenStream input):base(input) { payloadAtt = AddAttribute(); } public override bool IncrementToken() { bool hasNext = input.IncrementToken(); if (hasNext) { payloadAtt.Payload = new Payload(new byte[]{(byte) count++}); } return hasNext; } } 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; private bool isDisposed; 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); } protected override void Dispose(bool disposing) { if (isDisposed) return; if (disposing) { this.input.Close(); } isDisposed = true; } public override long FilePointer { get { return this.input.FilePointer; } } 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()); } } } }