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>&gt;=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>&gt;=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 &lt; 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 &lt; 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