1 package org.apache.lucene.facet.enhancements.association;
3 import java.io.IOException;
5 import org.apache.lucene.index.IndexReader;
6 import org.apache.lucene.index.Term;
8 import org.apache.lucene.facet.index.params.CategoryListParams;
9 import org.apache.lucene.facet.search.PayloadIntDecodingIterator;
10 import org.apache.lucene.util.collections.IntIterator;
11 import org.apache.lucene.util.collections.IntToIntMap;
12 import org.apache.lucene.util.encoding.SimpleIntDecoder;
15 * Licensed to the Apache Software Foundation (ASF) under one or more
16 * contributor license agreements. See the NOTICE file distributed with
17 * this work for additional information regarding copyright ownership.
18 * The ASF licenses this file to You under the Apache License, Version 2.0
19 * (the "License"); you may not use this file except in compliance with
20 * the License. You may obtain a copy of the License at
22 * http://www.apache.org/licenses/LICENSE-2.0
24 * Unless required by applicable law or agreed to in writing, software
25 * distributed under the License is distributed on an "AS IS" BASIS,
26 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
27 * See the License for the specific language governing permissions and
28 * limitations under the License.
32 * Allows easy iteration over the associations payload, decoding and breaking it
33 * to (ordinal, value) pairs, stored in a hash.
35 * @lucene.experimental
37 public class AssociationsPayloadIterator {
40 * Default Term for associations
42 public static final Term ASSOCIATION_POSTING_TERM = new Term(
43 CategoryListParams.DEFAULT_TERM.field(),
44 AssociationEnhancement.CATEGORY_LIST_TERM_TEXT);
47 * Hash mapping to ordinals to the associated int value
49 private IntToIntMap ordinalToAssociationMap;
52 * An inner payload decoder which actually goes through the posting and
53 * decode the ints representing the ordinals and the values
55 private PayloadIntDecodingIterator associationPayloadIter;
58 * Marking whether there are associations (at all) in the given index
60 private boolean hasAssociations = false;
63 * The long-special-value returned for ordinals which have no associated int
64 * value. It is not in the int range of values making it a valid mark.
66 public final static long NO_ASSOCIATION = Integer.MAX_VALUE + 1;
69 * Construct a new association-iterator, initializing the inner payload
70 * iterator, with the supplied term and checking whether there are any
71 * associations within the given index
74 * a reader containing the postings to be iterated
76 * the field containing the relevant associations list term
78 public AssociationsPayloadIterator(IndexReader reader, String field)
80 // Initialize the payloadDecodingIterator
81 associationPayloadIter = new PayloadIntDecodingIterator(
83 // TODO (Facet): should consolidate with AssociationListTokenizer which
84 // uses AssociationEnhancement.getCatTermText()
85 new Term(field, AssociationEnhancement.CATEGORY_LIST_TERM_TEXT),
86 new SimpleIntDecoder());
88 // Check whether there are any associations
89 hasAssociations = associationPayloadIter.init();
91 ordinalToAssociationMap = new IntToIntMap();
95 * Skipping to the next document, fetching its associations & populating the
99 * document id to be skipped to
100 * @return true if the document contains associations and they were fetched
101 * correctly. false otherwise.
102 * @throws IOException
105 public boolean setNextDoc(int docId) throws IOException {
106 ordinalToAssociationMap.clear();
107 boolean docContainsAssociations = false;
109 docContainsAssociations = fetchAssociations(docId);
110 } catch (IOException e) {
111 IOException ioe = new IOException(
112 "An Error occured while reading a document's associations payload (docId="
118 return docContainsAssociations;
122 * Get int association value for the given ordinal. <br>
123 * The return is either an int value casted as long if the ordinal has an
124 * associated value. Otherwise the returned value would be
125 * {@link #NO_ASSOCIATION} which is 'pure long' value (e.g not in the int
129 * for which the association value is requested
130 * @return the associated int value (encapsulated in a long) if the ordinal
131 * had an associated value, or {@link #NO_ASSOCIATION} otherwise
133 public long getAssociation(int ordinal) {
134 if (ordinalToAssociationMap.containsKey(ordinal)) {
135 return ordinalToAssociationMap.get(ordinal);
138 return NO_ASSOCIATION;
142 * Get an iterator over the ordinals which has an association for the
143 * document set by {@link #setNextDoc(int)}.
145 public IntIterator getAssociatedOrdinals() {
146 return ordinalToAssociationMap.keyIterator();
150 * Skips to the given docId, getting the values in pairs of (ordinal, value)
151 * and populating the map
154 * document id owning the associations
155 * @return true if associations were fetched successfully, false otherwise
156 * @throws IOException
159 private boolean fetchAssociations(int docId) throws IOException {
160 // No associations at all? don't bother trying to seek the docID in the
162 if (!hasAssociations) {
166 // No associations for this document? well, nothing to decode than,
168 if (!associationPayloadIter.skipTo(docId)) {
172 // loop over all the values decoded from the payload in pairs.
175 long ordinal = associationPayloadIter.nextCategory();
177 // if no ordinal - it's the end of data, break the loop
178 if (ordinal > Integer.MAX_VALUE) {
182 // get the associated value
183 long association = associationPayloadIter.nextCategory();
184 // If we're at this step - it means we have an ordinal, do we have
185 // an association for it?
186 if (association > Integer.MAX_VALUE) {
187 // No association!!! A Broken Pair!! PANIC!
188 throw new IOException(
189 "ERROR! Associations should come in pairs of (ordinal, value), yet this payload has an odd number of values! (docId="
192 // Populate the map with the given ordinal and association pair
193 ordinalToAssociationMap.put((int) ordinal, (int) association);
200 public int hashCode() {
201 final int prime = 31;
205 + ((associationPayloadIter == null) ? 0
206 : associationPayloadIter.hashCode());
211 public boolean equals(Object obj) {
220 if (getClass() != obj.getClass()) {
224 AssociationsPayloadIterator other = (AssociationsPayloadIterator) obj;
225 if (associationPayloadIter == null) {
226 if (other.associationPayloadIter != null) {
229 } else if (!associationPayloadIter.equals(other.associationPayloadIter)) {