mirror of https://github.com/apache/lucene.git
LUCENE-10245: Addition of MultiDoubleValues(Source) and MultiLongValues(Source) along with faceting capabilities (#543)
This commit is contained in:
parent
bff930c1bf
commit
82703757fe
|
@ -104,6 +104,12 @@ New Features
|
||||||
* LUCENE-10335: Add ModuleResourceLoader as complement to ClasspathResourceLoader.
|
* LUCENE-10335: Add ModuleResourceLoader as complement to ClasspathResourceLoader.
|
||||||
(Uwe Schindler)
|
(Uwe Schindler)
|
||||||
|
|
||||||
|
* LUCENE-10245: MultiDoubleValues(Source) and MultiLongValues(Source) were added as multi-valued
|
||||||
|
versions of DoubleValues(Source) and LongValues(Source) to the facets module. LongValueFacetCounts,
|
||||||
|
LongRangeFacetCounts and DoubleRangeFacetCounts were augmented to support these new multi-valued
|
||||||
|
abstractions. DoubleRange and LongRange also support creating queries from these multi-valued
|
||||||
|
sources. (Greg Miller)
|
||||||
|
|
||||||
* LUCENE-10250: Add support for arbitrary length hierarchical SSDV facets. (Marc D'mello)
|
* LUCENE-10250: Add support for arbitrary length hierarchical SSDV facets. (Marc D'mello)
|
||||||
|
|
||||||
Improvements
|
Improvements
|
||||||
|
|
|
@ -118,6 +118,8 @@ public class DistanceFacetsExample implements Closeable {
|
||||||
writer.close();
|
writer.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Would be nice to augment this example with documents containing multiple "locations",
|
||||||
|
// adding the ability to compute distance facets for the multi-valued case (see LUCENE-10245)
|
||||||
private DoubleValuesSource getDistanceValueSource() {
|
private DoubleValuesSource getDistanceValueSource() {
|
||||||
Expression distance;
|
Expression distance;
|
||||||
try {
|
try {
|
||||||
|
|
|
@ -70,7 +70,7 @@ public class LongValueFacetCounts extends Facets {
|
||||||
* been indexed).
|
* been indexed).
|
||||||
*/
|
*/
|
||||||
public LongValueFacetCounts(String field, FacetsCollector hits) throws IOException {
|
public LongValueFacetCounts(String field, FacetsCollector hits) throws IOException {
|
||||||
this(field, null, hits);
|
this(field, (LongValuesSource) null, hits);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -87,12 +87,32 @@ public class LongValueFacetCounts extends Facets {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create {@code LongValueFacetCounts}, using the provided {@link MultiLongValuesSource} if
|
||||||
|
* non-null. If {@code valuesSource} is null, doc values from the provided {@code field} will be
|
||||||
|
* used.
|
||||||
|
*/
|
||||||
|
public LongValueFacetCounts(
|
||||||
|
String field, MultiLongValuesSource valuesSource, FacetsCollector hits) throws IOException {
|
||||||
|
this.field = field;
|
||||||
|
if (valuesSource != null) {
|
||||||
|
LongValuesSource singleValues = MultiLongValuesSource.unwrapSingleton(valuesSource);
|
||||||
|
if (singleValues != null) {
|
||||||
|
count(singleValues, hits.getMatchingDocs());
|
||||||
|
} else {
|
||||||
|
count(valuesSource, hits.getMatchingDocs());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
count(field, hits.getMatchingDocs());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Counts all facet values for this reader. This produces the same result as computing facets on a
|
* Counts all facet values for this reader. This produces the same result as computing facets on a
|
||||||
* {@link org.apache.lucene.search.MatchAllDocsQuery}, but is more efficient.
|
* {@link org.apache.lucene.search.MatchAllDocsQuery}, but is more efficient.
|
||||||
*/
|
*/
|
||||||
public LongValueFacetCounts(String field, IndexReader reader) throws IOException {
|
public LongValueFacetCounts(String field, IndexReader reader) throws IOException {
|
||||||
this(field, null, reader);
|
this(field, (LongValuesSource) null, reader);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -111,6 +131,27 @@ public class LongValueFacetCounts extends Facets {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Counts all facet values for the provided {@link MultiLongValuesSource} if non-null. If {@code
|
||||||
|
* valueSource} is null, doc values from the provided {@code field} will be used. This produces
|
||||||
|
* the same result as computing facets on a {@link org.apache.lucene.search.MatchAllDocsQuery},
|
||||||
|
* but is more efficient.
|
||||||
|
*/
|
||||||
|
public LongValueFacetCounts(String field, MultiLongValuesSource valuesSource, IndexReader reader)
|
||||||
|
throws IOException {
|
||||||
|
this.field = field;
|
||||||
|
if (valuesSource != null) {
|
||||||
|
LongValuesSource singleValued = MultiLongValuesSource.unwrapSingleton(valuesSource);
|
||||||
|
if (singleValued != null) {
|
||||||
|
countAll(reader, singleValued);
|
||||||
|
} else {
|
||||||
|
countAll(reader, valuesSource);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
countAll(reader, field);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/** Counts from the provided valueSource. */
|
/** Counts from the provided valueSource. */
|
||||||
private void count(LongValuesSource valueSource, List<MatchingDocs> matchingDocs)
|
private void count(LongValuesSource valueSource, List<MatchingDocs> matchingDocs)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
|
@ -137,6 +178,37 @@ public class LongValueFacetCounts extends Facets {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Counts from the provided valuesSource. */
|
||||||
|
private void count(MultiLongValuesSource valuesSource, List<MatchingDocs> matchingDocs)
|
||||||
|
throws IOException {
|
||||||
|
for (MatchingDocs hits : matchingDocs) {
|
||||||
|
|
||||||
|
MultiLongValues multiValues = valuesSource.getValues(hits.context);
|
||||||
|
|
||||||
|
DocIdSetIterator docs = hits.bits.iterator();
|
||||||
|
for (int doc = docs.nextDoc(); doc != DocIdSetIterator.NO_MORE_DOCS; ) {
|
||||||
|
// Skip missing docs:
|
||||||
|
if (multiValues.advanceExact(doc)) {
|
||||||
|
long limit = multiValues.getValueCount();
|
||||||
|
if (limit > 0) {
|
||||||
|
totCount++;
|
||||||
|
}
|
||||||
|
long previousValue = 0;
|
||||||
|
for (int i = 0; i < limit; i++) {
|
||||||
|
long value = multiValues.nextValue();
|
||||||
|
// do not increment the count for duplicate values
|
||||||
|
if (i == 0 || value != previousValue) {
|
||||||
|
increment(value);
|
||||||
|
previousValue = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
doc = docs.nextDoc();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/** Counts from the field's indexed doc values. */
|
/** Counts from the field's indexed doc values. */
|
||||||
private void count(String field, List<MatchingDocs> matchingDocs) throws IOException {
|
private void count(String field, List<MatchingDocs> matchingDocs) throws IOException {
|
||||||
for (MatchingDocs hits : matchingDocs) {
|
for (MatchingDocs hits : matchingDocs) {
|
||||||
|
@ -194,6 +266,34 @@ public class LongValueFacetCounts extends Facets {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Count everything in the provided valueSource. */
|
||||||
|
private void countAll(IndexReader reader, MultiLongValuesSource valueSource) throws IOException {
|
||||||
|
|
||||||
|
for (LeafReaderContext context : reader.leaves()) {
|
||||||
|
MultiLongValues multiValues = valueSource.getValues(context);
|
||||||
|
int maxDoc = context.reader().maxDoc();
|
||||||
|
|
||||||
|
for (int doc = 0; doc < maxDoc; doc++) {
|
||||||
|
// Skip missing docs:
|
||||||
|
if (multiValues.advanceExact(doc)) {
|
||||||
|
long limit = multiValues.getValueCount();
|
||||||
|
if (limit > 0) {
|
||||||
|
totCount++;
|
||||||
|
}
|
||||||
|
long previousValue = 0;
|
||||||
|
for (int i = 0; i < limit; i++) {
|
||||||
|
long value = multiValues.nextValue();
|
||||||
|
// do not increment the count for duplicate values
|
||||||
|
if (i == 0 || value != previousValue) {
|
||||||
|
increment(value);
|
||||||
|
previousValue = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/** Count everything in the specified field. */
|
/** Count everything in the specified field. */
|
||||||
private void countAll(IndexReader reader, String field) throws IOException {
|
private void countAll(IndexReader reader, String field) throws IOException {
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,50 @@
|
||||||
|
/*
|
||||||
|
* 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.facet;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import org.apache.lucene.search.DoubleValues;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Per-segment, per-document double values, which can be calculated at search-time. Documents may
|
||||||
|
* produce multiple values. See also {@link DoubleValues} for a single-valued version.
|
||||||
|
*
|
||||||
|
* <p>Currently meant only for use within the faceting module. Could be further generalized and made
|
||||||
|
* available for more use-cases outside faceting if there is a desire to do so.
|
||||||
|
*
|
||||||
|
* @lucene.experimental
|
||||||
|
*/
|
||||||
|
public abstract class MultiDoubleValues {
|
||||||
|
|
||||||
|
/** Instantiates a new MultiDoubleValues */
|
||||||
|
public MultiDoubleValues() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the number of values for the current document. This must always be greater than zero.
|
||||||
|
* It is illegal to call this method after {@link #advanceExact(int)} returned {@code false}.
|
||||||
|
*/
|
||||||
|
public abstract long getValueCount();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Iterates to the next value in the current document. Do not call this more than {@link
|
||||||
|
* #getValueCount} times for the document.
|
||||||
|
*/
|
||||||
|
public abstract double nextValue() throws IOException;
|
||||||
|
|
||||||
|
/** Advance to exactly {@code doc} and return whether {@code doc} has a value. */
|
||||||
|
public abstract boolean advanceExact(int doc) throws IOException;
|
||||||
|
}
|
|
@ -0,0 +1,278 @@
|
||||||
|
/*
|
||||||
|
* 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.facet;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.function.LongToDoubleFunction;
|
||||||
|
import org.apache.lucene.index.DocValues;
|
||||||
|
import org.apache.lucene.index.LeafReaderContext;
|
||||||
|
import org.apache.lucene.index.SortedNumericDocValues;
|
||||||
|
import org.apache.lucene.search.DoubleValues;
|
||||||
|
import org.apache.lucene.search.DoubleValuesSource;
|
||||||
|
import org.apache.lucene.search.IndexSearcher;
|
||||||
|
import org.apache.lucene.search.SegmentCacheable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Base class for producing {@link MultiDoubleValues}. See also {@link DoubleValuesSource} for a
|
||||||
|
* single-valued version.
|
||||||
|
*
|
||||||
|
* <p>MultiDoubleValuesSource objects for NumericDocValues/SortedNumericDocValues fields can be
|
||||||
|
* obtained by calling {@link #fromFloatField(String)}, {@link #fromDoubleField(String)}, {@link
|
||||||
|
* #fromIntField(String)}, or {@link #fromLongField(String)}. If custom long-to-double logic is
|
||||||
|
* required, {@link #fromField(String, LongToDoubleFunction)} can be used. This is valid for both
|
||||||
|
* multi-valued and single-valued fields.
|
||||||
|
*
|
||||||
|
* <p>To obtain a MultiDoubleValuesSource from an existing {@link DoubleValuesSource}, see {@link
|
||||||
|
* #fromSingleValued(DoubleValuesSource)}. Instances created in this way can be "unwrapped" using
|
||||||
|
* {@link #unwrapSingleton(MultiDoubleValuesSource)} if necessary. Note that scores are never
|
||||||
|
* provided to the underlying {@code DoubleValuesSource}. {@link
|
||||||
|
* DoubleValuesSource#rewrite(IndexSearcher)} will also never be called. The user should be aware of
|
||||||
|
* this if using a {@code DoubleValuesSource} that relies on rewriting or scores. The faceting
|
||||||
|
* use-cases don't call rewrite or provide scores, which is why this simplification was made.
|
||||||
|
*
|
||||||
|
* <p>Currently meant only for use within the faceting module. Could be further generalized and made
|
||||||
|
* available for more use-cases outside faceting if there is a desire to do so.
|
||||||
|
*
|
||||||
|
* @lucene.experimental
|
||||||
|
*/
|
||||||
|
public abstract class MultiDoubleValuesSource implements SegmentCacheable {
|
||||||
|
|
||||||
|
/** Instantiates a new MultiDoubleValuesSource */
|
||||||
|
public MultiDoubleValuesSource() {}
|
||||||
|
|
||||||
|
/** Returns a {@link MultiDoubleValues} instance for the passed-in LeafReaderContext */
|
||||||
|
public abstract MultiDoubleValues getValues(LeafReaderContext ctx) throws IOException;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public abstract int hashCode();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public abstract boolean equals(Object o);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public abstract String toString();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a MultiDoubleValuesSource that wraps a generic NumericDocValues/SortedNumericDocValues
|
||||||
|
* field. Uses the long-to-double decoding logic specified in {@code decoder} for converting the
|
||||||
|
* stored value to a double.
|
||||||
|
*/
|
||||||
|
public static MultiDoubleValuesSource fromField(String field, LongToDoubleFunction decoder) {
|
||||||
|
return new FieldMultiValuedSource(field, decoder);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Creates a MultiDoubleValuesSource that wraps a double-valued field */
|
||||||
|
public static MultiDoubleValuesSource fromDoubleField(String field) {
|
||||||
|
return fromField(field, Double::longBitsToDouble);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Creates a MultiDoubleValuesSource that wraps a float-valued field */
|
||||||
|
public static MultiDoubleValuesSource fromFloatField(String field) {
|
||||||
|
return fromField(field, v -> (double) Float.intBitsToFloat((int) v));
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Creates a MultiDoubleValuesSource that wraps a long-valued field */
|
||||||
|
public static MultiDoubleValuesSource fromLongField(String field) {
|
||||||
|
return fromField(field, v -> (double) v);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Creates a MultiDoubleValuesSource that wraps an int-valued field */
|
||||||
|
public static MultiDoubleValuesSource fromIntField(String field) {
|
||||||
|
return fromLongField(field);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Creates a MultiDoubleValuesSource that wraps a single-valued {@code DoubleValuesSource} */
|
||||||
|
public static MultiDoubleValuesSource fromSingleValued(DoubleValuesSource singleValued) {
|
||||||
|
return new SingleValuedAsMultiValued(singleValued);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a single-valued view of the {@code MultiDoubleValuesSource} if it was previously
|
||||||
|
* wrapped with {@link #fromSingleValued(DoubleValuesSource)}, or null.
|
||||||
|
*/
|
||||||
|
public static DoubleValuesSource unwrapSingleton(MultiDoubleValuesSource in) {
|
||||||
|
if (in instanceof SingleValuedAsMultiValued) {
|
||||||
|
return ((SingleValuedAsMultiValued) in).in;
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Convert to a MultiLongValuesSource by casting the double values to longs */
|
||||||
|
public final MultiLongValuesSource toMultiLongValuesSource() {
|
||||||
|
return new LongDoubleValuesSource(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class FieldMultiValuedSource extends MultiDoubleValuesSource {
|
||||||
|
private final String field;
|
||||||
|
private final LongToDoubleFunction decoder;
|
||||||
|
|
||||||
|
FieldMultiValuedSource(String field, LongToDoubleFunction decoder) {
|
||||||
|
this.field = field;
|
||||||
|
this.decoder = decoder;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MultiDoubleValues getValues(LeafReaderContext ctx) throws IOException {
|
||||||
|
final SortedNumericDocValues docValues = DocValues.getSortedNumeric(ctx.reader(), field);
|
||||||
|
return new MultiDoubleValues() {
|
||||||
|
@Override
|
||||||
|
public long getValueCount() {
|
||||||
|
return docValues.docValueCount();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public double nextValue() throws IOException {
|
||||||
|
return decoder.applyAsDouble(docValues.nextValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean advanceExact(int doc) throws IOException {
|
||||||
|
return docValues.advanceExact(doc);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isCacheable(LeafReaderContext ctx) {
|
||||||
|
return DocValues.isCacheable(ctx, field);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return Objects.hash(field, decoder);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (o == this) return true;
|
||||||
|
if (o == null || getClass() != o.getClass()) return false;
|
||||||
|
FieldMultiValuedSource that = (FieldMultiValuedSource) o;
|
||||||
|
return Objects.equals(field, that.field) && Objects.equals(decoder, that.decoder);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "multi-double(" + field + ")";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class SingleValuedAsMultiValued extends MultiDoubleValuesSource {
|
||||||
|
private final DoubleValuesSource in;
|
||||||
|
|
||||||
|
SingleValuedAsMultiValued(DoubleValuesSource in) {
|
||||||
|
this.in = in;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MultiDoubleValues getValues(LeafReaderContext ctx) throws IOException {
|
||||||
|
final DoubleValues singleValues = in.getValues(ctx, null);
|
||||||
|
return new MultiDoubleValues() {
|
||||||
|
@Override
|
||||||
|
public long getValueCount() {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public double nextValue() throws IOException {
|
||||||
|
return singleValues.doubleValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean advanceExact(int doc) throws IOException {
|
||||||
|
return singleValues.advanceExact(doc);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isCacheable(LeafReaderContext ctx) {
|
||||||
|
return in.isCacheable(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return Objects.hash(in);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (o == this) return true;
|
||||||
|
if (o == null || getClass() != o.getClass()) return false;
|
||||||
|
SingleValuedAsMultiValued that = (SingleValuedAsMultiValued) o;
|
||||||
|
return Objects.equals(in, that.in);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "multi-double(" + in + ")";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class LongDoubleValuesSource extends MultiLongValuesSource {
|
||||||
|
private final MultiDoubleValuesSource in;
|
||||||
|
|
||||||
|
LongDoubleValuesSource(MultiDoubleValuesSource in) {
|
||||||
|
this.in = in;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MultiLongValues getValues(LeafReaderContext ctx) throws IOException {
|
||||||
|
final MultiDoubleValues doubleValues = in.getValues(ctx);
|
||||||
|
return new MultiLongValues() {
|
||||||
|
@Override
|
||||||
|
public long getValueCount() {
|
||||||
|
return doubleValues.getValueCount();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long nextValue() throws IOException {
|
||||||
|
return (long) doubleValues.nextValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean advanceExact(int doc) throws IOException {
|
||||||
|
return doubleValues.advanceExact(doc);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isCacheable(LeafReaderContext ctx) {
|
||||||
|
return in.isCacheable(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return Objects.hash(in);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (o == this) return true;
|
||||||
|
if (o == null || getClass() != o.getClass()) return false;
|
||||||
|
LongDoubleValuesSource that = (LongDoubleValuesSource) o;
|
||||||
|
return Objects.equals(in, that.in);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "multi-double(" + in + ")";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,50 @@
|
||||||
|
/*
|
||||||
|
* 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.facet;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import org.apache.lucene.search.LongValues;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Per-segment, per-document long values, which can be calculated at search-time. Documents may
|
||||||
|
* produce multiple values. See also {@link LongValues} for a single-valued version.
|
||||||
|
*
|
||||||
|
* <p>Currently meant only for use within the faceting module. Could be further generalized and made
|
||||||
|
* available for more use-cases outside faceting if there is a desire to do so.
|
||||||
|
*
|
||||||
|
* @lucene.experimental
|
||||||
|
*/
|
||||||
|
public abstract class MultiLongValues {
|
||||||
|
|
||||||
|
/** Instantiates a new MultiLongValues */
|
||||||
|
public MultiLongValues() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the number of values for the current document. This must always be greater than zero.
|
||||||
|
* It is illegal to call this method after {@link #advanceExact(int)} returned {@code false}.
|
||||||
|
*/
|
||||||
|
public abstract long getValueCount();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Iterates to the next value in the current document. Do not call this more than {@link
|
||||||
|
* #getValueCount} times for the document.
|
||||||
|
*/
|
||||||
|
public abstract long nextValue() throws IOException;
|
||||||
|
|
||||||
|
/** Advance to exactly {@code target} and return whether {@code target} has a value. */
|
||||||
|
public abstract boolean advanceExact(int doc) throws IOException;
|
||||||
|
}
|
|
@ -0,0 +1,260 @@
|
||||||
|
/*
|
||||||
|
* 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.facet;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Objects;
|
||||||
|
import org.apache.lucene.index.DocValues;
|
||||||
|
import org.apache.lucene.index.LeafReaderContext;
|
||||||
|
import org.apache.lucene.index.SortedNumericDocValues;
|
||||||
|
import org.apache.lucene.search.IndexSearcher;
|
||||||
|
import org.apache.lucene.search.LongValues;
|
||||||
|
import org.apache.lucene.search.LongValuesSource;
|
||||||
|
import org.apache.lucene.search.SegmentCacheable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Base class for producing {@link MultiLongValues}. See also {@link LongValuesSource} for a
|
||||||
|
* single-valued version.
|
||||||
|
*
|
||||||
|
* <p>MultiLongValuesSource objects for long and int-valued NumericDocValues/SortedNumericDocValues
|
||||||
|
* fields can be obtained by calling {@link #fromLongField(String)} and {@link
|
||||||
|
* #fromIntField(String)}. This is valid for both multi-valued and single-valued fields.
|
||||||
|
*
|
||||||
|
* <p>To obtain a MultiLongValuesSource from a float or double-valued
|
||||||
|
* NumericDocValues/SortedNumericDocValues field, use {@link
|
||||||
|
* MultiDoubleValuesSource#fromFloatField(String)} or {@link
|
||||||
|
* MultiDoubleValuesSource#fromDoubleField(String)} and then call {@link
|
||||||
|
* MultiDoubleValuesSource#toMultiLongValuesSource()}.
|
||||||
|
*
|
||||||
|
* <p>To obtain a MultiLongValuesSource from an existing {@link LongValuesSource}, see {@link
|
||||||
|
* #fromSingleValued(LongValuesSource)}. Instances created in this way can be "unwrapped" using
|
||||||
|
* {@link #unwrapSingleton(MultiLongValuesSource)} if necessary. Note that scores are never provided
|
||||||
|
* to the underlying {@code LongValuesSource}. {@link LongValuesSource#rewrite(IndexSearcher)} will
|
||||||
|
* also never be called. The user should be aware of this if using a {@code LongValuesSource} that
|
||||||
|
* relies on rewriting or scores. The faceting use-cases don't call rewrite or provide scores, which
|
||||||
|
* is why this simplification was made.
|
||||||
|
*
|
||||||
|
* <p>Currently meant only for use within the faceting module. Could be further generalized and made
|
||||||
|
* available for more use-cases outside faceting if there is a desire to do so.
|
||||||
|
*
|
||||||
|
* @lucene.experimental
|
||||||
|
*/
|
||||||
|
public abstract class MultiLongValuesSource implements SegmentCacheable {
|
||||||
|
|
||||||
|
/** Instantiates a new MultiLongValuesSource */
|
||||||
|
public MultiLongValuesSource() {}
|
||||||
|
|
||||||
|
/** Returns a {@link MultiLongValues} instance for the passed-in LeafReaderContext */
|
||||||
|
public abstract MultiLongValues getValues(LeafReaderContext ctx) throws IOException;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public abstract int hashCode();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public abstract boolean equals(Object o);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public abstract String toString();
|
||||||
|
|
||||||
|
/** Creates a MultiLongValuesSource that wraps a long-valued field */
|
||||||
|
public static MultiLongValuesSource fromLongField(String field) {
|
||||||
|
return new FieldMultiValueSource(field);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Creates a MultiLongValuesSource that wraps an int-valued field */
|
||||||
|
public static MultiLongValuesSource fromIntField(String field) {
|
||||||
|
return fromLongField(field);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Creates a MultiLongValuesSource that wraps a single-valued {@code LongValuesSource} */
|
||||||
|
public static MultiLongValuesSource fromSingleValued(LongValuesSource singleValued) {
|
||||||
|
return new SingleValuedAsMultiValued(singleValued);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a single-valued view of the {@code MultiLongValuesSource} if it was previously wrapped
|
||||||
|
* with {@link #fromSingleValued(LongValuesSource)}, or null.
|
||||||
|
*/
|
||||||
|
public static LongValuesSource unwrapSingleton(MultiLongValuesSource in) {
|
||||||
|
if (in instanceof SingleValuedAsMultiValued) {
|
||||||
|
return ((SingleValuedAsMultiValued) in).in;
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Convert to a MultiDoubleValuesSource by casting long values to doubles */
|
||||||
|
public final MultiDoubleValuesSource toMultiDoubleValuesSource() {
|
||||||
|
return new DoubleLongValuesSources(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class FieldMultiValueSource extends MultiLongValuesSource {
|
||||||
|
private final String field;
|
||||||
|
|
||||||
|
FieldMultiValueSource(String field) {
|
||||||
|
this.field = field;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MultiLongValues getValues(LeafReaderContext ctx) throws IOException {
|
||||||
|
final SortedNumericDocValues docValues = DocValues.getSortedNumeric(ctx.reader(), field);
|
||||||
|
return new MultiLongValues() {
|
||||||
|
@Override
|
||||||
|
public long getValueCount() {
|
||||||
|
return docValues.docValueCount();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long nextValue() throws IOException {
|
||||||
|
return docValues.nextValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean advanceExact(int doc) throws IOException {
|
||||||
|
return docValues.advanceExact(doc);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isCacheable(LeafReaderContext ctx) {
|
||||||
|
return DocValues.isCacheable(ctx, field);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return Objects.hash(field);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (o == this) return true;
|
||||||
|
if (o == null || getClass() != o.getClass()) return false;
|
||||||
|
FieldMultiValueSource that = (FieldMultiValueSource) o;
|
||||||
|
return Objects.equals(field, that.field);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "multi-long(" + field + ")";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class SingleValuedAsMultiValued extends MultiLongValuesSource {
|
||||||
|
private final LongValuesSource in;
|
||||||
|
|
||||||
|
SingleValuedAsMultiValued(LongValuesSource in) {
|
||||||
|
this.in = in;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MultiLongValues getValues(LeafReaderContext ctx) throws IOException {
|
||||||
|
final LongValues singleValued = in.getValues(ctx, null);
|
||||||
|
return new MultiLongValues() {
|
||||||
|
@Override
|
||||||
|
public long getValueCount() {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long nextValue() throws IOException {
|
||||||
|
return singleValued.longValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean advanceExact(int doc) throws IOException {
|
||||||
|
return singleValued.advanceExact(doc);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isCacheable(LeafReaderContext ctx) {
|
||||||
|
return in.isCacheable(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return Objects.hash(in);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (o == this) return true;
|
||||||
|
if (o == null || getClass() != o.getClass()) return false;
|
||||||
|
SingleValuedAsMultiValued that = (SingleValuedAsMultiValued) o;
|
||||||
|
return Objects.equals(in, that.in);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "multi-long(" + in + ")";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class DoubleLongValuesSources extends MultiDoubleValuesSource {
|
||||||
|
private final MultiLongValuesSource in;
|
||||||
|
|
||||||
|
DoubleLongValuesSources(MultiLongValuesSource in) {
|
||||||
|
this.in = in;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MultiDoubleValues getValues(LeafReaderContext ctx) throws IOException {
|
||||||
|
final MultiLongValues longValues = in.getValues(ctx);
|
||||||
|
return new MultiDoubleValues() {
|
||||||
|
@Override
|
||||||
|
public long getValueCount() {
|
||||||
|
return longValues.getValueCount();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public double nextValue() throws IOException {
|
||||||
|
return (double) longValues.nextValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean advanceExact(int doc) throws IOException {
|
||||||
|
return longValues.advanceExact(doc);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isCacheable(LeafReaderContext ctx) {
|
||||||
|
return in.isCacheable(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return Objects.hash(in);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (o == this) return true;
|
||||||
|
if (o == null || getClass() != o.getClass()) return false;
|
||||||
|
DoubleLongValuesSources that = (DoubleLongValuesSources) o;
|
||||||
|
return Objects.equals(in, that.in);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "multi-long(" + in + ")";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -18,6 +18,8 @@ package org.apache.lucene.facet.range;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
import org.apache.lucene.facet.MultiDoubleValues;
|
||||||
|
import org.apache.lucene.facet.MultiDoubleValuesSource;
|
||||||
import org.apache.lucene.index.IndexReader;
|
import org.apache.lucene.index.IndexReader;
|
||||||
import org.apache.lucene.index.LeafReaderContext;
|
import org.apache.lucene.index.LeafReaderContext;
|
||||||
import org.apache.lucene.search.ConstantScoreScorer;
|
import org.apache.lucene.search.ConstantScoreScorer;
|
||||||
|
@ -211,6 +213,112 @@ public final class DoubleRange extends Range {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static class MultiValueSourceQuery extends Query {
|
||||||
|
private final DoubleRange range;
|
||||||
|
private final Query fastMatchQuery;
|
||||||
|
private final MultiDoubleValuesSource valueSource;
|
||||||
|
|
||||||
|
MultiValueSourceQuery(
|
||||||
|
DoubleRange range, Query fastMatchQuery, MultiDoubleValuesSource valueSource) {
|
||||||
|
this.range = range;
|
||||||
|
this.fastMatchQuery = fastMatchQuery;
|
||||||
|
this.valueSource = valueSource;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object other) {
|
||||||
|
return sameClassAs(other) && equalsTo(getClass().cast(other));
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean equalsTo(MultiValueSourceQuery other) {
|
||||||
|
return range.equals(other.range)
|
||||||
|
&& Objects.equals(fastMatchQuery, other.fastMatchQuery)
|
||||||
|
&& valueSource.equals(other.valueSource);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return classHash() + 31 * Objects.hash(range, fastMatchQuery, valueSource);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString(String field) {
|
||||||
|
return "Filter(" + range.toString() + ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visit(QueryVisitor visitor) {
|
||||||
|
visitor.visitLeaf(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Query rewrite(IndexReader reader) throws IOException {
|
||||||
|
if (fastMatchQuery != null) {
|
||||||
|
final Query fastMatchRewritten = fastMatchQuery.rewrite(reader);
|
||||||
|
if (fastMatchRewritten != fastMatchQuery) {
|
||||||
|
return new MultiValueSourceQuery(range, fastMatchRewritten, valueSource);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return super.rewrite(reader);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Weight createWeight(IndexSearcher searcher, ScoreMode scoreMode, float boost)
|
||||||
|
throws IOException {
|
||||||
|
final Weight fastMatchWeight =
|
||||||
|
fastMatchQuery == null
|
||||||
|
? null
|
||||||
|
: searcher.createWeight(fastMatchQuery, ScoreMode.COMPLETE_NO_SCORES, 1f);
|
||||||
|
|
||||||
|
return new ConstantScoreWeight(this, boost) {
|
||||||
|
@Override
|
||||||
|
public Scorer scorer(LeafReaderContext context) throws IOException {
|
||||||
|
final int maxDoc = context.reader().maxDoc();
|
||||||
|
|
||||||
|
final DocIdSetIterator approximation;
|
||||||
|
if (fastMatchWeight == null) {
|
||||||
|
approximation = DocIdSetIterator.all(maxDoc);
|
||||||
|
} else {
|
||||||
|
Scorer s = fastMatchWeight.scorer(context);
|
||||||
|
if (s == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
approximation = s.iterator();
|
||||||
|
}
|
||||||
|
|
||||||
|
final MultiDoubleValues values = valueSource.getValues(context);
|
||||||
|
final TwoPhaseIterator twoPhase =
|
||||||
|
new TwoPhaseIterator(approximation) {
|
||||||
|
@Override
|
||||||
|
public boolean matches() throws IOException {
|
||||||
|
if (values.advanceExact(approximation.docID()) == false) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < values.getValueCount(); i++) {
|
||||||
|
if (range.accept(values.nextValue())) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public float matchCost() {
|
||||||
|
return 100; // TODO: use cost of range.accept()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return new ConstantScoreScorer(this, score(), scoreMode, twoPhase);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isCacheable(LeafReaderContext ctx) {
|
||||||
|
return valueSource.isCacheable(ctx);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a Query that matches documents in this range
|
* Create a Query that matches documents in this range
|
||||||
*
|
*
|
||||||
|
@ -226,4 +334,25 @@ public final class DoubleRange extends Range {
|
||||||
public Query getQuery(Query fastMatchQuery, DoubleValuesSource valueSource) {
|
public Query getQuery(Query fastMatchQuery, DoubleValuesSource valueSource) {
|
||||||
return new ValueSourceQuery(this, fastMatchQuery, valueSource);
|
return new ValueSourceQuery(this, fastMatchQuery, valueSource);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a Query that matches documents in this range
|
||||||
|
*
|
||||||
|
* <p>The query will check all documents that match the provided match query, or every document in
|
||||||
|
* the index if the match query is null.
|
||||||
|
*
|
||||||
|
* <p>If the value source is static, eg an indexed numeric field, it may be faster to use {@link
|
||||||
|
* org.apache.lucene.search.PointRangeQuery}
|
||||||
|
*
|
||||||
|
* @param fastMatchQuery a query to use as a filter
|
||||||
|
* @param valueSource the source of values for the range check
|
||||||
|
*/
|
||||||
|
public Query getQuery(Query fastMatchQuery, MultiDoubleValuesSource valueSource) {
|
||||||
|
DoubleValuesSource singleValues = MultiDoubleValuesSource.unwrapSingleton(valueSource);
|
||||||
|
if (singleValues != null) {
|
||||||
|
return new ValueSourceQuery(this, fastMatchQuery, singleValues);
|
||||||
|
} else {
|
||||||
|
return new MultiValueSourceQuery(this, fastMatchQuery, valueSource);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,6 +22,8 @@ import org.apache.lucene.document.FloatDocValuesField;
|
||||||
import org.apache.lucene.facet.Facets;
|
import org.apache.lucene.facet.Facets;
|
||||||
import org.apache.lucene.facet.FacetsCollector;
|
import org.apache.lucene.facet.FacetsCollector;
|
||||||
import org.apache.lucene.facet.FacetsCollector.MatchingDocs;
|
import org.apache.lucene.facet.FacetsCollector.MatchingDocs;
|
||||||
|
import org.apache.lucene.facet.MultiDoubleValues;
|
||||||
|
import org.apache.lucene.facet.MultiDoubleValuesSource;
|
||||||
import org.apache.lucene.index.NumericDocValues;
|
import org.apache.lucene.index.NumericDocValues;
|
||||||
import org.apache.lucene.index.SortedNumericDocValues;
|
import org.apache.lucene.index.SortedNumericDocValues;
|
||||||
import org.apache.lucene.search.DocIdSetIterator;
|
import org.apache.lucene.search.DocIdSetIterator;
|
||||||
|
@ -50,13 +52,14 @@ public class DoubleRangeFacetCounts extends RangeFacetCounts {
|
||||||
*
|
*
|
||||||
* <p>N.B This assumes that the field was indexed with {@link
|
* <p>N.B This assumes that the field was indexed with {@link
|
||||||
* org.apache.lucene.document.DoubleDocValuesField}. For float-valued fields, use {@link
|
* org.apache.lucene.document.DoubleDocValuesField}. For float-valued fields, use {@link
|
||||||
* #DoubleRangeFacetCounts(String, DoubleValuesSource, FacetsCollector, DoubleRange...)}
|
* #DoubleRangeFacetCounts(String, DoubleValuesSource, FacetsCollector, DoubleRange...)} or {@link
|
||||||
|
* #DoubleRangeFacetCounts(String, MultiDoubleValuesSource, FacetsCollector, DoubleRange...)}
|
||||||
*
|
*
|
||||||
* <p>TODO: Extend multi-valued support to fields that have been indexed as float values
|
* <p>TODO: Extend multi-valued support to fields that have been indexed as float values
|
||||||
*/
|
*/
|
||||||
public DoubleRangeFacetCounts(String field, FacetsCollector hits, DoubleRange... ranges)
|
public DoubleRangeFacetCounts(String field, FacetsCollector hits, DoubleRange... ranges)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
this(field, null, hits, ranges);
|
this(field, (DoubleValuesSource) null, hits, ranges);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -73,6 +76,24 @@ public class DoubleRangeFacetCounts extends RangeFacetCounts {
|
||||||
this(field, valueSource, hits, null, ranges);
|
this(field, valueSource, hits, null, ranges);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create {@code RangeFacetCounts}, using the provided {@link MultiDoubleValuesSource} if
|
||||||
|
* non-null. If {@code valuesSource} is null, doc values from the provided {@code field} will be
|
||||||
|
* used.
|
||||||
|
*
|
||||||
|
* <p>N.B If relying on the provided {@code field}, see javadoc notes associated with {@link
|
||||||
|
* #DoubleRangeFacetCounts(String, FacetsCollector, DoubleRange...)} for assumptions on how the
|
||||||
|
* field is indexed.
|
||||||
|
*/
|
||||||
|
public DoubleRangeFacetCounts(
|
||||||
|
String field,
|
||||||
|
MultiDoubleValuesSource valuesSource,
|
||||||
|
FacetsCollector hits,
|
||||||
|
DoubleRange... ranges)
|
||||||
|
throws IOException {
|
||||||
|
this(field, valuesSource, hits, null, ranges);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create {@code RangeFacetCounts}, using the provided {@link DoubleValuesSource} if non-null. If
|
* Create {@code RangeFacetCounts}, using the provided {@link DoubleValuesSource} if non-null. If
|
||||||
* {@code valueSource} is null, doc values from the provided {@code field} will be used. Use the
|
* {@code valueSource} is null, doc values from the provided {@code field} will be used. Use the
|
||||||
|
@ -100,6 +121,38 @@ public class DoubleRangeFacetCounts extends RangeFacetCounts {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create {@code RangeFacetCounts}, using the provided {@link MultiDoubleValuesSource} if
|
||||||
|
* non-null. If {@code valuesSource} is null, doc values from the provided {@code field} will be
|
||||||
|
* used. Use the provided {@code Query} as a fastmatch: only documents matching the query are
|
||||||
|
* checked for the matching ranges.
|
||||||
|
*
|
||||||
|
* <p>N.B If relying on the provided {@code field}, see javadoc notes associated with {@link
|
||||||
|
* #DoubleRangeFacetCounts(String, FacetsCollector, DoubleRange...)} for assumptions on how the
|
||||||
|
* field is indexed.
|
||||||
|
*/
|
||||||
|
public DoubleRangeFacetCounts(
|
||||||
|
String field,
|
||||||
|
MultiDoubleValuesSource valuesSource,
|
||||||
|
FacetsCollector hits,
|
||||||
|
Query fastMatchQuery,
|
||||||
|
DoubleRange... ranges)
|
||||||
|
throws IOException {
|
||||||
|
super(field, ranges, fastMatchQuery);
|
||||||
|
// use the provided valueSource if non-null, otherwise use the doc values associated with the
|
||||||
|
// field
|
||||||
|
if (valuesSource != null) {
|
||||||
|
DoubleValuesSource singleValues = MultiDoubleValuesSource.unwrapSingleton(valuesSource);
|
||||||
|
if (singleValues != null) {
|
||||||
|
count(singleValues, hits.getMatchingDocs());
|
||||||
|
} else {
|
||||||
|
count(valuesSource, hits.getMatchingDocs());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
count(field, hits.getMatchingDocs());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/** Counts from the provided valueSource. */
|
/** Counts from the provided valueSource. */
|
||||||
private void count(DoubleValuesSource valueSource, List<MatchingDocs> matchingDocs)
|
private void count(DoubleValuesSource valueSource, List<MatchingDocs> matchingDocs)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
|
@ -134,6 +187,55 @@ public class DoubleRangeFacetCounts extends RangeFacetCounts {
|
||||||
totCount -= missingCount;
|
totCount -= missingCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Counts from the provided valueSource. */
|
||||||
|
private void count(MultiDoubleValuesSource valueSource, List<MatchingDocs> matchingDocs)
|
||||||
|
throws IOException {
|
||||||
|
|
||||||
|
LongRange[] longRanges = getLongRanges();
|
||||||
|
|
||||||
|
LongRangeCounter counter = LongRangeCounter.create(longRanges, counts);
|
||||||
|
|
||||||
|
int missingCount = 0;
|
||||||
|
for (MatchingDocs hits : matchingDocs) {
|
||||||
|
MultiDoubleValues multiValues = valueSource.getValues(hits.context);
|
||||||
|
|
||||||
|
final DocIdSetIterator it = createIterator(hits);
|
||||||
|
if (it == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int doc = it.nextDoc(); doc != DocIdSetIterator.NO_MORE_DOCS; ) {
|
||||||
|
// Skip missing docs:
|
||||||
|
if (multiValues.advanceExact(doc)) {
|
||||||
|
long limit = multiValues.getValueCount();
|
||||||
|
// optimize single-valued case:
|
||||||
|
if (limit == 1) {
|
||||||
|
counter.addSingleValued(NumericUtils.doubleToSortableLong(multiValues.nextValue()));
|
||||||
|
totCount++;
|
||||||
|
} else {
|
||||||
|
counter.startMultiValuedDoc();
|
||||||
|
long previous = 0;
|
||||||
|
for (int i = 0; i < limit; i++) {
|
||||||
|
long val = NumericUtils.doubleToSortableLong(multiValues.nextValue());
|
||||||
|
if (i == 0 || val != previous) {
|
||||||
|
counter.addMultiValued(val);
|
||||||
|
previous = val;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (counter.endMultiValuedDoc()) {
|
||||||
|
totCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
doc = it.nextDoc();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
missingCount += counter.finish();
|
||||||
|
totCount -= missingCount;
|
||||||
|
}
|
||||||
|
|
||||||
/** Create long ranges from the double ranges. */
|
/** Create long ranges from the double ranges. */
|
||||||
@Override
|
@Override
|
||||||
protected LongRange[] getLongRanges() {
|
protected LongRange[] getLongRanges() {
|
||||||
|
|
|
@ -18,6 +18,8 @@ package org.apache.lucene.facet.range;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
import org.apache.lucene.facet.MultiLongValues;
|
||||||
|
import org.apache.lucene.facet.MultiLongValuesSource;
|
||||||
import org.apache.lucene.index.IndexReader;
|
import org.apache.lucene.index.IndexReader;
|
||||||
import org.apache.lucene.index.LeafReaderContext;
|
import org.apache.lucene.index.LeafReaderContext;
|
||||||
import org.apache.lucene.search.ConstantScoreScorer;
|
import org.apache.lucene.search.ConstantScoreScorer;
|
||||||
|
@ -198,6 +200,112 @@ public final class LongRange extends Range {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static class MultiValueSourceQuery extends Query {
|
||||||
|
private final LongRange range;
|
||||||
|
private final Query fastMatchQuery;
|
||||||
|
private final MultiLongValuesSource valuesSource;
|
||||||
|
|
||||||
|
MultiValueSourceQuery(
|
||||||
|
LongRange range, Query fastMatchQuery, MultiLongValuesSource valuesSource) {
|
||||||
|
this.range = range;
|
||||||
|
this.fastMatchQuery = fastMatchQuery;
|
||||||
|
this.valuesSource = valuesSource;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object other) {
|
||||||
|
return sameClassAs(other) && equalsTo(getClass().cast(other));
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean equalsTo(MultiValueSourceQuery other) {
|
||||||
|
return range.equals(other.range)
|
||||||
|
&& Objects.equals(fastMatchQuery, other.fastMatchQuery)
|
||||||
|
&& valuesSource.equals(other.valuesSource);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return classHash() + 31 * Objects.hash(range, fastMatchQuery, valuesSource);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString(String field) {
|
||||||
|
return "Filter(" + range.toString() + ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visit(QueryVisitor visitor) {
|
||||||
|
visitor.visitLeaf(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Query rewrite(IndexReader reader) throws IOException {
|
||||||
|
if (fastMatchQuery != null) {
|
||||||
|
final Query fastMatchRewritten = fastMatchQuery.rewrite(reader);
|
||||||
|
if (fastMatchRewritten != fastMatchQuery) {
|
||||||
|
return new MultiValueSourceQuery(range, fastMatchRewritten, valuesSource);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return super.rewrite(reader);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Weight createWeight(IndexSearcher searcher, ScoreMode scoreMode, float boost)
|
||||||
|
throws IOException {
|
||||||
|
final Weight fastMatchWeight =
|
||||||
|
fastMatchQuery == null
|
||||||
|
? null
|
||||||
|
: searcher.createWeight(fastMatchQuery, ScoreMode.COMPLETE_NO_SCORES, 1f);
|
||||||
|
|
||||||
|
return new ConstantScoreWeight(this, boost) {
|
||||||
|
@Override
|
||||||
|
public Scorer scorer(LeafReaderContext context) throws IOException {
|
||||||
|
final int maxDoc = context.reader().maxDoc();
|
||||||
|
|
||||||
|
final DocIdSetIterator approximation;
|
||||||
|
if (fastMatchWeight == null) {
|
||||||
|
approximation = DocIdSetIterator.all(maxDoc);
|
||||||
|
} else {
|
||||||
|
Scorer s = fastMatchWeight.scorer(context);
|
||||||
|
if (s == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
approximation = s.iterator();
|
||||||
|
}
|
||||||
|
|
||||||
|
final MultiLongValues values = valuesSource.getValues(context);
|
||||||
|
final TwoPhaseIterator twoPhase =
|
||||||
|
new TwoPhaseIterator(approximation) {
|
||||||
|
@Override
|
||||||
|
public boolean matches() throws IOException {
|
||||||
|
if (values.advanceExact(approximation.docID()) == false) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < values.getValueCount(); i++) {
|
||||||
|
if (range.accept(values.nextValue())) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public float matchCost() {
|
||||||
|
return 100; // TODO: use cost of range.accept()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return new ConstantScoreScorer(this, score(), scoreMode, twoPhase);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isCacheable(LeafReaderContext ctx) {
|
||||||
|
return valuesSource.isCacheable(ctx);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a Query that matches documents in this range
|
* Create a Query that matches documents in this range
|
||||||
*
|
*
|
||||||
|
@ -213,4 +321,25 @@ public final class LongRange extends Range {
|
||||||
public Query getQuery(Query fastMatchQuery, LongValuesSource valueSource) {
|
public Query getQuery(Query fastMatchQuery, LongValuesSource valueSource) {
|
||||||
return new ValueSourceQuery(this, fastMatchQuery, valueSource);
|
return new ValueSourceQuery(this, fastMatchQuery, valueSource);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a Query that matches documents in this range
|
||||||
|
*
|
||||||
|
* <p>The query will check all documents that match the provided match query, or every document in
|
||||||
|
* the index if the match query is null.
|
||||||
|
*
|
||||||
|
* <p>If the value source is static, eg an indexed numeric field, it may be faster to use {@link
|
||||||
|
* org.apache.lucene.search.PointRangeQuery}
|
||||||
|
*
|
||||||
|
* @param fastMatchQuery a query to use as a filter
|
||||||
|
* @param valuesSource the source of values for the range check
|
||||||
|
*/
|
||||||
|
public Query getQuery(Query fastMatchQuery, MultiLongValuesSource valuesSource) {
|
||||||
|
LongValuesSource singleValues = MultiLongValuesSource.unwrapSingleton(valuesSource);
|
||||||
|
if (singleValues != null) {
|
||||||
|
return new ValueSourceQuery(this, fastMatchQuery, singleValues);
|
||||||
|
} else {
|
||||||
|
return new MultiValueSourceQuery(this, fastMatchQuery, valuesSource);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,6 +21,8 @@ import java.util.List;
|
||||||
import org.apache.lucene.facet.Facets;
|
import org.apache.lucene.facet.Facets;
|
||||||
import org.apache.lucene.facet.FacetsCollector;
|
import org.apache.lucene.facet.FacetsCollector;
|
||||||
import org.apache.lucene.facet.FacetsCollector.MatchingDocs;
|
import org.apache.lucene.facet.FacetsCollector.MatchingDocs;
|
||||||
|
import org.apache.lucene.facet.MultiLongValues;
|
||||||
|
import org.apache.lucene.facet.MultiLongValuesSource;
|
||||||
import org.apache.lucene.index.NumericDocValues;
|
import org.apache.lucene.index.NumericDocValues;
|
||||||
import org.apache.lucene.index.SortedNumericDocValues;
|
import org.apache.lucene.index.SortedNumericDocValues;
|
||||||
import org.apache.lucene.search.DocIdSetIterator;
|
import org.apache.lucene.search.DocIdSetIterator;
|
||||||
|
@ -45,7 +47,7 @@ public class LongRangeFacetCounts extends RangeFacetCounts {
|
||||||
*/
|
*/
|
||||||
public LongRangeFacetCounts(String field, FacetsCollector hits, LongRange... ranges)
|
public LongRangeFacetCounts(String field, FacetsCollector hits, LongRange... ranges)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
this(field, null, hits, ranges);
|
this(field, (LongValuesSource) null, hits, ranges);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -58,6 +60,17 @@ public class LongRangeFacetCounts extends RangeFacetCounts {
|
||||||
this(field, valueSource, hits, null, ranges);
|
this(field, valueSource, hits, null, ranges);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create {@code LongRangeFacetCounts}, using the provided {@link MultiLongValuesSource} if
|
||||||
|
* non-null. If {@code valuesSource} is null, doc values from the provided {@code field} will be
|
||||||
|
* used.
|
||||||
|
*/
|
||||||
|
public LongRangeFacetCounts(
|
||||||
|
String field, MultiLongValuesSource valuesSource, FacetsCollector hits, LongRange... ranges)
|
||||||
|
throws IOException {
|
||||||
|
this(field, valuesSource, hits, null, ranges);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create {@code LongRangeFacetCounts}, using the provided {@link LongValuesSource} if non-null.
|
* Create {@code LongRangeFacetCounts}, using the provided {@link LongValuesSource} if non-null.
|
||||||
* If {@code valueSource} is null, doc values from the provided {@code field} will be used. Use
|
* If {@code valueSource} is null, doc values from the provided {@code field} will be used. Use
|
||||||
|
@ -83,12 +96,35 @@ public class LongRangeFacetCounts extends RangeFacetCounts {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Counts from the provided valueSource.
|
* Create {@code LongRangeFacetCounts}, using the provided {@link MultiLongValuesSource} if
|
||||||
*
|
* non-null. If {@code valuesSource} is null, doc values from the provided {@code field} will be
|
||||||
* <p>TODO: Seems like we could extract this into RangeFacetCounts and make the logic common
|
* used. Use the provided {@code Query} as a fastmatch: only documents passing the filter are
|
||||||
* between this class and DoubleRangeFacetCounts somehow. The blocker right now is that this
|
* checked for the matching ranges, which is helpful when the provided {@link LongValuesSource} is
|
||||||
* implementation expects LongValueSource and DoubleRangeFacetCounts expects DoubleValueSource.
|
* costly per-document, such as a geo distance.
|
||||||
*/
|
*/
|
||||||
|
public LongRangeFacetCounts(
|
||||||
|
String field,
|
||||||
|
MultiLongValuesSource valuesSource,
|
||||||
|
FacetsCollector hits,
|
||||||
|
Query fastMatchQuery,
|
||||||
|
LongRange... ranges)
|
||||||
|
throws IOException {
|
||||||
|
super(field, ranges, fastMatchQuery);
|
||||||
|
// use the provided valueSource if non-null, otherwise use the doc values associated with the
|
||||||
|
// field
|
||||||
|
if (valuesSource != null) {
|
||||||
|
LongValuesSource singleValues = MultiLongValuesSource.unwrapSingleton(valuesSource);
|
||||||
|
if (singleValues != null) {
|
||||||
|
count(singleValues, hits.getMatchingDocs());
|
||||||
|
} else {
|
||||||
|
count(valuesSource, hits.getMatchingDocs());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
count(field, hits.getMatchingDocs());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Counts from the provided valueSource. */
|
||||||
private void count(LongValuesSource valueSource, List<MatchingDocs> matchingDocs)
|
private void count(LongValuesSource valueSource, List<MatchingDocs> matchingDocs)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
|
|
||||||
|
@ -123,6 +159,54 @@ public class LongRangeFacetCounts extends RangeFacetCounts {
|
||||||
totCount -= missingCount;
|
totCount -= missingCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Counts from the provided valueSource. */
|
||||||
|
private void count(MultiLongValuesSource valueSource, List<MatchingDocs> matchingDocs)
|
||||||
|
throws IOException {
|
||||||
|
|
||||||
|
LongRange[] ranges = getLongRanges();
|
||||||
|
|
||||||
|
LongRangeCounter counter = LongRangeCounter.create(ranges, counts);
|
||||||
|
|
||||||
|
for (MatchingDocs hits : matchingDocs) {
|
||||||
|
MultiLongValues multiValues = valueSource.getValues(hits.context);
|
||||||
|
|
||||||
|
final DocIdSetIterator it = createIterator(hits);
|
||||||
|
if (it == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int doc = it.nextDoc(); doc != DocIdSetIterator.NO_MORE_DOCS; ) {
|
||||||
|
// Skip missing docs:
|
||||||
|
if (multiValues.advanceExact(doc)) {
|
||||||
|
long limit = multiValues.getValueCount();
|
||||||
|
// optimize single-valued case:
|
||||||
|
if (limit == 1) {
|
||||||
|
counter.addSingleValued(multiValues.nextValue());
|
||||||
|
totCount++;
|
||||||
|
} else {
|
||||||
|
counter.startMultiValuedDoc();
|
||||||
|
long previous = 0;
|
||||||
|
for (int i = 0; i < limit; i++) {
|
||||||
|
long val = multiValues.nextValue();
|
||||||
|
if (i == 0 || val != previous) {
|
||||||
|
counter.addMultiValued(val);
|
||||||
|
previous = val;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (counter.endMultiValuedDoc()) {
|
||||||
|
totCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
doc = it.nextDoc();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int missingCount = counter.finish();
|
||||||
|
totCount -= missingCount;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected LongRange[] getLongRanges() {
|
protected LongRange[] getLongRanges() {
|
||||||
return (LongRange[]) this.ranges;
|
return (LongRange[]) this.ranges;
|
||||||
|
|
|
@ -0,0 +1,199 @@
|
||||||
|
/*
|
||||||
|
* 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.facet;
|
||||||
|
|
||||||
|
import com.carrotsearch.randomizedtesting.generators.RandomNumbers;
|
||||||
|
import java.io.IOException;
|
||||||
|
import org.apache.lucene.document.Document;
|
||||||
|
import org.apache.lucene.document.DoubleDocValuesField;
|
||||||
|
import org.apache.lucene.document.FloatDocValuesField;
|
||||||
|
import org.apache.lucene.document.NumericDocValuesField;
|
||||||
|
import org.apache.lucene.document.SortedNumericDocValuesField;
|
||||||
|
import org.apache.lucene.index.IndexReader;
|
||||||
|
import org.apache.lucene.index.NumericDocValues;
|
||||||
|
import org.apache.lucene.index.SortedNumericDocValues;
|
||||||
|
import org.apache.lucene.search.DoubleValues;
|
||||||
|
import org.apache.lucene.search.IndexSearcher;
|
||||||
|
import org.apache.lucene.search.LongValues;
|
||||||
|
import org.apache.lucene.store.Directory;
|
||||||
|
import org.apache.lucene.tests.index.RandomIndexWriter;
|
||||||
|
import org.apache.lucene.tests.util.LuceneTestCase;
|
||||||
|
|
||||||
|
public abstract class MultiValuesSourceTestCase extends LuceneTestCase {
|
||||||
|
protected Directory dir;
|
||||||
|
protected IndexReader reader;
|
||||||
|
protected IndexSearcher searcher;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setUp() throws Exception {
|
||||||
|
super.setUp();
|
||||||
|
|
||||||
|
dir = newDirectory();
|
||||||
|
RandomIndexWriter iw = new RandomIndexWriter(random(), dir);
|
||||||
|
|
||||||
|
int numDocs = RandomNumbers.randomIntBetween(random(), 100, 1000);
|
||||||
|
for (int i = 0; i < numDocs; i++) {
|
||||||
|
Document doc = new Document();
|
||||||
|
|
||||||
|
if (random().nextInt(10) < 8) {
|
||||||
|
doc.add(new NumericDocValuesField("single_int", random().nextInt()));
|
||||||
|
}
|
||||||
|
if (random().nextInt(10) < 8) {
|
||||||
|
doc.add(new NumericDocValuesField("single_long", random().nextLong()));
|
||||||
|
}
|
||||||
|
if (random().nextInt(10) < 8) {
|
||||||
|
doc.add(new FloatDocValuesField("single_float", random().nextFloat()));
|
||||||
|
}
|
||||||
|
if (random().nextInt(10) < 8) {
|
||||||
|
doc.add(new DoubleDocValuesField("single_double", random().nextDouble()));
|
||||||
|
}
|
||||||
|
|
||||||
|
int limit = RandomNumbers.randomIntBetween(random(), 0, 100);
|
||||||
|
for (int j = 0; j < limit; j++) {
|
||||||
|
doc.add(new SortedNumericDocValuesField("multi_int", random().nextInt()));
|
||||||
|
}
|
||||||
|
limit = RandomNumbers.randomIntBetween(random(), 0, 100);
|
||||||
|
for (int j = 0; j < limit; j++) {
|
||||||
|
doc.add(new SortedNumericDocValuesField("multi_long", random().nextLong()));
|
||||||
|
}
|
||||||
|
limit = RandomNumbers.randomIntBetween(random(), 0, 100);
|
||||||
|
for (int j = 0; j < limit; j++) {
|
||||||
|
doc.add(
|
||||||
|
new SortedNumericDocValuesField(
|
||||||
|
"multi_float", Float.floatToRawIntBits(random().nextFloat())));
|
||||||
|
}
|
||||||
|
limit = RandomNumbers.randomIntBetween(random(), 0, 100);
|
||||||
|
for (int j = 0; j < limit; j++) {
|
||||||
|
doc.add(
|
||||||
|
new SortedNumericDocValuesField(
|
||||||
|
"multi_double", Double.doubleToRawLongBits(random().nextDouble())));
|
||||||
|
}
|
||||||
|
|
||||||
|
iw.addDocument(doc);
|
||||||
|
|
||||||
|
if (i % 100 == 0 && random().nextBoolean()) {
|
||||||
|
iw.commit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
reader = iw.getReader();
|
||||||
|
iw.close();
|
||||||
|
searcher = newSearcher(reader);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void tearDown() throws Exception {
|
||||||
|
reader.close();
|
||||||
|
dir.close();
|
||||||
|
super.tearDown();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void validateFieldBasedSource(NumericDocValues docValues, LongValues values, int maxDoc)
|
||||||
|
throws IOException {
|
||||||
|
for (int doc = 0; doc < maxDoc; doc++) {
|
||||||
|
boolean hasValues = docValues.advanceExact(doc);
|
||||||
|
assertEquals(hasValues, values.advanceExact(doc));
|
||||||
|
if (hasValues) {
|
||||||
|
assertEquals(docValues.longValue(), values.longValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void validateFieldBasedSource(
|
||||||
|
SortedNumericDocValues docValues, MultiLongValues values, int maxDoc) throws IOException {
|
||||||
|
for (int doc = 0; doc < maxDoc; doc++) {
|
||||||
|
boolean hasValues = docValues.advanceExact(doc);
|
||||||
|
assertEquals(hasValues, values.advanceExact(doc));
|
||||||
|
if (hasValues) {
|
||||||
|
int valueCount = docValues.docValueCount();
|
||||||
|
assertEquals(valueCount, values.getValueCount());
|
||||||
|
for (int i = 0; i < valueCount; i++) {
|
||||||
|
assertEquals(docValues.nextValue(), values.nextValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void validateFieldBasedSource(
|
||||||
|
NumericDocValues docValues, DoubleValues values, int maxDoc) throws IOException {
|
||||||
|
for (int doc = 0; doc < maxDoc; doc++) {
|
||||||
|
boolean hasValues = docValues.advanceExact(doc);
|
||||||
|
assertEquals(hasValues, values.advanceExact(doc));
|
||||||
|
if (hasValues) {
|
||||||
|
assertEquals((double) docValues.longValue(), values.doubleValue(), 0.00001);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void validateFieldBasedSource(
|
||||||
|
SortedNumericDocValues docValues, MultiDoubleValues values, int maxDoc) throws IOException {
|
||||||
|
for (int doc = 0; doc < maxDoc; doc++) {
|
||||||
|
boolean hasValues = docValues.advanceExact(doc);
|
||||||
|
assertEquals(hasValues, values.advanceExact(doc));
|
||||||
|
if (hasValues) {
|
||||||
|
int valueCount = docValues.docValueCount();
|
||||||
|
assertEquals(valueCount, values.getValueCount());
|
||||||
|
for (int i = 0; i < valueCount; i++) {
|
||||||
|
assertEquals((double) docValues.nextValue(), values.nextValue(), 0.00001);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void validateFieldBasedSource(
|
||||||
|
NumericDocValues docValues, DoubleValues values, int maxDoc, boolean useDoublePrecision)
|
||||||
|
throws IOException {
|
||||||
|
for (int doc = 0; doc < maxDoc; doc++) {
|
||||||
|
boolean hasValues = docValues.advanceExact(doc);
|
||||||
|
assertEquals(hasValues, values.advanceExact(doc));
|
||||||
|
if (hasValues) {
|
||||||
|
if (useDoublePrecision) {
|
||||||
|
long asLong = Double.doubleToRawLongBits(values.doubleValue());
|
||||||
|
assertEquals(docValues.longValue(), asLong);
|
||||||
|
} else {
|
||||||
|
int asInt = Float.floatToRawIntBits((float) values.doubleValue());
|
||||||
|
assertEquals((int) docValues.longValue(), asInt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void validateFieldBasedSource(
|
||||||
|
SortedNumericDocValues docValues,
|
||||||
|
MultiDoubleValues values,
|
||||||
|
int maxDoc,
|
||||||
|
boolean useDoublePrecision)
|
||||||
|
throws IOException {
|
||||||
|
for (int doc = 0; doc < maxDoc; doc++) {
|
||||||
|
boolean hasValues = docValues.advanceExact(doc);
|
||||||
|
assertEquals(hasValues, values.advanceExact(doc));
|
||||||
|
if (hasValues) {
|
||||||
|
int valueCount = docValues.docValueCount();
|
||||||
|
assertEquals(valueCount, values.getValueCount());
|
||||||
|
for (int i = 0; i < valueCount; i++) {
|
||||||
|
if (useDoublePrecision) {
|
||||||
|
long asLong = Double.doubleToRawLongBits(values.nextValue());
|
||||||
|
assertEquals(docValues.nextValue(), asLong);
|
||||||
|
} else {
|
||||||
|
int asInt = Float.floatToRawIntBits((float) values.nextValue());
|
||||||
|
assertEquals(docValues.nextValue(), asInt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -236,8 +236,19 @@ public class TestLongValueFacetCounts extends LuceneTestCase {
|
||||||
if (VERBOSE) {
|
if (VERBOSE) {
|
||||||
System.out.println(" use value source");
|
System.out.println(" use value source");
|
||||||
}
|
}
|
||||||
|
if (random().nextBoolean()) {
|
||||||
facetCounts =
|
facetCounts =
|
||||||
new LongValueFacetCounts("field", LongValuesSource.fromLongField("field"), fc);
|
new LongValueFacetCounts("field", LongValuesSource.fromLongField("field"), fc);
|
||||||
|
} else if (random().nextBoolean()) {
|
||||||
|
facetCounts =
|
||||||
|
new LongValueFacetCounts("field", MultiLongValuesSource.fromLongField("field"), fc);
|
||||||
|
} else {
|
||||||
|
facetCounts =
|
||||||
|
new LongValueFacetCounts(
|
||||||
|
"field",
|
||||||
|
MultiLongValuesSource.fromSingleValued(LongValuesSource.fromLongField("field")),
|
||||||
|
fc);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
if (VERBOSE) {
|
if (VERBOSE) {
|
||||||
System.out.println(" use doc values");
|
System.out.println(" use doc values");
|
||||||
|
@ -250,8 +261,19 @@ public class TestLongValueFacetCounts extends LuceneTestCase {
|
||||||
if (VERBOSE) {
|
if (VERBOSE) {
|
||||||
System.out.println(" count all value source");
|
System.out.println(" count all value source");
|
||||||
}
|
}
|
||||||
|
if (random().nextBoolean()) {
|
||||||
facetCounts =
|
facetCounts =
|
||||||
new LongValueFacetCounts("field", LongValuesSource.fromLongField("field"), r);
|
new LongValueFacetCounts("field", LongValuesSource.fromLongField("field"), r);
|
||||||
|
} else if (random().nextBoolean()) {
|
||||||
|
facetCounts =
|
||||||
|
new LongValueFacetCounts("field", MultiLongValuesSource.fromLongField("field"), r);
|
||||||
|
} else {
|
||||||
|
facetCounts =
|
||||||
|
new LongValueFacetCounts(
|
||||||
|
"field",
|
||||||
|
MultiLongValuesSource.fromSingleValued(LongValuesSource.fromLongField("field")),
|
||||||
|
r);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
if (VERBOSE) {
|
if (VERBOSE) {
|
||||||
System.out.println(" count all doc values");
|
System.out.println(" count all doc values");
|
||||||
|
@ -320,8 +342,19 @@ public class TestLongValueFacetCounts extends LuceneTestCase {
|
||||||
if (VERBOSE) {
|
if (VERBOSE) {
|
||||||
System.out.println(" use value source");
|
System.out.println(" use value source");
|
||||||
}
|
}
|
||||||
|
if (random().nextBoolean()) {
|
||||||
facetCounts =
|
facetCounts =
|
||||||
new LongValueFacetCounts("field", LongValuesSource.fromLongField("field"), fc);
|
new LongValueFacetCounts("field", LongValuesSource.fromLongField("field"), fc);
|
||||||
|
} else if (random().nextBoolean()) {
|
||||||
|
facetCounts =
|
||||||
|
new LongValueFacetCounts("field", MultiLongValuesSource.fromLongField("field"), fc);
|
||||||
|
} else {
|
||||||
|
facetCounts =
|
||||||
|
new LongValueFacetCounts(
|
||||||
|
"field",
|
||||||
|
MultiLongValuesSource.fromSingleValued(LongValuesSource.fromLongField("field")),
|
||||||
|
fc);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
expected = new HashMap<>();
|
expected = new HashMap<>();
|
||||||
|
@ -476,13 +509,23 @@ public class TestLongValueFacetCounts extends LuceneTestCase {
|
||||||
if (VERBOSE) {
|
if (VERBOSE) {
|
||||||
System.out.println(" use doc values");
|
System.out.println(" use doc values");
|
||||||
}
|
}
|
||||||
|
if (random().nextBoolean()) {
|
||||||
facetCounts = new LongValueFacetCounts("field", fc);
|
facetCounts = new LongValueFacetCounts("field", fc);
|
||||||
|
} else {
|
||||||
|
facetCounts =
|
||||||
|
new LongValueFacetCounts("field", MultiLongValuesSource.fromLongField("field"), fc);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// optimized count all:
|
// optimized count all:
|
||||||
if (VERBOSE) {
|
if (VERBOSE) {
|
||||||
System.out.println(" count all doc values");
|
System.out.println(" count all doc values");
|
||||||
}
|
}
|
||||||
|
if (random().nextBoolean()) {
|
||||||
facetCounts = new LongValueFacetCounts("field", r);
|
facetCounts = new LongValueFacetCounts("field", r);
|
||||||
|
} else {
|
||||||
|
facetCounts =
|
||||||
|
new LongValueFacetCounts("field", MultiLongValuesSource.fromLongField("field"), r);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
FacetResult actual = facetCounts.getAllChildrenSortByValue();
|
FacetResult actual = facetCounts.getAllChildrenSortByValue();
|
||||||
|
@ -536,8 +579,12 @@ public class TestLongValueFacetCounts extends LuceneTestCase {
|
||||||
|
|
||||||
fc = new FacetsCollector();
|
fc = new FacetsCollector();
|
||||||
s.search(IntPoint.newRangeQuery("id", minId, maxId), fc);
|
s.search(IntPoint.newRangeQuery("id", minId, maxId), fc);
|
||||||
// cannot use value source here because we are multi valued
|
if (random().nextBoolean()) {
|
||||||
facetCounts = new LongValueFacetCounts("field", fc);
|
facetCounts = new LongValueFacetCounts("field", fc);
|
||||||
|
} else {
|
||||||
|
facetCounts =
|
||||||
|
new LongValueFacetCounts("field", MultiLongValuesSource.fromLongField("field"), fc);
|
||||||
|
}
|
||||||
|
|
||||||
expected = new HashMap<>();
|
expected = new HashMap<>();
|
||||||
expectedTotalCount = 0;
|
expectedTotalCount = 0;
|
||||||
|
|
|
@ -0,0 +1,169 @@
|
||||||
|
/*
|
||||||
|
* 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.facet;
|
||||||
|
|
||||||
|
import org.apache.lucene.index.DocValues;
|
||||||
|
import org.apache.lucene.index.LeafReaderContext;
|
||||||
|
import org.apache.lucene.index.NumericDocValues;
|
||||||
|
import org.apache.lucene.index.SortedNumericDocValues;
|
||||||
|
import org.apache.lucene.search.DoubleValues;
|
||||||
|
import org.apache.lucene.search.DoubleValuesSource;
|
||||||
|
|
||||||
|
public class TestMultiDoubleValuesSource extends MultiValuesSourceTestCase {
|
||||||
|
|
||||||
|
public void testRandom() throws Exception {
|
||||||
|
MultiDoubleValuesSource valuesSource;
|
||||||
|
|
||||||
|
valuesSource = MultiDoubleValuesSource.fromIntField("single_int");
|
||||||
|
assertNotNull(valuesSource);
|
||||||
|
for (LeafReaderContext ctx : reader.leaves()) {
|
||||||
|
SortedNumericDocValues docValues = DocValues.getSortedNumeric(ctx.reader(), "single_int");
|
||||||
|
MultiDoubleValues values = valuesSource.getValues(ctx);
|
||||||
|
validateFieldBasedSource(docValues, values, ctx.reader().maxDoc());
|
||||||
|
}
|
||||||
|
|
||||||
|
valuesSource = MultiDoubleValuesSource.fromLongField("single_long");
|
||||||
|
assertNotNull(valuesSource);
|
||||||
|
for (LeafReaderContext ctx : reader.leaves()) {
|
||||||
|
SortedNumericDocValues docValues = DocValues.getSortedNumeric(ctx.reader(), "single_long");
|
||||||
|
MultiDoubleValues values = valuesSource.getValues(ctx);
|
||||||
|
validateFieldBasedSource(docValues, values, ctx.reader().maxDoc());
|
||||||
|
}
|
||||||
|
|
||||||
|
valuesSource = MultiDoubleValuesSource.fromIntField("multi_int");
|
||||||
|
for (LeafReaderContext ctx : reader.leaves()) {
|
||||||
|
SortedNumericDocValues docValues = DocValues.getSortedNumeric(ctx.reader(), "multi_int");
|
||||||
|
MultiDoubleValues values = valuesSource.getValues(ctx);
|
||||||
|
validateFieldBasedSource(docValues, values, ctx.reader().maxDoc());
|
||||||
|
}
|
||||||
|
|
||||||
|
valuesSource = MultiDoubleValuesSource.fromLongField("multi_long");
|
||||||
|
for (LeafReaderContext ctx : reader.leaves()) {
|
||||||
|
SortedNumericDocValues docValues = DocValues.getSortedNumeric(ctx.reader(), "multi_long");
|
||||||
|
MultiDoubleValues values = valuesSource.getValues(ctx);
|
||||||
|
validateFieldBasedSource(docValues, values, ctx.reader().maxDoc());
|
||||||
|
}
|
||||||
|
|
||||||
|
valuesSource = MultiDoubleValuesSource.fromFloatField("single_float");
|
||||||
|
assertNotNull(valuesSource);
|
||||||
|
for (LeafReaderContext ctx : reader.leaves()) {
|
||||||
|
SortedNumericDocValues docValues = DocValues.getSortedNumeric(ctx.reader(), "single_float");
|
||||||
|
MultiDoubleValues values = valuesSource.getValues(ctx);
|
||||||
|
validateFieldBasedSource(docValues, values, ctx.reader().maxDoc(), false);
|
||||||
|
}
|
||||||
|
|
||||||
|
valuesSource = MultiDoubleValuesSource.fromDoubleField("single_double");
|
||||||
|
assertNotNull(valuesSource);
|
||||||
|
for (LeafReaderContext ctx : reader.leaves()) {
|
||||||
|
SortedNumericDocValues docValues = DocValues.getSortedNumeric(ctx.reader(), "single_double");
|
||||||
|
MultiDoubleValues values = valuesSource.getValues(ctx);
|
||||||
|
validateFieldBasedSource(docValues, values, ctx.reader().maxDoc(), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
valuesSource = MultiDoubleValuesSource.fromFloatField("multi_float");
|
||||||
|
for (LeafReaderContext ctx : reader.leaves()) {
|
||||||
|
SortedNumericDocValues docValues = DocValues.getSortedNumeric(ctx.reader(), "multi_float");
|
||||||
|
MultiDoubleValues values = valuesSource.getValues(ctx);
|
||||||
|
validateFieldBasedSource(docValues, values, ctx.reader().maxDoc(), false);
|
||||||
|
}
|
||||||
|
|
||||||
|
valuesSource = MultiDoubleValuesSource.fromDoubleField("multi_double");
|
||||||
|
for (LeafReaderContext ctx : reader.leaves()) {
|
||||||
|
SortedNumericDocValues docValues = DocValues.getSortedNumeric(ctx.reader(), "multi_double");
|
||||||
|
MultiDoubleValues values = valuesSource.getValues(ctx);
|
||||||
|
validateFieldBasedSource(docValues, values, ctx.reader().maxDoc(), true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testFromSingleValued() throws Exception {
|
||||||
|
MultiDoubleValuesSource valuesSource;
|
||||||
|
DoubleValuesSource singleton;
|
||||||
|
|
||||||
|
valuesSource =
|
||||||
|
MultiDoubleValuesSource.fromSingleValued(DoubleValuesSource.fromFloatField("single_float"));
|
||||||
|
singleton = MultiDoubleValuesSource.unwrapSingleton(valuesSource);
|
||||||
|
assertNotNull(singleton);
|
||||||
|
for (LeafReaderContext ctx : reader.leaves()) {
|
||||||
|
SortedNumericDocValues docValues = DocValues.getSortedNumeric(ctx.reader(), "single_float");
|
||||||
|
MultiDoubleValues values = valuesSource.getValues(ctx);
|
||||||
|
validateFieldBasedSource(docValues, values, ctx.reader().maxDoc(), false);
|
||||||
|
|
||||||
|
NumericDocValues singletonDv = DocValues.getNumeric(ctx.reader(), "single_float");
|
||||||
|
DoubleValues singletonVals = singleton.getValues(ctx, null);
|
||||||
|
validateFieldBasedSource(singletonDv, singletonVals, ctx.reader().maxDoc(), false);
|
||||||
|
}
|
||||||
|
|
||||||
|
valuesSource =
|
||||||
|
MultiDoubleValuesSource.fromSingleValued(
|
||||||
|
DoubleValuesSource.fromDoubleField("single_double"));
|
||||||
|
singleton = MultiDoubleValuesSource.unwrapSingleton(valuesSource);
|
||||||
|
assertNotNull(singleton);
|
||||||
|
for (LeafReaderContext ctx : reader.leaves()) {
|
||||||
|
SortedNumericDocValues docValues = DocValues.getSortedNumeric(ctx.reader(), "single_double");
|
||||||
|
MultiDoubleValues values = valuesSource.getValues(ctx);
|
||||||
|
validateFieldBasedSource(docValues, values, ctx.reader().maxDoc(), true);
|
||||||
|
|
||||||
|
NumericDocValues singletonDv = DocValues.getNumeric(ctx.reader(), "single_double");
|
||||||
|
DoubleValues singletonVals = singleton.getValues(ctx, null);
|
||||||
|
validateFieldBasedSource(singletonDv, singletonVals, ctx.reader().maxDoc(), true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testCacheable() {
|
||||||
|
MultiDoubleValuesSource valuesSource = MultiDoubleValuesSource.fromDoubleField("multi_double");
|
||||||
|
for (LeafReaderContext ctx : searcher.getIndexReader().leaves()) {
|
||||||
|
assertEquals(DocValues.isCacheable(ctx, "multi_double"), valuesSource.isCacheable(ctx));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testEqualsAndHashcode() {
|
||||||
|
MultiDoubleValuesSource valuesSource1 = MultiDoubleValuesSource.fromLongField("multi_long");
|
||||||
|
MultiDoubleValuesSource valuesSource2 = MultiDoubleValuesSource.fromLongField("multi_long");
|
||||||
|
MultiDoubleValuesSource valuesSource3 = MultiDoubleValuesSource.fromLongField("multi_int");
|
||||||
|
assertEquals(valuesSource1, valuesSource2);
|
||||||
|
assertNotEquals(valuesSource1, valuesSource3);
|
||||||
|
assertEquals(valuesSource1.hashCode(), valuesSource2.hashCode());
|
||||||
|
assertNotEquals(valuesSource1.hashCode(), valuesSource3.hashCode());
|
||||||
|
|
||||||
|
valuesSource1 =
|
||||||
|
MultiDoubleValuesSource.fromSingleValued(DoubleValuesSource.fromLongField("single_long"));
|
||||||
|
valuesSource2 =
|
||||||
|
MultiDoubleValuesSource.fromSingleValued(DoubleValuesSource.fromLongField("single_long"));
|
||||||
|
valuesSource3 =
|
||||||
|
MultiDoubleValuesSource.fromSingleValued(DoubleValuesSource.fromLongField("single_int"));
|
||||||
|
assertEquals(valuesSource1, valuesSource2);
|
||||||
|
assertNotEquals(valuesSource1, valuesSource3);
|
||||||
|
assertEquals(valuesSource1.hashCode(), valuesSource2.hashCode());
|
||||||
|
assertNotEquals(valuesSource1.hashCode(), valuesSource3.hashCode());
|
||||||
|
|
||||||
|
DoubleValuesSource singleton1 = MultiDoubleValuesSource.unwrapSingleton(valuesSource1);
|
||||||
|
DoubleValuesSource singleton2 = MultiDoubleValuesSource.unwrapSingleton(valuesSource2);
|
||||||
|
DoubleValuesSource singleton3 = MultiDoubleValuesSource.unwrapSingleton(valuesSource3);
|
||||||
|
assertEquals(singleton1, singleton2);
|
||||||
|
assertNotEquals(singleton1, singleton3);
|
||||||
|
assertEquals(singleton1.hashCode(), singleton2.hashCode());
|
||||||
|
assertNotEquals(singleton1.hashCode(), singleton3.hashCode());
|
||||||
|
|
||||||
|
valuesSource1 = MultiDoubleValuesSource.fromField("single_long", Long::valueOf);
|
||||||
|
valuesSource2 = MultiDoubleValuesSource.fromField("single_long", v -> -1 * v);
|
||||||
|
valuesSource3 = MultiDoubleValuesSource.fromField("single_int", Long::valueOf);
|
||||||
|
assertNotEquals(valuesSource1, valuesSource2);
|
||||||
|
assertNotEquals(valuesSource1, valuesSource3);
|
||||||
|
assertNotEquals(valuesSource1.hashCode(), valuesSource2.hashCode());
|
||||||
|
assertNotEquals(valuesSource1.hashCode(), valuesSource3.hashCode());
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,148 @@
|
||||||
|
/*
|
||||||
|
* 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.facet;
|
||||||
|
|
||||||
|
import org.apache.lucene.index.DocValues;
|
||||||
|
import org.apache.lucene.index.LeafReaderContext;
|
||||||
|
import org.apache.lucene.index.NumericDocValues;
|
||||||
|
import org.apache.lucene.index.SortedNumericDocValues;
|
||||||
|
import org.apache.lucene.search.LongValues;
|
||||||
|
import org.apache.lucene.search.LongValuesSource;
|
||||||
|
|
||||||
|
public class TestMultiLongValuesSource extends MultiValuesSourceTestCase {
|
||||||
|
|
||||||
|
public void testRandom() throws Exception {
|
||||||
|
MultiLongValuesSource valuesSource;
|
||||||
|
|
||||||
|
valuesSource = MultiLongValuesSource.fromIntField("single_int");
|
||||||
|
assertNotNull(valuesSource);
|
||||||
|
for (LeafReaderContext ctx : reader.leaves()) {
|
||||||
|
SortedNumericDocValues docValues = DocValues.getSortedNumeric(ctx.reader(), "single_int");
|
||||||
|
MultiLongValues values = valuesSource.getValues(ctx);
|
||||||
|
validateFieldBasedSource(docValues, values, ctx.reader().maxDoc());
|
||||||
|
}
|
||||||
|
|
||||||
|
valuesSource = MultiLongValuesSource.fromLongField("single_long");
|
||||||
|
assertNotNull(valuesSource);
|
||||||
|
for (LeafReaderContext ctx : reader.leaves()) {
|
||||||
|
SortedNumericDocValues docValues = DocValues.getSortedNumeric(ctx.reader(), "single_long");
|
||||||
|
MultiLongValues values = valuesSource.getValues(ctx);
|
||||||
|
validateFieldBasedSource(docValues, values, ctx.reader().maxDoc());
|
||||||
|
}
|
||||||
|
|
||||||
|
valuesSource = MultiLongValuesSource.fromIntField("multi_int");
|
||||||
|
for (LeafReaderContext ctx : reader.leaves()) {
|
||||||
|
SortedNumericDocValues docValues = DocValues.getSortedNumeric(ctx.reader(), "multi_int");
|
||||||
|
MultiLongValues values = valuesSource.getValues(ctx);
|
||||||
|
validateFieldBasedSource(docValues, values, ctx.reader().maxDoc());
|
||||||
|
}
|
||||||
|
|
||||||
|
valuesSource = MultiLongValuesSource.fromLongField("multi_long");
|
||||||
|
for (LeafReaderContext ctx : reader.leaves()) {
|
||||||
|
SortedNumericDocValues docValues = DocValues.getSortedNumeric(ctx.reader(), "multi_long");
|
||||||
|
MultiLongValues values = valuesSource.getValues(ctx);
|
||||||
|
validateFieldBasedSource(docValues, values, ctx.reader().maxDoc());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testFromSingleValued() throws Exception {
|
||||||
|
MultiLongValuesSource valuesSource;
|
||||||
|
LongValuesSource singleton;
|
||||||
|
|
||||||
|
valuesSource =
|
||||||
|
MultiLongValuesSource.fromSingleValued(LongValuesSource.fromIntField("single_int"));
|
||||||
|
singleton = MultiLongValuesSource.unwrapSingleton(valuesSource);
|
||||||
|
assertNotNull(singleton);
|
||||||
|
for (LeafReaderContext ctx : reader.leaves()) {
|
||||||
|
SortedNumericDocValues docValues = DocValues.getSortedNumeric(ctx.reader(), "single_int");
|
||||||
|
MultiLongValues values = valuesSource.getValues(ctx);
|
||||||
|
validateFieldBasedSource(docValues, values, ctx.reader().maxDoc());
|
||||||
|
|
||||||
|
NumericDocValues singletonDv = DocValues.getNumeric(ctx.reader(), "single_int");
|
||||||
|
LongValues singletonVals = singleton.getValues(ctx, null);
|
||||||
|
validateFieldBasedSource(singletonDv, singletonVals, ctx.reader().maxDoc());
|
||||||
|
}
|
||||||
|
|
||||||
|
valuesSource =
|
||||||
|
MultiLongValuesSource.fromSingleValued(LongValuesSource.fromLongField("single_long"));
|
||||||
|
singleton = MultiLongValuesSource.unwrapSingleton(valuesSource);
|
||||||
|
assertNotNull(singleton);
|
||||||
|
for (LeafReaderContext ctx : reader.leaves()) {
|
||||||
|
SortedNumericDocValues docValues = DocValues.getSortedNumeric(ctx.reader(), "single_long");
|
||||||
|
MultiLongValues values = valuesSource.getValues(ctx);
|
||||||
|
validateFieldBasedSource(docValues, values, ctx.reader().maxDoc());
|
||||||
|
|
||||||
|
NumericDocValues singletonDv = DocValues.getNumeric(ctx.reader(), "single_long");
|
||||||
|
LongValues singletonVals = singleton.getValues(ctx, null);
|
||||||
|
validateFieldBasedSource(singletonDv, singletonVals, ctx.reader().maxDoc());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testToDouble() throws Exception {
|
||||||
|
MultiDoubleValuesSource valuesSource =
|
||||||
|
MultiLongValuesSource.fromLongField("multi_long").toMultiDoubleValuesSource();
|
||||||
|
for (LeafReaderContext ctx : reader.leaves()) {
|
||||||
|
SortedNumericDocValues docValues = DocValues.getSortedNumeric(ctx.reader(), "multi_long");
|
||||||
|
MultiDoubleValues values = valuesSource.getValues(ctx);
|
||||||
|
validateFieldBasedSource(docValues, values, ctx.reader().maxDoc());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testCacheable() {
|
||||||
|
MultiLongValuesSource valuesSource = MultiLongValuesSource.fromLongField("multi_long");
|
||||||
|
for (LeafReaderContext ctx : searcher.getIndexReader().leaves()) {
|
||||||
|
assertEquals(DocValues.isCacheable(ctx, "multi_long"), valuesSource.isCacheable(ctx));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testEqualsAndHashcode() {
|
||||||
|
MultiLongValuesSource valuesSource1 = MultiLongValuesSource.fromLongField("multi_long");
|
||||||
|
MultiLongValuesSource valuesSource2 = MultiLongValuesSource.fromLongField("multi_long");
|
||||||
|
MultiLongValuesSource valuesSource3 = MultiLongValuesSource.fromLongField("multi_int");
|
||||||
|
assertEquals(valuesSource1, valuesSource2);
|
||||||
|
assertNotEquals(valuesSource1, valuesSource3);
|
||||||
|
assertEquals(valuesSource1.hashCode(), valuesSource2.hashCode());
|
||||||
|
assertNotEquals(valuesSource1.hashCode(), valuesSource3.hashCode());
|
||||||
|
|
||||||
|
valuesSource1 =
|
||||||
|
MultiLongValuesSource.fromSingleValued(LongValuesSource.fromLongField("single_long"));
|
||||||
|
valuesSource2 =
|
||||||
|
MultiLongValuesSource.fromSingleValued(LongValuesSource.fromLongField("single_long"));
|
||||||
|
valuesSource3 =
|
||||||
|
MultiLongValuesSource.fromSingleValued(LongValuesSource.fromLongField("single_int"));
|
||||||
|
assertEquals(valuesSource1, valuesSource2);
|
||||||
|
assertNotEquals(valuesSource1, valuesSource3);
|
||||||
|
assertEquals(valuesSource1.hashCode(), valuesSource2.hashCode());
|
||||||
|
assertNotEquals(valuesSource1.hashCode(), valuesSource3.hashCode());
|
||||||
|
|
||||||
|
LongValuesSource singleton1 = MultiLongValuesSource.unwrapSingleton(valuesSource1);
|
||||||
|
LongValuesSource singleton2 = MultiLongValuesSource.unwrapSingleton(valuesSource2);
|
||||||
|
LongValuesSource singleton3 = MultiLongValuesSource.unwrapSingleton(valuesSource3);
|
||||||
|
assertEquals(singleton1, singleton2);
|
||||||
|
assertNotEquals(singleton1, singleton3);
|
||||||
|
assertEquals(singleton1.hashCode(), singleton2.hashCode());
|
||||||
|
assertNotEquals(singleton1.hashCode(), singleton3.hashCode());
|
||||||
|
|
||||||
|
MultiDoubleValuesSource doubleValuesSource1 = valuesSource1.toMultiDoubleValuesSource();
|
||||||
|
MultiDoubleValuesSource doubleValuesSource2 = valuesSource2.toMultiDoubleValuesSource();
|
||||||
|
MultiDoubleValuesSource doubleValuesSource3 = valuesSource3.toMultiDoubleValuesSource();
|
||||||
|
assertEquals(doubleValuesSource1, doubleValuesSource2);
|
||||||
|
assertNotEquals(doubleValuesSource1, doubleValuesSource3);
|
||||||
|
assertEquals(doubleValuesSource1.hashCode(), doubleValuesSource2.hashCode());
|
||||||
|
assertNotEquals(doubleValuesSource1.hashCode(), doubleValuesSource3.hashCode());
|
||||||
|
}
|
||||||
|
}
|
|
@ -38,7 +38,9 @@ import org.apache.lucene.facet.Facets;
|
||||||
import org.apache.lucene.facet.FacetsCollector;
|
import org.apache.lucene.facet.FacetsCollector;
|
||||||
import org.apache.lucene.facet.FacetsConfig;
|
import org.apache.lucene.facet.FacetsConfig;
|
||||||
import org.apache.lucene.facet.LabelAndValue;
|
import org.apache.lucene.facet.LabelAndValue;
|
||||||
|
import org.apache.lucene.facet.MultiDoubleValuesSource;
|
||||||
import org.apache.lucene.facet.MultiFacets;
|
import org.apache.lucene.facet.MultiFacets;
|
||||||
|
import org.apache.lucene.facet.MultiLongValuesSource;
|
||||||
import org.apache.lucene.facet.taxonomy.TaxonomyReader;
|
import org.apache.lucene.facet.taxonomy.TaxonomyReader;
|
||||||
import org.apache.lucene.facet.taxonomy.directory.DirectoryTaxonomyReader;
|
import org.apache.lucene.facet.taxonomy.directory.DirectoryTaxonomyReader;
|
||||||
import org.apache.lucene.facet.taxonomy.directory.DirectoryTaxonomyWriter;
|
import org.apache.lucene.facet.taxonomy.directory.DirectoryTaxonomyWriter;
|
||||||
|
@ -766,7 +768,45 @@ public class TestRangeFacetCounts extends FacetTestCase {
|
||||||
} else {
|
} else {
|
||||||
fastMatchQuery = null;
|
fastMatchQuery = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (random().nextBoolean()) {
|
||||||
LongValuesSource vs = LongValuesSource.fromLongField("field");
|
LongValuesSource vs = LongValuesSource.fromLongField("field");
|
||||||
|
MultiLongValuesSource mvs = MultiLongValuesSource.fromLongField("field");
|
||||||
|
Facets facets;
|
||||||
|
if (random().nextBoolean()) {
|
||||||
|
facets = new LongRangeFacetCounts("field", vs, sfc, fastMatchQuery, ranges);
|
||||||
|
} else if (random().nextBoolean()) {
|
||||||
|
facets = new LongRangeFacetCounts("field", mvs, sfc, fastMatchQuery, ranges);
|
||||||
|
} else {
|
||||||
|
facets =
|
||||||
|
new LongRangeFacetCounts(
|
||||||
|
"field", MultiLongValuesSource.fromSingleValued(vs), sfc, fastMatchQuery, ranges);
|
||||||
|
}
|
||||||
|
FacetResult result = facets.getTopChildren(10, "field");
|
||||||
|
assertEquals(numRange, result.labelValues.length);
|
||||||
|
for (int rangeID = 0; rangeID < numRange; rangeID++) {
|
||||||
|
if (VERBOSE) {
|
||||||
|
System.out.println(" range " + rangeID + " expectedCount=" + expectedCounts[rangeID]);
|
||||||
|
}
|
||||||
|
LabelAndValue subNode = result.labelValues[rangeID];
|
||||||
|
assertEquals("r" + rangeID, subNode.label);
|
||||||
|
assertEquals(expectedCounts[rangeID], subNode.value.intValue());
|
||||||
|
|
||||||
|
LongRange range = ranges[rangeID];
|
||||||
|
|
||||||
|
// Test drill-down:
|
||||||
|
DrillDownQuery ddq = new DrillDownQuery(config);
|
||||||
|
if (random().nextBoolean()) {
|
||||||
|
ddq.add("field", LongPoint.newRangeQuery("field", range.min, range.max));
|
||||||
|
} else if (random().nextBoolean()) {
|
||||||
|
ddq.add("field", range.getQuery(fastMatchQuery, mvs));
|
||||||
|
} else {
|
||||||
|
ddq.add("field", range.getQuery(fastMatchQuery, vs));
|
||||||
|
}
|
||||||
|
assertEquals(expectedCounts[rangeID], s.count(ddq));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
MultiLongValuesSource vs = MultiLongValuesSource.fromLongField("field");
|
||||||
Facets facets = new LongRangeFacetCounts("field", vs, sfc, fastMatchQuery, ranges);
|
Facets facets = new LongRangeFacetCounts("field", vs, sfc, fastMatchQuery, ranges);
|
||||||
FacetResult result = facets.getTopChildren(10, "field");
|
FacetResult result = facets.getTopChildren(10, "field");
|
||||||
assertEquals(numRange, result.labelValues.length);
|
assertEquals(numRange, result.labelValues.length);
|
||||||
|
@ -790,6 +830,7 @@ public class TestRangeFacetCounts extends FacetTestCase {
|
||||||
assertEquals(expectedCounts[rangeID], s.count(ddq));
|
assertEquals(expectedCounts[rangeID], s.count(ddq));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
w.close();
|
w.close();
|
||||||
IOUtils.close(r, dir);
|
IOUtils.close(r, dir);
|
||||||
|
@ -924,7 +965,16 @@ public class TestRangeFacetCounts extends FacetTestCase {
|
||||||
} else {
|
} else {
|
||||||
fastMatchQuery = null;
|
fastMatchQuery = null;
|
||||||
}
|
}
|
||||||
Facets facets = new LongRangeFacetCounts("field", null, sfc, fastMatchQuery, ranges);
|
Facets facets;
|
||||||
|
if (random().nextBoolean()) {
|
||||||
|
facets =
|
||||||
|
new LongRangeFacetCounts(
|
||||||
|
"field", MultiLongValuesSource.fromLongField("field"), sfc, fastMatchQuery, ranges);
|
||||||
|
} else {
|
||||||
|
facets =
|
||||||
|
new LongRangeFacetCounts(
|
||||||
|
"field", (MultiLongValuesSource) null, sfc, fastMatchQuery, ranges);
|
||||||
|
}
|
||||||
FacetResult result = facets.getTopChildren(10, "field");
|
FacetResult result = facets.getTopChildren(10, "field");
|
||||||
assertEquals(numRange, result.labelValues.length);
|
assertEquals(numRange, result.labelValues.length);
|
||||||
for (int rangeID = 0; rangeID < numRange; rangeID++) {
|
for (int rangeID = 0; rangeID < numRange; rangeID++) {
|
||||||
|
@ -1069,7 +1119,21 @@ public class TestRangeFacetCounts extends FacetTestCase {
|
||||||
fastMatchFilter = null;
|
fastMatchFilter = null;
|
||||||
}
|
}
|
||||||
DoubleValuesSource vs = DoubleValuesSource.fromDoubleField("field");
|
DoubleValuesSource vs = DoubleValuesSource.fromDoubleField("field");
|
||||||
Facets facets = new DoubleRangeFacetCounts("field", vs, sfc, fastMatchFilter, ranges);
|
MultiDoubleValuesSource mvs = MultiDoubleValuesSource.fromDoubleField("field");
|
||||||
|
Facets facets;
|
||||||
|
if (random().nextBoolean()) {
|
||||||
|
facets = new DoubleRangeFacetCounts("field", vs, sfc, fastMatchFilter, ranges);
|
||||||
|
} else if (random().nextBoolean()) {
|
||||||
|
facets =
|
||||||
|
new DoubleRangeFacetCounts(
|
||||||
|
"field",
|
||||||
|
MultiDoubleValuesSource.fromSingleValued(vs),
|
||||||
|
sfc,
|
||||||
|
fastMatchFilter,
|
||||||
|
ranges);
|
||||||
|
} else {
|
||||||
|
facets = new DoubleRangeFacetCounts("field", mvs, sfc, fastMatchFilter, ranges);
|
||||||
|
}
|
||||||
FacetResult result = facets.getTopChildren(10, "field");
|
FacetResult result = facets.getTopChildren(10, "field");
|
||||||
assertEquals(numRange, result.labelValues.length);
|
assertEquals(numRange, result.labelValues.length);
|
||||||
for (int rangeID = 0; rangeID < numRange; rangeID++) {
|
for (int rangeID = 0; rangeID < numRange; rangeID++) {
|
||||||
|
@ -1086,8 +1150,10 @@ public class TestRangeFacetCounts extends FacetTestCase {
|
||||||
DrillDownQuery ddq = new DrillDownQuery(config);
|
DrillDownQuery ddq = new DrillDownQuery(config);
|
||||||
if (random().nextBoolean()) {
|
if (random().nextBoolean()) {
|
||||||
ddq.add("field", DoublePoint.newRangeQuery("field", range.min, range.max));
|
ddq.add("field", DoublePoint.newRangeQuery("field", range.min, range.max));
|
||||||
} else {
|
} else if (random().nextBoolean()) {
|
||||||
ddq.add("field", range.getQuery(fastMatchFilter, vs));
|
ddq.add("field", range.getQuery(fastMatchFilter, vs));
|
||||||
|
} else {
|
||||||
|
ddq.add("field", range.getQuery(fastMatchFilter, mvs));
|
||||||
}
|
}
|
||||||
|
|
||||||
assertEquals(expectedCounts[rangeID], s.count(ddq));
|
assertEquals(expectedCounts[rangeID], s.count(ddq));
|
||||||
|
@ -1222,7 +1288,20 @@ public class TestRangeFacetCounts extends FacetTestCase {
|
||||||
} else {
|
} else {
|
||||||
fastMatchFilter = null;
|
fastMatchFilter = null;
|
||||||
}
|
}
|
||||||
Facets facets = new DoubleRangeFacetCounts("field", null, sfc, fastMatchFilter, ranges);
|
Facets facets;
|
||||||
|
if (random().nextBoolean()) {
|
||||||
|
facets =
|
||||||
|
new DoubleRangeFacetCounts(
|
||||||
|
"field",
|
||||||
|
MultiDoubleValuesSource.fromDoubleField("field"),
|
||||||
|
sfc,
|
||||||
|
fastMatchFilter,
|
||||||
|
ranges);
|
||||||
|
} else {
|
||||||
|
facets =
|
||||||
|
new DoubleRangeFacetCounts(
|
||||||
|
"field", (MultiDoubleValuesSource) null, sfc, fastMatchFilter, ranges);
|
||||||
|
}
|
||||||
FacetResult result = facets.getTopChildren(10, "field");
|
FacetResult result = facets.getTopChildren(10, "field");
|
||||||
assertEquals(numRange, result.labelValues.length);
|
assertEquals(numRange, result.labelValues.length);
|
||||||
for (int rangeID = 0; rangeID < numRange; rangeID++) {
|
for (int rangeID = 0; rangeID < numRange; rangeID++) {
|
||||||
|
@ -1496,7 +1575,14 @@ public class TestRangeFacetCounts extends FacetTestCase {
|
||||||
System.out.println("TEST: fastMatchFilter=" + fastMatchFilter);
|
System.out.println("TEST: fastMatchFilter=" + fastMatchFilter);
|
||||||
}
|
}
|
||||||
|
|
||||||
Facets facets = new DoubleRangeFacetCounts("field", vs, fc, fastMatchFilter, ranges);
|
Facets facets;
|
||||||
|
if (random().nextBoolean()) {
|
||||||
|
facets = new DoubleRangeFacetCounts("field", vs, fc, fastMatchFilter, ranges);
|
||||||
|
} else {
|
||||||
|
facets =
|
||||||
|
new DoubleRangeFacetCounts(
|
||||||
|
"field", MultiDoubleValuesSource.fromSingleValued(vs), fc, fastMatchFilter, ranges);
|
||||||
|
}
|
||||||
|
|
||||||
assertEquals(
|
assertEquals(
|
||||||
"dim=field path=[] value=3 childCount=6\n < 1 (0)\n < 2 (1)\n < 5 (3)\n < 10 (3)\n < 20 (3)\n < 50 (3)\n",
|
"dim=field path=[] value=3 childCount=6\n < 1 (0)\n < 2 (1)\n < 5 (3)\n < 10 (3)\n < 20 (3)\n < 50 (3)\n",
|
||||||
|
@ -1504,7 +1590,13 @@ public class TestRangeFacetCounts extends FacetTestCase {
|
||||||
assertTrue(fastMatchFilter == null || filterWasUsed.get());
|
assertTrue(fastMatchFilter == null || filterWasUsed.get());
|
||||||
|
|
||||||
DrillDownQuery ddq = new DrillDownQuery(config);
|
DrillDownQuery ddq = new DrillDownQuery(config);
|
||||||
|
if (random().nextBoolean()) {
|
||||||
ddq.add("field", ranges[1].getQuery(fastMatchFilter, vs));
|
ddq.add("field", ranges[1].getQuery(fastMatchFilter, vs));
|
||||||
|
} else {
|
||||||
|
ddq.add(
|
||||||
|
"field",
|
||||||
|
ranges[1].getQuery(fastMatchFilter, MultiDoubleValuesSource.fromSingleValued(vs)));
|
||||||
|
}
|
||||||
|
|
||||||
// Test simple drill-down:
|
// Test simple drill-down:
|
||||||
assertEquals(1, s.search(ddq, 10).totalHits.value);
|
assertEquals(1, s.search(ddq, 10).totalHits.value);
|
||||||
|
@ -1521,7 +1613,11 @@ public class TestRangeFacetCounts extends FacetTestCase {
|
||||||
throws IOException {
|
throws IOException {
|
||||||
assert drillSideways.length == 1;
|
assert drillSideways.length == 1;
|
||||||
return new DoubleRangeFacetCounts(
|
return new DoubleRangeFacetCounts(
|
||||||
"field", vs, drillSideways[0], fastMatchFilter, ranges);
|
"field",
|
||||||
|
MultiDoubleValuesSource.fromSingleValued(vs),
|
||||||
|
drillSideways[0],
|
||||||
|
fastMatchFilter,
|
||||||
|
ranges);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
Loading…
Reference in New Issue