You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@lucenenet.apache.org by sy...@apache.org on 2014/09/16 00:47:05 UTC
[07/11] git commit: Skeleton porting of Lucene.Net.Queries
Skeleton porting of Lucene.Net.Queries
Project: http://git-wip-us.apache.org/repos/asf/lucenenet/repo
Commit: http://git-wip-us.apache.org/repos/asf/lucenenet/commit/882f487d
Tree: http://git-wip-us.apache.org/repos/asf/lucenenet/tree/882f487d
Diff: http://git-wip-us.apache.org/repos/asf/lucenenet/diff/882f487d
Branch: refs/heads/master
Commit: 882f487db71361a68ed4c78308f02bd0f82cf75b
Parents: 895be41
Author: Itamar Syn-Hershko <it...@code972.com>
Authored: Tue Sep 16 01:34:54 2014 +0300
Committer: Itamar Syn-Hershko <it...@code972.com>
Committed: Tue Sep 16 01:34:54 2014 +0300
----------------------------------------------------------------------
src/Lucene.Net.Queries/BooleanFilter.cs | 204 ++++
src/Lucene.Net.Queries/BoostingQuery.cs | 178 ++++
src/Lucene.Net.Queries/ChainedFilter.cs | 285 ++++++
src/Lucene.Net.Queries/CommonTermsQuery.cs | 526 ++++++++++
src/Lucene.Net.Queries/CustomScoreProvider.cs | 192 ++++
src/Lucene.Net.Queries/CustomScoreQuery.cs | 490 +++++++++
src/Lucene.Net.Queries/FilterClause.cs | 97 ++
src/Lucene.Net.Queries/Function/BoostedQuery.cs | 287 ++++++
.../Function/DocValues/BoolDocValues.cs | 122 +++
.../DocValues/DocTermsIndexDocValues.cs | 234 +++++
.../Function/DocValues/DoubleDocValues.cs | 256 +++++
.../Function/DocValues/FloatDocValues.cs | 117 +++
.../Function/DocValues/IntDocValues.cs | 183 ++++
.../Function/DocValues/LongDocValues.cs | 193 ++++
.../Function/DocValues/StrDocValues.cs | 89 ++
.../Function/FunctionQuery.cs | 270 +++++
.../Function/FunctionValues.cs | 362 +++++++
src/Lucene.Net.Queries/Function/ValueSource.cs | 223 +++++
.../Function/ValueSource/BoolFunction.cs | 30 +
.../Function/ValueSource/ByteFieldSource.cs | 142 +++
.../Function/ValueSource/BytesRefFieldSource.cs | 140 +++
.../Function/ValueSource/ConstNumberSource.cs | 34 +
.../Function/ValueSource/ConstValueSource.cs | 156 +++
.../Function/ValueSource/DefFunction.cs | 153 +++
.../Function/ValueSource/DivFloatFunction.cs | 44 +
.../Function/ValueSource/DocFreqValueSource.cs | 186 ++++
.../ValueSource/DoubleConstValueSource.cs | 166 ++++
.../Function/ValueSource/DoubleFieldSource.cs | 149 +++
.../Function/ValueSource/DualFloatFunction.cs | 121 +++
.../Function/ValueSource/EnumFieldSource.cs | 343 +++++++
.../Function/ValueSource/FieldCacheSource.cs | 77 ++
.../Function/ValueSource/FloatFieldSource.cs | 152 +++
.../Function/ValueSource/IDFValueSource.cs | 85 ++
.../Function/ValueSource/IfFunction.cs | 186 ++++
.../Function/ValueSource/IntFieldSource.cs | 183 ++++
.../ValueSource/JoinDocFreqValueSource.cs | 134 +++
.../Function/ValueSource/LinearFloatFunction.cs | 113 +++
.../Function/ValueSource/LiteralValueSource.cs | 117 +++
.../Function/ValueSource/LongFieldSource.cs | 184 ++++
.../Function/ValueSource/MaxDocValueSource.cs | 70 ++
.../Function/ValueSource/MaxFloatFunction.cs | 53 +
.../Function/ValueSource/MinFloatFunction.cs | 53 +
.../Function/ValueSource/MultiBoolFunction.cs | 148 +++
.../Function/ValueSource/MultiFloatFunction.cs | 146 +++
.../Function/ValueSource/MultiFunction.cs | 161 +++
.../Function/ValueSource/MultiValueSource.cs | 32 +
.../Function/ValueSource/NormValueSource.cs | 125 +++
.../Function/ValueSource/NumDocsValueSource.cs | 63 ++
.../Function/ValueSource/OrdFieldSource.cs | 175 ++++
.../Function/ValueSource/PowFloatFunction.cs | 48 +
.../ValueSource/ProductFloatFunction.cs | 47 +
.../Function/ValueSource/QueryValueSource.cs | 328 +++++++
.../ValueSource/RangeMapFloatFunction.cs | 138 +++
.../ValueSource/ReciprocalFloatFunction.cs | 130 +++
.../ValueSource/ReverseOrdFieldSource.cs | 135 +++
.../Function/ValueSource/ScaleFloatFunction.cs | 200 ++++
.../Function/ValueSource/ShortFieldSource.cs | 137 +++
.../Function/ValueSource/SimpleBoolFunction.cs | 108 ++
.../Function/ValueSource/SimpleFloatFunction.cs | 71 ++
.../Function/ValueSource/SingleFunction.cs | 67 ++
.../Function/ValueSource/SumFloatFunction.cs | 46 +
.../ValueSource/SumTotalTermFreqValueSource.cs | 132 +++
.../Function/ValueSource/TFValueSource.cs | 197 ++++
.../Function/ValueSource/TermFreqValueSource.cs | 186 ++++
.../ValueSource/TotalTermFreqValueSource.cs | 127 +++
.../Function/ValueSource/VectorValueSource.cs | 293 ++++++
.../Function/ValueSourceScorer.cs | 126 +++
.../Lucene.Net.Queries.csproj | 129 +++
src/Lucene.Net.Queries/Mlt/MoreLikeThis.cs | 981 +++++++++++++++++++
src/Lucene.Net.Queries/Mlt/MoreLikeThisQuery.cs | 297 ++++++
.../Properties/AssemblyInfo.cs | 35 +
src/Lucene.Net.Queries/TermFilter.cs | 139 +++
src/Lucene.Net.Queries/TermsFilter.cs | 439 +++++++++
73 files changed, 12765 insertions(+)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/lucenenet/blob/882f487d/src/Lucene.Net.Queries/BooleanFilter.cs
----------------------------------------------------------------------
diff --git a/src/Lucene.Net.Queries/BooleanFilter.cs b/src/Lucene.Net.Queries/BooleanFilter.cs
new file mode 100644
index 0000000..bcbc6fb
--- /dev/null
+++ b/src/Lucene.Net.Queries/BooleanFilter.cs
@@ -0,0 +1,204 @@
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Text;
+using Lucene.Net.Index;
+using Lucene.Net.Search;
+using Lucene.Net.Util;
+using org.apache.lucene.queries;
+
+namespace Lucene.Net.Queries
+{
+
+ /*
+ * 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>
+ /// A container Filter that allows Boolean composition of Filters.
+ /// Filters are allocated into one of three logical constructs;
+ /// SHOULD, MUST NOT, MUST
+ /// The results Filter BitSet is constructed as follows:
+ /// SHOULD Filters are OR'd together
+ /// The resulting Filter is NOT'd with the NOT Filters
+ /// The resulting Filter is AND'd with the MUST Filters
+ /// </summary>
+ public class BooleanFilter : Filter, IEnumerable<FilterClause>
+ {
+
+ private readonly IList<FilterClause> clauses_Renamed = new List<FilterClause>();
+
+ /// <summary>
+ /// Returns the a DocIdSetIterator representing the Boolean composition
+ /// of the filters that have been added.
+ /// </summary>
+ public override DocIdSet GetDocIdSet(AtomicReaderContext context, Bits acceptDocs)
+ {
+ FixedBitSet res = null;
+ AtomicReader reader = context.reader();
+
+ bool hasShouldClauses = false;
+ foreach (FilterClause fc in clauses_Renamed)
+ {
+ if (fc.Occur == BooleanClause.Occur.SHOULD)
+ {
+ hasShouldClauses = true;
+ DocIdSetIterator disi = getDISI(fc.Filter, context);
+ if (disi == null)
+ {
+ continue;
+ }
+ if (res == null)
+ {
+ res = new FixedBitSet(reader.MaxDoc());
+ }
+ res.or(disi);
+ }
+ }
+ if (hasShouldClauses && res == null)
+ {
+ return null;
+ }
+
+ foreach (FilterClause fc in clauses_Renamed)
+ {
+ if (fc.Occur == BooleanClause.Occur.MUST_NOT)
+ {
+ if (res == null)
+ {
+ Debug.Assert(!hasShouldClauses);
+ res = new FixedBitSet(reader.MaxDoc());
+ res.Set(0, reader.MaxDoc()); // NOTE: may set bits on deleted docs
+ }
+
+ DocIdSetIterator disi = GetDISI(fc.Filter, context);
+ if (disi != null)
+ {
+ res.AndNot(disi);
+ }
+ }
+ }
+
+ foreach (FilterClause fc in clauses_Renamed)
+ {
+ if (fc.Occur == BooleanClause.Occur.MUST)
+ {
+ DocIdSetIterator disi = GetDISI(fc.Filter, context);
+ if (disi == null)
+ {
+ return null; // no documents can match
+ }
+ if (res == null)
+ {
+ res = new FixedBitSet(reader.maxDoc());
+ res.or(disi);
+ }
+ else
+ {
+ res.and(disi);
+ }
+ }
+ }
+
+ return BitsFilteredDocIdSet.wrap(res, acceptDocs);
+ }
+
+//JAVA TO C# CONVERTER WARNING: Method 'throws' clauses are not available in .NET:
+//ORIGINAL LINE: private static org.apache.lucene.search.DocIdSetIterator getDISI(org.apache.lucene.search.Filter filter, org.apache.lucene.index.AtomicReaderContext context) throws java.io.IOException
+ private static DocIdSetIterator GetDISI(Filter filter, AtomicReaderContext context)
+ {
+ // we dont pass acceptDocs, we will filter at the end using an additional filter
+//JAVA TO C# CONVERTER WARNING: The original Java variable was marked 'final':
+//ORIGINAL LINE: final org.apache.lucene.search.DocIdSet set = filter.getDocIdSet(context, null);
+ DocIdSet set = filter.GetDocIdSet(context, null);
+ return set == null ? null : set.GetEnumerator();
+ }
+
+ /// <summary>
+ /// Adds a new FilterClause to the Boolean Filter container </summary>
+ /// <param name="filterClause"> A FilterClause object containing a Filter and an Occur parameter </param>
+ public virtual void Add(FilterClause filterClause)
+ {
+ clauses_Renamed.Add(filterClause);
+ }
+
+ public void Add(Filter filter, BooleanClause.Occur occur)
+ {
+ Add(new FilterClause(filter, occur));
+ }
+
+ /// <summary>
+ /// Returns the list of clauses
+ /// </summary>
+ public virtual IList<FilterClause> clauses()
+ {
+ return clauses_Renamed;
+ }
+
+ /// <summary>
+ /// Returns an iterator on the clauses in this query. It implements the <seealso cref="Iterable"/> interface to
+ /// make it possible to do:
+ /// <pre class="prettyprint">for (FilterClause clause : booleanFilter) {}</pre>
+ /// </summary>
+ public IEnumerator<FilterClause> GetEnumerator()
+ {
+ return clauses().GetEnumerator();
+ }
+
+ public override bool Equals(object obj)
+ {
+ if (this == obj)
+ {
+ return true;
+ }
+
+ if ((obj == null) || (obj.GetType() != this.GetType()))
+ {
+ return false;
+ }
+
+//JAVA TO C# CONVERTER WARNING: The original Java variable was marked 'final':
+//ORIGINAL LINE: final BooleanFilter other = (BooleanFilter)obj;
+ BooleanFilter other = (BooleanFilter)obj;
+ return clauses_Renamed.Equals(other.clauses_Renamed);
+ }
+
+ public override int GetHashCode()
+ {
+ return 657153718 ^ clauses_Renamed.GetHashCode();
+ }
+
+ /// <summary>
+ /// Prints a user-readable version of this Filter. </summary>
+ public override string ToString()
+ {
+//JAVA TO C# CONVERTER WARNING: The original Java variable was marked 'final':
+//ORIGINAL LINE: final StringBuilder buffer = new StringBuilder("BooleanFilter(");
+ StringBuilder buffer = new StringBuilder("BooleanFilter(");
+//JAVA TO C# CONVERTER WARNING: The original Java variable was marked 'final':
+//ORIGINAL LINE: final int minLen = buffer.length();
+ int minLen = buffer.Length;
+ foreach (FilterClause c in clauses_Renamed)
+ {
+ if (buffer.Length > minLen)
+ {
+ buffer.Append(' ');
+ }
+ buffer.Append(c);
+ }
+ return buffer.Append(')').ToString();
+ }
+ }
+
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/lucenenet/blob/882f487d/src/Lucene.Net.Queries/BoostingQuery.cs
----------------------------------------------------------------------
diff --git a/src/Lucene.Net.Queries/BoostingQuery.cs b/src/Lucene.Net.Queries/BoostingQuery.cs
new file mode 100644
index 0000000..203a2ae
--- /dev/null
+++ b/src/Lucene.Net.Queries/BoostingQuery.cs
@@ -0,0 +1,178 @@
+namespace org.apache.lucene.queries
+{
+
+ /*
+ * 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 IndexReader = org.apache.lucene.index.IndexReader;
+ using org.apache.lucene.search;
+
+ /// <summary>
+ /// The BoostingQuery class can be used to effectively demote results that match a given query.
+ /// Unlike the "NOT" clause, this still selects documents that contain undesirable terms,
+ /// but reduces their overall score:
+ ///
+ /// Query balancedQuery = new BoostingQuery(positiveQuery, negativeQuery, 0.01f);
+ /// In this scenario the positiveQuery contains the mandatory, desirable criteria which is used to
+ /// select all matching documents, and the negativeQuery contains the undesirable elements which
+ /// are simply used to lessen the scores. Documents that match the negativeQuery have their score
+ /// multiplied by the supplied "boost" parameter, so this should be less than 1 to achieve a
+ /// demoting effect
+ ///
+ /// This code was originally made available here: [WWW] http://marc.theaimsgroup.com/?l=lucene-user&m=108058407130459&w=2
+ /// and is documented here: http://wiki.apache.org/lucene-java/CommunityContributions
+ /// </summary>
+ public class BoostingQuery : Query
+ {
+ private readonly float boost; // the amount to boost by
+ private readonly Query match; // query to match
+ private readonly Query context; // boost when matches too
+
+ public BoostingQuery(Query match, Query context, float boost)
+ {
+ this.match = match;
+ this.context = context.clone(); // clone before boost
+ this.boost = boost;
+ this.context.Boost = 0.0f; // ignore context-only matches
+ }
+
+//JAVA TO C# CONVERTER WARNING: Method 'throws' clauses are not available in .NET:
+//ORIGINAL LINE: @Override public Query rewrite(org.apache.lucene.index.IndexReader reader) throws java.io.IOException
+ public override Query rewrite(IndexReader reader)
+ {
+ BooleanQuery result = new BooleanQueryAnonymousInnerClassHelper(this);
+
+ result.add(match, BooleanClause.Occur.MUST);
+ result.add(context, BooleanClause.Occur.SHOULD);
+
+ return result;
+ }
+
+ private class BooleanQueryAnonymousInnerClassHelper : BooleanQuery
+ {
+ private readonly BoostingQuery outerInstance;
+
+ public BooleanQueryAnonymousInnerClassHelper(BoostingQuery outerInstance)
+ {
+ this.outerInstance = outerInstance;
+ }
+
+//JAVA TO C# CONVERTER WARNING: Method 'throws' clauses are not available in .NET:
+//ORIGINAL LINE: @Override public Weight createWeight(IndexSearcher searcher) throws java.io.IOException
+ public override Weight createWeight(IndexSearcher searcher)
+ {
+ return new BooleanWeightAnonymousInnerClassHelper(this, searcher);
+ }
+
+ private class BooleanWeightAnonymousInnerClassHelper : BooleanWeight
+ {
+ private readonly BooleanQueryAnonymousInnerClassHelper outerInstance;
+
+ public BooleanWeightAnonymousInnerClassHelper(BooleanQueryAnonymousInnerClassHelper outerInstance, IndexSearcher searcher) : base(searcher, false)
+ {
+ this.outerInstance = outerInstance;
+ }
+
+
+ public override float coord(int overlap, int max)
+ {
+ switch (overlap)
+ {
+
+ case 1: // matched only one clause
+ return 1.0f; // use the score as-is
+
+ case 2: // matched both clauses
+ return outerInstance.outerInstance.boost; // multiply by boost
+
+ default:
+ return 0.0f;
+
+ }
+ }
+ }
+ }
+
+ public override int GetHashCode()
+ {
+ const int prime = 31;
+ int result = base.GetHashCode();
+ result = prime * result + float.floatToIntBits(boost);
+ result = prime * result + ((context == null) ? 0 : context.GetHashCode());
+ result = prime * result + ((match == null) ? 0 : match.GetHashCode());
+ return result;
+ }
+
+ public override bool Equals(object obj)
+ {
+ if (this == obj)
+ {
+ return true;
+ }
+ if (obj == null)
+ {
+ return false;
+ }
+ if (this.GetType() != obj.GetType())
+ {
+ return false;
+ }
+
+ if (!base.Equals(obj))
+ {
+ return false;
+ }
+
+ BoostingQuery other = (BoostingQuery) obj;
+ if (float.floatToIntBits(boost) != float.floatToIntBits(other.boost))
+ {
+ return false;
+ }
+
+ if (context == null)
+ {
+ if (other.context != null)
+ {
+ return false;
+ }
+ }
+ else if (!context.Equals(other.context))
+ {
+ return false;
+ }
+
+ if (match == null)
+ {
+ if (other.match != null)
+ {
+ return false;
+ }
+ }
+ else if (!match.Equals(other.match))
+ {
+ return false;
+ }
+ return true;
+ }
+
+ public override string ToString(string field)
+ {
+ return match.ToString(field) + "/" + context.ToString(field);
+ }
+ }
+
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/lucenenet/blob/882f487d/src/Lucene.Net.Queries/ChainedFilter.cs
----------------------------------------------------------------------
diff --git a/src/Lucene.Net.Queries/ChainedFilter.cs b/src/Lucene.Net.Queries/ChainedFilter.cs
new file mode 100644
index 0000000..c6aec63
--- /dev/null
+++ b/src/Lucene.Net.Queries/ChainedFilter.cs
@@ -0,0 +1,285 @@
+using System.Text;
+
+namespace org.apache.lucene.queries
+{
+
+ /*
+ * 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 = org.apache.lucene.index.AtomicReader;
+ using AtomicReaderContext = org.apache.lucene.index.AtomicReaderContext;
+ using BitsFilteredDocIdSet = org.apache.lucene.search.BitsFilteredDocIdSet;
+ using DocIdSet = org.apache.lucene.search.DocIdSet;
+ using DocIdSetIterator = org.apache.lucene.search.DocIdSetIterator;
+ using Filter = org.apache.lucene.search.Filter;
+ using Bits = org.apache.lucene.util.Bits;
+ using FixedBitSet = org.apache.lucene.util.FixedBitSet;
+
+ /// <summary>
+ /// <para>
+ /// Allows multiple <seealso cref="Filter"/>s to be chained.
+ /// Logical operations such as <b>NOT</b> and <b>XOR</b>
+ /// are applied between filters. One operation can be used
+ /// for all filters, or a specific operation can be declared
+ /// for each filter.
+ /// </para>
+ /// <para>
+ /// Order in which filters are called depends on
+ /// the position of the filter in the chain. It's probably
+ /// more efficient to place the most restrictive filters
+ /// /least computationally-intensive filters first.
+ /// </para>
+ /// </summary>
+ public class ChainedFilter : Filter
+ {
+
+ public const int OR = 0;
+ public const int AND = 1;
+ public const int ANDNOT = 2;
+ public const int XOR = 3;
+ /// <summary>
+ /// Logical operation when none is declared. Defaults to OR.
+ /// </summary>
+ public const int DEFAULT = OR;
+
+ /// <summary>
+ /// The filter chain
+ /// </summary>
+ private Filter[] chain = null;
+
+ private int[] logicArray;
+
+ private int logic = -1;
+
+ /// <summary>
+ /// Ctor.
+ /// </summary>
+ /// <param name="chain"> The chain of filters </param>
+ public ChainedFilter(Filter[] chain)
+ {
+ this.chain = chain;
+ }
+
+ /// <summary>
+ /// Ctor.
+ /// </summary>
+ /// <param name="chain"> The chain of filters </param>
+ /// <param name="logicArray"> Logical operations to apply between filters </param>
+ public ChainedFilter(Filter[] chain, int[] logicArray)
+ {
+ this.chain = chain;
+ this.logicArray = logicArray;
+ }
+
+ /// <summary>
+ /// Ctor.
+ /// </summary>
+ /// <param name="chain"> The chain of filters </param>
+ /// <param name="logic"> Logical operation to apply to ALL filters </param>
+ public ChainedFilter(Filter[] chain, int logic)
+ {
+ this.chain = chain;
+ this.logic = logic;
+ }
+
+ /// <summary>
+ /// <seealso cref="Filter#getDocIdSet"/>.
+ /// </summary>
+//JAVA TO C# CONVERTER WARNING: Method 'throws' clauses are not available in .NET:
+//ORIGINAL LINE: @Override public org.apache.lucene.search.DocIdSet getDocIdSet(org.apache.lucene.index.AtomicReaderContext context, org.apache.lucene.util.Bits acceptDocs) throws java.io.IOException
+ public override DocIdSet getDocIdSet(AtomicReaderContext context, Bits acceptDocs)
+ {
+ int[] index = new int[1]; // use array as reference to modifiable int;
+ index[0] = 0; // an object attribute would not be thread safe.
+ if (logic != -1)
+ {
+ return BitsFilteredDocIdSet.wrap(getDocIdSet(context, logic, index), acceptDocs);
+ }
+ else if (logicArray != null)
+ {
+ return BitsFilteredDocIdSet.wrap(getDocIdSet(context, logicArray, index), acceptDocs);
+ }
+
+ return BitsFilteredDocIdSet.wrap(getDocIdSet(context, DEFAULT, index), acceptDocs);
+ }
+
+//JAVA TO C# CONVERTER WARNING: Method 'throws' clauses are not available in .NET:
+//ORIGINAL LINE: private org.apache.lucene.search.DocIdSetIterator getDISI(org.apache.lucene.search.Filter filter, org.apache.lucene.index.AtomicReaderContext context) throws java.io.IOException
+ private DocIdSetIterator getDISI(Filter filter, AtomicReaderContext context)
+ {
+ // we dont pass acceptDocs, we will filter at the end using an additional filter
+ DocIdSet docIdSet = filter.getDocIdSet(context, null);
+ if (docIdSet == null)
+ {
+ return DocIdSetIterator.empty();
+ }
+ else
+ {
+ DocIdSetIterator iter = docIdSet.GetEnumerator();
+ if (iter == null)
+ {
+ return DocIdSetIterator.empty();
+ }
+ else
+ {
+ return iter;
+ }
+ }
+ }
+
+//JAVA TO C# CONVERTER WARNING: Method 'throws' clauses are not available in .NET:
+//ORIGINAL LINE: private org.apache.lucene.util.FixedBitSet initialResult(org.apache.lucene.index.AtomicReaderContext context, int logic, int[] index) throws java.io.IOException
+ private FixedBitSet initialResult(AtomicReaderContext context, int logic, int[] index)
+ {
+ AtomicReader reader = context.reader();
+ FixedBitSet result = new FixedBitSet(reader.maxDoc());
+ if (logic == AND)
+ {
+ result.or(getDISI(chain[index[0]], context));
+ ++index[0];
+ }
+ else if (logic == ANDNOT)
+ {
+ result.or(getDISI(chain[index[0]], context));
+ result.flip(0, reader.maxDoc()); // NOTE: may set bits for deleted docs.
+ ++index[0];
+ }
+ return result;
+ }
+
+ /// <summary>
+ /// Delegates to each filter in the chain.
+ /// </summary>
+ /// <param name="context"> AtomicReaderContext </param>
+ /// <param name="logic"> Logical operation </param>
+ /// <returns> DocIdSet </returns>
+//JAVA TO C# CONVERTER WARNING: Method 'throws' clauses are not available in .NET:
+//ORIGINAL LINE: private org.apache.lucene.search.DocIdSet getDocIdSet(org.apache.lucene.index.AtomicReaderContext context, int logic, int[] index) throws java.io.IOException
+ private DocIdSet getDocIdSet(AtomicReaderContext context, int logic, int[] index)
+ {
+ FixedBitSet result = initialResult(context, logic, index);
+ for (; index[0] < chain.Length; index[0]++)
+ {
+ // we dont pass acceptDocs, we will filter at the end using an additional filter
+ doChain(result, logic, chain[index[0]].getDocIdSet(context, null));
+ }
+ return result;
+ }
+
+ /// <summary>
+ /// Delegates to each filter in the chain.
+ /// </summary>
+ /// <param name="context"> AtomicReaderContext </param>
+ /// <param name="logic"> Logical operation </param>
+ /// <returns> DocIdSet </returns>
+//JAVA TO C# CONVERTER WARNING: Method 'throws' clauses are not available in .NET:
+//ORIGINAL LINE: private org.apache.lucene.search.DocIdSet getDocIdSet(org.apache.lucene.index.AtomicReaderContext context, int[] logic, int[] index) throws java.io.IOException
+ private DocIdSet getDocIdSet(AtomicReaderContext context, int[] logic, int[] index)
+ {
+ if (logic.Length != chain.Length)
+ {
+ throw new System.ArgumentException("Invalid number of elements in logic array");
+ }
+
+ FixedBitSet result = initialResult(context, logic[0], index);
+ for (; index[0] < chain.Length; index[0]++)
+ {
+ // we dont pass acceptDocs, we will filter at the end using an additional filter
+ doChain(result, logic[index[0]], chain[index[0]].getDocIdSet(context, null));
+ }
+ return result;
+ }
+
+ public override string ToString()
+ {
+ StringBuilder sb = new StringBuilder();
+ sb.Append("ChainedFilter: [");
+ foreach (Filter aChain in chain)
+ {
+ sb.Append(aChain);
+ sb.Append(' ');
+ }
+ sb.Append(']');
+ return sb.ToString();
+ }
+
+//JAVA TO C# CONVERTER WARNING: Method 'throws' clauses are not available in .NET:
+//ORIGINAL LINE: private void doChain(org.apache.lucene.util.FixedBitSet result, int logic, org.apache.lucene.search.DocIdSet dis) throws java.io.IOException
+ private void doChain(FixedBitSet result, int logic, DocIdSet dis)
+ {
+ if (dis is FixedBitSet)
+ {
+ // optimized case for FixedBitSets
+ switch (logic)
+ {
+ case OR:
+ result.or((FixedBitSet) dis);
+ break;
+ case AND:
+ result.and((FixedBitSet) dis);
+ break;
+ case ANDNOT:
+ result.andNot((FixedBitSet) dis);
+ break;
+ case XOR:
+ result.xor((FixedBitSet) dis);
+ break;
+ default:
+ doChain(result, DEFAULT, dis);
+ break;
+ }
+ }
+ else
+ {
+ DocIdSetIterator disi;
+ if (dis == null)
+ {
+ disi = DocIdSetIterator.empty();
+ }
+ else
+ {
+ disi = dis.GetEnumerator();
+ if (disi == null)
+ {
+ disi = DocIdSetIterator.empty();
+ }
+ }
+
+ switch (logic)
+ {
+ case OR:
+ result.or(disi);
+ break;
+ case AND:
+ result.and(disi);
+ break;
+ case ANDNOT:
+ result.andNot(disi);
+ break;
+ case XOR:
+ result.xor(disi);
+ break;
+ default:
+ doChain(result, DEFAULT, dis);
+ break;
+ }
+ }
+ }
+
+ }
+
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/lucenenet/blob/882f487d/src/Lucene.Net.Queries/CommonTermsQuery.cs
----------------------------------------------------------------------
diff --git a/src/Lucene.Net.Queries/CommonTermsQuery.cs b/src/Lucene.Net.Queries/CommonTermsQuery.cs
new file mode 100644
index 0000000..0d78a68
--- /dev/null
+++ b/src/Lucene.Net.Queries/CommonTermsQuery.cs
@@ -0,0 +1,526 @@
+using System;
+using System.Diagnostics;
+using System.Collections.Generic;
+using System.Text;
+
+namespace org.apache.lucene.queries
+{
+
+ /*
+ * 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 AtomicReaderContext = org.apache.lucene.index.AtomicReaderContext;
+ using Fields = org.apache.lucene.index.Fields;
+ using IndexReader = org.apache.lucene.index.IndexReader;
+ using Term = org.apache.lucene.index.Term;
+ using TermContext = org.apache.lucene.index.TermContext;
+ using Terms = org.apache.lucene.index.Terms;
+ using TermsEnum = org.apache.lucene.index.TermsEnum;
+ using BooleanClause = org.apache.lucene.search.BooleanClause;
+ using Occur = org.apache.lucene.search.BooleanClause.Occur;
+ using BooleanQuery = org.apache.lucene.search.BooleanQuery;
+ using Query = org.apache.lucene.search.Query;
+ using TermQuery = org.apache.lucene.search.TermQuery;
+ using Similarity = org.apache.lucene.search.similarities.Similarity;
+ using ToStringUtils = org.apache.lucene.util.ToStringUtils;
+
+
+ /// <summary>
+ /// A query that executes high-frequency terms in a optional sub-query to prevent
+ /// slow queries due to "common" terms like stopwords. This query
+ /// builds 2 queries off the <seealso cref="#add(Term) added"/> terms: low-frequency
+ /// terms are added to a required boolean clause and high-frequency terms are
+ /// added to an optional boolean clause. The optional clause is only executed if
+ /// the required "low-frequency" clause matches. Scores produced by this query
+ /// will be slightly different than plain <seealso cref="BooleanQuery"/> scorer mainly due to
+ /// differences in the <seealso cref="Similarity#coord(int,int) number of leaf queries"/>
+ /// in the required boolean clause. In most cases, high-frequency terms are
+ /// unlikely to significantly contribute to the document score unless at least
+ /// one of the low-frequency terms are matched. This query can improve
+ /// query execution times significantly if applicable.
+ /// <para>
+ /// <seealso cref="CommonTermsQuery"/> has several advantages over stopword filtering at
+ /// index or query time since a term can be "classified" based on the actual
+ /// document frequency in the index and can prevent slow queries even across
+ /// domains without specialized stopword files.
+ /// </para>
+ /// <para>
+ /// <b>Note:</b> if the query only contains high-frequency terms the query is
+ /// rewritten into a plain conjunction query ie. all high-frequency terms need to
+ /// match in order to match a document.
+ /// </para>
+ /// </summary>
+ public class CommonTermsQuery : Query
+ {
+ /*
+ * TODO maybe it would make sense to abstract this even further and allow to
+ * rewrite to dismax rather than boolean. Yet, this can already be subclassed
+ * to do so.
+ */
+ protected internal readonly IList<Term> terms = new List<Term>();
+ protected internal readonly bool disableCoord;
+ protected internal readonly float maxTermFrequency;
+ protected internal readonly BooleanClause.Occur lowFreqOccur;
+ protected internal readonly BooleanClause.Occur highFreqOccur;
+ protected internal float lowFreqBoost = 1.0f;
+ protected internal float highFreqBoost = 1.0f;
+ protected internal float lowFreqMinNrShouldMatch = 0;
+ protected internal float highFreqMinNrShouldMatch = 0;
+
+ /// <summary>
+ /// Creates a new <seealso cref="CommonTermsQuery"/>
+ /// </summary>
+ /// <param name="highFreqOccur">
+ /// <seealso cref="Occur"/> used for high frequency terms </param>
+ /// <param name="lowFreqOccur">
+ /// <seealso cref="Occur"/> used for low frequency terms </param>
+ /// <param name="maxTermFrequency">
+ /// a value in [0..1) (or absolute number >=1) representing the
+ /// maximum threshold of a terms document frequency to be considered a
+ /// low frequency term. </param>
+ /// <exception cref="IllegalArgumentException">
+ /// if <seealso cref="Occur#MUST_NOT"/> is pass as lowFreqOccur or
+ /// highFreqOccur </exception>
+ public CommonTermsQuery(BooleanClause.Occur highFreqOccur, BooleanClause.Occur lowFreqOccur, float maxTermFrequency) : this(highFreqOccur, lowFreqOccur, maxTermFrequency, false)
+ {
+ }
+
+ /// <summary>
+ /// Creates a new <seealso cref="CommonTermsQuery"/>
+ /// </summary>
+ /// <param name="highFreqOccur">
+ /// <seealso cref="Occur"/> used for high frequency terms </param>
+ /// <param name="lowFreqOccur">
+ /// <seealso cref="Occur"/> used for low frequency terms </param>
+ /// <param name="maxTermFrequency">
+ /// a value in [0..1) (or absolute number >=1) representing the
+ /// maximum threshold of a terms document frequency to be considered a
+ /// low frequency term. </param>
+ /// <param name="disableCoord">
+ /// disables <seealso cref="Similarity#coord(int,int)"/> in scoring for the low
+ /// / high frequency sub-queries </param>
+ /// <exception cref="IllegalArgumentException">
+ /// if <seealso cref="Occur#MUST_NOT"/> is pass as lowFreqOccur or
+ /// highFreqOccur </exception>
+ public CommonTermsQuery(BooleanClause.Occur highFreqOccur, BooleanClause.Occur lowFreqOccur, float maxTermFrequency, bool disableCoord)
+ {
+ if (highFreqOccur == BooleanClause.Occur.MUST_NOT)
+ {
+ throw new System.ArgumentException("highFreqOccur should be MUST or SHOULD but was MUST_NOT");
+ }
+ if (lowFreqOccur == BooleanClause.Occur.MUST_NOT)
+ {
+ throw new System.ArgumentException("lowFreqOccur should be MUST or SHOULD but was MUST_NOT");
+ }
+ this.disableCoord = disableCoord;
+ this.highFreqOccur = highFreqOccur;
+ this.lowFreqOccur = lowFreqOccur;
+ this.maxTermFrequency = maxTermFrequency;
+ }
+
+ /// <summary>
+ /// Adds a term to the <seealso cref="CommonTermsQuery"/>
+ /// </summary>
+ /// <param name="term">
+ /// the term to add </param>
+ public virtual void add(Term term)
+ {
+ if (term == null)
+ {
+ throw new System.ArgumentException("Term must not be null");
+ }
+ this.terms.Add(term);
+ }
+
+//JAVA TO C# CONVERTER WARNING: Method 'throws' clauses are not available in .NET:
+//ORIGINAL LINE: @Override public org.apache.lucene.search.Query rewrite(org.apache.lucene.index.IndexReader reader) throws java.io.IOException
+ public override Query rewrite(IndexReader reader)
+ {
+ if (this.terms.Count == 0)
+ {
+ return new BooleanQuery();
+ }
+ else if (this.terms.Count == 1)
+ {
+//JAVA TO C# CONVERTER WARNING: The original Java variable was marked 'final':
+//ORIGINAL LINE: final org.apache.lucene.search.Query tq = newTermQuery(this.terms.get(0), null);
+ Query tq = newTermQuery(this.terms[0], null);
+ tq.Boost = Boost;
+ return tq;
+ }
+//JAVA TO C# CONVERTER WARNING: The original Java variable was marked 'final':
+//ORIGINAL LINE: final java.util.List<org.apache.lucene.index.AtomicReaderContext> leaves = reader.leaves();
+ IList<AtomicReaderContext> leaves = reader.leaves();
+//JAVA TO C# CONVERTER WARNING: The original Java variable was marked 'final':
+//ORIGINAL LINE: final int maxDoc = reader.maxDoc();
+ int maxDoc = reader.maxDoc();
+//JAVA TO C# CONVERTER WARNING: The original Java variable was marked 'final':
+//ORIGINAL LINE: final org.apache.lucene.index.TermContext[] contextArray = new org.apache.lucene.index.TermContext[terms.size()];
+ TermContext[] contextArray = new TermContext[terms.Count];
+//JAVA TO C# CONVERTER WARNING: The original Java variable was marked 'final':
+//ORIGINAL LINE: final org.apache.lucene.index.Term[] queryTerms = this.terms.toArray(new org.apache.lucene.index.Term[0]);
+ Term[] queryTerms = this.terms.ToArray();
+ collectTermContext(reader, leaves, contextArray, queryTerms);
+ return buildQuery(maxDoc, contextArray, queryTerms);
+ }
+
+ protected internal virtual int calcLowFreqMinimumNumberShouldMatch(int numOptional)
+ {
+ return minNrShouldMatch(lowFreqMinNrShouldMatch, numOptional);
+ }
+
+ protected internal virtual int calcHighFreqMinimumNumberShouldMatch(int numOptional)
+ {
+ return minNrShouldMatch(highFreqMinNrShouldMatch, numOptional);
+ }
+
+ private int minNrShouldMatch(float minNrShouldMatch, int numOptional)
+ {
+ if (minNrShouldMatch >= 1.0f || minNrShouldMatch == 0.0f)
+ {
+ return (int) minNrShouldMatch;
+ }
+ return Math.Round(minNrShouldMatch * numOptional);
+ }
+
+//JAVA TO C# CONVERTER WARNING: 'final' parameters are not available in .NET:
+//ORIGINAL LINE: protected org.apache.lucene.search.Query buildQuery(final int maxDoc, final org.apache.lucene.index.TermContext[] contextArray, final org.apache.lucene.index.Term[] queryTerms)
+ protected internal virtual Query buildQuery(int maxDoc, TermContext[] contextArray, Term[] queryTerms)
+ {
+ BooleanQuery lowFreq = new BooleanQuery(disableCoord);
+ BooleanQuery highFreq = new BooleanQuery(disableCoord);
+ highFreq.Boost = highFreqBoost;
+ lowFreq.Boost = lowFreqBoost;
+ BooleanQuery query = new BooleanQuery(true);
+ for (int i = 0; i < queryTerms.Length; i++)
+ {
+ TermContext termContext = contextArray[i];
+ if (termContext == null)
+ {
+ lowFreq.add(newTermQuery(queryTerms[i], null), lowFreqOccur);
+ }
+ else
+ {
+ if ((maxTermFrequency >= 1f && termContext.docFreq() > maxTermFrequency) || (termContext.docFreq() > (int) Math.Ceiling(maxTermFrequency * (float) maxDoc)))
+ {
+ highFreq.add(newTermQuery(queryTerms[i], termContext), highFreqOccur);
+ }
+ else
+ {
+ lowFreq.add(newTermQuery(queryTerms[i], termContext), lowFreqOccur);
+ }
+ }
+
+ }
+//JAVA TO C# CONVERTER WARNING: The original Java variable was marked 'final':
+//ORIGINAL LINE: final int numLowFreqClauses = lowFreq.clauses().size();
+ int numLowFreqClauses = lowFreq.clauses().size();
+//JAVA TO C# CONVERTER WARNING: The original Java variable was marked 'final':
+//ORIGINAL LINE: final int numHighFreqClauses = highFreq.clauses().size();
+ int numHighFreqClauses = highFreq.clauses().size();
+ if (lowFreqOccur == BooleanClause.Occur.SHOULD && numLowFreqClauses > 0)
+ {
+ int minMustMatch = calcLowFreqMinimumNumberShouldMatch(numLowFreqClauses);
+ lowFreq.MinimumNumberShouldMatch = minMustMatch;
+ }
+ if (highFreqOccur == BooleanClause.Occur.SHOULD && numHighFreqClauses > 0)
+ {
+ int minMustMatch = calcHighFreqMinimumNumberShouldMatch(numHighFreqClauses);
+ highFreq.MinimumNumberShouldMatch = minMustMatch;
+ }
+ if (lowFreq.clauses().Empty)
+ {
+ /*
+ * if lowFreq is empty we rewrite the high freq terms in a conjunction to
+ * prevent slow queries.
+ */
+ if (highFreq.MinimumNumberShouldMatch == 0 && highFreqOccur != BooleanClause.Occur.MUST)
+ {
+ foreach (BooleanClause booleanClause in highFreq)
+ {
+ booleanClause.Occur = BooleanClause.Occur.MUST;
+ }
+ }
+ highFreq.Boost = Boost;
+ return highFreq;
+ }
+ else if (highFreq.clauses().Empty)
+ {
+ // only do low freq terms - we don't have high freq terms
+ lowFreq.Boost = Boost;
+ return lowFreq;
+ }
+ else
+ {
+ query.add(highFreq, BooleanClause.Occur.SHOULD);
+ query.add(lowFreq, BooleanClause.Occur.MUST);
+ query.Boost = Boost;
+ return query;
+ }
+ }
+
+//JAVA TO C# CONVERTER WARNING: Method 'throws' clauses are not available in .NET:
+//ORIGINAL LINE: public void collectTermContext(org.apache.lucene.index.IndexReader reader, java.util.List<org.apache.lucene.index.AtomicReaderContext> leaves, org.apache.lucene.index.TermContext[] contextArray, org.apache.lucene.index.Term[] queryTerms) throws java.io.IOException
+ public virtual void collectTermContext(IndexReader reader, IList<AtomicReaderContext> leaves, TermContext[] contextArray, Term[] queryTerms)
+ {
+ TermsEnum termsEnum = null;
+ foreach (AtomicReaderContext context in leaves)
+ {
+//JAVA TO C# CONVERTER WARNING: The original Java variable was marked 'final':
+//ORIGINAL LINE: final org.apache.lucene.index.Fields fields = context.reader().fields();
+ Fields fields = context.reader().fields();
+ if (fields == null)
+ {
+ // reader has no fields
+ continue;
+ }
+ for (int i = 0; i < queryTerms.Length; i++)
+ {
+ Term term = queryTerms[i];
+ TermContext termContext = contextArray[i];
+//JAVA TO C# CONVERTER WARNING: The original Java variable was marked 'final':
+//ORIGINAL LINE: final org.apache.lucene.index.Terms terms = fields.terms(term.field());
+ Terms terms = fields.terms(term.field());
+ if (terms == null)
+ {
+ // field does not exist
+ continue;
+ }
+ termsEnum = terms.iterator(termsEnum);
+ Debug.Assert(termsEnum != null);
+
+ if (termsEnum == TermsEnum.EMPTY)
+ {
+ continue;
+ }
+ if (termsEnum.seekExact(term.bytes()))
+ {
+ if (termContext == null)
+ {
+ contextArray[i] = new TermContext(reader.Context, termsEnum.termState(), context.ord, termsEnum.docFreq(), termsEnum.totalTermFreq());
+ }
+ else
+ {
+ termContext.register(termsEnum.termState(), context.ord, termsEnum.docFreq(), termsEnum.totalTermFreq());
+ }
+
+ }
+
+ }
+ }
+ }
+
+ /// <summary>
+ /// Returns true iff <seealso cref="Similarity#coord(int,int)"/> is disabled in scoring
+ /// for the high and low frequency query instance. The top level query will
+ /// always disable coords.
+ /// </summary>
+ public virtual bool CoordDisabled
+ {
+ get
+ {
+ return disableCoord;
+ }
+ }
+
+ /// <summary>
+ /// Specifies a minimum number of the low frequent optional BooleanClauses which must be
+ /// satisfied in order to produce a match on the low frequency terms query
+ /// part. This method accepts a float value in the range [0..1) as a fraction
+ /// of the actual query terms in the low frequent clause or a number
+ /// <tt>>=1</tt> as an absolut number of clauses that need to match.
+ ///
+ /// <para>
+ /// By default no optional clauses are necessary for a match (unless there are
+ /// no required clauses). If this method is used, then the specified number of
+ /// clauses is required.
+ /// </para>
+ /// </summary>
+ /// <param name="min">
+ /// the number of optional clauses that must match </param>
+ public virtual float LowFreqMinimumNumberShouldMatch
+ {
+ set
+ {
+ this.lowFreqMinNrShouldMatch = value;
+ }
+ get
+ {
+ return lowFreqMinNrShouldMatch;
+ }
+ }
+
+
+ /// <summary>
+ /// Specifies a minimum number of the high frequent optional BooleanClauses which must be
+ /// satisfied in order to produce a match on the low frequency terms query
+ /// part. This method accepts a float value in the range [0..1) as a fraction
+ /// of the actual query terms in the low frequent clause or a number
+ /// <tt>>=1</tt> as an absolut number of clauses that need to match.
+ ///
+ /// <para>
+ /// By default no optional clauses are necessary for a match (unless there are
+ /// no required clauses). If this method is used, then the specified number of
+ /// clauses is required.
+ /// </para>
+ /// </summary>
+ /// <param name="min">
+ /// the number of optional clauses that must match </param>
+ public virtual float HighFreqMinimumNumberShouldMatch
+ {
+ set
+ {
+ this.highFreqMinNrShouldMatch = value;
+ }
+ get
+ {
+ return highFreqMinNrShouldMatch;
+ }
+ }
+
+
+ public override void extractTerms(HashSet<Term> terms)
+ {
+ terms.addAll(this.terms);
+ }
+
+ public override string ToString(string field)
+ {
+ StringBuilder buffer = new StringBuilder();
+ bool needParens = (Boost != 1.0) || (LowFreqMinimumNumberShouldMatch > 0);
+ if (needParens)
+ {
+ buffer.Append("(");
+ }
+ for (int i = 0; i < terms.Count; i++)
+ {
+ Term t = terms[i];
+ buffer.Append(newTermQuery(t, null).ToString());
+
+ if (i != terms.Count - 1)
+ {
+ buffer.Append(", ");
+ }
+ }
+ if (needParens)
+ {
+ buffer.Append(")");
+ }
+ if (LowFreqMinimumNumberShouldMatch > 0 || HighFreqMinimumNumberShouldMatch > 0)
+ {
+ buffer.Append('~');
+ buffer.Append("(");
+ buffer.Append(LowFreqMinimumNumberShouldMatch);
+ buffer.Append(HighFreqMinimumNumberShouldMatch);
+ buffer.Append(")");
+ }
+ if (Boost != 1.0f)
+ {
+ buffer.Append(ToStringUtils.boost(Boost));
+ }
+ return buffer.ToString();
+ }
+
+ public override int GetHashCode()
+ {
+ const int prime = 31;
+ int result = base.GetHashCode();
+ result = prime * result + (disableCoord ? 1231 : 1237);
+ result = prime * result + float.floatToIntBits(highFreqBoost);
+ result = prime * result + ((highFreqOccur == null) ? 0 : highFreqOccur.GetHashCode());
+ result = prime * result + float.floatToIntBits(lowFreqBoost);
+ result = prime * result + ((lowFreqOccur == null) ? 0 : lowFreqOccur.GetHashCode());
+ result = prime * result + float.floatToIntBits(maxTermFrequency);
+ result = prime * result + float.floatToIntBits(lowFreqMinNrShouldMatch);
+ result = prime * result + float.floatToIntBits(highFreqMinNrShouldMatch);
+ result = prime * result + ((terms == null) ? 0 : terms.GetHashCode());
+ return result;
+ }
+
+ public override bool Equals(object obj)
+ {
+ if (this == obj)
+ {
+ return true;
+ }
+ if (!base.Equals(obj))
+ {
+ return false;
+ }
+ if (this.GetType() != obj.GetType())
+ {
+ return false;
+ }
+ CommonTermsQuery other = (CommonTermsQuery) obj;
+ if (disableCoord != other.disableCoord)
+ {
+ return false;
+ }
+ if (float.floatToIntBits(highFreqBoost) != float.floatToIntBits(other.highFreqBoost))
+ {
+ return false;
+ }
+ if (highFreqOccur != other.highFreqOccur)
+ {
+ return false;
+ }
+ if (float.floatToIntBits(lowFreqBoost) != float.floatToIntBits(other.lowFreqBoost))
+ {
+ return false;
+ }
+ if (lowFreqOccur != other.lowFreqOccur)
+ {
+ return false;
+ }
+ if (float.floatToIntBits(maxTermFrequency) != float.floatToIntBits(other.maxTermFrequency))
+ {
+ return false;
+ }
+ if (lowFreqMinNrShouldMatch != other.lowFreqMinNrShouldMatch)
+ {
+ return false;
+ }
+ if (highFreqMinNrShouldMatch != other.highFreqMinNrShouldMatch)
+ {
+ return false;
+ }
+ if (terms == null)
+ {
+ if (other.terms != null)
+ {
+ return false;
+ }
+ }
+ else if (!terms.Equals(other.terms))
+ {
+ return false;
+ }
+ return true;
+ }
+
+ /// <summary>
+ /// Builds a new TermQuery instance.
+ /// <para>This is intended for subclasses that wish to customize the generated queries.</para> </summary>
+ /// <param name="term"> term </param>
+ /// <param name="context"> the TermContext to be used to create the low level term query. Can be <code>null</code>. </param>
+ /// <returns> new TermQuery instance </returns>
+ protected internal virtual Query newTermQuery(Term term, TermContext context)
+ {
+ return context == null ? new TermQuery(term) : new TermQuery(term, context);
+ }
+ }
+
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/lucenenet/blob/882f487d/src/Lucene.Net.Queries/CustomScoreProvider.cs
----------------------------------------------------------------------
diff --git a/src/Lucene.Net.Queries/CustomScoreProvider.cs b/src/Lucene.Net.Queries/CustomScoreProvider.cs
new file mode 100644
index 0000000..5491c86
--- /dev/null
+++ b/src/Lucene.Net.Queries/CustomScoreProvider.cs
@@ -0,0 +1,192 @@
+namespace org.apache.lucene.queries
+{
+
+ /*
+ * 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 AtomicReaderContext = org.apache.lucene.index.AtomicReaderContext;
+ using IndexReader = org.apache.lucene.index.IndexReader; // for javadocs
+ using FunctionQuery = org.apache.lucene.queries.function.FunctionQuery;
+ using Explanation = org.apache.lucene.search.Explanation;
+ using FieldCache = org.apache.lucene.search.FieldCache; // for javadocs
+
+ /// <summary>
+ /// An instance of this subclass should be returned by
+ /// <seealso cref="CustomScoreQuery#getCustomScoreProvider"/>, if you want
+ /// to modify the custom score calculation of a <seealso cref="CustomScoreQuery"/>.
+ /// <para>Since Lucene 2.9, queries operate on each segment of an index separately,
+ /// so the protected <seealso cref="#context"/> field can be used to resolve doc IDs,
+ /// as the supplied <code>doc</code> ID is per-segment and without knowledge
+ /// of the IndexReader you cannot access the document or <seealso cref="FieldCache"/>.
+ ///
+ /// @lucene.experimental
+ /// @since 2.9.2
+ /// </para>
+ /// </summary>
+ public class CustomScoreProvider
+ {
+
+ protected internal readonly AtomicReaderContext context;
+
+ /// <summary>
+ /// Creates a new instance of the provider class for the given <seealso cref="IndexReader"/>.
+ /// </summary>
+ public CustomScoreProvider(AtomicReaderContext context)
+ {
+ this.context = context;
+ }
+
+ /// <summary>
+ /// Compute a custom score by the subQuery score and a number of
+ /// <seealso cref="org.apache.lucene.queries.function.FunctionQuery"/> scores.
+ /// <para>
+ /// Subclasses can override this method to modify the custom score.
+ /// </para>
+ /// <para>
+ /// If your custom scoring is different than the default herein you
+ /// should override at least one of the two customScore() methods.
+ /// If the number of <seealso cref="FunctionQuery function queries"/> is always < 2 it is
+ /// sufficient to override the other
+ /// <seealso cref="#customScore(int, float, float) customScore()"/>
+ /// method, which is simpler.
+ /// </para>
+ /// <para>
+ /// The default computation herein is a multiplication of given scores:
+ /// <pre>
+ /// ModifiedScore = valSrcScore * valSrcScores[0] * valSrcScores[1] * ...
+ /// </pre>
+ ///
+ /// </para>
+ /// </summary>
+ /// <param name="doc"> id of scored doc. </param>
+ /// <param name="subQueryScore"> score of that doc by the subQuery. </param>
+ /// <param name="valSrcScores"> scores of that doc by the <seealso cref="FunctionQuery"/>. </param>
+ /// <returns> custom score. </returns>
+//JAVA TO C# CONVERTER WARNING: Method 'throws' clauses are not available in .NET:
+//ORIGINAL LINE: public float customScore(int doc, float subQueryScore, float valSrcScores[]) throws java.io.IOException
+ public virtual float customScore(int doc, float subQueryScore, float[] valSrcScores)
+ {
+ if (valSrcScores.Length == 1)
+ {
+ return customScore(doc, subQueryScore, valSrcScores[0]);
+ }
+ if (valSrcScores.Length == 0)
+ {
+ return customScore(doc, subQueryScore, 1);
+ }
+ float score = subQueryScore;
+ foreach (float valSrcScore in valSrcScores)
+ {
+ score *= valSrcScore;
+ }
+ return score;
+ }
+
+ /// <summary>
+ /// Compute a custom score by the subQuery score and the <seealso cref="FunctionQuery"/> score.
+ /// <para>
+ /// Subclasses can override this method to modify the custom score.
+ /// </para>
+ /// <para>
+ /// If your custom scoring is different than the default herein you
+ /// should override at least one of the two customScore() methods.
+ /// If the number of <seealso cref="FunctionQuery function queries"/> is always < 2 it is
+ /// sufficient to override this customScore() method, which is simpler.
+ /// </para>
+ /// <para>
+ /// The default computation herein is a multiplication of the two scores:
+ /// <pre>
+ /// ModifiedScore = subQueryScore * valSrcScore
+ /// </pre>
+ ///
+ /// </para>
+ /// </summary>
+ /// <param name="doc"> id of scored doc. </param>
+ /// <param name="subQueryScore"> score of that doc by the subQuery. </param>
+ /// <param name="valSrcScore"> score of that doc by the <seealso cref="FunctionQuery"/>. </param>
+ /// <returns> custom score. </returns>
+//JAVA TO C# CONVERTER WARNING: Method 'throws' clauses are not available in .NET:
+//ORIGINAL LINE: public float customScore(int doc, float subQueryScore, float valSrcScore) throws java.io.IOException
+ public virtual float customScore(int doc, float subQueryScore, float valSrcScore)
+ {
+ return subQueryScore * valSrcScore;
+ }
+
+ /// <summary>
+ /// Explain the custom score.
+ /// Whenever overriding <seealso cref="#customScore(int, float, float[])"/>,
+ /// this method should also be overridden to provide the correct explanation
+ /// for the part of the custom scoring.
+ /// </summary>
+ /// <param name="doc"> doc being explained. </param>
+ /// <param name="subQueryExpl"> explanation for the sub-query part. </param>
+ /// <param name="valSrcExpls"> explanation for the value source part. </param>
+ /// <returns> an explanation for the custom score </returns>
+//JAVA TO C# CONVERTER WARNING: Method 'throws' clauses are not available in .NET:
+//ORIGINAL LINE: public org.apache.lucene.search.Explanation customExplain(int doc, org.apache.lucene.search.Explanation subQueryExpl, org.apache.lucene.search.Explanation valSrcExpls[]) throws java.io.IOException
+ public virtual Explanation customExplain(int doc, Explanation subQueryExpl, Explanation[] valSrcExpls)
+ {
+ if (valSrcExpls.Length == 1)
+ {
+ return customExplain(doc, subQueryExpl, valSrcExpls[0]);
+ }
+ if (valSrcExpls.Length == 0)
+ {
+ return subQueryExpl;
+ }
+ float valSrcScore = 1;
+ foreach (Explanation valSrcExpl in valSrcExpls)
+ {
+ valSrcScore *= valSrcExpl.Value;
+ }
+ Explanation exp = new Explanation(valSrcScore * subQueryExpl.Value, "custom score: product of:");
+ exp.addDetail(subQueryExpl);
+ foreach (Explanation valSrcExpl in valSrcExpls)
+ {
+ exp.addDetail(valSrcExpl);
+ }
+ return exp;
+ }
+
+ /// <summary>
+ /// Explain the custom score.
+ /// Whenever overriding <seealso cref="#customScore(int, float, float)"/>,
+ /// this method should also be overridden to provide the correct explanation
+ /// for the part of the custom scoring.
+ /// </summary>
+ /// <param name="doc"> doc being explained. </param>
+ /// <param name="subQueryExpl"> explanation for the sub-query part. </param>
+ /// <param name="valSrcExpl"> explanation for the value source part. </param>
+ /// <returns> an explanation for the custom score </returns>
+//JAVA TO C# CONVERTER WARNING: Method 'throws' clauses are not available in .NET:
+//ORIGINAL LINE: public org.apache.lucene.search.Explanation customExplain(int doc, org.apache.lucene.search.Explanation subQueryExpl, org.apache.lucene.search.Explanation valSrcExpl) throws java.io.IOException
+ public virtual Explanation customExplain(int doc, Explanation subQueryExpl, Explanation valSrcExpl)
+ {
+ float valSrcScore = 1;
+ if (valSrcExpl != null)
+ {
+ valSrcScore *= valSrcExpl.Value;
+ }
+ Explanation exp = new Explanation(valSrcScore * subQueryExpl.Value, "custom score: product of:");
+ exp.addDetail(subQueryExpl);
+ exp.addDetail(valSrcExpl);
+ return exp;
+ }
+
+ }
+
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/lucenenet/blob/882f487d/src/Lucene.Net.Queries/CustomScoreQuery.cs
----------------------------------------------------------------------
diff --git a/src/Lucene.Net.Queries/CustomScoreQuery.cs b/src/Lucene.Net.Queries/CustomScoreQuery.cs
new file mode 100644
index 0000000..91c2597
--- /dev/null
+++ b/src/Lucene.Net.Queries/CustomScoreQuery.cs
@@ -0,0 +1,490 @@
+using System.Collections.Generic;
+using System.Text;
+
+namespace org.apache.lucene.queries
+{
+
+ /*
+ * 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 AtomicReaderContext = org.apache.lucene.index.AtomicReaderContext;
+ using IndexReader = org.apache.lucene.index.IndexReader;
+ using Term = org.apache.lucene.index.Term;
+ using FunctionQuery = org.apache.lucene.queries.function.FunctionQuery;
+ using ValueSource = org.apache.lucene.queries.function.ValueSource;
+ using ComplexExplanation = org.apache.lucene.search.ComplexExplanation;
+ using Explanation = org.apache.lucene.search.Explanation;
+ using Query = org.apache.lucene.search.Query;
+ using Weight = org.apache.lucene.search.Weight;
+ using Scorer = org.apache.lucene.search.Scorer;
+ using IndexSearcher = org.apache.lucene.search.IndexSearcher;
+ using Bits = org.apache.lucene.util.Bits;
+ using ToStringUtils = org.apache.lucene.util.ToStringUtils;
+
+ /// <summary>
+ /// Query that sets document score as a programmatic function of several (sub) scores:
+ /// <ol>
+ /// <li>the score of its subQuery (any query)</li>
+ /// <li>(optional) the score of its <seealso cref="FunctionQuery"/> (or queries).</li>
+ /// </ol>
+ /// Subclasses can modify the computation by overriding <seealso cref="#getCustomScoreProvider"/>.
+ ///
+ /// @lucene.experimental
+ /// </summary>
+ public class CustomScoreQuery : Query
+ {
+
+ private Query subQuery;
+ private Query[] scoringQueries; // never null (empty array if there are no valSrcQueries).
+ private bool strict = false; // if true, valueSource part of query does not take part in weights normalization.
+
+ /// <summary>
+ /// Create a CustomScoreQuery over input subQuery. </summary>
+ /// <param name="subQuery"> the sub query whose scored is being customized. Must not be null. </param>
+ public CustomScoreQuery(Query subQuery) : this(subQuery, new FunctionQuery[0])
+ {
+ }
+
+ /// <summary>
+ /// Create a CustomScoreQuery over input subQuery and a <seealso cref="org.apache.lucene.queries.function.FunctionQuery"/>. </summary>
+ /// <param name="subQuery"> the sub query whose score is being customized. Must not be null. </param>
+ /// <param name="scoringQuery"> a value source query whose scores are used in the custom score
+ /// computation. This parameter is optional - it can be null. </param>
+ public CustomScoreQuery(Query subQuery, FunctionQuery scoringQuery) : this(subQuery, scoringQuery != null ? new FunctionQuery[] {scoringQuery} : new FunctionQuery[0]); / / don't want an array that contains a single null..
+ {
+ }
+
+ /// <summary>
+ /// Create a CustomScoreQuery over input subQuery and a <seealso cref="org.apache.lucene.queries.function.FunctionQuery"/>. </summary>
+ /// <param name="subQuery"> the sub query whose score is being customized. Must not be null. </param>
+ /// <param name="scoringQueries"> value source queries whose scores are used in the custom score
+ /// computation. This parameter is optional - it can be null or even an empty array. </param>
+ public CustomScoreQuery(Query subQuery, params FunctionQuery[] scoringQueries)
+ {
+ this.subQuery = subQuery;
+ this.scoringQueries = scoringQueries != null? scoringQueries : new Query[0];
+ if (subQuery == null)
+ {
+ throw new System.ArgumentException("<subquery> must not be null!");
+ }
+ }
+
+ /*(non-Javadoc) @see org.apache.lucene.search.Query#rewrite(org.apache.lucene.index.IndexReader) */
+//JAVA TO C# CONVERTER WARNING: Method 'throws' clauses are not available in .NET:
+//ORIGINAL LINE: @Override public org.apache.lucene.search.Query rewrite(org.apache.lucene.index.IndexReader reader) throws java.io.IOException
+ public override Query rewrite(IndexReader reader)
+ {
+ CustomScoreQuery clone = null;
+
+//JAVA TO C# CONVERTER WARNING: The original Java variable was marked 'final':
+//ORIGINAL LINE: final org.apache.lucene.search.Query sq = subQuery.rewrite(reader);
+ Query sq = subQuery.rewrite(reader);
+ if (sq != subQuery)
+ {
+ clone = clone();
+ clone.subQuery = sq;
+ }
+
+ for (int i = 0; i < scoringQueries.Length; i++)
+ {
+//JAVA TO C# CONVERTER WARNING: The original Java variable was marked 'final':
+//ORIGINAL LINE: final org.apache.lucene.search.Query v = scoringQueries[i].rewrite(reader);
+ Query v = scoringQueries[i].rewrite(reader);
+ if (v != scoringQueries[i])
+ {
+ if (clone == null)
+ {
+ clone = clone();
+ }
+ clone.scoringQueries[i] = v;
+ }
+ }
+
+ return (clone == null) ? this : clone;
+ }
+
+ /*(non-Javadoc) @see org.apache.lucene.search.Query#extractTerms(java.util.Set) */
+ public override void extractTerms(HashSet<Term> terms)
+ {
+ subQuery.extractTerms(terms);
+ foreach (Query scoringQuery in scoringQueries)
+ {
+ scoringQuery.extractTerms(terms);
+ }
+ }
+
+ /*(non-Javadoc) @see org.apache.lucene.search.Query#clone() */
+ public override CustomScoreQuery clone()
+ {
+ CustomScoreQuery clone = (CustomScoreQuery)base.clone();
+ clone.subQuery = subQuery.clone();
+ clone.scoringQueries = new Query[scoringQueries.Length];
+ for (int i = 0; i < scoringQueries.Length; i++)
+ {
+ clone.scoringQueries[i] = scoringQueries[i].clone();
+ }
+ return clone;
+ }
+
+ /* (non-Javadoc) @see org.apache.lucene.search.Query#toString(java.lang.String) */
+ public override string ToString(string field)
+ {
+ StringBuilder sb = (new StringBuilder(name())).Append("(");
+ sb.Append(subQuery.ToString(field));
+ foreach (Query scoringQuery in scoringQueries)
+ {
+ sb.Append(", ").Append(scoringQuery.ToString(field));
+ }
+ sb.Append(")");
+ sb.Append(strict?" STRICT" : "");
+ return sb.ToString() + ToStringUtils.boost(Boost);
+ }
+
+ /// <summary>
+ /// Returns true if <code>o</code> is equal to this. </summary>
+ public override bool Equals(object o)
+ {
+ if (this == o)
+ {
+ return true;
+ }
+ if (!base.Equals(o))
+ {
+ return false;
+ }
+ if (this.GetType() != o.GetType())
+ {
+ return false;
+ }
+ CustomScoreQuery other = (CustomScoreQuery)o;
+ if (this.Boost != other.Boost || !this.subQuery.Equals(other.subQuery) || this.strict != other.strict || this.scoringQueries.Length != other.scoringQueries.Length)
+ {
+ return false;
+ }
+ return Arrays.Equals(scoringQueries, other.scoringQueries);
+ }
+
+ /// <summary>
+ /// Returns a hash code value for this object. </summary>
+ public override int GetHashCode()
+ {
+ return (this.GetType().GetHashCode() + subQuery.GetHashCode() + Arrays.GetHashCode(scoringQueries)) ^ float.floatToIntBits(Boost) ^ (strict ? 1234 : 4321);
+ }
+
+ /// <summary>
+ /// Returns a <seealso cref="CustomScoreProvider"/> that calculates the custom scores
+ /// for the given <seealso cref="IndexReader"/>. The default implementation returns a default
+ /// implementation as specified in the docs of <seealso cref="CustomScoreProvider"/>.
+ /// @since 2.9.2
+ /// </summary>
+//JAVA TO C# CONVERTER WARNING: Method 'throws' clauses are not available in .NET:
+//ORIGINAL LINE: protected CustomScoreProvider getCustomScoreProvider(org.apache.lucene.index.AtomicReaderContext context) throws java.io.IOException
+ protected internal virtual CustomScoreProvider getCustomScoreProvider(AtomicReaderContext context)
+ {
+ return new CustomScoreProvider(context);
+ }
+
+ //=========================== W E I G H T ============================
+
+ private class CustomWeight : Weight
+ {
+ private readonly CustomScoreQuery outerInstance;
+
+ internal Weight subQueryWeight;
+ internal Weight[] valSrcWeights;
+ internal bool qStrict;
+ internal float queryWeight;
+
+//JAVA TO C# CONVERTER WARNING: Method 'throws' clauses are not available in .NET:
+//ORIGINAL LINE: public CustomWeight(org.apache.lucene.search.IndexSearcher searcher) throws java.io.IOException
+ public CustomWeight(CustomScoreQuery outerInstance, IndexSearcher searcher)
+ {
+ this.outerInstance = outerInstance;
+ this.subQueryWeight = outerInstance.subQuery.createWeight(searcher);
+ this.valSrcWeights = new Weight[outerInstance.scoringQueries.Length];
+ for (int i = 0; i < outerInstance.scoringQueries.Length; i++)
+ {
+ this.valSrcWeights[i] = outerInstance.scoringQueries[i].createWeight(searcher);
+ }
+ this.qStrict = outerInstance.strict;
+ }
+
+ /*(non-Javadoc) @see org.apache.lucene.search.Weight#getQuery() */
+ public override Query Query
+ {
+ get
+ {
+ return outerInstance;
+ }
+ }
+
+//JAVA TO C# CONVERTER WARNING: Method 'throws' clauses are not available in .NET:
+//ORIGINAL LINE: @Override public float getValueForNormalization() throws java.io.IOException
+ public override float ValueForNormalization
+ {
+ get
+ {
+ float sum = subQueryWeight.ValueForNormalization;
+ foreach (Weight valSrcWeight in valSrcWeights)
+ {
+ if (qStrict)
+ {
+ valSrcWeight.ValueForNormalization; // do not include ValueSource part in the query normalization
+ }
+ else
+ {
+ sum += valSrcWeight.ValueForNormalization;
+ }
+ }
+ return sum;
+ }
+ }
+
+ /*(non-Javadoc) @see org.apache.lucene.search.Weight#normalize(float) */
+ public override void normalize(float norm, float topLevelBoost)
+ {
+ // note we DONT incorporate our boost, nor pass down any topLevelBoost
+ // (e.g. from outer BQ), as there is no guarantee that the CustomScoreProvider's
+ // function obeys the distributive law... it might call sqrt() on the subQuery score
+ // or some other arbitrary function other than multiplication.
+ // so, instead boosts are applied directly in score()
+ subQueryWeight.normalize(norm, 1f);
+ foreach (Weight valSrcWeight in valSrcWeights)
+ {
+ if (qStrict)
+ {
+ valSrcWeight.normalize(1, 1); // do not normalize the ValueSource part
+ }
+ else
+ {
+ valSrcWeight.normalize(norm, 1f);
+ }
+ }
+ queryWeight = topLevelBoost * Boost;
+ }
+
+//JAVA TO C# CONVERTER WARNING: Method 'throws' clauses are not available in .NET:
+//ORIGINAL LINE: @Override public org.apache.lucene.search.Scorer scorer(org.apache.lucene.index.AtomicReaderContext context, org.apache.lucene.util.Bits acceptDocs) throws java.io.IOException
+ public override Scorer scorer(AtomicReaderContext context, Bits acceptDocs)
+ {
+ Scorer subQueryScorer = subQueryWeight.scorer(context, acceptDocs);
+ if (subQueryScorer == null)
+ {
+ return null;
+ }
+ Scorer[] valSrcScorers = new Scorer[valSrcWeights.Length];
+ for (int i = 0; i < valSrcScorers.Length; i++)
+ {
+ valSrcScorers[i] = valSrcWeights[i].scorer(context, acceptDocs);
+ }
+ return new CustomScorer(outerInstance, outerInstance.getCustomScoreProvider(context), this, queryWeight, subQueryScorer, valSrcScorers);
+ }
+
+//JAVA TO C# CONVERTER WARNING: Method 'throws' clauses are not available in .NET:
+//ORIGINAL LINE: @Override public org.apache.lucene.search.Explanation explain(org.apache.lucene.index.AtomicReaderContext context, int doc) throws java.io.IOException
+ public override Explanation explain(AtomicReaderContext context, int doc)
+ {
+ Explanation explain = doExplain(context, doc);
+ return explain == null ? new Explanation(0.0f, "no matching docs") : explain;
+ }
+
+//JAVA TO C# CONVERTER WARNING: Method 'throws' clauses are not available in .NET:
+//ORIGINAL LINE: private org.apache.lucene.search.Explanation doExplain(org.apache.lucene.index.AtomicReaderContext info, int doc) throws java.io.IOException
+ internal virtual Explanation doExplain(AtomicReaderContext info, int doc)
+ {
+ Explanation subQueryExpl = subQueryWeight.explain(info, doc);
+ if (!subQueryExpl.Match)
+ {
+ return subQueryExpl;
+ }
+ // match
+ Explanation[] valSrcExpls = new Explanation[valSrcWeights.Length];
+ for (int i = 0; i < valSrcWeights.Length; i++)
+ {
+ valSrcExpls[i] = valSrcWeights[i].explain(info, doc);
+ }
+ Explanation customExp = outerInstance.getCustomScoreProvider(info).customExplain(doc,subQueryExpl,valSrcExpls);
+ float sc = Boost * customExp.Value;
+ Explanation res = new ComplexExplanation(true, sc, outerInstance.ToString() + ", product of:");
+ res.addDetail(customExp);
+ res.addDetail(new Explanation(Boost, "queryBoost")); // actually using the q boost as q weight (== weight value)
+ return res;
+ }
+
+ public override bool scoresDocsOutOfOrder()
+ {
+ return false;
+ }
+
+ }
+
+
+ //=========================== S C O R E R ============================
+
+ /// <summary>
+ /// A scorer that applies a (callback) function on scores of the subQuery.
+ /// </summary>
+ private class CustomScorer : Scorer
+ {
+ private readonly CustomScoreQuery outerInstance;
+
+ internal readonly float qWeight;
+ internal readonly Scorer subQueryScorer;
+ internal readonly Scorer[] valSrcScorers;
+ internal readonly CustomScoreProvider provider;
+ internal readonly float[] vScores; // reused in score() to avoid allocating this array for each doc
+
+ // constructor
+ internal CustomScorer(CustomScoreQuery outerInstance, CustomScoreProvider provider, CustomWeight w, float qWeight, Scorer subQueryScorer, Scorer[] valSrcScorers) : base(w)
+ {
+ this.outerInstance = outerInstance;
+ this.qWeight = qWeight;
+ this.subQueryScorer = subQueryScorer;
+ this.valSrcScorers = valSrcScorers;
+ this.vScores = new float[valSrcScorers.Length];
+ this.provider = provider;
+ }
+
+//JAVA TO C# CONVERTER WARNING: Method 'throws' clauses are not available in .NET:
+//ORIGINAL LINE: @Override public int nextDoc() throws java.io.IOException
+ public override int nextDoc()
+ {
+ int doc = subQueryScorer.nextDoc();
+ if (doc != NO_MORE_DOCS)
+ {
+ foreach (Scorer valSrcScorer in valSrcScorers)
+ {
+ valSrcScorer.advance(doc);
+ }
+ }
+ return doc;
+ }
+
+ public override int docID()
+ {
+ return subQueryScorer.docID();
+ }
+
+ /*(non-Javadoc) @see org.apache.lucene.search.Scorer#score() */
+//JAVA TO C# CONVERTER WARNING: Method 'throws' clauses are not available in .NET:
+//ORIGINAL LINE: @Override public float score() throws java.io.IOException
+ public override float score()
+ {
+ for (int i = 0; i < valSrcScorers.Length; i++)
+ {
+ vScores[i] = valSrcScorers[i].score();
+ }
+ return qWeight * provider.customScore(subQueryScorer.docID(), subQueryScorer.score(), vScores);
+ }
+
+//JAVA TO C# CONVERTER WARNING: Method 'throws' clauses are not available in .NET:
+//ORIGINAL LINE: @Override public int freq() throws java.io.IOException
+ public override int freq()
+ {
+ return subQueryScorer.freq();
+ }
+
+ public override ICollection<ChildScorer> Children
+ {
+ get
+ {
+ return Collections.singleton(new ChildScorer(subQueryScorer, "CUSTOM"));
+ }
+ }
+
+//JAVA TO C# CONVERTER WARNING: Method 'throws' clauses are not available in .NET:
+//ORIGINAL LINE: @Override public int advance(int target) throws java.io.IOException
+ public override int advance(int target)
+ {
+ int doc = subQueryScorer.advance(target);
+ if (doc != NO_MORE_DOCS)
+ {
+ foreach (Scorer valSrcScorer in valSrcScorers)
+ {
+ valSrcScorer.advance(doc);
+ }
+ }
+ return doc;
+ }
+
+ public override long cost()
+ {
+ return subQueryScorer.cost();
+ }
+ }
+
+//JAVA TO C# CONVERTER WARNING: Method 'throws' clauses are not available in .NET:
+//ORIGINAL LINE: @Override public org.apache.lucene.search.Weight createWeight(org.apache.lucene.search.IndexSearcher searcher) throws java.io.IOException
+ public override Weight createWeight(IndexSearcher searcher)
+ {
+ return new CustomWeight(this, searcher);
+ }
+
+ /// <summary>
+ /// Checks if this is strict custom scoring.
+ /// In strict custom scoring, the <seealso cref="ValueSource"/> part does not participate in weight normalization.
+ /// This may be useful when one wants full control over how scores are modified, and does
+ /// not care about normalizing by the <seealso cref="ValueSource"/> part.
+ /// One particular case where this is useful if for testing this query.
+ /// <P>
+ /// Note: only has effect when the <seealso cref="ValueSource"/> part is not null.
+ /// </summary>
+ public virtual bool Strict
+ {
+ get
+ {
+ return strict;
+ }
+ set
+ {
+ this.strict = value;
+ }
+ }
+
+
+ /// <summary>
+ /// The sub-query that CustomScoreQuery wraps, affecting both the score and which documents match. </summary>
+ public virtual Query SubQuery
+ {
+ get
+ {
+ return subQuery;
+ }
+ }
+
+ /// <summary>
+ /// The scoring queries that only affect the score of CustomScoreQuery. </summary>
+ public virtual Query[] ScoringQueries
+ {
+ get
+ {
+ return scoringQueries;
+ }
+ }
+
+ /// <summary>
+ /// A short name of this query, used in <seealso cref="#toString(String)"/>.
+ /// </summary>
+ public virtual string name()
+ {
+ return "custom";
+ }
+
+ }
+
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/lucenenet/blob/882f487d/src/Lucene.Net.Queries/FilterClause.cs
----------------------------------------------------------------------
diff --git a/src/Lucene.Net.Queries/FilterClause.cs b/src/Lucene.Net.Queries/FilterClause.cs
new file mode 100644
index 0000000..cbfe284
--- /dev/null
+++ b/src/Lucene.Net.Queries/FilterClause.cs
@@ -0,0 +1,97 @@
+namespace org.apache.lucene.queries
+{
+
+ /*
+ * 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 Occur = org.apache.lucene.search.BooleanClause.Occur;
+ using Filter = org.apache.lucene.search.Filter;
+
+ /// <summary>
+ /// A Filter that wrapped with an indication of how that filter
+ /// is used when composed with another filter.
+ /// (Follows the boolean logic in BooleanClause for composition
+ /// of queries.)
+ /// </summary>
+ public sealed class FilterClause
+ {
+
+ private readonly Occur occur;
+ private readonly Filter filter;
+
+ /// <summary>
+ /// Create a new FilterClause </summary>
+ /// <param name="filter"> A Filter object containing a BitSet </param>
+ /// <param name="occur"> A parameter implementation indicating SHOULD, MUST or MUST NOT </param>
+
+ public FilterClause(Filter filter, Occur occur)
+ {
+ this.occur = occur;
+ this.filter = filter;
+ }
+
+ /// <summary>
+ /// Returns this FilterClause's filter </summary>
+ /// <returns> A Filter object </returns>
+ public Filter Filter
+ {
+ get
+ {
+ return filter;
+ }
+ }
+
+ /// <summary>
+ /// Returns this FilterClause's occur parameter </summary>
+ /// <returns> An Occur object </returns>
+ public Occur Occur
+ {
+ get
+ {
+ return occur;
+ }
+ }
+
+ public override bool Equals(object o)
+ {
+ if (o == this)
+ {
+ return true;
+ }
+ if (o == null || !(o is FilterClause))
+ {
+ return false;
+ }
+//JAVA TO C# CONVERTER WARNING: The original Java variable was marked 'final':
+//ORIGINAL LINE: final FilterClause other = (FilterClause)o;
+ FilterClause other = (FilterClause)o;
+ return this.filter.Equals(other.filter) && this.occur == other.occur;
+ }
+
+ public override int GetHashCode()
+ {
+ return filter.GetHashCode() ^ occur.GetHashCode();
+ }
+
+ public override string ToString()
+ {
+ return occur.ToString() + filter.ToString();
+ }
+
+ }
+
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/lucenenet/blob/882f487d/src/Lucene.Net.Queries/Function/BoostedQuery.cs
----------------------------------------------------------------------
diff --git a/src/Lucene.Net.Queries/Function/BoostedQuery.cs b/src/Lucene.Net.Queries/Function/BoostedQuery.cs
new file mode 100644
index 0000000..928db91
--- /dev/null
+++ b/src/Lucene.Net.Queries/Function/BoostedQuery.cs
@@ -0,0 +1,287 @@
+using System.Collections;
+using System.Collections.Generic;
+using System.Text;
+
+namespace org.apache.lucene.queries.function
+{
+
+ /*
+ * 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 org.apache.lucene.search;
+ using AtomicReaderContext = org.apache.lucene.index.AtomicReaderContext;
+ using IndexReader = org.apache.lucene.index.IndexReader;
+ using Term = org.apache.lucene.index.Term;
+ using Bits = org.apache.lucene.util.Bits;
+ using ToStringUtils = org.apache.lucene.util.ToStringUtils;
+
+
+ /// <summary>
+ /// Query that is boosted by a ValueSource
+ /// </summary>
+ // TODO: BoostedQuery and BoostingQuery in the same module?
+ // something has to give
+ public class BoostedQuery : Query
+ {
+ private Query q;
+ private readonly ValueSource boostVal; // optional, can be null
+
+ public BoostedQuery(Query subQuery, ValueSource boostVal)
+ {
+ this.q = subQuery;
+ this.boostVal = boostVal;
+ }
+
+ public virtual Query Query
+ {
+ get
+ {
+ return q;
+ }
+ }
+ public virtual ValueSource ValueSource
+ {
+ get
+ {
+ return boostVal;
+ }
+ }
+
+//JAVA TO C# CONVERTER WARNING: Method 'throws' clauses are not available in .NET:
+//ORIGINAL LINE: @Override public Query rewrite(org.apache.lucene.index.IndexReader reader) throws java.io.IOException
+ public override Query rewrite(IndexReader reader)
+ {
+ Query newQ = q.rewrite(reader);
+ if (newQ == q)
+ {
+ return this;
+ }
+ BoostedQuery bq = (BoostedQuery)this.MemberwiseClone();
+ bq.q = newQ;
+ return bq;
+ }
+
+ public override void extractTerms(HashSet<Term> terms)
+ {
+ q.extractTerms(terms);
+ }
+
+//JAVA TO C# CONVERTER WARNING: Method 'throws' clauses are not available in .NET:
+//ORIGINAL LINE: @Override public Weight createWeight(IndexSearcher searcher) throws java.io.IOException
+ public override Weight createWeight(IndexSearcher searcher)
+ {
+ return new BoostedQuery.BoostedWeight(this, searcher);
+ }
+
+ private class BoostedWeight : Weight
+ {
+ private readonly BoostedQuery outerInstance;
+
+ internal readonly IndexSearcher searcher;
+ internal Weight qWeight;
+ internal IDictionary fcontext;
+
+//JAVA TO C# CONVERTER WARNING: Method 'throws' clauses are not available in .NET:
+//ORIGINAL LINE: public BoostedWeight(IndexSearcher searcher) throws java.io.IOException
+ public BoostedWeight(BoostedQuery outerInstance, IndexSearcher searcher)
+ {
+ this.outerInstance = outerInstance;
+ this.searcher = searcher;
+ this.qWeight = outerInstance.q.createWeight(searcher);
+ this.fcontext = ValueSource.newContext(searcher);
+ outerInstance.boostVal.createWeight(fcontext,searcher);
+ }
+
+ public override Query Query
+ {
+ get
+ {
+ return outerInstance;
+ }
+ }
+
+//JAVA TO C# CONVERTER WARNING: Method 'throws' clauses are not available in .NET:
+//ORIGINAL LINE: @Override public float getValueForNormalization() throws java.io.IOException
+ public override float ValueForNormalization
+ {
+ get
+ {
+ float sum = qWeight.ValueForNormalization;
+ sum *= Boost * Boost;
+ return sum;
+ }
+ }
+
+ public override void normalize(float norm, float topLevelBoost)
+ {
+ topLevelBoost *= Boost;
+ qWeight.normalize(norm, topLevelBoost);
+ }
+
+//JAVA TO C# CONVERTER WARNING: Method 'throws' clauses are not available in .NET:
+//ORIGINAL LINE: @Override public Scorer scorer(org.apache.lucene.index.AtomicReaderContext context, org.apache.lucene.util.Bits acceptDocs) throws java.io.IOException
+ public override Scorer scorer(AtomicReaderContext context, Bits acceptDocs)
+ {
+ Scorer subQueryScorer = qWeight.scorer(context, acceptDocs);
+ if (subQueryScorer == null)
+ {
+ return null;
+ }
+ return new BoostedQuery.CustomScorer(outerInstance, context, this, Boost, subQueryScorer, outerInstance.boostVal);
+ }
+
+//JAVA TO C# CONVERTER WARNING: Method 'throws' clauses are not available in .NET:
+//ORIGINAL LINE: @Override public Explanation explain(org.apache.lucene.index.AtomicReaderContext readerContext, int doc) throws java.io.IOException
+ public override Explanation explain(AtomicReaderContext readerContext, int doc)
+ {
+ Explanation subQueryExpl = qWeight.explain(readerContext,doc);
+ if (!subQueryExpl.Match)
+ {
+ return subQueryExpl;
+ }
+ FunctionValues vals = outerInstance.boostVal.getValues(fcontext, readerContext);
+ float sc = subQueryExpl.Value * vals.floatVal(doc);
+ Explanation res = new ComplexExplanation(true, sc, outerInstance.ToString() + ", product of:");
+ res.addDetail(subQueryExpl);
+ res.addDetail(vals.explain(doc));
+ return res;
+ }
+ }
+
+
+ private class CustomScorer : Scorer
+ {
+ private readonly BoostedQuery outerInstance;
+
+ internal readonly BoostedQuery.BoostedWeight weight;
+ internal readonly float qWeight;
+ internal readonly Scorer scorer;
+ internal readonly FunctionValues vals;
+ internal readonly AtomicReaderContext readerContext;
+
+//JAVA TO C# CONVERTER WARNING: Method 'throws' clauses are not available in .NET:
+//ORIGINAL LINE: private CustomScorer(org.apache.lucene.index.AtomicReaderContext readerContext, BoostedQuery.BoostedWeight w, float qWeight, Scorer scorer, ValueSource vs) throws java.io.IOException
+ internal CustomScorer(BoostedQuery outerInstance, AtomicReaderContext readerContext, BoostedQuery.BoostedWeight w, float qWeight, Scorer scorer, ValueSource vs) : base(w)
+ {
+ this.outerInstance = outerInstance;
+ this.weight = w;
+ this.qWeight = qWeight;
+ this.scorer = scorer;
+ this.readerContext = readerContext;
+ this.vals = vs.getValues(weight.fcontext, readerContext);
+ }
+
+ public override int docID()
+ {
+ return scorer.docID();
+ }
+
+//JAVA TO C# CONVERTER WARNING: Method 'throws' clauses are not available in .NET:
+//ORIGINAL LINE: @Override public int advance(int target) throws java.io.IOException
+ public override int advance(int target)
+ {
+ return scorer.advance(target);
+ }
+
+//JAVA TO C# CONVERTER WARNING: Method 'throws' clauses are not available in .NET:
+//ORIGINAL LINE: @Override public int nextDoc() throws java.io.IOException
+ public override int nextDoc()
+ {
+ return scorer.nextDoc();
+ }
+
+//JAVA TO C# CONVERTER WARNING: Method 'throws' clauses are not available in .NET:
+//ORIGINAL LINE: @Override public float score() throws java.io.IOException
+ public override float score()
+ {
+ float score = qWeight * scorer.score() * vals.floatVal(scorer.docID());
+
+ // Current Lucene priority queues can't handle NaN and -Infinity, so
+ // map to -Float.MAX_VALUE. This conditional handles both -infinity
+ // and NaN since comparisons with NaN are always false.
+ return score > float.NegativeInfinity ? score : -float.MaxValue;
+ }
+
+//JAVA TO C# CONVERTER WARNING: Method 'throws' clauses are not available in .NET:
+//ORIGINAL LINE: @Override public int freq() throws java.io.IOException
+ public override int freq()
+ {
+ return scorer.freq();
+ }
+
+ public override ICollection<ChildScorer> Children
+ {
+ get
+ {
+ return Collections.singleton(new ChildScorer(scorer, "CUSTOM"));
+ }
+ }
+
+//JAVA TO C# CONVERTER WARNING: Method 'throws' clauses are not available in .NET:
+//ORIGINAL LINE: public Explanation explain(int doc) throws java.io.IOException
+ public virtual Explanation explain(int doc)
+ {
+ Explanation subQueryExpl = weight.qWeight.explain(readerContext,doc);
+ if (!subQueryExpl.Match)
+ {
+ return subQueryExpl;
+ }
+ float sc = subQueryExpl.Value * vals.floatVal(doc);
+ Explanation res = new ComplexExplanation(true, sc, outerInstance.ToString() + ", product of:");
+ res.addDetail(subQueryExpl);
+ res.addDetail(vals.explain(doc));
+ return res;
+ }
+
+ public override long cost()
+ {
+ return scorer.cost();
+ }
+ }
+
+
+ public override string ToString(string field)
+ {
+ StringBuilder sb = new StringBuilder();
+ sb.Append("boost(").Append(q.ToString(field)).Append(',').Append(boostVal).Append(')');
+ sb.Append(ToStringUtils.boost(Boost));
+ return sb.ToString();
+ }
+
+ public override bool Equals(object o)
+ {
+ if (!base.Equals(o))
+ {
+ return false;
+ }
+ BoostedQuery other = (BoostedQuery)o;
+ return this.q.Equals(other.q) && this.boostVal.Equals(other.boostVal);
+ }
+
+ public override int GetHashCode()
+ {
+ int h = q.GetHashCode();
+ h ^= (h << 17) | ((int)((uint)h >> 16));
+ h += boostVal.GetHashCode();
+ h ^= (h << 8) | ((int)((uint)h >> 25));
+ h += float.floatToIntBits(Boost);
+ return h;
+ }
+
+ }
+
+}
\ No newline at end of file