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 2015/08/24 00:34:02 UTC

[03/17] lucenenet git commit: Completed the implementation port of the Join project

http://git-wip-us.apache.org/repos/asf/lucenenet/blob/47f20b9a/Lucene.Net.Join/ToParentBlockJoinQuery.cs
----------------------------------------------------------------------
diff --git a/Lucene.Net.Join/ToParentBlockJoinQuery.cs b/Lucene.Net.Join/ToParentBlockJoinQuery.cs
new file mode 100644
index 0000000..810f30e
--- /dev/null
+++ b/Lucene.Net.Join/ToParentBlockJoinQuery.cs
@@ -0,0 +1,516 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using Lucene.Net.Index;
+using Lucene.Net.Search;
+using Lucene.Net.Util;
+
+namespace Lucene.Net.Join
+{
+    /*
+	 * 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>
+    /// This query requires that you index
+    /// children and parent docs as a single block, using the
+    /// <see cref="IndexWriter#addDocuments IndexWriter.addDocuments()"/> or {@link
+    /// IndexWriter#updateDocuments IndexWriter.updateDocuments()} API.  In each block, the
+    /// child documents must appear first, ending with the parent
+    /// document.  At search time you provide a Filter
+    /// identifying the parents, however this Filter must provide
+    /// an <see cref="FixedBitSet"/> per sub-reader.
+    /// 
+    /// <p>Once the block index is built, use this query to wrap
+    /// any sub-query matching only child docs and join matches in that
+    /// child document space up to the parent document space.
+    /// You can then use this Query as a clause with
+    /// other queries in the parent document space.</p>
+    /// 
+    /// <p>See <see cref="ToChildBlockJoinQuery"/> if you need to join
+    /// in the reverse order.
+    /// 
+    /// <p>The child documents must be orthogonal to the parent
+    /// documents: the wrapped child query must never
+    /// return a parent document.</p>
+    /// 
+    /// If you'd like to retrieve <see cref="TopGroups"/> for the
+    /// resulting query, use the <see cref="ToParentBlockJoinCollector"/>.
+    /// Note that this is not necessary, ie, if you simply want
+    /// to collect the parent documents and don't need to see
+    /// which child documents matched under that parent, then
+    /// you can use any collector.
+    /// 
+    /// <p><b>NOTE</b>: If the overall query contains parent-only
+    /// matches, for example you OR a parent-only query with a
+    /// joined child-only query, then the resulting collected documents
+    /// will be correct, however the <see cref="TopGroups"/> you get
+    /// from <see cref="ToParentBlockJoinCollector"/> will not contain every
+    /// child for parents that had matched.
+    /// 
+    /// <p>See <see cref="org.apache.lucene.search.join"/> for an
+    /// overview. </p>
+    /// 
+    /// @lucene.experimental
+    /// </summary>
+    public class ToParentBlockJoinQuery : Query
+    {
+
+        private readonly Filter _parentsFilter;
+        private readonly Query _childQuery;
+
+        // If we are rewritten, this is the original childQuery we
+        // were passed; we use this for .equals() and
+        // .hashCode().  This makes rewritten query equal the
+        // original, so that user does not have to .rewrite() their
+        // query before searching:
+        private readonly Query _origChildQuery;
+        private readonly ScoreMode _scoreMode;
+
+        /// <summary>
+        /// Create a ToParentBlockJoinQuery.
+        /// </summary>
+        /// <param name="childQuery"> Query matching child documents. </param>
+        /// <param name="parentsFilter"> Filter (must produce FixedBitSet
+        /// per-segment, like <see cref="FixedBitSetCachingWrapperFilter"/>)
+        /// identifying the parent documents. </param>
+        /// <param name="scoreMode"> How to aggregate multiple child scores
+        /// into a single parent score.
+        ///  </param>
+        public ToParentBlockJoinQuery(Query childQuery, Filter parentsFilter, ScoreMode scoreMode)
+        {
+            _origChildQuery = childQuery;
+            _childQuery = childQuery;
+            _parentsFilter = parentsFilter;
+            _scoreMode = scoreMode;
+        }
+
+        private ToParentBlockJoinQuery(Query origChildQuery, Query childQuery, Filter parentsFilter, ScoreMode scoreMode) : base()
+        {
+            _origChildQuery = origChildQuery;
+            _childQuery = childQuery;
+            _parentsFilter = parentsFilter;
+            _scoreMode = scoreMode;
+        }
+        
+        public override Weight CreateWeight(IndexSearcher searcher)
+        {
+            return new BlockJoinWeight(this, _childQuery.CreateWeight(searcher), _parentsFilter, _scoreMode);
+        }
+
+        private class BlockJoinWeight : Weight
+        {
+            internal readonly Query JoinQuery;
+            internal readonly Weight ChildWeight;
+            internal readonly Filter ParentsFilter;
+            internal readonly ScoreMode ScoreMode;
+
+            public BlockJoinWeight(Query joinQuery, Weight childWeight, Filter parentsFilter, ScoreMode scoreMode) : base()
+            {
+                JoinQuery = joinQuery;
+                ChildWeight = childWeight;
+                ParentsFilter = parentsFilter;
+                ScoreMode = scoreMode;
+            }
+
+            public override Query Query
+            {
+                get { return JoinQuery; }
+            }
+            
+            public override float ValueForNormalization
+            {
+                get { return ChildWeight.ValueForNormalization*JoinQuery.Boost*JoinQuery.Boost; }
+            }
+
+            public override void Normalize(float norm, float topLevelBoost)
+            {
+                ChildWeight.Normalize(norm, topLevelBoost * JoinQuery.Boost);
+            }
+
+            // NOTE: acceptDocs applies (and is checked) only in the parent document space
+            public override Scorer Scorer(AtomicReaderContext readerContext, Bits acceptDocs)
+            {
+
+                Scorer childScorer = ChildWeight.Scorer(readerContext, readerContext.AtomicReader.LiveDocs);
+                if (childScorer == null)
+                {
+                    // No matches
+                    return null;
+                }
+
+                int firstChildDoc = childScorer.NextDoc();
+                if (firstChildDoc == DocIdSetIterator.NO_MORE_DOCS)
+                {
+                    // No matches
+                    return null;
+                }
+
+                // NOTE: we cannot pass acceptDocs here because this
+                // will (most likely, justifiably) cause the filter to
+                // not return a FixedBitSet but rather a
+                // BitsFilteredDocIdSet.  Instead, we filter by
+                // acceptDocs when we score:
+                DocIdSet parents = ParentsFilter.GetDocIdSet(readerContext, null);
+
+                if (parents == null)
+                {
+                    // No matches
+                    return null;
+                }
+                if (!(parents is FixedBitSet))
+                {
+                    throw new InvalidOperationException("parentFilter must return FixedBitSet; got " + parents);
+                }
+
+                return new BlockJoinScorer(this, childScorer, (FixedBitSet)parents, firstChildDoc, ScoreMode, acceptDocs);
+            }
+            
+            public override Explanation Explain(AtomicReaderContext context, int doc)
+            {
+                BlockJoinScorer scorer = (BlockJoinScorer)Scorer(context, context.AtomicReader.LiveDocs);
+                if (scorer != null && scorer.Advance(doc) == doc)
+                {
+                    return scorer.Explain(context.DocBase);
+                }
+                return new ComplexExplanation(false, 0.0f, "Not a match");
+            }
+
+            public override bool ScoresDocsOutOfOrder()
+            {
+                return false;
+            }
+        }
+
+        internal class BlockJoinScorer : Scorer
+        {
+            private readonly Scorer _childScorer;
+            private readonly FixedBitSet _parentBits;
+            private readonly ScoreMode _scoreMode;
+            private readonly Bits _acceptDocs;
+            private int _parentDocRenamed = -1;
+            private int _prevParentDoc;
+            private float _parentScore;
+            private int _parentFreq;
+            private int _nextChildDoc;
+            private int[] _pendingChildDocs;
+            private float[] _pendingChildScores;
+            private int _childDocUpto;
+
+            public BlockJoinScorer(Weight weight, Scorer childScorer, FixedBitSet parentBits, int firstChildDoc, ScoreMode scoreMode, Bits acceptDocs) : base(weight)
+            {
+                //System.out.println("Q.init firstChildDoc=" + firstChildDoc);
+                _parentBits = parentBits;
+                _childScorer = childScorer;
+                _scoreMode = scoreMode;
+                _acceptDocs = acceptDocs;
+                _nextChildDoc = firstChildDoc;
+            }
+
+            public override ICollection<ChildScorer> Children
+            {
+                get { return Collections.Singleton(new ChildScorer(_childScorer, "BLOCK_JOIN")); }
+            }
+
+            internal virtual int ChildCount
+            {
+                get { return _childDocUpto; }
+            }
+
+            internal virtual int ParentDoc
+            {
+                get { return _parentDocRenamed; }
+            }
+
+            internal virtual int[] SwapChildDocs(int[] other)
+            {
+                int[] ret = _pendingChildDocs;
+                if (other == null)
+                {
+                    _pendingChildDocs = new int[5];
+                }
+                else
+                {
+                    _pendingChildDocs = other;
+                }
+                return ret;
+            }
+
+            internal virtual float[] SwapChildScores(float[] other)
+            {
+                if (_scoreMode == ScoreMode.None)
+                {
+                    throw new InvalidOperationException("ScoreMode is None; you must pass trackScores=false to ToParentBlockJoinCollector");
+                }
+                float[] ret = _pendingChildScores;
+                if (other == null)
+                {
+                    _pendingChildScores = new float[5];
+                }
+                else
+                {
+                    _pendingChildScores = other;
+                }
+                return ret;
+            }
+            
+            public override int NextDoc()
+            {
+                //System.out.println("Q.nextDoc() nextChildDoc=" + nextChildDoc);
+                // Loop until we hit a parentDoc that's accepted
+                while (true)
+                {
+                    if (_nextChildDoc == NO_MORE_DOCS)
+                    {
+                        //System.out.println("  end");
+                        return _parentDocRenamed = NO_MORE_DOCS;
+                    }
+
+                    // Gather all children sharing the same parent as
+                    // nextChildDoc
+
+                    _parentDocRenamed = _parentBits.NextSetBit(_nextChildDoc);
+
+                    // Parent & child docs are supposed to be
+                    // orthogonal:
+                    if (_nextChildDoc == _parentDocRenamed)
+                    {
+                        throw new InvalidOperationException("child query must only match non-parent docs, but parent docID=" + _nextChildDoc + " matched childScorer=" + _childScorer.GetType());
+                    }
+
+                    //System.out.println("  parentDoc=" + parentDoc);
+                    Debug.Assert(_parentDocRenamed != -1);
+
+                    //System.out.println("  nextChildDoc=" + nextChildDoc);
+                    if (_acceptDocs != null && !_acceptDocs.Get(_parentDocRenamed))
+                    {
+                        // Parent doc not accepted; skip child docs until
+                        // we hit a new parent doc:
+                        do
+                        {
+                            _nextChildDoc = _childScorer.NextDoc();
+                        } while (_nextChildDoc < _parentDocRenamed);
+
+                        // Parent & child docs are supposed to be
+                        // orthogonal:
+                        if (_nextChildDoc == _parentDocRenamed)
+                        {
+                            throw new InvalidOperationException("child query must only match non-parent docs, but parent docID=" + _nextChildDoc + " matched childScorer=" + _childScorer.GetType());
+                        }
+
+                        continue;
+                    }
+
+                    float totalScore = 0;
+                    float maxScore = float.NegativeInfinity;
+
+                    _childDocUpto = 0;
+                    _parentFreq = 0;
+                    do
+                    {
+                        //System.out.println("  c=" + nextChildDoc);
+                        if (_pendingChildDocs != null && _pendingChildDocs.Length == _childDocUpto)
+                        {
+                            _pendingChildDocs = ArrayUtil.Grow(_pendingChildDocs);
+                        }
+                        if (_pendingChildScores != null && _scoreMode != ScoreMode.None && _pendingChildScores.Length == _childDocUpto)
+                        {
+                            _pendingChildScores = ArrayUtil.Grow(_pendingChildScores);
+                        }
+                        if (_pendingChildDocs != null)
+                        {
+                            _pendingChildDocs[_childDocUpto] = _nextChildDoc;
+                        }
+                        if (_scoreMode != ScoreMode.None)
+                        {
+                            // TODO: specialize this into dedicated classes per-scoreMode
+                            float childScore = _childScorer.Score();
+                            int childFreq = _childScorer.Freq();
+                            if (_pendingChildScores != null)
+                            {
+                                _pendingChildScores[_childDocUpto] = childScore;
+                            }
+                            maxScore = Math.Max(childScore, maxScore);
+                            totalScore += childScore;
+                            _parentFreq += childFreq;
+                        }
+                        _childDocUpto++;
+                        _nextChildDoc = _childScorer.NextDoc();
+                    } while (_nextChildDoc < _parentDocRenamed);
+
+                    // Parent & child docs are supposed to be
+                    // orthogonal:
+                    if (_nextChildDoc == _parentDocRenamed)
+                    {
+                        throw new InvalidOperationException("child query must only match non-parent docs, but parent docID=" + _nextChildDoc + " matched childScorer=" + _childScorer.GetType());
+                    }
+
+                    switch (_scoreMode)
+                    {
+                        case ScoreMode.Avg:
+                            _parentScore = totalScore / _childDocUpto;
+                            break;
+                        case ScoreMode.Max:
+                            _parentScore = maxScore;
+                            break;
+                        case ScoreMode.Total:
+                            _parentScore = totalScore;
+                            break;
+                        case ScoreMode.None:
+                            break;
+                    }
+
+                    //System.out.println("  return parentDoc=" + parentDoc + " childDocUpto=" + childDocUpto);
+                    return _parentDocRenamed;
+                }
+            }
+
+            public override int DocID()
+            {
+                return _parentDocRenamed;
+            }
+
+            //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()
+            {
+                return _parentScore;
+            }
+
+            public override int Freq()
+            {
+                return _parentFreq;
+            }
+            
+            public override int Advance(int parentTarget)
+            {
+
+                //System.out.println("Q.advance parentTarget=" + parentTarget);
+                if (parentTarget == NO_MORE_DOCS)
+                {
+                    return _parentDocRenamed = NO_MORE_DOCS;
+                }
+
+                if (parentTarget == 0)
+                {
+                    // Callers should only be passing in a docID from
+                    // the parent space, so this means this parent
+                    // has no children (it got docID 0), so it cannot
+                    // possibly match.  We must handle this case
+                    // separately otherwise we pass invalid -1 to
+                    // prevSetBit below:
+                    return NextDoc();
+                }
+
+                _prevParentDoc = _parentBits.PrevSetBit(parentTarget - 1);
+
+                //System.out.println("  rolled back to prevParentDoc=" + prevParentDoc + " vs parentDoc=" + parentDoc);
+                Debug.Assert(_prevParentDoc >= _parentDocRenamed);
+                if (_prevParentDoc > _nextChildDoc)
+                {
+                    _nextChildDoc = _childScorer.Advance(_prevParentDoc);
+                    // System.out.println("  childScorer advanced to child docID=" + nextChildDoc);
+                    //} else {
+                    //System.out.println("  skip childScorer advance");
+                }
+
+                // Parent & child docs are supposed to be orthogonal:
+                if (_nextChildDoc == _prevParentDoc)
+                {
+                    throw new InvalidOperationException("child query must only match non-parent docs, but parent docID=" + _nextChildDoc + " matched childScorer=" + _childScorer.GetType());
+                }
+
+                int nd = NextDoc();
+                //System.out.println("  return nextParentDoc=" + nd);
+                return nd;
+            }
+            
+            public virtual Explanation Explain(int docBase)
+            {
+                int start = docBase + _prevParentDoc + 1; // +1 b/c prevParentDoc is previous parent doc
+                int end = docBase + _parentDocRenamed - 1; // -1 b/c parentDoc is parent doc
+                return new ComplexExplanation(true, Score(), string.Format("Score based on child doc range from {0} to {1}", start, end));
+            }
+
+            public override long Cost()
+            {
+                return _childScorer.Cost();
+            }
+
+            /// <summary>
+            /// Instructs this scorer to keep track of the child docIds and score ids for retrieval purposes.
+            /// </summary>
+            public virtual void TrackPendingChildHits()
+            {
+                _pendingChildDocs = new int[5];
+                if (_scoreMode != ScoreMode.None)
+                {
+                    _pendingChildScores = new float[5];
+                }
+            }
+        }
+
+        public override void ExtractTerms(ISet<Term> terms)
+        {
+            _childQuery.ExtractTerms(terms);
+        }
+        
+        public override Query Rewrite(IndexReader reader)
+        {
+            Query childRewrite = _childQuery.Rewrite(reader);
+            if (childRewrite != _childQuery)
+            {
+                Query rewritten = new ToParentBlockJoinQuery(_origChildQuery, childRewrite, _parentsFilter, _scoreMode);
+                rewritten.Boost = Boost;
+                return rewritten;
+            }
+            return this;
+        }
+
+        public override string ToString(string field)
+        {
+            return "ToParentBlockJoinQuery (" + _childQuery + ")";
+        }
+
+        protected bool Equals(ToParentBlockJoinQuery other)
+        {
+            return base.Equals(other) && 
+                Equals(_parentsFilter, other._parentsFilter) && 
+                _scoreMode == other._scoreMode && 
+                Equals(_origChildQuery, other._origChildQuery);
+        }
+
+        public override bool Equals(object obj)
+        {
+            if (ReferenceEquals(null, obj)) return false;
+            if (ReferenceEquals(this, obj)) return true;
+            if (obj.GetType() != GetType()) return false;
+            return Equals((ToParentBlockJoinQuery) obj);
+        }
+
+        public override int GetHashCode()
+        {
+            unchecked
+            {
+                int hashCode = base.GetHashCode();
+                hashCode = (hashCode*397) ^ (_parentsFilter != null ? _parentsFilter.GetHashCode() : 0);
+                hashCode = (hashCode*397) ^ (int) _scoreMode;
+                hashCode = (hashCode*397) ^ (_origChildQuery != null ? _origChildQuery.GetHashCode() : 0);
+                return hashCode;
+            }
+        }
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/lucenenet/blob/47f20b9a/Lucene.Net.Tests.Join/Lucene.Net.Tests.Join.csproj
----------------------------------------------------------------------
diff --git a/Lucene.Net.Tests.Join/Lucene.Net.Tests.Join.csproj b/Lucene.Net.Tests.Join/Lucene.Net.Tests.Join.csproj
new file mode 100644
index 0000000..30d5a7b
--- /dev/null
+++ b/Lucene.Net.Tests.Join/Lucene.Net.Tests.Join.csproj
@@ -0,0 +1,64 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
+  <PropertyGroup>
+    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+    <ProjectGuid>{1866F8E4-ABF5-4CBE-B23B-4BADF6CD20DC}</ProjectGuid>
+    <OutputType>Library</OutputType>
+    <AppDesignerFolder>Properties</AppDesignerFolder>
+    <RootNamespace>Lucene.Net.Tests.Join</RootNamespace>
+    <AssemblyName>Lucene.Net.Tests.Join</AssemblyName>
+    <TargetFrameworkVersion>v4.5.1</TargetFrameworkVersion>
+    <FileAlignment>512</FileAlignment>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+    <DebugSymbols>true</DebugSymbols>
+    <DebugType>full</DebugType>
+    <Optimize>false</Optimize>
+    <OutputPath>bin\Debug\</OutputPath>
+    <DefineConstants>DEBUG;TRACE</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+    <DebugType>pdbonly</DebugType>
+    <Optimize>true</Optimize>
+    <OutputPath>bin\Release\</OutputPath>
+    <DefineConstants>TRACE</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+  </PropertyGroup>
+  <ItemGroup>
+    <Reference Include="System" />
+    <Reference Include="System.Core" />
+    <Reference Include="System.Xml.Linq" />
+    <Reference Include="System.Data.DataSetExtensions" />
+    <Reference Include="Microsoft.CSharp" />
+    <Reference Include="System.Data" />
+    <Reference Include="System.Net.Http" />
+    <Reference Include="System.Xml" />
+  </ItemGroup>
+  <ItemGroup>
+    <Compile Include="Properties\AssemblyInfo.cs" />
+    <Compile Include="TestBlockJoin.cs" />
+  </ItemGroup>
+  <ItemGroup>
+    <ProjectReference Include="..\Lucene.Net.Join\Lucene.Net.Join.csproj">
+      <Project>{e8a339c7-fcf6-4a72-8586-56d8961d7b99}</Project>
+      <Name>Lucene.Net.Join</Name>
+    </ProjectReference>
+    <ProjectReference Include="..\src\Lucene.Net.Core\Lucene.Net.csproj">
+      <Project>{5d4ad9be-1ffb-41ab-9943-25737971bf57}</Project>
+      <Name>Lucene.Net</Name>
+    </ProjectReference>
+  </ItemGroup>
+  <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
+  <!-- To modify your build process, add your task inside one of the targets below and uncomment it. 
+       Other similar extension points exist, see Microsoft.Common.targets.
+  <Target Name="BeforeBuild">
+  </Target>
+  <Target Name="AfterBuild">
+  </Target>
+  -->
+</Project>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/lucenenet/blob/47f20b9a/Lucene.Net.Tests.Join/Properties/AssemblyInfo.cs
----------------------------------------------------------------------
diff --git a/Lucene.Net.Tests.Join/Properties/AssemblyInfo.cs b/Lucene.Net.Tests.Join/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000..cd01ea7
--- /dev/null
+++ b/Lucene.Net.Tests.Join/Properties/AssemblyInfo.cs
@@ -0,0 +1,36 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following 
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("Lucene.Net.Tests.Join")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("Lucene.Net.Tests.Join")]
+[assembly: AssemblyCopyright("Copyright ©  2015")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible 
+// to COM components.  If you need to access a type in this assembly from 
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("1866f8e4-abf5-4cbe-b23b-4badf6cd20dc")]
+
+// Version information for an assembly consists of the following four values:
+//
+//      Major Version
+//      Minor Version 
+//      Build Number
+//      Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers 
+// by using the '*' as shown below:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]

http://git-wip-us.apache.org/repos/asf/lucenenet/blob/47f20b9a/Lucene.Net.Tests.Join/TestBlockJoin.cs
----------------------------------------------------------------------
diff --git a/Lucene.Net.Tests.Join/TestBlockJoin.cs b/Lucene.Net.Tests.Join/TestBlockJoin.cs
new file mode 100644
index 0000000..1278782
--- /dev/null
+++ b/Lucene.Net.Tests.Join/TestBlockJoin.cs
@@ -0,0 +1,7 @@
+namespace Lucene.Net.Tests.Join
+{
+    public class TestBlockJoin
+    {
+         
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/lucenenet/blob/47f20b9a/Lucene.Net.sln
----------------------------------------------------------------------
diff --git a/Lucene.Net.sln b/Lucene.Net.sln
index 34c4804..d76fe0c 100644
--- a/Lucene.Net.sln
+++ b/Lucene.Net.sln
@@ -40,6 +40,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Lucene.Net.Codecs.Tests", "
 EndProject
 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Lucene.Net.Join", "Lucene.Net.Join\Lucene.Net.Join.csproj", "{E8A339C7-FCF6-4A72-8586-56D8961D7B99}"
 EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Lucene.Net.Grouping", "Lucene.Net.Grouping\Lucene.Net.Grouping.csproj", "{02BAB603-067D-48B1-AEDD-316849652568}"
+EndProject
 Global
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
 		Debug|Any CPU = Debug|Any CPU
@@ -193,6 +195,18 @@ Global
 		{E8A339C7-FCF6-4A72-8586-56D8961D7B99}.Release|Mixed Platforms.Build.0 = Release|Any CPU
 		{E8A339C7-FCF6-4A72-8586-56D8961D7B99}.Release|x86.ActiveCfg = Release|Any CPU
 		{E8A339C7-FCF6-4A72-8586-56D8961D7B99}.Release|x86.Build.0 = Release|Any CPU
+		{02BAB603-067D-48B1-AEDD-316849652568}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{02BAB603-067D-48B1-AEDD-316849652568}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{02BAB603-067D-48B1-AEDD-316849652568}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
+		{02BAB603-067D-48B1-AEDD-316849652568}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
+		{02BAB603-067D-48B1-AEDD-316849652568}.Debug|x86.ActiveCfg = Debug|Any CPU
+		{02BAB603-067D-48B1-AEDD-316849652568}.Debug|x86.Build.0 = Debug|Any CPU
+		{02BAB603-067D-48B1-AEDD-316849652568}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{02BAB603-067D-48B1-AEDD-316849652568}.Release|Any CPU.Build.0 = Release|Any CPU
+		{02BAB603-067D-48B1-AEDD-316849652568}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
+		{02BAB603-067D-48B1-AEDD-316849652568}.Release|Mixed Platforms.Build.0 = Release|Any CPU
+		{02BAB603-067D-48B1-AEDD-316849652568}.Release|x86.ActiveCfg = Release|Any CPU
+		{02BAB603-067D-48B1-AEDD-316849652568}.Release|x86.Build.0 = Release|Any CPU
 	EndGlobalSection
 	GlobalSection(SolutionProperties) = preSolution
 		HideSolutionNode = FALSE

http://git-wip-us.apache.org/repos/asf/lucenenet/blob/47f20b9a/src/Lucene.Net.Core/Search/FieldValueHitQueue.cs
----------------------------------------------------------------------
diff --git a/src/Lucene.Net.Core/Search/FieldValueHitQueue.cs b/src/Lucene.Net.Core/Search/FieldValueHitQueue.cs
index 3249110..df19e86 100644
--- a/src/Lucene.Net.Core/Search/FieldValueHitQueue.cs
+++ b/src/Lucene.Net.Core/Search/FieldValueHitQueue.cs
@@ -28,9 +28,9 @@ namespace Lucene.Net.Search
         // had to change from internal to public, due to public accessability of FieldValueHitQueue
         public class Entry : ScoreDoc
         {
-            internal int Slot;
+            public int Slot;
 
-            internal Entry(int slot, int doc, float score)
+            public Entry(int slot, int doc, float score)
                 : base(doc, score)
             {
                 this.Slot = slot;