pylucene 3.5.0-3
[pylucene.git] / lucene-java-3.5.0 / lucene / contrib / queries / src / java / org / apache / lucene / search / ChainedFilter.java
diff --git a/lucene-java-3.5.0/lucene/contrib/queries/src/java/org/apache/lucene/search/ChainedFilter.java b/lucene-java-3.5.0/lucene/contrib/queries/src/java/org/apache/lucene/search/ChainedFilter.java
new file mode 100644 (file)
index 0000000..f06d0e2
--- /dev/null
@@ -0,0 +1,268 @@
+package org.apache.lucene.search;
+
+/**
+ * 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 java.io.IOException;
+
+import org.apache.lucene.index.IndexReader;
+import org.apache.lucene.search.DocIdSet;
+import org.apache.lucene.search.DocIdSetIterator;
+import org.apache.lucene.search.Filter;
+import org.apache.lucene.util.OpenBitSet;
+import org.apache.lucene.util.OpenBitSetDISI;
+
+/**
+ * <p>
+ * Allows multiple {@link 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.
+ * </p>
+ * <p>
+ * 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.
+ * </p>
+ *
+ */
+public class ChainedFilter extends Filter
+{
+    public static final int OR = 0;
+    public static final int AND = 1;
+    public static final int ANDNOT = 2;
+    public static final int XOR = 3;
+    /**
+     * Logical operation when none is declared. Defaults to
+     * OR.
+     */
+    public static int DEFAULT = OR;
+
+    /** The filter chain */
+    private Filter[] chain = null;
+
+    private int[] logicArray;
+
+    private int logic = -1;
+
+    /**
+     * Ctor.
+     * @param chain The chain of filters
+     */
+    public ChainedFilter(Filter[] chain)
+    {
+        this.chain = chain;
+    }
+
+    /**
+     * Ctor.
+     * @param chain The chain of filters
+     * @param logicArray Logical operations to apply between filters
+     */
+    public ChainedFilter(Filter[] chain, int[] logicArray)
+    {
+        this.chain = chain;
+        this.logicArray = logicArray;
+    }
+
+    /**
+     * Ctor.
+     * @param chain The chain of filters
+     * @param logic Logical operation to apply to ALL filters
+     */
+    public ChainedFilter(Filter[] chain, int logic)
+    {
+        this.chain = chain;
+        this.logic = logic;
+    }
+
+    /**
+     * {@link Filter#getDocIdSet}.
+     */
+    @Override
+    public DocIdSet getDocIdSet(IndexReader reader) throws IOException
+    {
+        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 getDocIdSet(reader, logic, index);
+        else if (logicArray != null)
+            return getDocIdSet(reader, logicArray, index);
+        else
+            return getDocIdSet(reader, DEFAULT, index);
+    }
+
+    private DocIdSetIterator getDISI(Filter filter, IndexReader reader)
+    throws IOException {
+        DocIdSet docIdSet = filter.getDocIdSet(reader);
+        if (docIdSet == null) {
+          return DocIdSet.EMPTY_DOCIDSET.iterator();
+        } else {
+          DocIdSetIterator iter = docIdSet.iterator();
+          if (iter == null) {
+            return DocIdSet.EMPTY_DOCIDSET.iterator();
+          } else {
+            return iter;
+          }
+        }
+    }
+
+    private OpenBitSetDISI initialResult(IndexReader reader, int logic, int[] index)
+    throws IOException
+    {
+        OpenBitSetDISI result;
+        /**
+         * First AND operation takes place against a completely false
+         * bitset and will always return zero results.
+         */
+        if (logic == AND)
+        {
+            result = new OpenBitSetDISI(getDISI(chain[index[0]], reader), reader.maxDoc());
+            ++index[0];
+        }
+        else if (logic == ANDNOT)
+        {
+            result = new OpenBitSetDISI(getDISI(chain[index[0]], reader), reader.maxDoc());
+            result.flip(0,reader.maxDoc()); // NOTE: may set bits for deleted docs.
+            ++index[0];
+        }
+        else
+        {
+            result = new OpenBitSetDISI(reader.maxDoc());
+        }
+        return result;
+    }
+
+    /** Provide a SortedVIntList when it is definitely
+     *  smaller than an OpenBitSet
+     *  @deprecated Either use CachingWrapperFilter, or
+     *  switch to a different DocIdSet implementation yourself.
+     *  This method will be removed in Lucene 4.0 
+     **/
+    @Deprecated
+    protected final DocIdSet finalResult(OpenBitSetDISI result, int maxDocs) {
+        return result;
+    }
+        
+
+    /**
+     * Delegates to each filter in the chain.
+     * @param reader IndexReader
+     * @param logic Logical operation
+     * @return DocIdSet
+     */
+    private DocIdSet getDocIdSet(IndexReader reader, int logic, int[] index)
+    throws IOException
+    {
+        OpenBitSetDISI result = initialResult(reader, logic, index);
+        for (; index[0] < chain.length; index[0]++)
+        {
+            doChain(result, logic, chain[index[0]].getDocIdSet(reader));
+        }
+        return finalResult(result, reader.maxDoc());
+    }
+
+    /**
+     * Delegates to each filter in the chain.
+     * @param reader IndexReader
+     * @param logic Logical operation
+     * @return DocIdSet
+     */
+    private DocIdSet getDocIdSet(IndexReader reader, int[] logic, int[] index)
+    throws IOException
+    {
+        if (logic.length != chain.length)
+            throw new IllegalArgumentException("Invalid number of elements in logic array");
+
+        OpenBitSetDISI result = initialResult(reader, logic[0], index);
+        for (; index[0] < chain.length; index[0]++)
+        {
+            doChain(result, logic[index[0]], chain[index[0]].getDocIdSet(reader));
+        }
+        return finalResult(result, reader.maxDoc());
+    }
+
+    @Override
+    public String toString()
+    {
+        StringBuilder sb = new StringBuilder();
+        sb.append("ChainedFilter: [");
+        for (int i = 0; i < chain.length; i++)
+        {
+            sb.append(chain[i]);
+            sb.append(' ');
+        }
+        sb.append(']');
+        return sb.toString();
+    }
+
+    private void doChain(OpenBitSetDISI result, int logic, DocIdSet dis)
+    throws IOException {
+      
+      if (dis instanceof OpenBitSet) {
+        // optimized case for OpenBitSets
+        switch (logic) {
+            case OR:
+                result.or((OpenBitSet) dis);
+                break;
+            case AND:
+                result.and((OpenBitSet) dis);
+                break;
+            case ANDNOT:
+                result.andNot((OpenBitSet) dis);
+                break;
+            case XOR:
+                result.xor((OpenBitSet) dis);
+                break;
+            default:
+                doChain(result, DEFAULT, dis);
+                break;
+        }
+      } else {
+        DocIdSetIterator disi;
+        if (dis == null) {
+          disi = DocIdSet.EMPTY_DOCIDSET.iterator();
+        } else {
+          disi = dis.iterator();
+          if (disi == null) {
+            disi = DocIdSet.EMPTY_DOCIDSET.iterator();            
+          }
+        }
+
+        switch (logic) {
+            case OR:
+                result.inPlaceOr(disi);
+                break;
+            case AND:
+                result.inPlaceAnd(disi);
+                break;
+            case ANDNOT:
+                result.inPlaceNot(disi);
+                break;
+            case XOR:
+                result.inPlaceXor(disi);
+                break;
+            default:
+                doChain(result, DEFAULT, dis);
+                break;
+        }
+      }
+    }
+
+}