| using J2N.Runtime.CompilerServices; |
| using Lucene.Net.Support; |
| using System; |
| using System.Collections.Generic; |
| using System.IO; |
| using System.Text; |
| using JCG = J2N.Collections.Generic; |
| |
| 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 IBits = Lucene.Net.Util.IBits; |
| |
| /// <summary> |
| /// An <see cref="AtomicReader"/> which reads multiple, parallel indexes. Each index |
| /// added must have the same number of documents, but typically each contains |
| /// different fields. Deletions are taken from the first reader. |
| /// Each document contains the union of the fields of all documents |
| /// with the same document number. When searching, matches for a |
| /// query term are from the first index added that has the field. |
| /// |
| /// <para/>This is useful, e.g., with collections that have large fields which |
| /// change rarely and small fields that change more frequently. The smaller |
| /// fields may be re-indexed in a new index and both indexes may be searched |
| /// together. |
| /// |
| /// <para/><strong>Warning:</strong> It is up to you to make sure all indexes |
| /// are created and modified the same way. For example, if you add |
| /// documents to one index, you need to add the same documents in the |
| /// same order to the other indexes. <em>Failure to do so will result in |
| /// undefined behavior</em>. |
| /// </summary> |
| public class ParallelAtomicReader : AtomicReader |
| { |
| private void InitializeInstanceFields() |
| { |
| fields = new ParallelFields(this); |
| } |
| |
| private readonly FieldInfos fieldInfos; |
| private ParallelFields fields; |
| private readonly AtomicReader[] parallelReaders, storedFieldsReaders; |
| private readonly ISet<AtomicReader> completeReaderSet = new JCG.HashSet<AtomicReader>(IdentityEqualityComparer<AtomicReader>.Default); |
| private readonly bool closeSubReaders; |
| private readonly int maxDoc, numDocs; |
| private readonly bool hasDeletions; |
| |
| // LUCENENET specific: Use StringComparer.Ordinal to get the same ordering as Java |
| private readonly IDictionary<string, AtomicReader> fieldToReader = new JCG.SortedDictionary<string, AtomicReader>(StringComparer.Ordinal); |
| private readonly IDictionary<string, AtomicReader> tvFieldToReader = new JCG.SortedDictionary<string, AtomicReader>(StringComparer.Ordinal); |
| |
| /// <summary> |
| /// Create a <see cref="ParallelAtomicReader"/> based on the provided |
| /// readers; auto-disposes the given <paramref name="readers"/> on <see cref="IndexReader.Dispose()"/>. |
| /// </summary> |
| public ParallelAtomicReader(params AtomicReader[] readers) |
| : this(true, readers) |
| { |
| } |
| |
| /// <summary> |
| /// Create a <see cref="ParallelAtomicReader"/> based on the provided |
| /// <paramref name="readers"/>. |
| /// </summary> |
| public ParallelAtomicReader(bool closeSubReaders, params AtomicReader[] readers) |
| : this(closeSubReaders, readers, readers) |
| { |
| } |
| |
| /// <summary> |
| /// Expert: create a <see cref="ParallelAtomicReader"/> based on the provided |
| /// <paramref name="readers"/> and <paramref name="storedFieldsReaders"/>; when a document is |
| /// loaded, only <paramref name="storedFieldsReaders"/> will be used. |
| /// </summary> |
| public ParallelAtomicReader(bool closeSubReaders, AtomicReader[] readers, AtomicReader[] storedFieldsReaders) |
| { |
| InitializeInstanceFields(); |
| this.closeSubReaders = closeSubReaders; |
| if (readers.Length == 0 && storedFieldsReaders.Length > 0) |
| { |
| throw new System.ArgumentException("There must be at least one main reader if storedFieldsReaders are used."); |
| } |
| this.parallelReaders = (AtomicReader[])readers.Clone(); |
| this.storedFieldsReaders = (AtomicReader[])storedFieldsReaders.Clone(); |
| if (parallelReaders.Length > 0) |
| { |
| AtomicReader first = parallelReaders[0]; |
| this.maxDoc = first.MaxDoc; |
| this.numDocs = first.NumDocs; |
| this.hasDeletions = first.HasDeletions; |
| } |
| else |
| { |
| this.maxDoc = this.numDocs = 0; |
| this.hasDeletions = false; |
| } |
| completeReaderSet.UnionWith(this.parallelReaders); |
| completeReaderSet.UnionWith(this.storedFieldsReaders); |
| |
| // check compatibility: |
| foreach (AtomicReader reader in completeReaderSet) |
| { |
| if (reader.MaxDoc != maxDoc) |
| { |
| throw new System.ArgumentException("All readers must have same MaxDoc: " + maxDoc + "!=" + reader.MaxDoc); |
| } |
| } |
| |
| // TODO: make this read-only in a cleaner way? |
| FieldInfos.Builder builder = new FieldInfos.Builder(); |
| // build FieldInfos and fieldToReader map: |
| foreach (AtomicReader reader in this.parallelReaders) |
| { |
| FieldInfos readerFieldInfos = reader.FieldInfos; |
| foreach (FieldInfo fieldInfo in readerFieldInfos) |
| { |
| // NOTE: first reader having a given field "wins": |
| if (!fieldToReader.ContainsKey(fieldInfo.Name)) |
| { |
| builder.Add(fieldInfo); |
| fieldToReader[fieldInfo.Name] = reader; |
| if (fieldInfo.HasVectors) |
| { |
| tvFieldToReader[fieldInfo.Name] = reader; |
| } |
| } |
| } |
| } |
| fieldInfos = builder.Finish(); |
| |
| // build Fields instance |
| foreach (AtomicReader reader in this.parallelReaders) |
| { |
| Fields readerFields = reader.Fields; |
| if (readerFields != null) |
| { |
| foreach (string field in readerFields) |
| { |
| // only add if the reader responsible for that field name is the current: |
| if (fieldToReader[field].Equals(reader)) |
| { |
| this.fields.AddField(field, readerFields.GetTerms(field)); |
| } |
| } |
| } |
| } |
| |
| // do this finally so any Exceptions occurred before don't affect refcounts: |
| foreach (AtomicReader reader in completeReaderSet) |
| { |
| if (!closeSubReaders) |
| { |
| reader.IncRef(); |
| } |
| reader.RegisterParentReader(this); |
| } |
| } |
| |
| public override string ToString() |
| { |
| StringBuilder buffer = new StringBuilder("ParallelAtomicReader("); |
| bool removeLastCommaSpace = false; |
| foreach (AtomicReader reader in completeReaderSet) |
| { |
| buffer.Append(reader); |
| buffer.Append(", "); |
| removeLastCommaSpace = true; |
| } |
| |
| if (removeLastCommaSpace) |
| { |
| buffer.Remove(buffer.Length - 2, 2); |
| } |
| |
| return buffer.Append(')').ToString(); |
| } |
| |
| // Single instance of this, per ParallelReader instance |
| private sealed class ParallelFields : Fields |
| { |
| private readonly ParallelAtomicReader outerInstance; |
| |
| // LUCENENET specific: Use StringComparer.Ordinal to get the same ordering as Java |
| internal readonly IDictionary<string, Terms> fields = new JCG.SortedDictionary<string, Terms>(StringComparer.Ordinal); |
| |
| internal ParallelFields(ParallelAtomicReader outerInstance) |
| { |
| this.outerInstance = outerInstance; |
| } |
| |
| internal void AddField(string fieldName, Terms terms) |
| { |
| fields[fieldName] = terms; |
| } |
| |
| public override IEnumerator<string> GetEnumerator() |
| { |
| return fields.Keys.GetEnumerator(); |
| } |
| |
| public override Terms GetTerms(string field) |
| { |
| Terms result; |
| fields.TryGetValue(field, out result); |
| return result; |
| } |
| |
| public override int Count |
| { |
| get { return fields.Count; } |
| } |
| } |
| |
| /// <summary> |
| /// Get the <see cref="Index.FieldInfos"/> describing all fields in |
| /// this reader. |
| /// <para/> |
| /// NOTE: the returned field numbers will likely not |
| /// correspond to the actual field numbers in the underlying |
| /// readers, and codec metadata (<see cref="FieldInfo.GetAttribute(string)"/> |
| /// will be unavailable. |
| /// </summary> |
| public override FieldInfos FieldInfos |
| { |
| get |
| { |
| return fieldInfos; |
| } |
| } |
| |
| public override IBits LiveDocs |
| { |
| get |
| { |
| EnsureOpen(); |
| return hasDeletions ? parallelReaders[0].LiveDocs : null; |
| } |
| } |
| |
| public override Fields Fields |
| { |
| get |
| { |
| EnsureOpen(); |
| return fields; |
| } |
| } |
| |
| public override int NumDocs |
| { |
| get |
| { |
| // Don't call ensureOpen() here (it could affect performance) |
| return numDocs; |
| } |
| } |
| |
| public override int MaxDoc |
| { |
| get |
| { |
| // Don't call ensureOpen() here (it could affect performance) |
| return maxDoc; |
| } |
| } |
| |
| public override void Document(int docID, StoredFieldVisitor visitor) |
| { |
| EnsureOpen(); |
| foreach (AtomicReader reader in storedFieldsReaders) |
| { |
| reader.Document(docID, visitor); |
| } |
| } |
| |
| public override Fields GetTermVectors(int docID) |
| { |
| EnsureOpen(); |
| ParallelFields fields = null; |
| foreach (KeyValuePair<string, AtomicReader> ent in tvFieldToReader/*.EntrySet()*/) |
| { |
| string fieldName = ent.Key; |
| Terms vector = ent.Value.GetTermVector(docID, fieldName); |
| if (vector != null) |
| { |
| if (fields == null) |
| { |
| fields = new ParallelFields(this); |
| } |
| fields.AddField(fieldName, vector); |
| } |
| } |
| |
| return fields; |
| } |
| |
| protected internal override void DoClose() |
| { |
| lock (this) |
| { |
| IOException ioe = null; |
| foreach (AtomicReader reader in completeReaderSet) |
| { |
| try |
| { |
| if (closeSubReaders) |
| { |
| reader.Dispose(); |
| } |
| else |
| { |
| reader.DecRef(); |
| } |
| } |
| catch (IOException e) |
| { |
| if (ioe == null) |
| { |
| ioe = e; |
| } |
| } |
| } |
| // throw the first exception |
| if (ioe != null) |
| { |
| throw ioe; |
| } |
| } |
| } |
| |
| public override NumericDocValues GetNumericDocValues(string field) |
| { |
| EnsureOpen(); |
| AtomicReader reader; |
| return fieldToReader.TryGetValue(field, out reader) ? reader.GetNumericDocValues(field) : null; |
| } |
| |
| public override BinaryDocValues GetBinaryDocValues(string field) |
| { |
| EnsureOpen(); |
| AtomicReader reader; |
| return fieldToReader.TryGetValue(field, out reader) ? reader.GetBinaryDocValues(field) : null; |
| } |
| |
| public override SortedDocValues GetSortedDocValues(string field) |
| { |
| EnsureOpen(); |
| AtomicReader reader; |
| return fieldToReader.TryGetValue(field, out reader) ? reader.GetSortedDocValues(field) : null; |
| } |
| |
| public override SortedSetDocValues GetSortedSetDocValues(string field) |
| { |
| EnsureOpen(); |
| AtomicReader reader; |
| return fieldToReader.TryGetValue(field, out reader) ? reader.GetSortedSetDocValues(field) : null; |
| } |
| |
| public override IBits GetDocsWithField(string field) |
| { |
| EnsureOpen(); |
| AtomicReader reader; |
| return fieldToReader.TryGetValue(field, out reader) ? reader.GetDocsWithField(field) : null; |
| } |
| |
| public override NumericDocValues GetNormValues(string field) |
| { |
| EnsureOpen(); |
| AtomicReader reader; |
| NumericDocValues values = null; |
| if (fieldToReader.TryGetValue(field, out reader)) |
| { |
| values = reader.GetNormValues(field); |
| } |
| return values; |
| } |
| |
| public override void CheckIntegrity() |
| { |
| EnsureOpen(); |
| foreach (AtomicReader reader in completeReaderSet) |
| { |
| reader.CheckIntegrity(); |
| } |
| } |
| } |
| } |