Index: modules/join/src/java/org/apache/lucene/search/join/JoinUtil.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- modules/join/src/java/org/apache/lucene/search/join/JoinUtil.java	(revision )
+++ modules/join/src/java/org/apache/lucene/search/join/JoinUtil.java	(revision )
@@ -0,0 +1,110 @@
+package org.apache.lucene.search.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.
+ */
+
+import org.apache.lucene.index.IndexReader;
+import org.apache.lucene.search.*;
+import org.apache.lucene.util.*;
+
+import java.io.IOException;
+import java.util.*;
+
+/**
+ * @author Martijn van Groningen
+ */
+public class JoinUtil {
+
+  public static FixedBitSet join(String fromField, String toField, Query query, IndexSearcher fromSearcher, IndexSearcher toSearcher) throws IOException {
+    final FixedBitSet queryHits = new FixedBitSet(fromSearcher.getIndexReader().maxDoc());
+    fromSearcher.search(query, new Collector() {
+
+      private int docBase;
+      
+      public void collect(int doc) throws IOException {
+        queryHits.set(docBase + doc);
+      }
+
+      public void setNextReader(IndexReader.AtomicReaderContext context) throws IOException {
+        this.docBase = context.docBase;
+      }
+
+      public void setScorer(Scorer scorer) throws IOException {}
+      public boolean acceptsDocsOutOfOrder() {return true;}
+    });
+    return join(fromField, toField, queryHits, fromSearcher, toSearcher);
+  }
+  
+  public static FixedBitSet join(final String fromField, final String toField, final FixedBitSet hits, IndexSearcher fromSearcher, IndexSearcher toSearcher) throws IOException {
+    final FixedBitSet joinResult = new FixedBitSet(toSearcher.getIndexReader().maxDoc());
+    final Set<BytesRef> fromTerms = new HashSet<BytesRef>();
+    
+    fromSearcher.search(new MatchAllDocsQuery(), new Collector() {
+
+      private int docBase;
+      private FieldCache.DocTerms fromDocTerms;
+      
+      public void collect(int doc) throws IOException {
+        if (hits.get(doc + docBase)) {
+          BytesRef term = fromDocTerms.getTerm(doc, new BytesRef());
+          fromTerms.add(term);
+        }
+      }
+
+      public void setNextReader(IndexReader.AtomicReaderContext context) throws IOException {
+        docBase = context.docBase;
+        fromDocTerms = FieldCache.DEFAULT.getTerms(context.reader, fromField);
+      }
+
+      public boolean acceptsDocsOutOfOrder() {return true;}
+      public void setScorer(Scorer scorer) throws IOException {}
+    });
+
+    toSearcher.search(new MatchAllDocsQuery(), new Collector() {
+
+      private final SentinelIntSet toOrds = new SentinelIntSet(fromTerms.size(), -1);
+      private final BytesRef spare = new BytesRef();
+
+      private FieldCache.DocTermsIndex docTermsIndex;
+      private int docBase;
+
+      public void collect(int doc) throws IOException {
+        if (toOrds.exists(docTermsIndex.getOrd(doc))) {
+          joinResult.set(doc + docBase);
+        }
+      }
+
+      public void setNextReader(IndexReader.AtomicReaderContext context) throws IOException {
+        docTermsIndex = FieldCache.DEFAULT.getTermsIndex(context.reader, toField);
+        docBase = context.docBase;
+        toOrds.clear();
+        for (BytesRef fromTerm : fromTerms) {
+          int ord  = docTermsIndex.binarySearchLookup(fromTerm, spare);
+          if (ord > 0) {
+            toOrds.put(ord);
+          }
+        }
+      }
+
+      public boolean acceptsDocsOutOfOrder() {return true;}
+      public void setScorer(Scorer scorer) throws IOException {}
+    });
+
+    return joinResult;
+  }
+  
+}
