1 package org.apache.lucene.search;
4 * Licensed to the Apache Software Foundation (ASF) under one or more
5 * contributor license agreements. See the NOTICE file distributed with
6 * this work for additional information regarding copyright ownership.
7 * The ASF licenses this file to You under the Apache License, Version 2.0
8 * (the "License"); you may not use this file except in compliance with
9 * the License. You may obtain a copy of the License at
11 * http://www.apache.org/licenses/LICENSE-2.0
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
20 import java.io.IOException;
22 import org.apache.lucene.index.IndexReader;
23 import org.apache.lucene.search.DocIdSet;
24 import org.apache.lucene.search.DocIdSetIterator;
25 import org.apache.lucene.search.Filter;
26 import org.apache.lucene.util.OpenBitSet;
27 import org.apache.lucene.util.OpenBitSetDISI;
31 * Allows multiple {@link Filter}s to be chained.
32 * Logical operations such as <b>NOT</b> and <b>XOR</b>
33 * are applied between filters. One operation can be used
34 * for all filters, or a specific operation can be declared
38 * Order in which filters are called depends on
39 * the position of the filter in the chain. It's probably
40 * more efficient to place the most restrictive filters
41 * /least computationally-intensive filters first.
45 public class ChainedFilter extends Filter
47 public static final int OR = 0;
48 public static final int AND = 1;
49 public static final int ANDNOT = 2;
50 public static final int XOR = 3;
52 * Logical operation when none is declared. Defaults to
55 public static int DEFAULT = OR;
57 /** The filter chain */
58 private Filter[] chain = null;
60 private int[] logicArray;
62 private int logic = -1;
66 * @param chain The chain of filters
68 public ChainedFilter(Filter[] chain)
75 * @param chain The chain of filters
76 * @param logicArray Logical operations to apply between filters
78 public ChainedFilter(Filter[] chain, int[] logicArray)
81 this.logicArray = logicArray;
86 * @param chain The chain of filters
87 * @param logic Logical operation to apply to ALL filters
89 public ChainedFilter(Filter[] chain, int logic)
96 * {@link Filter#getDocIdSet}.
99 public DocIdSet getDocIdSet(IndexReader reader) throws IOException
101 int[] index = new int[1]; // use array as reference to modifiable int;
102 index[0] = 0; // an object attribute would not be thread safe.
104 return getDocIdSet(reader, logic, index);
105 else if (logicArray != null)
106 return getDocIdSet(reader, logicArray, index);
108 return getDocIdSet(reader, DEFAULT, index);
111 private DocIdSetIterator getDISI(Filter filter, IndexReader reader)
113 DocIdSet docIdSet = filter.getDocIdSet(reader);
114 if (docIdSet == null) {
115 return DocIdSet.EMPTY_DOCIDSET.iterator();
117 DocIdSetIterator iter = docIdSet.iterator();
119 return DocIdSet.EMPTY_DOCIDSET.iterator();
126 private OpenBitSetDISI initialResult(IndexReader reader, int logic, int[] index)
129 OpenBitSetDISI result;
131 * First AND operation takes place against a completely false
132 * bitset and will always return zero results.
136 result = new OpenBitSetDISI(getDISI(chain[index[0]], reader), reader.maxDoc());
139 else if (logic == ANDNOT)
141 result = new OpenBitSetDISI(getDISI(chain[index[0]], reader), reader.maxDoc());
142 result.flip(0,reader.maxDoc()); // NOTE: may set bits for deleted docs.
147 result = new OpenBitSetDISI(reader.maxDoc());
152 /** Provide a SortedVIntList when it is definitely
153 * smaller than an OpenBitSet
154 * @deprecated Either use CachingWrapperFilter, or
155 * switch to a different DocIdSet implementation yourself.
156 * This method will be removed in Lucene 4.0
159 protected final DocIdSet finalResult(OpenBitSetDISI result, int maxDocs) {
165 * Delegates to each filter in the chain.
166 * @param reader IndexReader
167 * @param logic Logical operation
170 private DocIdSet getDocIdSet(IndexReader reader, int logic, int[] index)
173 OpenBitSetDISI result = initialResult(reader, logic, index);
174 for (; index[0] < chain.length; index[0]++)
176 doChain(result, logic, chain[index[0]].getDocIdSet(reader));
178 return finalResult(result, reader.maxDoc());
182 * Delegates to each filter in the chain.
183 * @param reader IndexReader
184 * @param logic Logical operation
187 private DocIdSet getDocIdSet(IndexReader reader, int[] logic, int[] index)
190 if (logic.length != chain.length)
191 throw new IllegalArgumentException("Invalid number of elements in logic array");
193 OpenBitSetDISI result = initialResult(reader, logic[0], index);
194 for (; index[0] < chain.length; index[0]++)
196 doChain(result, logic[index[0]], chain[index[0]].getDocIdSet(reader));
198 return finalResult(result, reader.maxDoc());
202 public String toString()
204 StringBuilder sb = new StringBuilder();
205 sb.append("ChainedFilter: [");
206 for (int i = 0; i < chain.length; i++)
212 return sb.toString();
215 private void doChain(OpenBitSetDISI result, int logic, DocIdSet dis)
218 if (dis instanceof OpenBitSet) {
219 // optimized case for OpenBitSets
222 result.or((OpenBitSet) dis);
225 result.and((OpenBitSet) dis);
228 result.andNot((OpenBitSet) dis);
231 result.xor((OpenBitSet) dis);
234 doChain(result, DEFAULT, dis);
238 DocIdSetIterator disi;
240 disi = DocIdSet.EMPTY_DOCIDSET.iterator();
242 disi = dis.iterator();
244 disi = DocIdSet.EMPTY_DOCIDSET.iterator();
250 result.inPlaceOr(disi);
253 result.inPlaceAnd(disi);
256 result.inPlaceNot(disi);
259 result.inPlaceXor(disi);
262 doChain(result, DEFAULT, dis);