blob: ae72314e6f15ea3ce8e901687b6b993fd6bfcbfc [file] [log] [blame]
/*
* 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.PointValues;
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.LeafFieldComparator;
import org.apache.lucene.search.SortField;
import org.apache.lucene.search.comparators.DoubleComparator;
import org.apache.lucene.search.comparators.FloatComparator;
import org.apache.lucene.search.comparators.IntComparator;
import org.apache.lucene.search.comparators.LongComparator;
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
*/
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).
*
* @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.
*
* @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, sortPos);
case FLOAT:
return getFloatComparator(numHits, sortPos);
case LONG:
return getLongComparator(numHits, sortPos);
case INT:
return getIntComparator(numHits, sortPos);
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, int sortPos) {
return new IntComparator(numHits, getField(), (Integer) missingValue, getReverse(), sortPos) {
@Override
public LeafFieldComparator getLeafComparator(LeafReaderContext context) throws IOException {
return new IntLeafComparator(context) {
@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);
}
// no sort optimization with points
@Override
protected PointValues getPointValues(LeafReaderContext context, String field) {
return null;
}
};
}
};
}
private FieldComparator<?> getLongComparator(int numHits, int sortPos) {
return new LongComparator(numHits, getField(), (Long) missingValue, getReverse(), sortPos) {
@Override
public LeafFieldComparator getLeafComparator(LeafReaderContext context) throws IOException {
return new LongLeafComparator(context) {
@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);
}
// no sort optimization with points
@Override
protected PointValues getPointValues(LeafReaderContext context, String field) {
return null;
}
};
}
};
}
private FieldComparator<?> getFloatComparator(int numHits, int sortPos) {
return new FloatComparator(numHits, getField(), (Float) missingValue, getReverse(), sortPos) {
@Override
public LeafFieldComparator getLeafComparator(LeafReaderContext context) throws IOException {
return new FloatLeafComparator(context) {
@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());
}
};
}
// no sort optimization with points
@Override
protected PointValues getPointValues(LeafReaderContext context, String field) {
return null;
}
};
};
};
}
private FieldComparator<?> getDoubleComparator(int numHits, int sortPost) {
return new DoubleComparator(numHits, getField(), (Double) missingValue, getReverse(), sortPost) {
@Override
public LeafFieldComparator getLeafComparator(LeafReaderContext context) throws IOException {
return new DoubleLeafComparator(context) {
@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());
}
};
}
// no sort optimization with points
@Override
protected PointValues getPointValues(LeafReaderContext context, String field) {
return null;
}
};
}
};
}
@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;
}
}