mirror of https://github.com/apache/lucene.git
SOLR-10939: add point support to join query
This commit is contained in:
parent
5fb800f018
commit
bd5c09b1ee
|
@ -343,6 +343,9 @@ New Features
|
|||
* SOLR-10845: Add support for PointFields to {!graphTerms} query that is internally
|
||||
used by some graph traversal streaming expressions. (yonik)
|
||||
|
||||
* SOLR-10939: Add support for PointsFields to {!join} query. Joined fields should
|
||||
also have docValues enabled. (yonik)
|
||||
|
||||
Bug Fixes
|
||||
----------------------
|
||||
* SOLR-9262: Connection and read timeouts are being ignored by UpdateShardHandler after SOLR-4509.
|
||||
|
|
|
@ -52,7 +52,9 @@ import org.apache.solr.handler.component.ResponseBuilder;
|
|||
import org.apache.solr.request.LocalSolrQueryRequest;
|
||||
import org.apache.solr.request.SolrQueryRequest;
|
||||
import org.apache.solr.request.SolrRequestInfo;
|
||||
import org.apache.solr.schema.SchemaField;
|
||||
import org.apache.solr.schema.TrieField;
|
||||
import org.apache.solr.search.join.GraphPointsCollector;
|
||||
import org.apache.solr.search.join.ScoreJoinQParserPlugin;
|
||||
import org.apache.solr.util.RTimer;
|
||||
import org.apache.solr.util.RefCounted;
|
||||
|
@ -281,6 +283,7 @@ class JoinQuery extends Query {
|
|||
}
|
||||
|
||||
|
||||
// most of these statistics are only used for the enum method
|
||||
int fromSetSize; // number of docs in the fromSet (that match the from query)
|
||||
long resultListDocs; // total number of docs collected
|
||||
int fromTermCount;
|
||||
|
@ -295,6 +298,33 @@ class JoinQuery extends Query {
|
|||
|
||||
|
||||
public DocSet getDocSet() throws IOException {
|
||||
SchemaField fromSchemaField = fromSearcher.getSchema().getField(fromField);
|
||||
SchemaField toSchemaField = toSearcher.getSchema().getField(toField);
|
||||
|
||||
boolean usePoints = false;
|
||||
if (toSchemaField.getType().isPointField()) {
|
||||
if (!fromSchemaField.hasDocValues()) {
|
||||
throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, "join from field " + fromSchemaField + " should have docValues to join with points field " + toSchemaField);
|
||||
}
|
||||
usePoints = true;
|
||||
}
|
||||
|
||||
if (!usePoints) {
|
||||
return getDocSetEnumerate();
|
||||
}
|
||||
|
||||
// point fields
|
||||
GraphPointsCollector collector = new GraphPointsCollector(fromSchemaField, null, null);
|
||||
fromSearcher.search(q, collector);
|
||||
Query resultQ = collector.getResultQuery(toSchemaField, false);
|
||||
// don't cache the resulting docSet... the query may be very large. Better to cache the results of the join query itself
|
||||
DocSet result = resultQ==null ? DocSet.EMPTY : toSearcher.getDocSetNC(resultQ, null);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public DocSet getDocSetEnumerate() throws IOException {
|
||||
FixedBitSet resultBits = null;
|
||||
|
||||
// minimum docFreq to use the cache
|
||||
|
|
|
@ -0,0 +1,122 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.solr.search.join;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.apache.lucene.document.DoublePoint;
|
||||
import org.apache.lucene.document.FloatPoint;
|
||||
import org.apache.lucene.document.IntPoint;
|
||||
import org.apache.lucene.document.LongPoint;
|
||||
import org.apache.lucene.index.DocValues;
|
||||
import org.apache.lucene.index.LeafReaderContext;
|
||||
import org.apache.lucene.index.SortedNumericDocValues;
|
||||
import org.apache.lucene.search.Query;
|
||||
import org.apache.lucene.util.NumericUtils;
|
||||
import org.apache.solr.schema.NumberType;
|
||||
import org.apache.solr.schema.SchemaField;
|
||||
import org.apache.solr.search.DocSet;
|
||||
import org.apache.solr.util.LongIterator;
|
||||
import org.apache.solr.util.LongSet;
|
||||
|
||||
/** @lucene.internal */
|
||||
public class GraphPointsCollector extends GraphEdgeCollector {
|
||||
final LongSet set = new LongSet(256);
|
||||
|
||||
SortedNumericDocValues values = null;
|
||||
|
||||
public GraphPointsCollector(SchemaField collectField, DocSet skipSet, DocSet leafNodes) {
|
||||
super(collectField, skipSet, leafNodes);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doSetNextReader(LeafReaderContext context) throws IOException {
|
||||
super.doSetNextReader(context);
|
||||
values = DocValues.getSortedNumeric(context.reader(), collectField.getName());
|
||||
}
|
||||
|
||||
@Override
|
||||
void addEdgeIdsToResult(int doc) throws IOException {
|
||||
// set the doc to pull the edges ids for.
|
||||
int valuesDoc = values.docID();
|
||||
if (valuesDoc < doc) {
|
||||
valuesDoc = values.advance(doc);
|
||||
}
|
||||
if (valuesDoc == doc) {
|
||||
int count = values.docValueCount();
|
||||
for (int i = 0; i < count; i++) {
|
||||
long v = values.nextValue();
|
||||
set.add(v);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Query getResultQuery(SchemaField matchField, boolean useAutomaton) {
|
||||
if (set.cardinality() == 0) return null;
|
||||
|
||||
Query q = null;
|
||||
|
||||
// How we interpret the longs collected depends on the field we collect from (single valued can be diff from multi valued)
|
||||
// The basic type of the from & to field must match though (int/long/float/double)
|
||||
NumberType ntype = collectField.getType().getNumberType();
|
||||
boolean multiValued = collectField.multiValued();
|
||||
|
||||
if (ntype == NumberType.LONG || ntype == NumberType.DATE) {
|
||||
long[] vals = new long[set.cardinality()];
|
||||
int i = 0;
|
||||
for (LongIterator iter = set.iterator(); iter.hasNext(); ) {
|
||||
long bits = iter.next();
|
||||
long v = bits;
|
||||
vals[i++] = v;
|
||||
}
|
||||
q = LongPoint.newSetQuery(matchField.getName(), vals);
|
||||
} else if (ntype == NumberType.INTEGER) {
|
||||
int[] vals = new int[set.cardinality()];
|
||||
int i = 0;
|
||||
for (LongIterator iter = set.iterator(); iter.hasNext(); ) {
|
||||
long bits = iter.next();
|
||||
int v = (int)bits;
|
||||
vals[i++] = v;
|
||||
}
|
||||
q = IntPoint.newSetQuery(matchField.getName(), vals);
|
||||
} else if (ntype == NumberType.DOUBLE) {
|
||||
double[] vals = new double[set.cardinality()];
|
||||
int i = 0;
|
||||
for (LongIterator iter = set.iterator(); iter.hasNext(); ) {
|
||||
long bits = iter.next();
|
||||
double v = multiValued ? NumericUtils.sortableLongToDouble(bits) : Double.longBitsToDouble(bits);
|
||||
vals[i++] = v;
|
||||
}
|
||||
q = DoublePoint.newSetQuery(matchField.getName(), vals);
|
||||
} else if (ntype == NumberType.FLOAT) {
|
||||
float[] vals = new float[set.cardinality()];
|
||||
int i = 0;
|
||||
for (LongIterator iter = set.iterator(); iter.hasNext(); ) {
|
||||
long bits = iter.next();
|
||||
float v = multiValued ? NumericUtils.sortableIntToFloat((int) bits) : Float.intBitsToFloat((int) bits);
|
||||
vals[i++] = v;
|
||||
}
|
||||
q = FloatPoint.newSetQuery(matchField.getName(), vals);
|
||||
}
|
||||
|
||||
return q;
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -25,6 +25,7 @@ import java.util.TreeSet;
|
|||
|
||||
import org.apache.lucene.index.LeafReaderContext;
|
||||
import org.apache.lucene.index.Term;
|
||||
import org.apache.lucene.search.BooleanClause;
|
||||
import org.apache.lucene.search.BooleanClause.Occur;
|
||||
import org.apache.lucene.search.BooleanQuery;
|
||||
import org.apache.lucene.search.DocIdSet;
|
||||
|
@ -133,15 +134,15 @@ public class GraphQuery extends Query {
|
|||
private int currentDepth = -1;
|
||||
private Filter filter;
|
||||
private DocSet resultSet;
|
||||
SchemaField fromSchemaField;
|
||||
SchemaField toSchemaField;
|
||||
SchemaField collectSchemaField; // the field to collect values from
|
||||
SchemaField matchSchemaField; // the field to match those values
|
||||
|
||||
public GraphQueryWeight(SolrIndexSearcher searcher, float boost) {
|
||||
// Grab the searcher so we can run additional searches.
|
||||
super(null);
|
||||
this.fromSearcher = searcher;
|
||||
this.fromSchemaField = searcher.getSchema().getField(fromField);
|
||||
this.toSchemaField = searcher.getSchema().getField(toField);
|
||||
this.matchSchemaField = searcher.getSchema().getField(fromField);
|
||||
this.collectSchemaField = searcher.getSchema().getField(toField);
|
||||
}
|
||||
|
||||
GraphQuery getGraphQuery() {
|
||||
|
@ -196,13 +197,25 @@ public class GraphQuery extends Query {
|
|||
} else {
|
||||
// when we're not at the max depth level, we need to collect edges
|
||||
// Create the graph result collector for this level
|
||||
GraphEdgeCollector graphResultCollector = toSchemaField.getType().isPointField()
|
||||
? new GraphPointsCollector(this, capacity, resultBits, leafNodes)
|
||||
: new GraphTermsCollector(this, capacity, resultBits, leafNodes);
|
||||
GraphEdgeCollector graphResultCollector = collectSchemaField.getType().isPointField()
|
||||
? new GraphPointsCollector(collectSchemaField, new BitDocSet(resultBits), leafNodes)
|
||||
: new GraphTermsCollector(collectSchemaField, new BitDocSet(resultBits), leafNodes);
|
||||
|
||||
fromSet = new BitDocSet(new FixedBitSet(capacity));
|
||||
graphResultCollector.setCollectDocs(fromSet.getBits());
|
||||
|
||||
fromSearcher.search(frontierQuery, graphResultCollector);
|
||||
fromSet = graphResultCollector.getDocSet();
|
||||
frontierQuery = graphResultCollector.getFrontierQuery();
|
||||
|
||||
frontierQuery = graphResultCollector.getResultQuery(matchSchemaField, isUseAutn());
|
||||
// If there is a filter to be used while crawling the graph, add that.
|
||||
if (frontierQuery != null && getTraversalFilter() != null) {
|
||||
BooleanQuery.Builder builder = new BooleanQuery.Builder();
|
||||
builder.add(frontierQuery, BooleanClause.Occur.MUST);
|
||||
builder.add(getTraversalFilter(), BooleanClause.Occur.MUST);
|
||||
frontierQuery = builder.build();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
if (currentDepth == 0 && !returnRoot) {
|
||||
// grab a copy of the root bits but only if we need it.
|
||||
|
@ -230,9 +243,9 @@ public class GraphQuery extends Query {
|
|||
}
|
||||
|
||||
private DocSet resolveLeafNodes() throws IOException {
|
||||
String field = toSchemaField.getName();
|
||||
String field = collectSchemaField.getName();
|
||||
BooleanQuery.Builder leafNodeQuery = new BooleanQuery.Builder();
|
||||
Query edgeQuery = toSchemaField.hasDocValues() ? new DocValuesFieldExistsQuery(field) : new WildcardQuery(new Term(field, "*"));
|
||||
Query edgeQuery = collectSchemaField.hasDocValues() ? new DocValuesFieldExistsQuery(field) : new WildcardQuery(new Term(field, "*"));
|
||||
leafNodeQuery.add(edgeQuery, Occur.MUST_NOT);
|
||||
DocSet leafNodes = fromSearcher.getDocSet(leafNodeQuery.build());
|
||||
return leafNodes;
|
||||
|
|
|
@ -21,36 +21,23 @@ import java.util.ArrayList;
|
|||
import java.util.List;
|
||||
import java.util.TreeSet;
|
||||
|
||||
import org.apache.lucene.document.DoublePoint;
|
||||
import org.apache.lucene.document.FloatPoint;
|
||||
import org.apache.lucene.document.IntPoint;
|
||||
import org.apache.lucene.document.LongPoint;
|
||||
import org.apache.lucene.index.DocValues;
|
||||
import org.apache.lucene.index.LeafReaderContext;
|
||||
import org.apache.lucene.index.SortedNumericDocValues;
|
||||
import org.apache.lucene.index.SortedSetDocValues;
|
||||
import org.apache.lucene.index.Term;
|
||||
import org.apache.lucene.search.AutomatonQuery;
|
||||
import org.apache.lucene.search.BooleanClause;
|
||||
import org.apache.lucene.search.BooleanQuery;
|
||||
import org.apache.lucene.search.Collector;
|
||||
import org.apache.lucene.search.Query;
|
||||
import org.apache.lucene.search.SimpleCollector;
|
||||
import org.apache.lucene.search.TermInSetQuery;
|
||||
import org.apache.lucene.util.BitSet;
|
||||
import org.apache.lucene.util.Bits;
|
||||
import org.apache.lucene.util.BytesRef;
|
||||
import org.apache.lucene.util.BytesRefHash;
|
||||
import org.apache.lucene.util.FixedBitSet;
|
||||
import org.apache.lucene.util.NumericUtils;
|
||||
import org.apache.lucene.util.automaton.Automaton;
|
||||
import org.apache.lucene.util.automaton.DaciukMihovAutomatonBuilder;
|
||||
import org.apache.solr.schema.NumberType;
|
||||
import org.apache.solr.schema.SchemaField;
|
||||
import org.apache.solr.search.BitDocSet;
|
||||
import org.apache.solr.search.DocSet;
|
||||
import org.apache.solr.util.LongIterator;
|
||||
import org.apache.solr.util.LongSet;
|
||||
|
||||
/**
|
||||
* A graph hit collector. This accumulates the edges for a given graph traversal.
|
||||
|
@ -59,49 +46,51 @@ import org.apache.solr.util.LongSet;
|
|||
* @lucene.internal
|
||||
*/
|
||||
abstract class GraphEdgeCollector extends SimpleCollector implements Collector {
|
||||
|
||||
GraphQuery.GraphQueryWeight weight;
|
||||
|
||||
// the result set that is being collected.
|
||||
Bits currentResult;
|
||||
// For graph traversal, the result set that has already been visited and thus can be skipped for during value collection.
|
||||
DocSet skipSet;
|
||||
// known leaf nodes
|
||||
DocSet leafNodes;
|
||||
// number of hits discovered at this level.
|
||||
int numHits=0;
|
||||
BitSet bits;
|
||||
final int maxDoc;
|
||||
int base;
|
||||
int baseInParent;
|
||||
// if we care to track this.
|
||||
boolean hasCycles = false;
|
||||
|
||||
GraphEdgeCollector(GraphQuery.GraphQueryWeight weight, int maxDoc, Bits currentResult, DocSet leafNodes) {
|
||||
this.weight = weight;
|
||||
this.maxDoc = maxDoc;
|
||||
this.currentResult = currentResult;
|
||||
int numHits=0; // number of documents visited
|
||||
BitSet bits; // if not null, used to collect documents visited
|
||||
|
||||
int base;
|
||||
|
||||
SchemaField collectField;
|
||||
|
||||
// skipSet and leafNodes may be null
|
||||
GraphEdgeCollector(SchemaField collectField, DocSet skipSet, DocSet leafNodes) {
|
||||
this.collectField = collectField;
|
||||
this.skipSet = skipSet;
|
||||
this.leafNodes = leafNodes;
|
||||
if (bits==null) {
|
||||
// create a bitset at the start that will hold the graph traversal result set
|
||||
bits = new FixedBitSet(maxDoc);
|
||||
}
|
||||
}
|
||||
|
||||
// Set to use to collect docs being visited
|
||||
// TODO: this should be replaced with a more general delegating collector
|
||||
public void setCollectDocs(FixedBitSet target) {
|
||||
this.bits = target;
|
||||
}
|
||||
|
||||
// the number of docs visited
|
||||
public int getNumHits() { return numHits; }
|
||||
|
||||
public void collect(int doc) throws IOException {
|
||||
doc += base;
|
||||
if (currentResult.get(doc)) {
|
||||
// cycle detected / already been here.
|
||||
// knowing if your graph had a cycle might be useful and it's lightweight to implement here.
|
||||
hasCycles = true;
|
||||
public void collect(int segDoc) throws IOException {
|
||||
int doc = segDoc + base;
|
||||
if (skipSet != null && skipSet.exists(doc)) {
|
||||
// when skipSet == all nodes visited so far, then this represents a cycle and we can
|
||||
// keep track of that here in the future if we need to.
|
||||
return;
|
||||
}
|
||||
// collect the docs
|
||||
addDocToResult(doc);
|
||||
// Optimization to not look up edges for a document that is a leaf node
|
||||
|
||||
if (bits != null) bits.set(doc);
|
||||
// increment the hit count so we know how many docs we traversed this time.
|
||||
numHits++;
|
||||
|
||||
// Optimization to not look up edges for a document that is a leaf node (i.e. has no outgoing edges)
|
||||
if (leafNodes == null || !leafNodes.exists(doc)) {
|
||||
addEdgeIdsToResult(doc-base);
|
||||
addEdgeIdsToResult(segDoc);
|
||||
}
|
||||
// Note: tracking links in for each result would be a huge memory hog... so not implementing at this time.
|
||||
|
||||
}
|
||||
|
||||
abstract void addEdgeIdsToResult(int doc) throws IOException;
|
||||
|
@ -113,37 +102,13 @@ abstract class GraphEdgeCollector extends SimpleCollector implements Collector {
|
|||
numHits++;
|
||||
}
|
||||
|
||||
public BitDocSet getDocSet() {
|
||||
if (bits == null) {
|
||||
// TODO: this shouldn't happen
|
||||
bits = new FixedBitSet(maxDoc);
|
||||
}
|
||||
return new BitDocSet((FixedBitSet)bits,numHits);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doSetNextReader(LeafReaderContext context) throws IOException {
|
||||
base = context.docBase;
|
||||
baseInParent = context.docBaseInParent;
|
||||
}
|
||||
|
||||
protected abstract Query getResultQuery();
|
||||
public abstract Query getResultQuery(SchemaField matchField, boolean useAutomaton);
|
||||
|
||||
public Query getFrontierQuery() {
|
||||
Query q = getResultQuery();
|
||||
if (q == null) return null;
|
||||
|
||||
// If there is a filter to be used while crawling the graph, add that.
|
||||
if (weight.getGraphQuery().getTraversalFilter() != null) {
|
||||
BooleanQuery.Builder builder = new BooleanQuery.Builder();
|
||||
builder.add(q, BooleanClause.Occur.MUST);
|
||||
builder.add(weight.getGraphQuery().getTraversalFilter(), BooleanClause.Occur.MUST);
|
||||
q = builder.build();
|
||||
}
|
||||
|
||||
return q;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean needsScores() {
|
||||
return false;
|
||||
|
@ -157,8 +122,8 @@ class GraphTermsCollector extends GraphEdgeCollector {
|
|||
private SortedSetDocValues docTermOrds;
|
||||
|
||||
|
||||
GraphTermsCollector(GraphQuery.GraphQueryWeight weight, int maxDoc, Bits currentResult, DocSet leafNodes) {
|
||||
super(weight, maxDoc, currentResult, leafNodes);
|
||||
GraphTermsCollector(SchemaField collectField, DocSet skipSet, DocSet leafNodes) {
|
||||
super(collectField, skipSet, leafNodes);
|
||||
this.collectorTerms = new BytesRefHash();
|
||||
}
|
||||
|
||||
|
@ -166,7 +131,7 @@ class GraphTermsCollector extends GraphEdgeCollector {
|
|||
public void doSetNextReader(LeafReaderContext context) throws IOException {
|
||||
super.doSetNextReader(context);
|
||||
// Grab the updated doc values.
|
||||
docTermOrds = DocValues.getSortedSet(context.reader(), weight.getGraphQuery().getToField());
|
||||
docTermOrds = DocValues.getSortedSet(context.reader(), collectField.getName());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -187,7 +152,7 @@ class GraphTermsCollector extends GraphEdgeCollector {
|
|||
}
|
||||
|
||||
@Override
|
||||
protected Query getResultQuery() {
|
||||
public Query getResultQuery(SchemaField matchField, boolean useAutomaton) {
|
||||
if (collectorTerms == null || collectorTerms.size() == 0) {
|
||||
// return null if there are no terms (edges) to traverse.
|
||||
return null;
|
||||
|
@ -195,12 +160,11 @@ class GraphTermsCollector extends GraphEdgeCollector {
|
|||
// Create a query
|
||||
Query q = null;
|
||||
|
||||
GraphQuery gq = weight.getGraphQuery();
|
||||
// TODO: see if we should dynamically select this based on the frontier size.
|
||||
if (gq.isUseAutn()) {
|
||||
if (useAutomaton) {
|
||||
// build an automaton based query for the frontier.
|
||||
Automaton autn = buildAutomaton(collectorTerms);
|
||||
AutomatonQuery autnQuery = new AutomatonQuery(new Term(gq.getFromField()), autn);
|
||||
AutomatonQuery autnQuery = new AutomatonQuery(new Term(matchField.getName()), autn);
|
||||
q = autnQuery;
|
||||
} else {
|
||||
List<BytesRef> termList = new ArrayList<>(collectorTerms.size());
|
||||
|
@ -209,7 +173,7 @@ class GraphTermsCollector extends GraphEdgeCollector {
|
|||
collectorTerms.get(i, ref);
|
||||
termList.add(ref);
|
||||
}
|
||||
q = new TermInSetQuery(gq.getFromField(), termList);
|
||||
q = new TermInSetQuery(matchField.getName(), termList);
|
||||
}
|
||||
|
||||
return q;
|
||||
|
@ -232,98 +196,3 @@ class GraphTermsCollector extends GraphEdgeCollector {
|
|||
}
|
||||
|
||||
|
||||
class GraphPointsCollector extends GraphEdgeCollector {
|
||||
final LongSet set = new LongSet(256);
|
||||
|
||||
SortedNumericDocValues values = null;
|
||||
|
||||
GraphPointsCollector(GraphQuery.GraphQueryWeight weight, int maxDoc, Bits currentResult, DocSet leafNodes) {
|
||||
super(weight, maxDoc, currentResult, leafNodes);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doSetNextReader(LeafReaderContext context) throws IOException {
|
||||
super.doSetNextReader(context);
|
||||
values = DocValues.getSortedNumeric(context.reader(), weight.getGraphQuery().getToField());
|
||||
}
|
||||
|
||||
@Override
|
||||
void addEdgeIdsToResult(int doc) throws IOException {
|
||||
// set the doc to pull the edges ids for.
|
||||
int valuesDoc = values.docID();
|
||||
if (valuesDoc < doc) {
|
||||
valuesDoc = values.advance(doc);
|
||||
}
|
||||
if (valuesDoc == doc) {
|
||||
int count = values.docValueCount();
|
||||
for (int i = 0; i < count; i++) {
|
||||
long v = values.nextValue();
|
||||
set.add(v);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Query getResultQuery() {
|
||||
if (set.cardinality() == 0) return null;
|
||||
|
||||
Query q = null;
|
||||
SchemaField sfield = weight.fromSchemaField;
|
||||
NumberType ntype = sfield.getType().getNumberType();
|
||||
boolean multiValued = sfield.multiValued();
|
||||
|
||||
if (ntype == NumberType.LONG || ntype == NumberType.DATE) {
|
||||
long[] vals = new long[set.cardinality()];
|
||||
int i = 0;
|
||||
for (LongIterator iter = set.iterator(); iter.hasNext(); ) {
|
||||
long bits = iter.next();
|
||||
long v = bits;
|
||||
vals[i++] = v;
|
||||
}
|
||||
q = LongPoint.newSetQuery(sfield.getName(), vals);
|
||||
} else if (ntype == NumberType.INTEGER) {
|
||||
int[] vals = new int[set.cardinality()];
|
||||
int i = 0;
|
||||
for (LongIterator iter = set.iterator(); iter.hasNext(); ) {
|
||||
long bits = iter.next();
|
||||
int v = (int)bits;
|
||||
vals[i++] = v;
|
||||
}
|
||||
q = IntPoint.newSetQuery(sfield.getName(), vals);
|
||||
} else if (ntype == NumberType.DOUBLE) {
|
||||
double[] vals = new double[set.cardinality()];
|
||||
int i = 0;
|
||||
for (LongIterator iter = set.iterator(); iter.hasNext(); ) {
|
||||
long bits = iter.next();
|
||||
double v = multiValued ? NumericUtils.sortableLongToDouble(bits) : Double.longBitsToDouble(bits);
|
||||
vals[i++] = v;
|
||||
}
|
||||
q = DoublePoint.newSetQuery(sfield.getName(), vals);
|
||||
} else if (ntype == NumberType.FLOAT) {
|
||||
float[] vals = new float[set.cardinality()];
|
||||
int i = 0;
|
||||
for (LongIterator iter = set.iterator(); iter.hasNext(); ) {
|
||||
long bits = iter.next();
|
||||
float v = multiValued ? NumericUtils.sortableIntToFloat((int) bits) : Float.intBitsToFloat((int) bits);
|
||||
vals[i++] = v;
|
||||
}
|
||||
q = FloatPoint.newSetQuery(sfield.getName(), vals);
|
||||
}
|
||||
|
||||
return q;
|
||||
}
|
||||
|
||||
|
||||
/** Build an automaton to represent the frontier query */
|
||||
private Automaton buildAutomaton(BytesRefHash termBytesHash) {
|
||||
// need top pass a sorted set of terms to the autn builder (maybe a better way to avoid this?)
|
||||
final TreeSet<BytesRef> terms = new TreeSet<BytesRef>();
|
||||
for (int i = 0 ; i < termBytesHash.size(); i++) {
|
||||
BytesRef ref = new BytesRef();
|
||||
termBytesHash.get(i, ref);
|
||||
terms.add(ref);
|
||||
}
|
||||
final Automaton a = DaciukMihovAutomatonBuilder.build(terms);
|
||||
return a;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,7 +16,6 @@
|
|||
*/
|
||||
package org.apache.solr;
|
||||
|
||||
import org.apache.solr.SolrTestCaseJ4.SuppressPointFields;
|
||||
import org.apache.solr.common.params.ModifiableSolrParams;
|
||||
import org.noggit.JSONUtil;
|
||||
import org.noggit.ObjectBuilder;
|
||||
|
@ -37,7 +36,6 @@ import java.util.List;
|
|||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
@SuppressPointFields(bugUrl="https://issues.apache.org/jira/browse/SOLR-10939")
|
||||
public class TestJoin extends SolrTestCaseJ4 {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
|
||||
|
@ -45,6 +43,12 @@ public class TestJoin extends SolrTestCaseJ4 {
|
|||
@BeforeClass
|
||||
public static void beforeTests() throws Exception {
|
||||
System.setProperty("enable.update.log", "false"); // schema12 doesn't support _version_
|
||||
|
||||
if (System.getProperty("solr.tests.IntegerFieldType").contains("Point")) { // all points change at the same time
|
||||
// point fields need docvalues
|
||||
System.setProperty("solr.tests.numeric.dv", "true");
|
||||
}
|
||||
|
||||
initCore("solrconfig.xml","schema12.xml");
|
||||
}
|
||||
|
||||
|
@ -181,12 +185,15 @@ public class TestJoin extends SolrTestCaseJ4 {
|
|||
for (int qiter=0; qiter<queryIter; qiter++) {
|
||||
String fromField;
|
||||
String toField;
|
||||
/* disable matching incompatible fields since 7.0... it doesn't work with point fields and doesn't really make sense?
|
||||
if (random().nextInt(100) < 5) {
|
||||
// pick random fields 5% of the time
|
||||
fromField = types.get(random().nextInt(types.size())).fname;
|
||||
// pick the same field 50% of the time we pick a random field (since other fields won't match anything)
|
||||
toField = (random().nextInt(100) < 50) ? fromField : types.get(random().nextInt(types.size())).fname;
|
||||
} else {
|
||||
} else
|
||||
*/
|
||||
{
|
||||
// otherwise, pick compatible fields that have a chance of matching indexed tokens
|
||||
String[] group = compat[random().nextInt(compat.length)];
|
||||
fromField = group[random().nextInt(group.length)];
|
||||
|
|
|
@ -1819,7 +1819,7 @@ public class TestJsonFacets extends SolrTestCaseHS {
|
|||
* to parent/child domains preserve any existing parent/children from the original domain - eg: when q=*:*)
|
||||
* </p>
|
||||
*/
|
||||
public void testQureyJoinBooksAndPages() throws Exception {
|
||||
public void testQueryJoinBooksAndPages() throws Exception {
|
||||
|
||||
final Client client = Client.localClient();
|
||||
|
||||
|
@ -1854,8 +1854,8 @@ public class TestJsonFacets extends SolrTestCaseHS {
|
|||
// the domains we'll be testing, initially setup for block join
|
||||
final String toChildren = "join: { from:'id', to:'book_id_s' }";
|
||||
final String toParents = "join: { from:'book_id_s', to:'id' }";
|
||||
final String toBogusChildren = "join: { from:'id', to:'does_not_exist' }";
|
||||
final String toBogusParents = "join: { from:'book_id_s', to:'does_not_exist' }";
|
||||
final String toBogusChildren = "join: { from:'id', to:'does_not_exist_s' }";
|
||||
final String toBogusParents = "join: { from:'book_id_s', to:'does_not_exist_s' }";
|
||||
|
||||
client.testJQ(params(p, "q", "*:*"
|
||||
, "json.facet", "{ " +
|
||||
|
|
|
@ -32,13 +32,18 @@ public class GraphQueryTest extends SolrTestCaseJ4 {
|
|||
@Test
|
||||
public void testGraph() throws Exception {
|
||||
// normal strings
|
||||
doGraph( params("node_id","node_s", "edge_id","edge_ss") );
|
||||
doGraph( params("node_id","node_s", "edge_id","edge_ss") );
|
||||
doGraph( params("node_id","node_ss", "edge_id","edge_ss") );
|
||||
|
||||
// point based fields with docvalues
|
||||
doGraph( params("node_id","node_ip", "edge_id","edge_ips") );
|
||||
doGraph( params("node_id","node_lp", "edge_id","edge_lps") );
|
||||
doGraph( params("node_id","node_fp", "edge_id","edge_fps") );
|
||||
doGraph( params("node_id","node_dp", "edge_id","edge_dps") );
|
||||
// point based fields with docvalues (single and multi-valued for the node field)
|
||||
doGraph( params("node_id","node_ip", "edge_id","edge_ips") );
|
||||
doGraph( params("node_id","node_ips", "edge_id","edge_ips") );
|
||||
doGraph( params("node_id","node_lp", "edge_id","edge_lps") );
|
||||
doGraph( params("node_id","node_lps", "edge_id","edge_lps") );
|
||||
doGraph( params("node_id","node_fp", "edge_id","edge_fps") );
|
||||
doGraph( params("node_id","node_fps", "edge_id","edge_fps") );
|
||||
doGraph( params("node_id","node_dp", "edge_id","edge_dps") );
|
||||
doGraph( params("node_id","node_dps", "edge_id","edge_dps") );
|
||||
}
|
||||
|
||||
public void doGraph(SolrParams p) throws Exception {
|
||||
|
@ -46,10 +51,10 @@ public class GraphQueryTest extends SolrTestCaseJ4 {
|
|||
String edge_id = p.get("edge_id");
|
||||
|
||||
// NOTE: from/to fields are reversed from {!join}... values are looked up in the "toField" and then matched on the "fromField"
|
||||
// 1->2->(3,9)->(4,5)->7
|
||||
// 8->(1,2)->...
|
||||
assertU(adoc("id", "doc_1", node_id, "1", edge_id, "2", "text", "foo", "title", "foo10" ));
|
||||
assertU(adoc("id", "doc_2", node_id, "2", edge_id, "3", "text", "foo" ));
|
||||
// 1->-2->(3,9)->(4,5)->7
|
||||
// 8->(1,-2)->...
|
||||
assertU(adoc("id", "doc_1", node_id, "1", edge_id, "-2", "text", "foo", "title", "foo10" ));
|
||||
assertU(adoc("id", "doc_2", node_id, "-2", edge_id, "3", "text", "foo" ));
|
||||
assertU(commit());
|
||||
assertU(adoc("id", "doc_3", node_id, "3", edge_id, "4", edge_id, "5"));
|
||||
assertU(adoc("id", "doc_4", node_id, "4" ));
|
||||
|
@ -57,12 +62,12 @@ public class GraphQueryTest extends SolrTestCaseJ4 {
|
|||
assertU(adoc("id", "doc_5", node_id, "5", edge_id, "7" ));
|
||||
assertU(adoc("id", "doc_6", node_id, "6", edge_id, "3" ));
|
||||
assertU(adoc("id", "doc_7", node_id, "7", edge_id, "1" ));
|
||||
assertU(adoc("id", "doc_8", node_id, "8", edge_id, "1", edge_id, "2" ));
|
||||
assertU(adoc("id", "doc_8", node_id, "8", edge_id, "1", edge_id, "-2" ));
|
||||
assertU(adoc("id", "doc_9", node_id, "9"));
|
||||
assertU(commit());
|
||||
// update docs so they're in a new segment.
|
||||
assertU(adoc("id", "doc_1", node_id, "1", edge_id, "2", "text", "foo"));
|
||||
assertU(adoc("id", "doc_2", node_id, "2", edge_id, "3", edge_id, "9", "text", "foo11"));
|
||||
assertU(adoc("id", "doc_1", node_id, "1", edge_id, "-2", "text", "foo"));
|
||||
assertU(adoc("id", "doc_2", node_id, "-2", edge_id, "3", edge_id, "9", "text", "foo11"));
|
||||
assertU(commit());
|
||||
// a graph for testing traversal filter 10 - 11 -> (12 | 13)
|
||||
assertU(adoc("id", "doc_10", node_id, "10", edge_id, "11", "title", "foo"));
|
||||
|
|
Loading…
Reference in New Issue