| using Lucene.Net.Analysis; |
| using Lucene.Net.Diagnostics; |
| using Lucene.Net.Index; |
| using Lucene.Net.Util; |
| using System; |
| using System.Collections.Generic; |
| using System.Diagnostics; |
| using System.IO; |
| using JCG = J2N.Collections.Generic; |
| |
| namespace Lucene.Net.Documents |
| { |
| /* |
| * 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. |
| */ |
| |
| /// <summary> |
| /// Defers actually loading a field's value until you ask |
| /// for it. You must not use the returned Field instances |
| /// after the provided reader has been closed. </summary> |
| /// <seealso cref="GetField(FieldInfo)"/> |
| public class LazyDocument |
| { |
| private readonly IndexReader reader; |
| private readonly int docID; |
| |
| // null until first field is loaded |
| private Document doc; |
| |
| private readonly IDictionary<int?, IList<LazyField>> fields = new Dictionary<int?, IList<LazyField>>(); // LUCENENET: marked readonly |
| private readonly ISet<string> fieldNames = new JCG.HashSet<string>(); // LUCENENET: marked readonly |
| |
| public LazyDocument(IndexReader reader, int docID) |
| { |
| this.reader = reader; |
| this.docID = docID; |
| } |
| |
| /// <summary> |
| /// Creates an IndexableField whose value will be lazy loaded if and |
| /// when it is used. |
| /// <para> |
| /// <b>NOTE:</b> This method must be called once for each value of the field |
| /// name specified in sequence that the values exist. This method may not be |
| /// used to generate multiple, lazy, IndexableField instances refering to |
| /// the same underlying IndexableField instance. |
| /// </para> |
| /// <para> |
| /// The lazy loading of field values from all instances of IndexableField |
| /// objects returned by this method are all backed by a single Document |
| /// per LazyDocument instance. |
| /// </para> |
| /// </summary> |
| public virtual IIndexableField GetField(FieldInfo fieldInfo) |
| { |
| fieldNames.Add(fieldInfo.Name); |
| if (!fields.TryGetValue(fieldInfo.Number, out IList<LazyField> values) || null == values) |
| { |
| values = new List<LazyField>(); |
| fields[fieldInfo.Number] = values; |
| } |
| |
| LazyField value = new LazyField(this, fieldInfo.Name, fieldInfo.Number); |
| values.Add(value); |
| |
| lock (this) |
| { |
| // edge case: if someone asks this LazyDoc for more LazyFields |
| // after other LazyFields from the same LazyDoc have been |
| // actuallized, we need to force the doc to be re-fetched |
| // so the new LazyFields are also populated. |
| doc = null; |
| } |
| return value; |
| } |
| |
| /// <summary> |
| /// non-private for test only access |
| /// @lucene.internal |
| /// </summary> |
| internal virtual Document GetDocument() |
| { |
| lock (this) |
| { |
| if (doc == null) |
| { |
| try |
| { |
| doc = reader.Document(docID, fieldNames); |
| } |
| catch (IOException ioe) |
| { |
| throw new InvalidOperationException("unable to load document", ioe); |
| } |
| } |
| return doc; |
| } |
| } |
| |
| // :TODO: synchronize to prevent redundent copying? (sync per field name?) |
| private void FetchRealValues(string name, int fieldNum) |
| { |
| Document d = GetDocument(); |
| |
| fields.TryGetValue(fieldNum, out IList<LazyField> lazyValues); |
| IIndexableField[] realValues = d.GetFields(name); |
| |
| if (Debugging.AssertsEnabled) Debugging.Assert(realValues.Length <= lazyValues.Count, |
| "More lazy values then real values for field: {0}", name); |
| |
| for (int i = 0; i < lazyValues.Count; i++) |
| { |
| LazyField f = lazyValues[i]; |
| if (null != f) |
| { |
| f.realValue = realValues[i]; |
| } |
| } |
| } |
| |
| |
| /// <summary> |
| /// @lucene.internal |
| /// </summary> |
| public class LazyField : IIndexableField |
| { |
| private readonly LazyDocument outerInstance; |
| |
| internal string name; |
| internal int fieldNum; |
| internal volatile IIndexableField realValue = null; |
| |
| internal LazyField(LazyDocument outerInstance, string name, int fieldNum) |
| { |
| this.outerInstance = outerInstance; |
| this.name = name; |
| this.fieldNum = fieldNum; |
| } |
| |
| /// <summary> |
| /// non-private for test only access |
| /// @lucene.internal |
| /// </summary> |
| public virtual bool HasBeenLoaded => null != realValue; |
| |
| internal virtual IIndexableField GetRealValue() |
| { |
| if (null == realValue) |
| { |
| outerInstance.FetchRealValues(name, fieldNum); |
| } |
| if (Debugging.AssertsEnabled) |
| { |
| Debugging.Assert(HasBeenLoaded, "field value was not lazy loaded"); |
| Debugging.Assert(realValue.Name.Equals(Name, StringComparison.Ordinal), "realvalue name != name: {0} != {1}", realValue.Name, Name); |
| } |
| |
| return realValue; |
| } |
| |
| /// <summary> |
| /// The field's name |
| /// </summary> |
| public virtual string Name => name; |
| |
| /// <summary> |
| /// Gets the boost factor on this field. |
| /// </summary> |
| public virtual float Boost => 1.0f; |
| |
| /// <summary> |
| /// Non-null if this field has a binary value. </summary> |
| public virtual BytesRef GetBinaryValue() |
| { |
| return GetRealValue().GetBinaryValue(); |
| } |
| |
| /// <summary> |
| /// The value of the field as a <see cref="string"/>, or <c>null</c>. If <c>null</c>, the <see cref="TextReader"/> value or |
| /// binary value is used. Exactly one of <see cref="GetStringValue()"/>, <see cref="GetReaderValue()"/>, and |
| /// <see cref="GetBinaryValue()"/> must be set. |
| /// </summary> |
| /// <returns>The string representation of the value if it is either a <see cref="string"/> or numeric type.</returns> |
| public virtual string GetStringValue() |
| { |
| return GetRealValue().GetStringValue(); |
| } |
| |
| /// <summary> |
| /// The value of the field as a <see cref="string"/>, or <c>null</c>. If <c>null</c>, the <see cref="TextReader"/> value or |
| /// binary value is used. Exactly one of <see cref="GetStringValue()"/>, <see cref="GetReaderValue()"/>, and |
| /// <see cref="GetBinaryValue()"/> must be set. |
| /// </summary> |
| /// <param name="provider">An object that supplies culture-specific formatting information. This parameter has no effect if this field is non-numeric.</param> |
| /// <returns>The string representation of the value if it is either a <see cref="string"/> or numeric type.</returns> |
| // LUCENENET specific - created overload so we can format an underlying numeric type using specified provider |
| public virtual string GetStringValue(IFormatProvider provider) |
| { |
| return GetRealValue().GetStringValue(provider); |
| } |
| |
| /// <summary> |
| /// The value of the field as a <see cref="string"/>, or <c>null</c>. If <c>null</c>, the <see cref="TextReader"/> value or |
| /// binary value is used. Exactly one of <see cref="GetStringValue()"/>, <see cref="GetReaderValue()"/>, and |
| /// <see cref="GetBinaryValue()"/> must be set. |
| /// </summary> |
| /// <param name="format">A standard or custom numeric format string. This parameter has no effect if this field is non-numeric.</param> |
| /// <returns>The string representation of the value if it is either a <see cref="string"/> or numeric type.</returns> |
| // LUCENENET specific - created overload so we can format an underlying numeric type using specified format |
| public virtual string GetStringValue(string format) |
| { |
| return GetRealValue().GetStringValue(format); |
| } |
| |
| /// <summary> |
| /// The value of the field as a <see cref="string"/>, or <c>null</c>. If <c>null</c>, the <see cref="TextReader"/> value or |
| /// binary value is used. Exactly one of <see cref="GetStringValue()"/>, <see cref="GetReaderValue()"/>, and |
| /// <see cref="GetBinaryValue()"/> must be set. |
| /// </summary> |
| /// <param name="format">A standard or custom numeric format string. This parameter has no effect if this field is non-numeric.</param> |
| /// <param name="provider">An object that supplies culture-specific formatting information. This parameter has no effect if this field is non-numeric.</param> |
| /// <returns>The string representation of the value if it is either a <see cref="string"/> or numeric type.</returns> |
| // LUCENENET specific - created overload so we can format an underlying numeric type using specified format and provider |
| public virtual string GetStringValue(string format, IFormatProvider provider) |
| { |
| return GetRealValue().GetStringValue(format, provider); |
| } |
| |
| /// <summary> |
| /// The value of the field as a <see cref="TextReader"/>, or <c>null</c>. If <c>null</c>, the <see cref="string"/> value or |
| /// binary value is used. Exactly one of <see cref="GetStringValue()"/>, <see cref="GetReaderValue()"/>, and |
| /// <see cref="GetBinaryValue()"/> must be set. |
| /// </summary> |
| public virtual TextReader GetReaderValue() |
| { |
| return GetRealValue().GetReaderValue(); |
| } |
| |
| [Obsolete("In .NET, use of this method will cause boxing/unboxing. Instead, use the NumericType property to check the underlying type and call the appropriate GetXXXValue() method to retrieve the value.")] |
| public virtual object GetNumericValue() |
| { |
| return GetRealValue().GetNumericValue(); |
| } |
| |
| /// <summary> |
| /// Gets the <see cref="NumericFieldType"/> of the underlying value, or <see cref="NumericFieldType.NONE"/> if the value is not set or non-numeric. |
| /// <para/> |
| /// Expert: The difference between this property and <see cref="FieldType.NumericType"/> is |
| /// this is represents the current state of the field (whether being written or read) and the |
| /// <see cref="FieldType"/> property represents instructions on how the field will be written, |
| /// but does not re-populate when reading back from an index (it is write-only). |
| /// <para/> |
| /// In Java, the numeric type was determined by checking the type of |
| /// <see cref="GetNumericValue()"/>. However, since there are no reference number |
| /// types in .NET, using <see cref="GetNumericValue()"/> so will cause boxing/unboxing. It is |
| /// therefore recommended to use this property to check the underlying type and the corresponding |
| /// <c>Get*Value()</c> method to retrieve the value. |
| /// <para/> |
| /// NOTE: Since Lucene codecs do not support <see cref="NumericFieldType.BYTE"/> or <see cref="NumericFieldType.INT16"/>, |
| /// fields created with these types will always be <see cref="NumericFieldType.INT32"/> when read back from the index. |
| /// </summary> |
| // LUCENENET specific - Since we have no numeric reference types in .NET, this method was added to check |
| // the numeric type of the inner field without boxing/unboxing. |
| public virtual NumericFieldType NumericType => GetRealValue().NumericType; |
| |
| /// <summary> |
| /// Returns the field value as <see cref="byte"/> or <c>null</c> if the type |
| /// is non-numeric. |
| /// </summary> |
| /// <returns>The field value or <c>null</c> if the type is non-numeric.</returns> |
| // LUCENENET specific - created overload for Byte, since we have no Number class in .NET |
| public virtual byte? GetByteValue() |
| { |
| return GetRealValue().GetByteValue(); |
| } |
| |
| /// <summary> |
| /// Returns the field value as <see cref="short"/> or <c>null</c> if the type |
| /// is non-numeric. |
| /// </summary> |
| /// <returns>The field value or <c>null</c> if the type is non-numeric.</returns> |
| // LUCENENET specific - created overload for Short, since we have no Number class in .NET |
| public virtual short? GetInt16Value() |
| { |
| return GetRealValue().GetInt16Value(); |
| } |
| |
| /// <summary> |
| /// Returns the field value as <see cref="int"/> or <c>null</c> if the type |
| /// is non-numeric. |
| /// </summary> |
| /// <returns>The field value or <c>null</c> if the type is non-numeric.</returns> |
| // LUCENENET specific - created overload for Int32, since we have no Number class in .NET |
| public virtual int? GetInt32Value() |
| { |
| return GetRealValue().GetInt32Value(); |
| } |
| |
| /// <summary> |
| /// Returns the field value as <see cref="long"/> or <c>null</c> if the type |
| /// is non-numeric. |
| /// </summary> |
| /// <returns>The field value or <c>null</c> if the type is non-numeric.</returns> |
| // LUCENENET specific - created overload for Int64, since we have no Number class in .NET |
| public virtual long? GetInt64Value() |
| { |
| return GetRealValue().GetInt64Value(); |
| } |
| |
| /// <summary> |
| /// Returns the field value as <see cref="float"/> or <c>null</c> if the type |
| /// is non-numeric. |
| /// </summary> |
| /// <returns>The field value or <c>null</c> if the type is non-numeric.</returns> |
| // LUCENENET specific - created overload for Single, since we have no Number class in .NET |
| public virtual float? GetSingleValue() |
| { |
| return GetRealValue().GetSingleValue(); |
| } |
| |
| /// <summary> |
| /// Returns the field value as <see cref="double"/> or <c>null</c> if the type |
| /// is non-numeric. |
| /// </summary> |
| /// <returns>The field value or <c>null</c> if the type is non-numeric.</returns> |
| // LUCENENET specific - created overload for Double, since we have no Number class in .NET |
| public virtual double? GetDoubleValue() |
| { |
| return GetRealValue().GetDoubleValue(); |
| } |
| |
| /// <summary> |
| /// Returns the <see cref="Documents.FieldType"/> for this field as type <see cref="Documents.FieldType"/>. </summary> |
| public virtual FieldType FieldType => GetRealValue().IndexableFieldType as FieldType; |
| |
| /// <summary> |
| /// Returns the <see cref="Documents.FieldType"/> for this field as type <see cref="IIndexableFieldType"/>. </summary> |
| public virtual IIndexableFieldType IndexableFieldType => GetRealValue().IndexableFieldType; |
| |
| public virtual TokenStream GetTokenStream(Analyzer analyzer) |
| { |
| return GetRealValue().GetTokenStream(analyzer); |
| } |
| |
| public virtual string ToString(IFormatProvider provider) |
| { |
| return GetRealValue().ToString(provider); |
| } |
| } |
| } |
| } |