/** * 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. */ package org.apache.solr.search; import org.apache.solr.common.SolrException; import org.apache.lucene.util.OpenBitSet; import org.apache.lucene.search.DocIdSet; import org.apache.lucene.search.Filter; import org.apache.lucene.search.DocIdSetIterator; import org.apache.lucene.index.IndexReader; import java.io.IOException; /** * <code>DocSet</code> represents an unordered set of Lucene Document Ids. * * <p> * WARNING: Any DocSet returned from SolrIndexSearcher should <b>not</b> be modified as it may have been retrieved from * a cache and could be shared. * </p> * * @version $Id: DocSet.java 1065312 2011-01-30 16:08:25Z rmuir $ * @since solr 0.9 */ public interface DocSet /* extends Collection<Integer> */ { /** * Adds the specified document if it is not currently in the DocSet * (optional operation). * * @see #addUnique * @throws SolrException if the implementation does not allow modifications */ public void add(int doc); /** * Adds a document the caller knows is not currently in the DocSet * (optional operation). * * <p> * This method may be faster then <code>add(doc)</code> in some * implementaions provided the caller is certain of the precondition. * </p> * * @see #add * @throws SolrException if the implementation does not allow modifications */ public void addUnique(int doc); /** * Returns the number of documents in the set. */ public int size(); /** * Returns true if a document is in the DocSet. */ public boolean exists(int docid); /** * Returns an iterator that may be used to iterate over all of the documents in the set. * * <p> * The order of the documents returned by this iterator is * non-deterministic, and any scoring information is meaningless * </p> */ public DocIterator iterator(); /** * Returns a BitSet view of the DocSet. Any changes to this BitSet <b>may</b> * be reflected in the DocSet, hence if the DocSet is shared or was returned from * a SolrIndexSearcher method, it's not safe to modify the BitSet. * * @return * An OpenBitSet with the bit number of every docid set in the set. * * @deprecated Use {@link #iterator()} to access all docs instead. */ @Deprecated public OpenBitSet getBits(); /** * Returns the approximate amount of memory taken by this DocSet. * This is only an approximation and doesn't take into account java object overhead. * * @return * the approximate memory consumption in bytes */ public long memSize(); /** * Returns the intersection of this set with another set. Neither set is modified - a new DocSet is * created and returned. * @return a DocSet representing the intersection */ public DocSet intersection(DocSet other); /** * Returns the number of documents of the intersection of this set with another set. * May be more efficient than actually creating the intersection and then getting it's size. */ public int intersectionSize(DocSet other); /** * Returns the union of this set with another set. Neither set is modified - a new DocSet is * created and returned. * @return a DocSet representing the union */ public DocSet union(DocSet other); /** * Returns the number of documents of the union of this set with another set. * May be more efficient than actually creating the union and then getting it's size. */ public int unionSize(DocSet other); /** * Returns the documents in this set that are not in the other set. Neither set is modified - a new DocSet is * created and returned. * @return a DocSet representing this AND NOT other */ public DocSet andNot(DocSet other); /** * Returns the number of documents in this set that are not in the other set. */ public int andNotSize(DocSet other); /** * Returns a Filter for use in Lucene search methods, assuming this DocSet * was generated from the top-level MultiReader that the Lucene search * methods will be invoked with. */ public Filter getTopFilter(); } /** A base class that may be usefull for implementing DocSets */ abstract class DocSetBase implements DocSet { // Not implemented efficiently... for testing purposes only @Override public boolean equals(Object obj) { if (!(obj instanceof DocSet)) return false; DocSet other = (DocSet)obj; if (this.size() != other.size()) return false; if (this instanceof DocList && other instanceof DocList) { // compare ordering DocIterator i1=this.iterator(); DocIterator i2=other.iterator(); while(i1.hasNext() && i2.hasNext()) { if (i1.nextDoc() != i2.nextDoc()) return false; } return true; // don't compare matches } // if (this.size() != other.size()) return false; return this.getBits().equals(other.getBits()); } /** * @throws SolrException Base implementation does not allow modifications */ public void add(int doc) { throw new SolrException( SolrException.ErrorCode.SERVER_ERROR,"Unsupported Operation"); } /** * @throws SolrException Base implementation does not allow modifications */ public void addUnique(int doc) { throw new SolrException( SolrException.ErrorCode.SERVER_ERROR,"Unsupported Operation"); } /** * Inefficient base implementation. * * @see BitDocSet#getBits */ public OpenBitSet getBits() { OpenBitSet bits = new OpenBitSet(); for (DocIterator iter = iterator(); iter.hasNext();) { bits.set(iter.nextDoc()); } return bits; }; public DocSet intersection(DocSet other) { // intersection is overloaded in the smaller DocSets to be more // efficient, so dispatch off of it instead. if (!(other instanceof BitDocSet)) { return other.intersection(this); } // Default... handle with bitsets. OpenBitSet newbits = (OpenBitSet)(this.getBits().clone()); newbits.and(other.getBits()); return new BitDocSet(newbits); } public DocSet union(DocSet other) { OpenBitSet newbits = (OpenBitSet)(this.getBits().clone()); newbits.or(other.getBits()); return new BitDocSet(newbits); } public int intersectionSize(DocSet other) { // intersection is overloaded in the smaller DocSets to be more // efficient, so dispatch off of it instead. if (!(other instanceof BitDocSet)) { return other.intersectionSize(this); } // less efficient way: do the intersection then get it's size return intersection(other).size(); } public int unionSize(DocSet other) { return this.size() + other.size() - this.intersectionSize(other); } public DocSet andNot(DocSet other) { OpenBitSet newbits = (OpenBitSet)(this.getBits().clone()); newbits.andNot(other.getBits()); return new BitDocSet(newbits); } public int andNotSize(DocSet other) { return this.size() - this.intersectionSize(other); } public Filter getTopFilter() { final OpenBitSet bs = getBits(); return new Filter() { @Override public DocIdSet getDocIdSet(IndexReader reader) throws IOException { int offset = 0; SolrIndexReader r = (SolrIndexReader)reader; while (r.getParent() != null) { offset += r.getBase(); r = r.getParent(); } if (r==reader) return bs; final int base = offset; final int maxDoc = reader.maxDoc(); final int max = base + maxDoc; // one past the max doc in this segment. return new DocIdSet() { @Override public DocIdSetIterator iterator() throws IOException { return new DocIdSetIterator() { int pos=base-1; int adjustedDoc=-1; @Override public int docID() { return adjustedDoc; } @Override public int nextDoc() throws IOException { pos = bs.nextSetBit(pos+1); return adjustedDoc = (pos>=0 && pos<max) ? pos-base : NO_MORE_DOCS; } @Override public int advance(int target) throws IOException { if (target==NO_MORE_DOCS) return adjustedDoc=NO_MORE_DOCS; pos = bs.nextSetBit(target+base); return adjustedDoc = (pos>=0 && pos<max) ? pos-base : NO_MORE_DOCS; } }; } @Override public boolean isCacheable() { return true; } }; } }; } }