blob: 9f033a963d483cb3e351acd810b3ae375cee0f3e [file] [log] [blame]
using System;
namespace Lucene.Net.Search
{
/*
* 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 IBits = Lucene.Net.Util.IBits;
using FixedBitSet = Lucene.Net.Util.FixedBitSet;
using OpenBitSet = Lucene.Net.Util.OpenBitSet;
/// <summary>
/// Base class for <see cref="DocIdSet"/> to be used with <see cref="IFieldCache"/>. The implementation
/// of its iterator is very stupid and slow if the implementation of the
/// <see cref="MatchDoc(int)"/> method is not optimized, as iterators simply increment
/// the document id until <see cref="MatchDoc(int)"/> returns <c>true</c>. Because of this
/// <see cref="MatchDoc(int)"/> must be as fast as possible and in no case do any
/// I/O.
/// <para/>
/// @lucene.internal
/// </summary>
public class FieldCacheDocIdSet : DocIdSet
{
protected readonly int m_maxDoc;
protected readonly IBits m_acceptDocs;
private readonly Predicate<int> matchDoc;
private readonly bool hasMatchDoc;
// LUCENENET specific - added constructor to allow the class to be used without hand-coding
// a subclass by passing a predicate.
public FieldCacheDocIdSet(int maxDoc, IBits acceptDocs, Predicate<int> matchDoc)
{
this.matchDoc = matchDoc ?? throw new ArgumentNullException(nameof(matchDoc));
this.hasMatchDoc = true;
this.m_maxDoc = maxDoc;
this.m_acceptDocs = acceptDocs;
}
protected FieldCacheDocIdSet(int maxDoc, IBits acceptDocs)
{
this.m_maxDoc = maxDoc;
this.m_acceptDocs = acceptDocs;
}
/// <summary>
/// This method checks, if a doc is a hit
/// </summary>
protected internal virtual bool MatchDoc(int doc) => hasMatchDoc && matchDoc(doc);
/// <summary>
/// This DocIdSet is always cacheable (does not go back
/// to the reader for iteration)
/// </summary>
public override sealed bool IsCacheable => true;
public override sealed IBits Bits => (m_acceptDocs == null) ? (IBits)new BitsAnonymousClass(this) : new BitsAnonymousClass2(this);
private class BitsAnonymousClass : IBits
{
private readonly FieldCacheDocIdSet outerInstance;
public BitsAnonymousClass(FieldCacheDocIdSet outerInstance)
{
this.outerInstance = outerInstance;
}
public virtual bool Get(int docid)
{
return outerInstance.MatchDoc(docid);
}
public virtual int Length => outerInstance.m_maxDoc;
}
private class BitsAnonymousClass2 : IBits
{
private readonly FieldCacheDocIdSet outerInstance;
public BitsAnonymousClass2(FieldCacheDocIdSet outerInstance)
{
this.outerInstance = outerInstance;
}
public virtual bool Get(int docid)
{
return outerInstance.MatchDoc(docid) && outerInstance.m_acceptDocs.Get(docid);
}
public virtual int Length => outerInstance.m_maxDoc;
}
public override sealed DocIdSetIterator GetIterator()
{
if (m_acceptDocs == null)
{
// Specialization optimization disregard acceptDocs
return new DocIdSetIteratorAnonymousClass(this);
}
else if (m_acceptDocs is FixedBitSet || m_acceptDocs is OpenBitSet)
{
// special case for FixedBitSet / OpenBitSet: use the iterator and filter it
// (used e.g. when Filters are chained by FilteredQuery)
return new FilteredDocIdSetIteratorAnonymousClass(this, ((DocIdSet)m_acceptDocs).GetIterator());
}
else
{
// Stupid consultation of acceptDocs and matchDoc()
return new DocIdSetIteratorAnonymousClass2(this);
}
}
private class DocIdSetIteratorAnonymousClass : DocIdSetIterator
{
private readonly FieldCacheDocIdSet outerInstance;
public DocIdSetIteratorAnonymousClass(FieldCacheDocIdSet outerInstance)
{
this.outerInstance = outerInstance;
doc = -1;
}
private int doc;
public override int DocID => doc;
public override int NextDoc()
{
do
{
doc++;
if (doc >= outerInstance.m_maxDoc)
{
return doc = NO_MORE_DOCS;
}
} while (!outerInstance.MatchDoc(doc));
return doc;
}
public override int Advance(int target)
{
for (doc = target; doc < outerInstance.m_maxDoc; doc++)
{
if (outerInstance.MatchDoc(doc))
{
return doc;
}
}
return doc = NO_MORE_DOCS;
}
public override long GetCost()
{
return outerInstance.m_maxDoc;
}
}
private class FilteredDocIdSetIteratorAnonymousClass : FilteredDocIdSetIterator
{
private readonly FieldCacheDocIdSet outerInstance;
public FilteredDocIdSetIteratorAnonymousClass(FieldCacheDocIdSet outerInstance, Lucene.Net.Search.DocIdSetIterator iterator)
: base(iterator)
{
this.outerInstance = outerInstance;
}
protected override bool Match(int doc)
{
return outerInstance.MatchDoc(doc);
}
}
private class DocIdSetIteratorAnonymousClass2 : DocIdSetIterator
{
private readonly FieldCacheDocIdSet outerInstance;
public DocIdSetIteratorAnonymousClass2(FieldCacheDocIdSet outerInstance)
{
this.outerInstance = outerInstance;
doc = -1;
}
private int doc;
public override int DocID => doc;
public override int NextDoc()
{
do
{
doc++;
if (doc >= outerInstance.m_maxDoc)
{
return doc = NO_MORE_DOCS;
}
} while (!(outerInstance.MatchDoc(doc) && outerInstance.m_acceptDocs.Get(doc)));
return doc;
}
public override int Advance(int target)
{
for (doc = target; doc < outerInstance.m_maxDoc; doc++)
{
if (outerInstance.MatchDoc(doc) && outerInstance.m_acceptDocs.Get(doc))
{
return doc;
}
}
return doc = NO_MORE_DOCS;
}
public override long GetCost()
{
return outerInstance.m_maxDoc;
}
}
}
}