blob: eaa158e671e6b2932a953b3c72fcb18b2f18a30f [file] [log] [blame]
using Lucene.Net.Index;
using Lucene.Net.Support;
using Lucene.Net.Support.IO;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Runtime.CompilerServices;
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 AtomicReader = Lucene.Net.Index.AtomicReader;
using BinaryDocValues = Lucene.Net.Index.BinaryDocValues;
using BytesRef = Lucene.Net.Util.BytesRef;
using DocsEnum = Lucene.Net.Index.DocsEnum;
using DocTermOrds = Lucene.Net.Index.DocTermOrds;
using DocValues = Lucene.Net.Index.DocValues;
using FieldCacheSanityChecker = Lucene.Net.Util.FieldCacheSanityChecker;
using FieldInfo = Lucene.Net.Index.FieldInfo;
using FixedBitSet = Lucene.Net.Util.FixedBitSet;
using GrowableWriter = Lucene.Net.Util.Packed.GrowableWriter;
using IBits = Lucene.Net.Util.IBits;
using IndexReader = Lucene.Net.Index.IndexReader;
using MonotonicAppendingInt64Buffer = Lucene.Net.Util.Packed.MonotonicAppendingInt64Buffer;
using NumericDocValues = Lucene.Net.Index.NumericDocValues;
using PackedInt32s = Lucene.Net.Util.Packed.PackedInt32s;
using PagedBytes = Lucene.Net.Util.PagedBytes;
using SegmentReader = Lucene.Net.Index.SegmentReader;
using SortedDocValues = Lucene.Net.Index.SortedDocValues;
using SortedSetDocValues = Lucene.Net.Index.SortedSetDocValues;
using Terms = Lucene.Net.Index.Terms;
using TermsEnum = Lucene.Net.Index.TermsEnum;
/// <summary>
/// Expert: The default cache implementation, storing all values in memory.
/// A WeakHashMap is used for storage.
/// <para/>
/// @since lucene 1.4
/// </summary>
internal class FieldCacheImpl : IFieldCache
{
private IDictionary<Type, Cache> caches;
internal FieldCacheImpl()
{
Init();
//Have to do this here because no 'this' in class definition
purgeCore = new CoreClosedListenerAnonymousInnerClassHelper(this);
purgeReader = new ReaderClosedListenerAnonymousInnerClassHelper(this);
}
private void Init()
{
lock (this)
{
caches = new Dictionary<Type, Cache>(9);
caches[typeof(sbyte)] = new ByteCache(this);
caches[typeof(short)] = new Int16Cache(this);
caches[typeof(int)] = new Int32Cache(this);
caches[typeof(float)] = new SingleCache(this);
caches[typeof(long)] = new Int64Cache(this);
caches[typeof(double)] = new DoubleCache(this);
caches[typeof(BinaryDocValues)] = new BinaryDocValuesCache(this);
caches[typeof(SortedDocValues)] = new SortedDocValuesCache(this);
caches[typeof(DocTermOrds)] = new DocTermOrdsCache(this);
caches[typeof(DocsWithFieldCache)] = new DocsWithFieldCache(this);
}
}
public virtual void PurgeAllCaches()
{
lock (this)
{
Init();
}
}
public virtual void PurgeByCacheKey(object coreCacheKey)
{
lock (this)
{
foreach (Cache c in caches.Values)
{
c.PurgeByCacheKey(coreCacheKey);
}
}
}
public virtual FieldCache.CacheEntry[] GetCacheEntries()
{
lock (this)
{
IList<FieldCache.CacheEntry> result = new List<FieldCache.CacheEntry>(17);
foreach (KeyValuePair<Type, Cache> cacheEntry in caches)
{
Cache cache = cacheEntry.Value;
Type cacheType = cacheEntry.Key;
lock (cache.readerCache)
{
foreach (KeyValuePair<object, IDictionary<CacheKey, object>> readerCacheEntry in cache.readerCache)
{
object readerKey = readerCacheEntry.Key;
if (readerKey == null)
{
continue;
}
IDictionary<CacheKey, object> innerCache = readerCacheEntry.Value;
foreach (KeyValuePair<CacheKey, object> mapEntry in innerCache)
{
CacheKey entry = mapEntry.Key;
result.Add(new FieldCache.CacheEntry(readerKey, entry.field, cacheType, entry.custom, mapEntry.Value));
}
}
}
}
return result.ToArray();
}
}
// per-segment fieldcaches don't purge until the shared core closes.
internal readonly SegmentReader.ICoreDisposedListener purgeCore;
private class CoreClosedListenerAnonymousInnerClassHelper : SegmentReader.ICoreDisposedListener
{
private FieldCacheImpl outerInstance;
public CoreClosedListenerAnonymousInnerClassHelper(FieldCacheImpl outerInstance)
{
this.outerInstance = outerInstance;
}
public void OnDispose(object ownerCoreCacheKey)
{
outerInstance.PurgeByCacheKey(ownerCoreCacheKey);
}
}
// composite/SlowMultiReaderWrapper fieldcaches don't purge until composite reader is closed.
internal readonly IndexReader.IReaderClosedListener purgeReader;
private class ReaderClosedListenerAnonymousInnerClassHelper : IndexReader.IReaderClosedListener
{
private FieldCacheImpl outerInstance;
public ReaderClosedListenerAnonymousInnerClassHelper(FieldCacheImpl outerInstance)
{
this.outerInstance = outerInstance;
}
public void OnClose(IndexReader owner)
{
Debug.Assert(owner is AtomicReader);
outerInstance.PurgeByCacheKey(((AtomicReader)owner).CoreCacheKey);
}
}
private void InitReader(AtomicReader reader)
{
if (reader is SegmentReader)
{
((SegmentReader)reader).AddCoreDisposedListener(purgeCore);
}
else
{
// we have a slow reader of some sort, try to register a purge event
// rather than relying on gc:
object key = reader.CoreCacheKey;
if (key is AtomicReader)
{
((AtomicReader)key).AddReaderClosedListener(purgeReader);
}
else
{
// last chance
reader.AddReaderClosedListener(purgeReader);
}
}
}
/// <summary>
/// Expert: Internal cache. </summary>
internal abstract class Cache
{
internal Cache(FieldCacheImpl wrapper)
{
this.wrapper = wrapper;
}
internal readonly FieldCacheImpl wrapper;
#if FEATURE_CONDITIONALWEAKTABLE_ENUMERATOR
internal ConditionalWeakTable<object, IDictionary<CacheKey, object>> readerCache = new ConditionalWeakTable<object, IDictionary<CacheKey, object>>();
#else
internal WeakDictionary<object, IDictionary<CacheKey, object>> readerCache = new WeakDictionary<object, IDictionary<CacheKey, object>>();
#endif
protected abstract object CreateValue(AtomicReader reader, CacheKey key, bool setDocsWithField);
/// <summary>
/// Remove this reader from the cache, if present. </summary>
public virtual void PurgeByCacheKey(object coreCacheKey)
{
lock (readerCache)
{
readerCache.Remove(coreCacheKey);
}
}
/// <summary>
/// Sets the key to the value for the provided reader;
/// if the key is already set then this doesn't change it.
/// </summary>
public virtual void Put(AtomicReader reader, CacheKey key, object value)
{
object readerKey = reader.CoreCacheKey;
lock (readerCache)
{
if (!readerCache.TryGetValue(readerKey, out IDictionary<CacheKey, object> innerCache) || innerCache == null)
{
// First time this reader is using FieldCache
innerCache = new Dictionary<CacheKey, object>();
readerCache.AddOrUpdate(readerKey, innerCache);
wrapper.InitReader(reader);
}
// LUCENENET NOTE: We declare a temp variable here so we
// don't overwrite value variable with the null
// that will result when this if block succeeds; otherwise
// we won't have a value to put in the cache.
if (!innerCache.TryGetValue(key, out object temp))
{
innerCache[key] = value;
}
else
{
// Another thread beat us to it; leave the current
// value
}
}
}
public virtual object Get(AtomicReader reader, CacheKey key, bool setDocsWithField)
{
IDictionary<CacheKey, object> innerCache;
object value;
object readerKey = reader.CoreCacheKey;
lock (readerCache)
{
if (!readerCache.TryGetValue(readerKey, out innerCache) || innerCache == null)
{
// First time this reader is using FieldCache
innerCache = new Dictionary<CacheKey, object>();
readerCache.AddOrUpdate(readerKey, innerCache);
wrapper.InitReader(reader);
value = null;
}
else
{
innerCache.TryGetValue(key, out value);
}
if (value == null)
{
value = new FieldCache.CreationPlaceholder();
innerCache[key] = value;
}
}
if (value is FieldCache.CreationPlaceholder)
{
lock (value)
{
FieldCache.CreationPlaceholder progress = (FieldCache.CreationPlaceholder)value;
if (progress.Value == null)
{
progress.Value = CreateValue(reader, key, setDocsWithField);
lock (readerCache)
{
innerCache[key] = progress.Value;
}
// Only check if key.custom (the parser) is
// non-null; else, we check twice for a single
// call to FieldCache.getXXX
if (key.custom != null && wrapper != null)
{
TextWriter infoStream = wrapper.InfoStream;
if (infoStream != null)
{
PrintNewInsanity(infoStream, progress.Value);
}
}
}
return progress.Value;
}
}
return value;
}
private void PrintNewInsanity(TextWriter infoStream, object value)
{
FieldCacheSanityChecker.Insanity[] insanities = FieldCacheSanityChecker.CheckSanity(wrapper);
for (int i = 0; i < insanities.Length; i++)
{
FieldCacheSanityChecker.Insanity insanity = insanities[i];
FieldCache.CacheEntry[] entries = insanity.CacheEntries;
for (int j = 0; j < entries.Length; j++)
{
if (entries[j].Value == value)
{
// OK this insanity involves our entry
infoStream.WriteLine("WARNING: new FieldCache insanity created\nDetails: " + insanity.ToString());
infoStream.WriteLine("\nStack:\n");
infoStream.WriteLine(new Exception().StackTrace);
break;
}
}
}
}
}
/// <summary>
/// Expert: Every composite-key in the internal cache is of this type. </summary>
internal class CacheKey
{
internal readonly string field; // which Field
internal readonly object custom; // which custom comparer or parser
/// <summary>
/// Creates one of these objects for a custom comparer/parser. </summary>
internal CacheKey(string field, object custom)
{
this.field = field;
this.custom = custom;
}
/// <summary>
/// Two of these are equal if they reference the same field and type. </summary>
public override bool Equals(object o)
{
if (o is CacheKey)
{
CacheKey other = (CacheKey)o;
if (other.field.Equals(field, StringComparison.Ordinal))
{
if (other.custom == null)
{
if (custom == null)
{
return true;
}
}
else if (other.custom.Equals(custom))
{
return true;
}
}
}
return false;
}
/// <summary>
/// Composes a hashcode based on the field and type. </summary>
public override int GetHashCode()
{
return field.GetHashCode() ^ (custom == null ? 0 : custom.GetHashCode());
}
}
private abstract class Uninvert
{
internal IBits docsWithField; // LUCENENET NOTE: Changed from public to internal, since FieldCacheImpl is internal anyway
public virtual void DoUninvert(AtomicReader reader, string field, bool setDocsWithField)
{
int maxDoc = reader.MaxDoc;
Terms terms = reader.GetTerms(field);
if (terms != null)
{
if (setDocsWithField)
{
int termsDocCount = terms.DocCount;
Debug.Assert(termsDocCount <= maxDoc);
if (termsDocCount == maxDoc)
{
// Fast case: all docs have this field:
this.docsWithField = new Lucene.Net.Util.Bits.MatchAllBits(maxDoc);
setDocsWithField = false;
}
}
TermsEnum termsEnum = GetTermsEnum(terms);
DocsEnum docs = null;
FixedBitSet docsWithField = null;
while (true)
{
BytesRef term = termsEnum.Next();
if (term == null)
{
break;
}
VisitTerm(term);
docs = termsEnum.Docs(null, docs, DocsFlags.NONE);
while (true)
{
int docID = docs.NextDoc();
if (docID == DocIdSetIterator.NO_MORE_DOCS)
{
break;
}
VisitDoc(docID);
if (setDocsWithField)
{
if (docsWithField == null)
{
// Lazy init
this.docsWithField = docsWithField = new FixedBitSet(maxDoc);
}
docsWithField.Set(docID);
}
}
}
}
}
protected abstract TermsEnum GetTermsEnum(Terms terms); // LUCENENET specific - renamed from TermsEnum()
protected abstract void VisitTerm(BytesRef term);
protected abstract void VisitDoc(int docID);
}
// null Bits means no docs matched
internal virtual void SetDocsWithField(AtomicReader reader, string field, IBits docsWithField)
{
int maxDoc = reader.MaxDoc;
IBits bits;
if (docsWithField == null)
{
bits = new Lucene.Net.Util.Bits.MatchNoBits(maxDoc);
}
else if (docsWithField is FixedBitSet)
{
int numSet = ((FixedBitSet)docsWithField).Cardinality();
if (numSet >= maxDoc)
{
// The cardinality of the BitSet is maxDoc if all documents have a value.
Debug.Assert(numSet == maxDoc);
bits = new Lucene.Net.Util.Bits.MatchAllBits(maxDoc);
}
else
{
bits = docsWithField;
}
}
else
{
bits = docsWithField;
}
caches[typeof(DocsWithFieldCache)].Put(reader, new CacheKey(field, null), bits);
}
/// <summary>
/// Checks the internal cache for an appropriate entry, and if none is
/// found, reads the terms in <paramref name="field"/> as a single <see cref="byte"/> and returns an array
/// of size <c>reader.MaxDoc</c> of the value each document
/// has in the given field. </summary>
/// <param name="reader"> Used to get field values. </param>
/// <param name="field"> Which field contains the single <see cref="byte"/> values. </param>
/// <param name="setDocsWithField"> If true then <see cref="GetDocsWithField(AtomicReader, string)"/> will
/// also be computed and stored in the <see cref="IFieldCache"/>. </param>
/// <returns> The values in the given field for each document. </returns>
/// <exception cref="IOException"> If any error occurs. </exception>
[Obsolete("(4.4) Index as a numeric field using Int32Field and then use GetInt32s(AtomicReader, string, bool) instead.")]
public virtual FieldCache.Bytes GetBytes(AtomicReader reader, string field, bool setDocsWithField)
{
return GetBytes(reader, field, null, setDocsWithField);
}
#pragma warning disable 612, 618
public virtual FieldCache.Bytes GetBytes(AtomicReader reader, string field, FieldCache.IByteParser parser, bool setDocsWithField)
#pragma warning restore 612, 618
{
NumericDocValues valuesIn = reader.GetNumericDocValues(field);
if (valuesIn != null)
{
// Not cached here by FieldCacheImpl (cached instead
// per-thread by SegmentReader):
return new FieldCache_BytesAnonymousInnerClassHelper(this, valuesIn);
}
else
{
FieldInfo info = reader.FieldInfos.FieldInfo(field);
if (info == null)
{
return FieldCache.Bytes.EMPTY;
}
else if (info.HasDocValues)
{
throw new InvalidOperationException("Type mismatch: " + field + " was indexed as " + info.DocValuesType);
}
else if (!info.IsIndexed)
{
return FieldCache.Bytes.EMPTY;
}
return (FieldCache.Bytes)caches[typeof(sbyte)].Get(reader, new CacheKey(field, parser), setDocsWithField);
}
}
private class FieldCache_BytesAnonymousInnerClassHelper : FieldCache.Bytes
{
private readonly FieldCacheImpl outerInstance;
private NumericDocValues valuesIn;
public FieldCache_BytesAnonymousInnerClassHelper(FieldCacheImpl outerInstance, NumericDocValues valuesIn)
{
this.outerInstance = outerInstance;
this.valuesIn = valuesIn;
}
public override byte Get(int docID)
{
return (byte)valuesIn.Get(docID);
}
}
internal class BytesFromArray : FieldCache.Bytes
{
private readonly sbyte[] values;
public BytesFromArray(sbyte[] values)
{
this.values = values;
}
public override byte Get(int docID)
{
return (byte)values[docID];
}
}
internal sealed class ByteCache : Cache
{
internal ByteCache(FieldCacheImpl wrapper)
: base(wrapper)
{
}
protected override object CreateValue(AtomicReader reader, CacheKey key, bool setDocsWithField)
{
int maxDoc = reader.MaxDoc;
sbyte[] values;
#pragma warning disable 612, 618
FieldCache.IByteParser parser = (FieldCache.IByteParser)key.custom;
#pragma warning restore 612, 618
if (parser == null)
{
// Confusing: must delegate to wrapper (vs simply
// setting parser = DEFAULT_INT16_PARSER) so cache
// key includes DEFAULT_INT16_PARSER:
#pragma warning disable 612, 618
return wrapper.GetBytes(reader, key.field, FieldCache.DEFAULT_BYTE_PARSER, setDocsWithField);
#pragma warning restore 612, 618
}
values = new sbyte[maxDoc];
Uninvert u = new UninvertAnonymousInnerClassHelper(values, parser);
u.DoUninvert(reader, key.field, setDocsWithField);
if (setDocsWithField)
{
wrapper.SetDocsWithField(reader, key.field, u.docsWithField);
}
return new BytesFromArray(values);
}
private class UninvertAnonymousInnerClassHelper : Uninvert
{
private readonly sbyte[] values;
#pragma warning disable 612, 618
private readonly FieldCache.IByteParser parser;
public UninvertAnonymousInnerClassHelper(sbyte[] values, FieldCache.IByteParser parser)
#pragma warning restore 612, 618
{
this.values = values;
this.parser = parser;
}
private sbyte currentValue;
protected override void VisitTerm(BytesRef term)
{
currentValue = (sbyte)parser.ParseByte(term);
}
protected override void VisitDoc(int docID)
{
values[docID] = currentValue;
}
protected override TermsEnum GetTermsEnum(Terms terms)
{
return parser.TermsEnum(terms);
}
}
}
/// <summary>
/// Checks the internal cache for an appropriate entry, and if none is
/// found, reads the terms in <paramref name="field"/> as <see cref="short"/>s and returns an array
/// of size <c>reader.MaxDoc</c> of the value each document
/// has in the given field.
/// <para/>
/// NOTE: this was getShorts() in Lucene
/// </summary>
/// <param name="reader"> Used to get field values. </param>
/// <param name="field"> Which field contains the <see cref="short"/>s. </param>
/// <param name="setDocsWithField"> If true then <see cref="GetDocsWithField(AtomicReader, string)"/> will
/// also be computed and stored in the <see cref="IFieldCache"/>. </param>
/// <returns> The values in the given field for each document. </returns>
/// <exception cref="IOException"> If any error occurs. </exception>
[Obsolete("(4.4) Index as a numeric field using Int32Field and then use GetInt32s(AtomicReader, string, bool) instead.")]
public virtual FieldCache.Int16s GetInt16s(AtomicReader reader, string field, bool setDocsWithField)
{
return GetInt16s(reader, field, null, setDocsWithField);
}
/// <summary>
/// Checks the internal cache for an appropriate entry, and if none is found,
/// reads the terms in <paramref name="field"/> as shorts and returns an array of
/// size <c>reader.MaxDoc</c> of the value each document has in the
/// given field.
/// <para/>
/// NOTE: this was getShorts() in Lucene
/// </summary>
/// <param name="reader"> Used to get field values. </param>
/// <param name="field"> Which field contains the <see cref="short"/>s. </param>
/// <param name="parser"> Computes <see cref="short"/> for string values. </param>
/// <param name="setDocsWithField"> If true then <see cref="GetDocsWithField(AtomicReader, string)"/> will
/// also be computed and stored in the <see cref="IFieldCache"/>. </param>
/// <returns> The values in the given field for each document. </returns>
/// <exception cref="IOException"> If any error occurs. </exception>
[Obsolete("(4.4) Index as a numeric field using Int32Field and then use GetInt32s(AtomicReader, string, bool) instead.")]
#pragma warning disable 612, 618
public virtual FieldCache.Int16s GetInt16s(AtomicReader reader, string field, FieldCache.IInt16Parser parser, bool setDocsWithField)
#pragma warning restore 612, 618
{
NumericDocValues valuesIn = reader.GetNumericDocValues(field);
if (valuesIn != null)
{
// Not cached here by FieldCacheImpl (cached instead
// per-thread by SegmentReader):
return new FieldCache_Int16sAnonymousInnerClassHelper(this, valuesIn);
}
else
{
FieldInfo info = reader.FieldInfos.FieldInfo(field);
if (info == null)
{
return FieldCache.Int16s.EMPTY;
}
else if (info.HasDocValues)
{
throw new InvalidOperationException("Type mismatch: " + field + " was indexed as " + info.DocValuesType);
}
else if (!info.IsIndexed)
{
return FieldCache.Int16s.EMPTY;
}
return (FieldCache.Int16s)caches[typeof(short)].Get(reader, new CacheKey(field, parser), setDocsWithField);
}
}
private class FieldCache_Int16sAnonymousInnerClassHelper : FieldCache.Int16s
{
private readonly FieldCacheImpl outerInstance;
private NumericDocValues valuesIn;
public FieldCache_Int16sAnonymousInnerClassHelper(FieldCacheImpl outerInstance, NumericDocValues valuesIn)
{
this.outerInstance = outerInstance;
this.valuesIn = valuesIn;
}
public override short Get(int docID)
{
return (short)valuesIn.Get(docID);
}
}
/// <summary>
/// NOTE: This was ShortsFromArray in Lucene
/// </summary>
internal class Int16sFromArray : FieldCache.Int16s
{
private readonly short[] values;
public Int16sFromArray(short[] values)
{
this.values = values;
}
public override short Get(int docID)
{
return values[docID];
}
}
/// <summary>
/// NOTE: This was ShortCache in Lucene
/// </summary>
internal sealed class Int16Cache : Cache
{
internal Int16Cache(FieldCacheImpl wrapper)
: base(wrapper)
{
}
protected override object CreateValue(AtomicReader reader, CacheKey key, bool setDocsWithField)
{
int maxDoc = reader.MaxDoc;
short[] values;
#pragma warning disable 612, 618
FieldCache.IInt16Parser parser = (FieldCache.IInt16Parser)key.custom;
if (parser == null)
{
// Confusing: must delegate to wrapper (vs simply
// setting parser = DEFAULT_INT16_PARSER) so cache
// key includes DEFAULT_INT16_PARSER:
return wrapper.GetInt16s(reader, key.field, FieldCache.DEFAULT_INT16_PARSER, setDocsWithField);
}
#pragma warning restore 612, 618
values = new short[maxDoc];
Uninvert u = new UninvertAnonymousInnerClassHelper(this, values, parser);
u.DoUninvert(reader, key.field, setDocsWithField);
if (setDocsWithField)
{
wrapper.SetDocsWithField(reader, key.field, u.docsWithField);
}
return new Int16sFromArray(values);
}
private class UninvertAnonymousInnerClassHelper : Uninvert
{
private readonly Int16Cache outerInstance;
private short[] values;
#pragma warning disable 612, 618
private FieldCache.IInt16Parser parser;
public UninvertAnonymousInnerClassHelper(Int16Cache outerInstance, short[] values, FieldCache.IInt16Parser parser)
#pragma warning restore 612, 618
{
this.outerInstance = outerInstance;
this.values = values;
this.parser = parser;
}
private short currentValue;
protected override void VisitTerm(BytesRef term)
{
currentValue = parser.ParseInt16(term);
}
protected override void VisitDoc(int docID)
{
values[docID] = currentValue;
}
protected override TermsEnum GetTermsEnum(Terms terms)
{
return parser.TermsEnum(terms);
}
}
}
/// <summary>
/// Returns an <see cref="FieldCache.Int32s"/> over the values found in documents in the given
/// field.
/// <para/>
/// NOTE: this was getInts() in Lucene
/// </summary>
/// <seealso cref="GetInt32s(AtomicReader, string, FieldCache.IInt32Parser, bool)"/>
public virtual FieldCache.Int32s GetInt32s(AtomicReader reader, string field, bool setDocsWithField)
{
return GetInt32s(reader, field, null, setDocsWithField);
}
/// <summary>
/// Returns an <see cref="FieldCache.Int32s"/> over the values found in documents in the given
/// field. If the field was indexed as <see cref="Documents.NumericDocValuesField"/>, it simply
/// uses <see cref="AtomicReader.GetNumericDocValues(string)"/> to read the values.
/// Otherwise, it checks the internal cache for an appropriate entry, and if
/// none is found, reads the terms in <paramref name="field"/> as <see cref="int"/>s and returns
/// an array of size <c>reader.MaxDoc</c> of the value each document
/// has in the given field.
/// <para/>
/// NOTE: this was getInts() in Lucene
/// </summary>
/// <param name="reader">
/// Used to get field values. </param>
/// <param name="field">
/// Which field contains the <see cref="int"/>s. </param>
/// <param name="parser">
/// Computes <see cref="int"/> for string values. May be <c>null</c> if the
/// requested field was indexed as <see cref="Documents.NumericDocValuesField"/> or
/// <see cref="Documents.Int32Field"/>. </param>
/// <param name="setDocsWithField">
/// If true then <see cref="GetDocsWithField(AtomicReader, string)"/> will also be computed and
/// stored in the <see cref="IFieldCache"/>. </param>
/// <returns> The values in the given field for each document. </returns>
/// <exception cref="IOException">
/// If any error occurs. </exception>
public virtual FieldCache.Int32s GetInt32s(AtomicReader reader, string field, FieldCache.IInt32Parser parser, bool setDocsWithField)
{
NumericDocValues valuesIn = reader.GetNumericDocValues(field);
if (valuesIn != null)
{
// Not cached here by FieldCacheImpl (cached instead
// per-thread by SegmentReader):
return new FieldCache_Int32sAnonymousInnerClassHelper(this, valuesIn);
}
else
{
FieldInfo info = reader.FieldInfos.FieldInfo(field);
if (info == null)
{
return FieldCache.Int32s.EMPTY;
}
else if (info.HasDocValues)
{
throw new InvalidOperationException("Type mismatch: " + field + " was indexed as " + info.DocValuesType);
}
else if (!info.IsIndexed)
{
return FieldCache.Int32s.EMPTY;
}
return (FieldCache.Int32s)caches[typeof(int)].Get(reader, new CacheKey(field, parser), setDocsWithField);
}
}
private class FieldCache_Int32sAnonymousInnerClassHelper : FieldCache.Int32s
{
private readonly FieldCacheImpl outerInstance;
private NumericDocValues valuesIn;
public FieldCache_Int32sAnonymousInnerClassHelper(FieldCacheImpl outerInstance, NumericDocValues valuesIn)
{
this.outerInstance = outerInstance;
this.valuesIn = valuesIn;
}
public override int Get(int docID)
{
return (int)valuesIn.Get(docID);
}
}
/// <summary>
/// NOTE: This was IntsFromArray in Lucene
/// </summary>
internal class Int32sFromArray : FieldCache.Int32s
{
private readonly PackedInt32s.Reader values;
private readonly int minValue;
public Int32sFromArray(PackedInt32s.Reader values, int minValue)
{
Debug.Assert(values.BitsPerValue <= 32);
this.values = values;
this.minValue = minValue;
}
public override int Get(int docID)
{
long delta = values.Get(docID);
return minValue + (int)delta;
}
}
private class HoldsOneThing<T>
{
private T it;
public virtual void Set(T it)
{
this.it = it;
}
public virtual T Get()
{
return it;
}
}
private class GrowableWriterAndMinValue
{
internal GrowableWriterAndMinValue(GrowableWriter array, long minValue)
{
this.Writer = array;
this.MinValue = minValue;
}
public GrowableWriter Writer { get; set; } // LUCENENET NOTE: for some reason, this was not marked readonly
public long MinValue { get; set; } // LUCENENET NOTE: for some reason, this was not marked readonly
}
/// <summary>
/// NOTE: This was IntCache in Lucene
/// </summary>
internal sealed class Int32Cache : Cache
{
internal Int32Cache(FieldCacheImpl wrapper)
: base(wrapper)
{
}
protected override object CreateValue(AtomicReader reader, CacheKey key, bool setDocsWithField)
{
FieldCache.IInt32Parser parser = (FieldCache.IInt32Parser)key.custom;
if (parser == null)
{
// Confusing: must delegate to wrapper (vs simply
// setting parser =
// DEFAULT_INT32_PARSER/NUMERIC_UTILS_INT32_PARSER) so
// cache key includes
// DEFAULT_INT32_PARSER/NUMERIC_UTILS_INT32_PARSER:
try
{
#pragma warning disable 612, 618
return wrapper.GetInt32s(reader, key.field, FieldCache.DEFAULT_INT32_PARSER, setDocsWithField);
#pragma warning restore 612, 618
}
catch (System.FormatException)
{
return wrapper.GetInt32s(reader, key.field, FieldCache.NUMERIC_UTILS_INT32_PARSER, setDocsWithField);
}
}
HoldsOneThing<GrowableWriterAndMinValue> valuesRef = new HoldsOneThing<GrowableWriterAndMinValue>();
Uninvert u = new UninvertAnonymousInnerClassHelper(this, reader, parser, valuesRef);
u.DoUninvert(reader, key.field, setDocsWithField);
if (setDocsWithField)
{
wrapper.SetDocsWithField(reader, key.field, u.docsWithField);
}
GrowableWriterAndMinValue values = valuesRef.Get();
if (values == null)
{
return new Int32sFromArray(new PackedInt32s.NullReader(reader.MaxDoc), 0);
}
return new Int32sFromArray(values.Writer.Mutable, (int)values.MinValue);
}
private class UninvertAnonymousInnerClassHelper : Uninvert
{
private readonly Int32Cache outerInstance;
private AtomicReader reader;
private FieldCache.IInt32Parser parser;
private FieldCacheImpl.HoldsOneThing<GrowableWriterAndMinValue> valuesRef;
public UninvertAnonymousInnerClassHelper(Int32Cache outerInstance, AtomicReader reader, FieldCache.IInt32Parser parser, FieldCacheImpl.HoldsOneThing<GrowableWriterAndMinValue> valuesRef)
{
this.outerInstance = outerInstance;
this.reader = reader;
this.parser = parser;
this.valuesRef = valuesRef;
}
private int minValue;
private int currentValue;
private GrowableWriter values;
protected override void VisitTerm(BytesRef term)
{
currentValue = parser.ParseInt32(term);
if (values == null)
{
// Lazy alloc so for the numeric field case
// (which will hit a System.FormatException
// when we first try the DEFAULT_INT32_PARSER),
// we don't double-alloc:
int startBitsPerValue;
// Make sure than missing values (0) can be stored without resizing
if (currentValue < 0)
{
minValue = currentValue;
startBitsPerValue = PackedInt32s.BitsRequired((-minValue) & 0xFFFFFFFFL);
}
else
{
minValue = 0;
startBitsPerValue = PackedInt32s.BitsRequired(currentValue);
}
values = new GrowableWriter(startBitsPerValue, reader.MaxDoc, PackedInt32s.FAST);
if (minValue != 0)
{
values.Fill(0, values.Count, (-minValue) & 0xFFFFFFFFL); // default value must be 0
}
valuesRef.Set(new GrowableWriterAndMinValue(values, minValue));
}
}
protected override void VisitDoc(int docID)
{
values.Set(docID, (currentValue - minValue) & 0xFFFFFFFFL);
}
protected override TermsEnum GetTermsEnum(Terms terms)
{
return parser.TermsEnum(terms);
}
}
}
public virtual IBits GetDocsWithField(AtomicReader reader, string field)
{
FieldInfo fieldInfo = reader.FieldInfos.FieldInfo(field);
if (fieldInfo == null)
{
// field does not exist or has no value
return new Lucene.Net.Util.Bits.MatchNoBits(reader.MaxDoc);
}
else if (fieldInfo.HasDocValues)
{
return reader.GetDocsWithField(field);
}
else if (!fieldInfo.IsIndexed)
{
return new Lucene.Net.Util.Bits.MatchNoBits(reader.MaxDoc);
}
return (IBits)caches[typeof(DocsWithFieldCache)].Get(reader, new CacheKey(field, null), false);
}
internal sealed class DocsWithFieldCache : Cache
{
internal DocsWithFieldCache(FieldCacheImpl wrapper)
: base(wrapper)
{
}
protected override object CreateValue(AtomicReader reader, CacheKey key, bool setDocsWithField) // ignored
{
string field = key.field;
int maxDoc = reader.MaxDoc;
// Visit all docs that have terms for this field
FixedBitSet res = null;
Terms terms = reader.GetTerms(field);
if (terms != null)
{
int termsDocCount = terms.DocCount;
Debug.Assert(termsDocCount <= maxDoc);
if (termsDocCount == maxDoc)
{
// Fast case: all docs have this field:
return new Lucene.Net.Util.Bits.MatchAllBits(maxDoc);
}
TermsEnum termsEnum = terms.GetIterator(null);
DocsEnum docs = null;
while (true)
{
BytesRef term = termsEnum.Next();
if (term == null)
{
break;
}
if (res == null)
{
// lazy init
res = new FixedBitSet(maxDoc);
}
docs = termsEnum.Docs(null, docs, DocsFlags.NONE);
// TODO: use bulk API
while (true)
{
int docID = docs.NextDoc();
if (docID == DocIdSetIterator.NO_MORE_DOCS)
{
break;
}
res.Set(docID);
}
}
}
if (res == null)
{
return new Lucene.Net.Util.Bits.MatchNoBits(maxDoc);
}
int numSet = res.Cardinality();
if (numSet >= maxDoc)
{
// The cardinality of the BitSet is maxDoc if all documents have a value.
Debug.Assert(numSet == maxDoc);
return new Lucene.Net.Util.Bits.MatchAllBits(maxDoc);
}
return res;
}
}
/// <summary>
/// NOTE: this was getFloats() in Lucene
/// </summary>
public virtual FieldCache.Singles GetSingles(AtomicReader reader, string field, bool setDocsWithField)
{
return GetSingles(reader, field, null, setDocsWithField);
}
/// <summary>
/// NOTE: this was getFloats() in Lucene
/// </summary>
public virtual FieldCache.Singles GetSingles(AtomicReader reader, string field, FieldCache.ISingleParser parser, bool setDocsWithField)
{
NumericDocValues valuesIn = reader.GetNumericDocValues(field);
if (valuesIn != null)
{
// Not cached here by FieldCacheImpl (cached instead
// per-thread by SegmentReader):
return new FieldCache_SinglesAnonymousInnerClassHelper(this, valuesIn);
}
else
{
FieldInfo info = reader.FieldInfos.FieldInfo(field);
if (info == null)
{
return FieldCache.Singles.EMPTY;
}
else if (info.HasDocValues)
{
throw new InvalidOperationException("Type mismatch: " + field + " was indexed as " + info.DocValuesType);
}
else if (!info.IsIndexed)
{
return FieldCache.Singles.EMPTY;
}
return (FieldCache.Singles)caches[typeof(float)].Get(reader, new CacheKey(field, parser), setDocsWithField);
}
}
private class FieldCache_SinglesAnonymousInnerClassHelper : FieldCache.Singles
{
private readonly FieldCacheImpl outerInstance;
private NumericDocValues valuesIn;
public FieldCache_SinglesAnonymousInnerClassHelper(FieldCacheImpl outerInstance, NumericDocValues valuesIn)
{
this.outerInstance = outerInstance;
this.valuesIn = valuesIn;
}
public override float Get(int docID)
{
return J2N.BitConversion.Int32BitsToSingle((int)valuesIn.Get(docID));
}
}
/// <summary>
/// NOTE: This was FloatsFromArray in Lucene
/// </summary>
internal class SinglesFromArray : FieldCache.Singles
{
private readonly float[] values;
public SinglesFromArray(float[] values)
{
this.values = values;
}
public override float Get(int docID)
{
return values[docID];
}
}
/// <summary>
/// NOTE: This was FloatCache in Lucene
/// </summary>
internal sealed class SingleCache : Cache
{
internal SingleCache(FieldCacheImpl wrapper)
: base(wrapper)
{
}
protected override object CreateValue(AtomicReader reader, CacheKey key, bool setDocsWithField)
{
FieldCache.ISingleParser parser = (FieldCache.ISingleParser)key.custom;
if (parser == null)
{
// Confusing: must delegate to wrapper (vs simply
// setting parser =
// DEFAULT_SINGLE_PARSER/NUMERIC_UTILS_SINGLE_PARSER) so
// cache key includes
// DEFAULT_SINGLE_PARSER/NUMERIC_UTILS_SINGLE_PARSER:
try
{
#pragma warning disable 612, 618
return wrapper.GetSingles(reader, key.field, FieldCache.DEFAULT_SINGLE_PARSER, setDocsWithField);
#pragma warning restore 612, 618
}
catch (System.FormatException)
{
return wrapper.GetSingles(reader, key.field, FieldCache.NUMERIC_UTILS_SINGLE_PARSER, setDocsWithField);
}
}
HoldsOneThing<float[]> valuesRef = new HoldsOneThing<float[]>();
Uninvert u = new UninvertAnonymousInnerClassHelper(this, reader, parser, valuesRef);
u.DoUninvert(reader, key.field, setDocsWithField);
if (setDocsWithField)
{
wrapper.SetDocsWithField(reader, key.field, u.docsWithField);
}
float[] values = valuesRef.Get();
if (values == null)
{
values = new float[reader.MaxDoc];
}
return new SinglesFromArray(values);
}
private class UninvertAnonymousInnerClassHelper : Uninvert
{
private readonly SingleCache outerInstance;
private AtomicReader reader;
private FieldCache.ISingleParser parser;
private FieldCacheImpl.HoldsOneThing<float[]> valuesRef;
public UninvertAnonymousInnerClassHelper(SingleCache outerInstance, AtomicReader reader, FieldCache.ISingleParser parser, FieldCacheImpl.HoldsOneThing<float[]> valuesRef)
{
this.outerInstance = outerInstance;
this.reader = reader;
this.parser = parser;
this.valuesRef = valuesRef;
}
private float currentValue;
private float[] values;
protected override void VisitTerm(BytesRef term)
{
currentValue = parser.ParseSingle(term);
if (values == null)
{
// Lazy alloc so for the numeric field case
// (which will hit a System.FormatException
// when we first try the DEFAULT_INT32_PARSER),
// we don't double-alloc:
values = new float[reader.MaxDoc];
valuesRef.Set(values);
}
}
protected override void VisitDoc(int docID)
{
values[docID] = currentValue;
}
protected override TermsEnum GetTermsEnum(Terms terms)
{
return parser.TermsEnum(terms);
}
}
}
/// <summary>
/// NOTE: this was getLongs() in Lucene
/// </summary>
public virtual FieldCache.Int64s GetInt64s(AtomicReader reader, string field, bool setDocsWithField)
{
return GetInt64s(reader, field, null, setDocsWithField);
}
/// <summary>
/// NOTE: this was getLongs() in Lucene
/// </summary>
public virtual FieldCache.Int64s GetInt64s(AtomicReader reader, string field, FieldCache.IInt64Parser parser, bool setDocsWithField)
{
NumericDocValues valuesIn = reader.GetNumericDocValues(field);
if (valuesIn != null)
{
// Not cached here by FieldCacheImpl (cached instead
// per-thread by SegmentReader):
return new FieldCache_Int64sAnonymousInnerClassHelper(this, valuesIn);
}
else
{
FieldInfo info = reader.FieldInfos.FieldInfo(field);
if (info == null)
{
return FieldCache.Int64s.EMPTY;
}
else if (info.HasDocValues)
{
throw new InvalidOperationException("Type mismatch: " + field + " was indexed as " + info.DocValuesType);
}
else if (!info.IsIndexed)
{
return FieldCache.Int64s.EMPTY;
}
return (FieldCache.Int64s)caches[typeof(long)].Get(reader, new CacheKey(field, parser), setDocsWithField);
}
}
private class FieldCache_Int64sAnonymousInnerClassHelper : FieldCache.Int64s
{
private readonly FieldCacheImpl outerInstance;
private NumericDocValues valuesIn;
public FieldCache_Int64sAnonymousInnerClassHelper(FieldCacheImpl outerInstance, NumericDocValues valuesIn)
{
this.outerInstance = outerInstance;
this.valuesIn = valuesIn;
}
public override long Get(int docID)
{
return valuesIn.Get(docID);
}
}
/// <summary>
/// NOTE: This was LongsFromArray in Lucene
/// </summary>
internal class Int64sFromArray : FieldCache.Int64s
{
private readonly PackedInt32s.Reader values;
private readonly long minValue;
public Int64sFromArray(PackedInt32s.Reader values, long minValue)
{
this.values = values;
this.minValue = minValue;
}
public override long Get(int docID)
{
return minValue + values.Get(docID);
}
}
/// <summary>
/// NOTE: This was LongCache in Lucene
/// </summary>
internal sealed class Int64Cache : Cache
{
internal Int64Cache(FieldCacheImpl wrapper)
: base(wrapper)
{
}
protected override object CreateValue(AtomicReader reader, CacheKey key, bool setDocsWithField)
{
FieldCache.IInt64Parser parser = (FieldCache.IInt64Parser)key.custom;
if (parser == null)
{
// Confusing: must delegate to wrapper (vs simply
// setting parser =
// DEFAULT_INT64_PARSER/NUMERIC_UTILS_INT64_PARSER) so
// cache key includes
// DEFAULT_INT64_PARSER/NUMERIC_UTILS_INT64_PARSER:
try
{
#pragma warning disable 612, 618
return wrapper.GetInt64s(reader, key.field, FieldCache.DEFAULT_INT64_PARSER, setDocsWithField);
#pragma warning restore 612, 618
}
catch (System.FormatException)
{
return wrapper.GetInt64s(reader, key.field, FieldCache.NUMERIC_UTILS_INT64_PARSER, setDocsWithField);
}
}
HoldsOneThing<GrowableWriterAndMinValue> valuesRef = new HoldsOneThing<GrowableWriterAndMinValue>();
Uninvert u = new UninvertAnonymousInnerClassHelper(this, reader, parser, valuesRef);
u.DoUninvert(reader, key.field, setDocsWithField);
if (setDocsWithField)
{
wrapper.SetDocsWithField(reader, key.field, u.docsWithField);
}
GrowableWriterAndMinValue values = valuesRef.Get();
if (values == null)
{
return new Int64sFromArray(new PackedInt32s.NullReader(reader.MaxDoc), 0L);
}
return new Int64sFromArray(values.Writer.Mutable, values.MinValue);
}
private class UninvertAnonymousInnerClassHelper : Uninvert
{
private readonly Int64Cache outerInstance;
private AtomicReader reader;
private FieldCache.IInt64Parser parser;
private FieldCacheImpl.HoldsOneThing<GrowableWriterAndMinValue> valuesRef;
public UninvertAnonymousInnerClassHelper(Int64Cache outerInstance, AtomicReader reader, FieldCache.IInt64Parser parser, FieldCacheImpl.HoldsOneThing<GrowableWriterAndMinValue> valuesRef)
{
this.outerInstance = outerInstance;
this.reader = reader;
this.parser = parser;
this.valuesRef = valuesRef;
}
private long minValue;
private long currentValue;
private GrowableWriter values;
protected override void VisitTerm(BytesRef term)
{
currentValue = parser.ParseInt64(term);
if (values == null)
{
// Lazy alloc so for the numeric field case
// (which will hit a System.FormatException
// when we first try the DEFAULT_INT32_PARSER),
// we don't double-alloc:
int startBitsPerValue;
// Make sure than missing values (0) can be stored without resizing
if (currentValue < 0)
{
minValue = currentValue;
startBitsPerValue = minValue == long.MinValue ? 64 : PackedInt32s.BitsRequired(-minValue);
}
else
{
minValue = 0;
startBitsPerValue = PackedInt32s.BitsRequired(currentValue);
}
values = new GrowableWriter(startBitsPerValue, reader.MaxDoc, PackedInt32s.FAST);
if (minValue != 0)
{
values.Fill(0, values.Count, -minValue); // default value must be 0
}
valuesRef.Set(new GrowableWriterAndMinValue(values, minValue));
}
}
protected override void VisitDoc(int docID)
{
values.Set(docID, currentValue - minValue);
}
protected override TermsEnum GetTermsEnum(Terms terms)
{
return parser.TermsEnum(terms);
}
}
}
public virtual FieldCache.Doubles GetDoubles(AtomicReader reader, string field, bool setDocsWithField)
{
return GetDoubles(reader, field, null, setDocsWithField);
}
public virtual FieldCache.Doubles GetDoubles(AtomicReader reader, string field, FieldCache.IDoubleParser parser, bool setDocsWithField)
{
NumericDocValues valuesIn = reader.GetNumericDocValues(field);
if (valuesIn != null)
{
// Not cached here by FieldCacheImpl (cached instead
// per-thread by SegmentReader):
return new FieldCache_DoublesAnonymousInnerClassHelper(this, valuesIn);
}
else
{
FieldInfo info = reader.FieldInfos.FieldInfo(field);
if (info == null)
{
return FieldCache.Doubles.EMPTY;
}
else if (info.HasDocValues)
{
throw new InvalidOperationException("Type mismatch: " + field + " was indexed as " + info.DocValuesType);
}
else if (!info.IsIndexed)
{
return FieldCache.Doubles.EMPTY;
}
return (FieldCache.Doubles)caches[typeof(double)].Get(reader, new CacheKey(field, parser), setDocsWithField);
}
}
private class FieldCache_DoublesAnonymousInnerClassHelper : FieldCache.Doubles
{
private readonly FieldCacheImpl outerInstance;
private NumericDocValues valuesIn;
public FieldCache_DoublesAnonymousInnerClassHelper(FieldCacheImpl outerInstance, NumericDocValues valuesIn)
{
this.outerInstance = outerInstance;
this.valuesIn = valuesIn;
}
public override double Get(int docID)
{
return J2N.BitConversion.Int64BitsToDouble(valuesIn.Get(docID));
}
}
internal class DoublesFromArray : FieldCache.Doubles
{
private readonly double[] values;
public DoublesFromArray(double[] values)
{
this.values = values;
}
public override double Get(int docID)
{
return values[docID];
}
}
internal sealed class DoubleCache : Cache
{
internal DoubleCache(FieldCacheImpl wrapper)
: base(wrapper)
{
}
protected override object CreateValue(AtomicReader reader, CacheKey key, bool setDocsWithField)
{
FieldCache.IDoubleParser parser = (FieldCache.IDoubleParser)key.custom;
if (parser == null)
{
// Confusing: must delegate to wrapper (vs simply
// setting parser =
// DEFAULT_DOUBLE_PARSER/NUMERIC_UTILS_DOUBLE_PARSER) so
// cache key includes
// DEFAULT_DOUBLE_PARSER/NUMERIC_UTILS_DOUBLE_PARSER:
try
{
#pragma warning disable 612, 618
return wrapper.GetDoubles(reader, key.field, FieldCache.DEFAULT_DOUBLE_PARSER, setDocsWithField);
#pragma warning restore 612, 618
}
catch (System.FormatException)
{
return wrapper.GetDoubles(reader, key.field, FieldCache.NUMERIC_UTILS_DOUBLE_PARSER, setDocsWithField);
}
}
HoldsOneThing<double[]> valuesRef = new HoldsOneThing<double[]>();
Uninvert u = new UninvertAnonymousInnerClassHelper(this, reader, parser, valuesRef);
u.DoUninvert(reader, key.field, setDocsWithField);
if (setDocsWithField)
{
wrapper.SetDocsWithField(reader, key.field, u.docsWithField);
}
double[] values = valuesRef.Get();
if (values == null)
{
values = new double[reader.MaxDoc];
}
return new DoublesFromArray(values);
}
private class UninvertAnonymousInnerClassHelper : Uninvert
{
private readonly DoubleCache outerInstance;
private AtomicReader reader;
private FieldCache.IDoubleParser parser;
private FieldCacheImpl.HoldsOneThing<double[]> valuesRef;
public UninvertAnonymousInnerClassHelper(DoubleCache outerInstance, AtomicReader reader, FieldCache.IDoubleParser parser, FieldCacheImpl.HoldsOneThing<double[]> valuesRef)
{
this.outerInstance = outerInstance;
this.reader = reader;
this.parser = parser;
this.valuesRef = valuesRef;
}
private double currentValue;
private double[] values;
protected override void VisitTerm(BytesRef term)
{
currentValue = parser.ParseDouble(term);
if (values == null)
{
// Lazy alloc so for the numeric field case
// (which will hit a System.FormatException
// when we first try the DEFAULT_INT32_PARSER),
// we don't double-alloc:
values = new double[reader.MaxDoc];
valuesRef.Set(values);
}
}
protected override void VisitDoc(int docID)
{
values[docID] = currentValue;
}
protected override TermsEnum GetTermsEnum(Terms terms)
{
return parser.TermsEnum(terms);
}
}
}
public class SortedDocValuesImpl : SortedDocValues
{
private readonly PagedBytes.Reader bytes;
private readonly MonotonicAppendingInt64Buffer termOrdToBytesOffset;
private readonly PackedInt32s.Reader docToTermOrd;
private readonly int numOrd;
public SortedDocValuesImpl(PagedBytes.Reader bytes, MonotonicAppendingInt64Buffer termOrdToBytesOffset, PackedInt32s.Reader docToTermOrd, int numOrd)
{
this.bytes = bytes;
this.docToTermOrd = docToTermOrd;
this.termOrdToBytesOffset = termOrdToBytesOffset;
this.numOrd = numOrd;
}
public override int ValueCount
{
get
{
return numOrd;
}
}
public override int GetOrd(int docID)
{
// Subtract 1, matching the 1+ord we did when
// storing, so that missing values, which are 0 in the
// packed ints, are returned as -1 ord:
return (int)docToTermOrd.Get(docID) - 1;
}
public override void LookupOrd(int ord, BytesRef ret)
{
if (ord < 0)
{
throw new System.ArgumentException("ord must be >=0 (got ord=" + ord + ")");
}
bytes.Fill(ret, termOrdToBytesOffset.Get(ord));
}
}
public virtual SortedDocValues GetTermsIndex(AtomicReader reader, string field)
{
return GetTermsIndex(reader, field, PackedInt32s.FAST);
}
public virtual SortedDocValues GetTermsIndex(AtomicReader reader, string field, float acceptableOverheadRatio)
{
SortedDocValues valuesIn = reader.GetSortedDocValues(field);
if (valuesIn != null)
{
// Not cached here by FieldCacheImpl (cached instead
// per-thread by SegmentReader):
return valuesIn;
}
else
{
FieldInfo info = reader.FieldInfos.FieldInfo(field);
if (info == null)
{
return DocValues.EMPTY_SORTED;
}
else if (info.HasDocValues)
{
// we don't try to build a sorted instance from numeric/binary doc
// values because dedup can be very costly
throw new InvalidOperationException("Type mismatch: " + field + " was indexed as " + info.DocValuesType);
}
else if (!info.IsIndexed)
{
return DocValues.EMPTY_SORTED;
}
return (SortedDocValues)caches[typeof(SortedDocValues)].Get(reader, new CacheKey(field, acceptableOverheadRatio), false);
}
}
internal class SortedDocValuesCache : Cache
{
internal SortedDocValuesCache(FieldCacheImpl wrapper)
: base(wrapper)
{
}
protected override object CreateValue(AtomicReader reader, CacheKey key, bool setDocsWithField) // ignored
{
int maxDoc = reader.MaxDoc;
Terms terms = reader.GetTerms(key.field);
float acceptableOverheadRatio = (float)((float?)key.custom);
PagedBytes bytes = new PagedBytes(15);
int startTermsBPV;
int termCountHardLimit;
if (maxDoc == int.MaxValue)
{
termCountHardLimit = int.MaxValue;
}
else
{
termCountHardLimit = maxDoc + 1;
}
// TODO: use Uninvert?
if (terms != null)
{
// Try for coarse estimate for number of bits; this
// should be an underestimate most of the time, which
// is fine -- GrowableWriter will reallocate as needed
long numUniqueTerms = terms.Count;
if (numUniqueTerms != -1L)
{
if (numUniqueTerms > termCountHardLimit)
{
// app is misusing the API (there is more than
// one term per doc); in this case we make best
// effort to load what we can (see LUCENE-2142)
numUniqueTerms = termCountHardLimit;
}
startTermsBPV = PackedInt32s.BitsRequired(numUniqueTerms);
}
else
{
startTermsBPV = 1;
}
}
else
{
startTermsBPV = 1;
}
MonotonicAppendingInt64Buffer termOrdToBytesOffset = new MonotonicAppendingInt64Buffer();
GrowableWriter docToTermOrd = new GrowableWriter(startTermsBPV, maxDoc, acceptableOverheadRatio);
int termOrd = 0;
// TODO: use Uninvert?
if (terms != null)
{
TermsEnum termsEnum = terms.GetIterator(null);
DocsEnum docs = null;
while (true)
{
BytesRef term = termsEnum.Next();
if (term == null)
{
break;
}
if (termOrd >= termCountHardLimit)
{
break;
}
termOrdToBytesOffset.Add(bytes.CopyUsingLengthPrefix(term));
docs = termsEnum.Docs(null, docs, DocsFlags.NONE);
while (true)
{
int docID = docs.NextDoc();
if (docID == DocIdSetIterator.NO_MORE_DOCS)
{
break;
}
// Store 1+ ord into packed bits
docToTermOrd.Set(docID, 1 + termOrd);
}
termOrd++;
}
}
termOrdToBytesOffset.Freeze();
// maybe an int-only impl?
return new SortedDocValuesImpl(bytes.Freeze(true), termOrdToBytesOffset, docToTermOrd.Mutable, termOrd);
}
}
private class BinaryDocValuesImpl : BinaryDocValues
{
private readonly PagedBytes.Reader bytes;
private readonly PackedInt32s.Reader docToOffset;
public BinaryDocValuesImpl(PagedBytes.Reader bytes, PackedInt32s.Reader docToOffset)
{
this.bytes = bytes;
this.docToOffset = docToOffset;
}
public override void Get(int docID, BytesRef ret)
{
int pointer = (int)docToOffset.Get(docID);
if (pointer == 0)
{
ret.Bytes = BytesRef.EMPTY_BYTES;
ret.Offset = 0;
ret.Length = 0;
}
else
{
bytes.Fill(ret, pointer);
}
}
}
// TODO: this if DocTermsIndex was already created, we
// should share it...
public virtual BinaryDocValues GetTerms(AtomicReader reader, string field, bool setDocsWithField)
{
return GetTerms(reader, field, setDocsWithField, PackedInt32s.FAST);
}
public virtual BinaryDocValues GetTerms(AtomicReader reader, string field, bool setDocsWithField, float acceptableOverheadRatio)
{
BinaryDocValues valuesIn = reader.GetBinaryDocValues(field);
if (valuesIn == null)
{
valuesIn = reader.GetSortedDocValues(field);
}
if (valuesIn != null)
{
// Not cached here by FieldCacheImpl (cached instead
// per-thread by SegmentReader):
return valuesIn;
}
FieldInfo info = reader.FieldInfos.FieldInfo(field);
if (info == null)
{
return DocValues.EMPTY_BINARY;
}
else if (info.HasDocValues)
{
throw new InvalidOperationException("Type mismatch: " + field + " was indexed as " + info.DocValuesType);
}
else if (!info.IsIndexed)
{
return DocValues.EMPTY_BINARY;
}
return (BinaryDocValues)caches[typeof(BinaryDocValues)].Get(reader, new CacheKey(field, acceptableOverheadRatio), setDocsWithField);
}
internal sealed class BinaryDocValuesCache : Cache
{
internal BinaryDocValuesCache(FieldCacheImpl wrapper)
: base(wrapper)
{
}
protected override object CreateValue(AtomicReader reader, CacheKey key, bool setDocsWithField)
{
// TODO: would be nice to first check if DocTermsIndex
// was already cached for this field and then return
// that instead, to avoid insanity
int maxDoc = reader.MaxDoc;
Terms terms = reader.GetTerms(key.field);
float acceptableOverheadRatio = (float)((float?)key.custom);
int termCountHardLimit = maxDoc;
// Holds the actual term data, expanded.
PagedBytes bytes = new PagedBytes(15);
int startBPV;
if (terms != null)
{
// Try for coarse estimate for number of bits; this
// should be an underestimate most of the time, which
// is fine -- GrowableWriter will reallocate as needed
long numUniqueTerms = terms.Count;
if (numUniqueTerms != -1L)
{
if (numUniqueTerms > termCountHardLimit)
{
numUniqueTerms = termCountHardLimit;
}
startBPV = PackedInt32s.BitsRequired(numUniqueTerms * 4);
}
else
{
startBPV = 1;
}
}
else
{
startBPV = 1;
}
GrowableWriter docToOffset = new GrowableWriter(startBPV, maxDoc, acceptableOverheadRatio);
// pointer==0 means not set
bytes.CopyUsingLengthPrefix(new BytesRef());
if (terms != null)
{
int termCount = 0;
TermsEnum termsEnum = terms.GetIterator(null);
DocsEnum docs = null;
while (true)
{
if (termCount++ == termCountHardLimit)
{
// app is misusing the API (there is more than
// one term per doc); in this case we make best
// effort to load what we can (see LUCENE-2142)
break;
}
BytesRef term = termsEnum.Next();
if (term == null)
{
break;
}
long pointer = bytes.CopyUsingLengthPrefix(term);
docs = termsEnum.Docs(null, docs, DocsFlags.NONE);
while (true)
{
int docID = docs.NextDoc();
if (docID == DocIdSetIterator.NO_MORE_DOCS)
{
break;
}
docToOffset.Set(docID, pointer);
}
}
}
PackedInt32s.Reader offsetReader = docToOffset.Mutable;
if (setDocsWithField)
{
wrapper.SetDocsWithField(reader, key.field, new BitsAnonymousInnerClassHelper(this, maxDoc, offsetReader));
}
// maybe an int-only impl?
return new BinaryDocValuesImpl(bytes.Freeze(true), offsetReader);
}
private class BitsAnonymousInnerClassHelper : IBits
{
private readonly BinaryDocValuesCache outerInstance;
private int maxDoc;
private PackedInt32s.Reader offsetReader;
public BitsAnonymousInnerClassHelper(BinaryDocValuesCache outerInstance, int maxDoc, PackedInt32s.Reader offsetReader)
{
this.outerInstance = outerInstance;
this.maxDoc = maxDoc;
this.offsetReader = offsetReader;
}
public virtual bool Get(int index)
{
return offsetReader.Get(index) != 0;
}
public virtual int Length
{
get { return maxDoc; }
}
}
}
// TODO: this if DocTermsIndex was already created, we
// should share it...
public virtual SortedSetDocValues GetDocTermOrds(AtomicReader reader, string field)
{
SortedSetDocValues dv = reader.GetSortedSetDocValues(field);
if (dv != null)
{
return dv;
}
SortedDocValues sdv = reader.GetSortedDocValues(field);
if (sdv != null)
{
return DocValues.Singleton(sdv);
}
FieldInfo info = reader.FieldInfos.FieldInfo(field);
if (info == null)
{
return DocValues.EMPTY_SORTED_SET;
}
else if (info.HasDocValues)
{
throw new InvalidOperationException("Type mismatch: " + field + " was indexed as " + info.DocValuesType);
}
else if (!info.IsIndexed)
{
return DocValues.EMPTY_SORTED_SET;
}
DocTermOrds dto = (DocTermOrds)caches[typeof(DocTermOrds)].Get(reader, new CacheKey(field, null), false);
return dto.GetIterator(reader);
}
internal sealed class DocTermOrdsCache : Cache
{
internal DocTermOrdsCache(FieldCacheImpl wrapper)
: base(wrapper)
{
}
protected override object CreateValue(AtomicReader reader, CacheKey key, bool setDocsWithField) // ignored
{
return new DocTermOrds(reader, null, key.field);
}
}
private volatile TextWriter infoStream;
public virtual TextWriter InfoStream
{
set
{
// LUCENENET specific - use a SafeTextWriterWrapper to ensure that if the TextWriter
// is disposed by the caller (using block) we don't get any exceptions if we keep using it.
infoStream = value == null
? null
: (value is SafeTextWriterWrapper ? value : new SafeTextWriterWrapper(value));
}
get
{
return infoStream;
}
}
}
}