/*
 * 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.lucene.search.join;

import java.io.IOException;

import org.apache.lucene.index.DocValues;
import org.apache.lucene.index.FilterNumericDocValues;
import org.apache.lucene.index.LeafReaderContext;
import org.apache.lucene.index.NumericDocValues;
import org.apache.lucene.index.SortedDocValues;
import org.apache.lucene.index.SortedNumericDocValues;
import org.apache.lucene.index.SortedSetDocValues;
import org.apache.lucene.search.FieldComparator;
import org.apache.lucene.search.SortField;
import org.apache.lucene.util.BitSet;
import org.apache.lucene.util.NumericUtils;

A special sort field that allows sorting parent docs based on nested / child level fields. Based on the sort order it either takes the document with the lowest or highest field value into account.
@lucene.experimental
/** * A special sort field that allows sorting parent docs based on nested / child level fields. * Based on the sort order it either takes the document with the lowest or highest field value into account. * * @lucene.experimental */
public class ToParentBlockJoinSortField extends SortField { private final boolean order; private final BitSetProducer parentFilter; private final BitSetProducer childFilter;
Create ToParentBlockJoinSortField. The parent document ordering is based on child document ordering (reverse).
Params:
  • field – The sort field on the nested / child level.
  • type – The sort type on the nested / child level.
  • reverse – Whether natural order should be reversed on the nested / child level.
  • parentFilter – Filter that identifies the parent documents.
  • childFilter – Filter that defines which child documents participates in sorting.
/** * Create ToParentBlockJoinSortField. The parent document ordering is based on child document ordering (reverse). * * @param field The sort field on the nested / child level. * @param type The sort type on the nested / child level. * @param reverse Whether natural order should be reversed on the nested / child level. * @param parentFilter Filter that identifies the parent documents. * @param childFilter Filter that defines which child documents participates in sorting. */
public ToParentBlockJoinSortField(String field, Type type, boolean reverse, BitSetProducer parentFilter, BitSetProducer childFilter) { super(field, type, reverse); switch (getType()) { case STRING: case DOUBLE: case FLOAT: case LONG: case INT: // ok break; default: throw new UnsupportedOperationException("Sort type " + type + " is not supported"); } this.order = reverse; this.parentFilter = parentFilter; this.childFilter = childFilter; }
Create ToParentBlockJoinSortField.
Params:
  • field – The sort field on the nested / child level.
  • type – The sort type on the nested / child level.
  • reverse – Whether natural order should be reversed on the nested / child document level.
  • order – Whether natural order should be reversed on the parent level.
  • parentFilter – Filter that identifies the parent documents.
  • childFilter – Filter that defines which child documents participates in sorting.
/** * Create ToParentBlockJoinSortField. * * @param field The sort field on the nested / child level. * @param type The sort type on the nested / child level. * @param reverse Whether natural order should be reversed on the nested / child document level. * @param order Whether natural order should be reversed on the parent level. * @param parentFilter Filter that identifies the parent documents. * @param childFilter Filter that defines which child documents participates in sorting. */
public ToParentBlockJoinSortField(String field, Type type, boolean reverse, boolean order, BitSetProducer parentFilter, BitSetProducer childFilter) { super(field, type, reverse); this.order = order; this.parentFilter = parentFilter; this.childFilter = childFilter; } @Override public FieldComparator<?> getComparator(int numHits, int sortPos) { switch (getType()) { case STRING: return getStringComparator(numHits); case DOUBLE: return getDoubleComparator(numHits); case FLOAT: return getFloatComparator(numHits); case LONG: return getLongComparator(numHits); case INT: return getIntComparator(numHits); default: throw new UnsupportedOperationException("Sort type " + getType() + " is not supported"); } } private FieldComparator<?> getStringComparator(int numHits) { return new FieldComparator.TermOrdValComparator(numHits, getField(), missingValue == STRING_LAST) { @Override protected SortedDocValues getSortedDocValues(LeafReaderContext context, String field) throws IOException { SortedSetDocValues sortedSet = DocValues.getSortedSet(context.reader(), field); final BlockJoinSelector.Type type = order ? BlockJoinSelector.Type.MAX : BlockJoinSelector.Type.MIN; final BitSet parents = parentFilter.getBitSet(context); final BitSet children = childFilter.getBitSet(context); if (children == null) { return DocValues.emptySorted(); } return BlockJoinSelector.wrap(sortedSet, type, parents, children); } }; } private FieldComparator<?> getIntComparator(int numHits) { return new FieldComparator.IntComparator(numHits, getField(), (Integer) missingValue) { @Override protected NumericDocValues getNumericDocValues(LeafReaderContext context, String field) throws IOException { SortedNumericDocValues sortedNumeric = DocValues.getSortedNumeric(context.reader(), field); final BlockJoinSelector.Type type = order ? BlockJoinSelector.Type.MAX : BlockJoinSelector.Type.MIN; final BitSet parents = parentFilter.getBitSet(context); final BitSet children = childFilter.getBitSet(context); if (children == null) { return DocValues.emptyNumeric(); } return BlockJoinSelector.wrap(sortedNumeric, type, parents, children); } }; } private FieldComparator<?> getLongComparator(int numHits) { return new FieldComparator.LongComparator(numHits, getField(), (Long) missingValue) { @Override protected NumericDocValues getNumericDocValues(LeafReaderContext context, String field) throws IOException { SortedNumericDocValues sortedNumeric = DocValues.getSortedNumeric(context.reader(), field); final BlockJoinSelector.Type type = order ? BlockJoinSelector.Type.MAX : BlockJoinSelector.Type.MIN; final BitSet parents = parentFilter.getBitSet(context); final BitSet children = childFilter.getBitSet(context); if (children == null) { return DocValues.emptyNumeric(); } return BlockJoinSelector.wrap(sortedNumeric, type, parents, children); } }; } private FieldComparator<?> getFloatComparator(int numHits) { return new FieldComparator.FloatComparator(numHits, getField(), (Float) missingValue) { @Override protected NumericDocValues getNumericDocValues(LeafReaderContext context, String field) throws IOException { SortedNumericDocValues sortedNumeric = DocValues.getSortedNumeric(context.reader(), field); final BlockJoinSelector.Type type = order ? BlockJoinSelector.Type.MAX : BlockJoinSelector.Type.MIN; final BitSet parents = parentFilter.getBitSet(context); final BitSet children = childFilter.getBitSet(context); if (children == null) { return DocValues.emptyNumeric(); } return new FilterNumericDocValues(BlockJoinSelector.wrap(sortedNumeric, type, parents, children)) { @Override public long longValue() throws IOException { // undo the numericutils sortability return NumericUtils.sortableFloatBits((int) super.longValue()); } }; } }; } private FieldComparator<?> getDoubleComparator(int numHits) { return new FieldComparator.DoubleComparator(numHits, getField(), (Double) missingValue) { @Override protected NumericDocValues getNumericDocValues(LeafReaderContext context, String field) throws IOException { SortedNumericDocValues sortedNumeric = DocValues.getSortedNumeric(context.reader(), field); final BlockJoinSelector.Type type = order ? BlockJoinSelector.Type.MAX : BlockJoinSelector.Type.MIN; final BitSet parents = parentFilter.getBitSet(context); final BitSet children = childFilter.getBitSet(context); if (children == null) { return DocValues.emptyNumeric(); } return new FilterNumericDocValues(BlockJoinSelector.wrap(sortedNumeric, type, parents, children)) { @Override public long longValue() throws IOException { // undo the numericutils sortability return NumericUtils.sortableDoubleBits(super.longValue()); } }; } }; } @Override public int hashCode() { final int prime = 31; int result = super.hashCode(); result = prime * result + ((childFilter == null) ? 0 : childFilter.hashCode()); result = prime * result + (order ? 1231 : 1237); result = prime * result + ((parentFilter == null) ? 0 : parentFilter.hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (!super.equals(obj)) return false; if (getClass() != obj.getClass()) return false; ToParentBlockJoinSortField other = (ToParentBlockJoinSortField) obj; if (childFilter == null) { if (other.childFilter != null) return false; } else if (!childFilter.equals(other.childFilter)) return false; if (order != other.order) return false; if (parentFilter == null) { if (other.parentFilter != null) return false; } else if (!parentFilter.equals(other.parentFilter)) return false; return true; } }