mirror of https://github.com/apache/lucene.git
SOLR-2136: function queries - add bool type and functions
git-svn-id: https://svn.apache.org/repos/asf/lucene/dev/trunk@1131228 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
18e1bee0de
commit
1da4ffee6e
|
@ -144,6 +144,10 @@ New Features
|
||||||
to IndexReader.open (in the case you have a custom IndexReaderFactory).
|
to IndexReader.open (in the case you have a custom IndexReaderFactory).
|
||||||
(simonw via rmuir)
|
(simonw via rmuir)
|
||||||
|
|
||||||
|
* SOLR-2136: Boolean type added to function queries, along with
|
||||||
|
new functions exists(), if(), and(), or(), xor(), not(), def(),
|
||||||
|
and true and false constants. (yonik)
|
||||||
|
|
||||||
|
|
||||||
Optimizations
|
Optimizations
|
||||||
----------------------
|
----------------------
|
||||||
|
|
|
@ -17,12 +17,16 @@
|
||||||
|
|
||||||
package org.apache.solr.schema;
|
package org.apache.solr.schema;
|
||||||
|
|
||||||
|
import org.apache.lucene.index.IndexReader;
|
||||||
|
import org.apache.lucene.search.FieldCache;
|
||||||
import org.apache.lucene.search.SortField;
|
import org.apache.lucene.search.SortField;
|
||||||
import org.apache.lucene.util.BytesRef;
|
import org.apache.lucene.util.BytesRef;
|
||||||
import org.apache.lucene.util.CharsRef;
|
import org.apache.lucene.util.CharsRef;
|
||||||
|
import org.apache.solr.search.MutableValue;
|
||||||
|
import org.apache.solr.search.MutableValueBool;
|
||||||
|
import org.apache.solr.search.MutableValueInt;
|
||||||
import org.apache.solr.search.QParser;
|
import org.apache.solr.search.QParser;
|
||||||
import org.apache.solr.search.function.ValueSource;
|
import org.apache.solr.search.function.*;
|
||||||
import org.apache.solr.search.function.OrdFieldSource;
|
|
||||||
import org.apache.lucene.analysis.Analyzer;
|
import org.apache.lucene.analysis.Analyzer;
|
||||||
import org.apache.lucene.analysis.Tokenizer;
|
import org.apache.lucene.analysis.Tokenizer;
|
||||||
import org.apache.lucene.analysis.tokenattributes.CharTermAttribute;
|
import org.apache.lucene.analysis.tokenattributes.CharTermAttribute;
|
||||||
|
@ -50,7 +54,7 @@ public class BoolField extends FieldType {
|
||||||
@Override
|
@Override
|
||||||
public ValueSource getValueSource(SchemaField field, QParser qparser) {
|
public ValueSource getValueSource(SchemaField field, QParser qparser) {
|
||||||
field.checkFieldCacheSource(qparser);
|
field.checkFieldCacheSource(qparser);
|
||||||
return new OrdFieldSource(field.name);
|
return new BoolFieldSource(field.name);
|
||||||
}
|
}
|
||||||
|
|
||||||
// avoid instantiating every time...
|
// avoid instantiating every time...
|
||||||
|
@ -121,7 +125,7 @@ public class BoolField extends FieldType {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object toObject(SchemaField sf, BytesRef term) {
|
public Object toObject(SchemaField sf, BytesRef term) {
|
||||||
return term.bytes[0] == 'T';
|
return term.bytes[term.offset] == 'T';
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -145,6 +149,83 @@ public class BoolField extends FieldType {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void write(TextResponseWriter writer, String name, Fieldable f) throws IOException {
|
public void write(TextResponseWriter writer, String name, Fieldable f) throws IOException {
|
||||||
writer.writeBool(name, f.stringValue().charAt(0) =='T');
|
writer.writeBool(name, f.stringValue().charAt(0) == 'T');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO - this can be much more efficient - use OpenBitSet or Bits
|
||||||
|
class BoolFieldSource extends ValueSource {
|
||||||
|
protected String field;
|
||||||
|
|
||||||
|
public BoolFieldSource(String field) {
|
||||||
|
this.field = field;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String description() {
|
||||||
|
return "bool(" + field + ')';
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public DocValues getValues(Map context, IndexReader.AtomicReaderContext readerContext) throws IOException {
|
||||||
|
final FieldCache.DocTermsIndex sindex = FieldCache.DEFAULT.getTermsIndex(readerContext.reader, field);
|
||||||
|
|
||||||
|
// figure out what ord maps to true
|
||||||
|
int nord = sindex.numOrd();
|
||||||
|
BytesRef br = new BytesRef();
|
||||||
|
int tord = -1;
|
||||||
|
for (int i=1; i<nord; i++) {
|
||||||
|
sindex.lookup(i, br);
|
||||||
|
if (br.length==1 && br.bytes[br.offset]=='T') {
|
||||||
|
tord = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final int trueOrd = tord;
|
||||||
|
|
||||||
|
return new BoolDocValues(this) {
|
||||||
|
@Override
|
||||||
|
public boolean boolVal(int doc) {
|
||||||
|
return sindex.getOrd(doc) == trueOrd;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean exists(int doc) {
|
||||||
|
return sindex.getOrd(doc) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ValueFiller getValueFiller() {
|
||||||
|
return new ValueFiller() {
|
||||||
|
private final MutableValueBool mval = new MutableValueBool();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MutableValue getValue() {
|
||||||
|
return mval;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void fillValue(int doc) {
|
||||||
|
int ord = sindex.getOrd(doc);
|
||||||
|
mval.value = (ord == trueOrd);
|
||||||
|
mval.exists = (ord != 0);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
return o.getClass() == BoolFieldSource.class && this.field.equals(((BoolFieldSource)o).field);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final int hcode = OrdFieldSource.class.hashCode();
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return hcode + field.hashCode();
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
|
@ -364,8 +364,14 @@ public class FunctionQParser extends QParser {
|
||||||
sp.expect(")");
|
sp.expect(")");
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
SchemaField f = req.getSchema().getField(id);
|
if ("true".equals(id)) {
|
||||||
valueSource = f.getType().getValueSource(f, this);
|
valueSource = new BoolConstValueSource(true);
|
||||||
|
} else if ("false".equals(id)) {
|
||||||
|
valueSource = new BoolConstValueSource(false);
|
||||||
|
} else {
|
||||||
|
SchemaField f = req.getSchema().getField(id);
|
||||||
|
valueSource = f.getType().getValueSource(f, this);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,60 @@
|
||||||
|
/**
|
||||||
|
* 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;
|
||||||
|
|
||||||
|
public class MutableValueBool extends MutableValue {
|
||||||
|
public boolean value;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object toObject() {
|
||||||
|
return exists ? value : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void copy(MutableValue source) {
|
||||||
|
MutableValueBool s = (MutableValueBool) source;
|
||||||
|
value = s.value;
|
||||||
|
exists = s.exists;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MutableValue duplicate() {
|
||||||
|
MutableValueBool v = new MutableValueBool();
|
||||||
|
v.value = this.value;
|
||||||
|
v.exists = this.exists;
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equalsSameType(Object other) {
|
||||||
|
MutableValueBool b = (MutableValueBool)other;
|
||||||
|
return value == b.value && exists == b.exists;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int compareSameType(Object other) {
|
||||||
|
MutableValueBool b = (MutableValueBool)other;
|
||||||
|
if (value != b.value) return value ? 1 : 0;
|
||||||
|
if (exists == b.exists) return 0;
|
||||||
|
return exists ? 1 : -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return value ? 2 : (exists ? 1 : 0);
|
||||||
|
}
|
||||||
|
}
|
|
@ -579,6 +579,134 @@ public abstract class ValueSourceParser implements NamedListInitializedPlugin {
|
||||||
return new NumDocsValueSource();
|
return new NumDocsValueSource();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
addParser("true", new ValueSourceParser() {
|
||||||
|
@Override
|
||||||
|
public ValueSource parse(FunctionQParser fp) throws ParseException {
|
||||||
|
return new BoolConstValueSource(true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
addParser("false", new ValueSourceParser() {
|
||||||
|
@Override
|
||||||
|
public ValueSource parse(FunctionQParser fp) throws ParseException {
|
||||||
|
return new BoolConstValueSource(false);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
addParser("exists", new ValueSourceParser() {
|
||||||
|
@Override
|
||||||
|
public ValueSource parse(FunctionQParser fp) throws ParseException {
|
||||||
|
ValueSource vs = fp.parseValueSource();
|
||||||
|
return new SimpleBoolFunction(vs) {
|
||||||
|
@Override
|
||||||
|
protected String name() {
|
||||||
|
return "exists";
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
protected boolean func(int doc, DocValues vals) {
|
||||||
|
return vals.exists(doc);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
addParser("not", new ValueSourceParser() {
|
||||||
|
@Override
|
||||||
|
public ValueSource parse(FunctionQParser fp) throws ParseException {
|
||||||
|
ValueSource vs = fp.parseValueSource();
|
||||||
|
return new SimpleBoolFunction(vs) {
|
||||||
|
@Override
|
||||||
|
protected boolean func(int doc, DocValues vals) {
|
||||||
|
return !vals.boolVal(doc);
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
protected String name() {
|
||||||
|
return "not";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
addParser("and", new ValueSourceParser() {
|
||||||
|
@Override
|
||||||
|
public ValueSource parse(FunctionQParser fp) throws ParseException {
|
||||||
|
List<ValueSource> sources = fp.parseValueSourceList();
|
||||||
|
return new MultiBoolFunction(sources) {
|
||||||
|
@Override
|
||||||
|
protected String name() {
|
||||||
|
return "and";
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
protected boolean func(int doc, DocValues[] vals) {
|
||||||
|
for (DocValues dv : vals)
|
||||||
|
if (!dv.boolVal(doc)) return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
addParser("or", new ValueSourceParser() {
|
||||||
|
@Override
|
||||||
|
public ValueSource parse(FunctionQParser fp) throws ParseException {
|
||||||
|
List<ValueSource> sources = fp.parseValueSourceList();
|
||||||
|
return new MultiBoolFunction(sources) {
|
||||||
|
@Override
|
||||||
|
protected String name() {
|
||||||
|
return "or";
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
protected boolean func(int doc, DocValues[] vals) {
|
||||||
|
for (DocValues dv : vals)
|
||||||
|
if (dv.boolVal(doc)) return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
addParser("xor", new ValueSourceParser() {
|
||||||
|
@Override
|
||||||
|
public ValueSource parse(FunctionQParser fp) throws ParseException {
|
||||||
|
List<ValueSource> sources = fp.parseValueSourceList();
|
||||||
|
return new MultiBoolFunction(sources) {
|
||||||
|
@Override
|
||||||
|
protected String name() {
|
||||||
|
return "xor";
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
protected boolean func(int doc, DocValues[] vals) {
|
||||||
|
int nTrue=0, nFalse=0;
|
||||||
|
for (DocValues dv : vals) {
|
||||||
|
if (dv.boolVal(doc)) nTrue++;
|
||||||
|
else nFalse++;
|
||||||
|
}
|
||||||
|
return nTrue != 0 && nFalse != 0;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
addParser("if", new ValueSourceParser() {
|
||||||
|
@Override
|
||||||
|
public ValueSource parse(FunctionQParser fp) throws ParseException {
|
||||||
|
ValueSource ifValueSource = fp.parseValueSource();
|
||||||
|
ValueSource trueValueSource = fp.parseValueSource();
|
||||||
|
ValueSource falseValueSource = fp.parseValueSource();
|
||||||
|
|
||||||
|
return new IfFunction(ifValueSource, trueValueSource, falseValueSource);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
addParser("def", new ValueSourceParser() {
|
||||||
|
@Override
|
||||||
|
public ValueSource parse(FunctionQParser fp) throws ParseException {
|
||||||
|
return new DefFunction(fp.parseValueSourceList());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static TInfo parseTerm(FunctionQParser fp) throws ParseException {
|
private static TInfo parseTerm(FunctionQParser fp) throws ParseException {
|
||||||
|
@ -857,6 +985,11 @@ class LongConstValueSource extends ConstNumberSource {
|
||||||
public Number getNumber() {
|
public Number getNumber() {
|
||||||
return constant;
|
return constant;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean getBool() {
|
||||||
|
return constant != 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -981,3 +1114,69 @@ abstract class Double2Parser extends NamedParser {
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class BoolConstValueSource extends ConstNumberSource {
|
||||||
|
final boolean constant;
|
||||||
|
|
||||||
|
public BoolConstValueSource(boolean constant) {
|
||||||
|
this.constant = constant;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String description() {
|
||||||
|
return "const(" + constant + ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public DocValues getValues(Map context, AtomicReaderContext readerContext) throws IOException {
|
||||||
|
return new BoolDocValues(this) {
|
||||||
|
@Override
|
||||||
|
public boolean boolVal(int doc) {
|
||||||
|
return constant;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return constant ? 0x12345678 : 0x87654321;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (BoolConstValueSource.class != o.getClass()) return false;
|
||||||
|
BoolConstValueSource other = (BoolConstValueSource) o;
|
||||||
|
return this.constant == other.constant;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getInt() {
|
||||||
|
return constant ? 1 : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getLong() {
|
||||||
|
return constant ? 1 : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public float getFloat() {
|
||||||
|
return constant ? 1 : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public double getDouble() {
|
||||||
|
return constant ? 1 : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Number getNumber() {
|
||||||
|
return constant ? 1 : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean getBool() {
|
||||||
|
return constant;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,79 @@
|
||||||
|
package org.apache.solr.search.function;
|
||||||
|
|
||||||
|
import org.apache.solr.search.MutableValue;
|
||||||
|
import org.apache.solr.search.MutableValueBool;
|
||||||
|
import org.apache.solr.search.MutableValueInt;
|
||||||
|
|
||||||
|
public abstract class BoolDocValues extends DocValues {
|
||||||
|
protected final ValueSource vs;
|
||||||
|
|
||||||
|
public BoolDocValues(ValueSource vs) {
|
||||||
|
this.vs = vs;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public abstract boolean boolVal(int doc);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public byte byteVal(int doc) {
|
||||||
|
return boolVal(doc) ? (byte)1 : (byte)0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public short shortVal(int doc) {
|
||||||
|
return boolVal(doc) ? (short)1 : (short)0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public float floatVal(int doc) {
|
||||||
|
return boolVal(doc) ? (float)1 : (float)0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int intVal(int doc) {
|
||||||
|
return boolVal(doc) ? 1 : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long longVal(int doc) {
|
||||||
|
return boolVal(doc) ? (long)1 : (long)0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public double doubleVal(int doc) {
|
||||||
|
return boolVal(doc) ? (double)1 : (double)0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String strVal(int doc) {
|
||||||
|
return Boolean.toString(boolVal(doc));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object objectVal(int doc) {
|
||||||
|
return exists(doc) ? boolVal(doc) : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString(int doc) {
|
||||||
|
return vs.description() + '=' + strVal(doc);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ValueFiller getValueFiller() {
|
||||||
|
return new ValueFiller() {
|
||||||
|
private final MutableValueBool mval = new MutableValueBool();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MutableValue getValue() {
|
||||||
|
return mval;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void fillValue(int doc) {
|
||||||
|
mval.value = boolVal(doc);
|
||||||
|
mval.exists = exists(doc);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,23 @@
|
||||||
|
/**
|
||||||
|
* 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.function;
|
||||||
|
|
||||||
|
|
||||||
|
public abstract class BoolFunction extends ValueSource {
|
||||||
|
// TODO: placeholder to return type, among other common future functionality
|
||||||
|
}
|
|
@ -26,4 +26,5 @@ public abstract class ConstNumberSource extends ValueSource {
|
||||||
public abstract float getFloat();
|
public abstract float getFloat();
|
||||||
public abstract double getDouble();
|
public abstract double getDouble();
|
||||||
public abstract Number getNumber();
|
public abstract Number getNumber();
|
||||||
|
public abstract boolean getBool();
|
||||||
}
|
}
|
||||||
|
|
|
@ -66,6 +66,10 @@ public class ConstValueSource extends ConstNumberSource {
|
||||||
public Object objectVal(int doc) {
|
public Object objectVal(int doc) {
|
||||||
return constant;
|
return constant;
|
||||||
}
|
}
|
||||||
|
@Override
|
||||||
|
public boolean boolVal(int doc) {
|
||||||
|
return constant != 0.0f;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -105,4 +109,9 @@ public class ConstValueSource extends ConstNumberSource {
|
||||||
public Number getNumber() {
|
public Number getNumber() {
|
||||||
return constant;
|
return constant;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean getBool() {
|
||||||
|
return constant != 0.0f;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,124 @@
|
||||||
|
package org.apache.solr.search.function;
|
||||||
|
/**
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import org.apache.lucene.index.IndexReader.AtomicReaderContext;
|
||||||
|
import org.apache.lucene.search.IndexSearcher;
|
||||||
|
import org.apache.lucene.util.BytesRef;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public class DefFunction extends MultiFunction {
|
||||||
|
public DefFunction(List<ValueSource> sources) {
|
||||||
|
super(sources);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String name() {
|
||||||
|
return "def";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public DocValues getValues(Map fcontext, AtomicReaderContext readerContext) throws IOException {
|
||||||
|
|
||||||
|
|
||||||
|
return new Values(valsArr(sources, fcontext, readerContext)) {
|
||||||
|
final int upto = valsArr.length - 1;
|
||||||
|
|
||||||
|
private DocValues get(int doc) {
|
||||||
|
for (int i=0; i<upto; i++) {
|
||||||
|
DocValues vals = valsArr[i];
|
||||||
|
if (vals.exists(doc)) {
|
||||||
|
return vals;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return valsArr[upto];
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public byte byteVal(int doc) {
|
||||||
|
return get(doc).byteVal(doc);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public short shortVal(int doc) {
|
||||||
|
return get(doc).shortVal(doc);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public float floatVal(int doc) {
|
||||||
|
return get(doc).floatVal(doc);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int intVal(int doc) {
|
||||||
|
return get(doc).intVal(doc);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long longVal(int doc) {
|
||||||
|
return get(doc).longVal(doc);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public double doubleVal(int doc) {
|
||||||
|
return get(doc).doubleVal(doc);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String strVal(int doc) {
|
||||||
|
return get(doc).strVal(doc);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean boolVal(int doc) {
|
||||||
|
return get(doc).boolVal(doc);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean bytesVal(int doc, BytesRef target) {
|
||||||
|
return get(doc).bytesVal(doc, target);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object objectVal(int doc) {
|
||||||
|
return get(doc).objectVal(doc);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean exists(int doc) {
|
||||||
|
// return true if any source is exists?
|
||||||
|
for (DocValues vals : valsArr) {
|
||||||
|
if (vals.exists(doc)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ValueFiller getValueFiller() {
|
||||||
|
// TODO: need ValueSource.type() to determine correct type
|
||||||
|
return super.getValueFiller();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
|
@ -48,6 +48,10 @@ public abstract class DocValues {
|
||||||
// TODO: should we make a termVal, returns BytesRef?
|
// TODO: should we make a termVal, returns BytesRef?
|
||||||
public String strVal(int doc) { throw new UnsupportedOperationException(); }
|
public String strVal(int doc) { throw new UnsupportedOperationException(); }
|
||||||
|
|
||||||
|
public boolean boolVal(int doc) {
|
||||||
|
return intVal(doc) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
/** returns the bytes representation of the string val - TODO: should this return the indexed raw bytes not? */
|
/** returns the bytes representation of the string val - TODO: should this return the indexed raw bytes not? */
|
||||||
public boolean bytesVal(int doc, BytesRef target) {
|
public boolean bytesVal(int doc, BytesRef target) {
|
||||||
String s = strVal(doc);
|
String s = strVal(doc);
|
||||||
|
|
|
@ -115,4 +115,9 @@ public class DoubleConstValueSource extends ConstNumberSource {
|
||||||
public Number getNumber() {
|
public Number getNumber() {
|
||||||
return constant;
|
return constant;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean getBool() {
|
||||||
|
return constant != 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,6 +35,11 @@ public abstract class DoubleDocValues extends DocValues {
|
||||||
return (long)doubleVal(doc);
|
return (long)doubleVal(doc);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean boolVal(int doc) {
|
||||||
|
return doubleVal(doc) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public abstract double doubleVal(int doc);
|
public abstract double doubleVal(int doc);
|
||||||
|
|
||||||
|
|
|
@ -53,40 +53,15 @@ public class DoubleFieldSource extends NumericFieldCacheSource<DoubleValues> {
|
||||||
final double[] arr = vals.values;
|
final double[] arr = vals.values;
|
||||||
final Bits valid = vals.valid;
|
final Bits valid = vals.valid;
|
||||||
|
|
||||||
return new DocValues() {
|
return new DoubleDocValues(this) {
|
||||||
@Override
|
|
||||||
public float floatVal(int doc) {
|
|
||||||
return (float) arr[doc];
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int intVal(int doc) {
|
|
||||||
return (int) arr[doc];
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public long longVal(int doc) {
|
|
||||||
return (long) arr[doc];
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public double doubleVal(int doc) {
|
public double doubleVal(int doc) {
|
||||||
return arr[doc];
|
return arr[doc];
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String strVal(int doc) {
|
public boolean exists(int doc) {
|
||||||
return Double.toString(arr[doc]);
|
return valid.get(doc);
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Object objectVal(int doc) {
|
|
||||||
return valid.get(doc) ? arr[doc] : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString(int doc) {
|
|
||||||
return description() + '=' + doubleVal(doc);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -147,7 +122,7 @@ public class DoubleFieldSource extends NumericFieldCacheSource<DoubleValues> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ValueFiller getValueFiller() {
|
public ValueFiller getValueFiller() {
|
||||||
return new ValueFiller() {
|
return new ValueFiller() {
|
||||||
private final double[] doubleArr = arr;
|
private final double[] doubleArr = arr;
|
||||||
|
|
|
@ -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.solr.search.function;
|
||||||
|
|
||||||
|
import org.apache.lucene.index.IndexReader;
|
||||||
|
import org.apache.lucene.index.IndexReader.AtomicReaderContext;
|
||||||
|
import org.apache.lucene.search.Explanation;
|
||||||
|
import org.apache.lucene.search.IndexSearcher;
|
||||||
|
import org.apache.lucene.util.BytesRef;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
|
||||||
|
public class IfFunction extends BoolFunction {
|
||||||
|
private ValueSource ifSource;
|
||||||
|
private ValueSource trueSource;
|
||||||
|
private ValueSource falseSource;
|
||||||
|
|
||||||
|
|
||||||
|
public IfFunction(ValueSource ifSource, ValueSource trueSource, ValueSource falseSource) {
|
||||||
|
this.ifSource = ifSource;
|
||||||
|
this.trueSource = trueSource;
|
||||||
|
this.falseSource = falseSource;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public DocValues getValues(Map context, AtomicReaderContext readerContext) throws IOException {
|
||||||
|
final DocValues ifVals = ifSource.getValues(context, readerContext);
|
||||||
|
final DocValues trueVals = trueSource.getValues(context, readerContext);
|
||||||
|
final DocValues falseVals = falseSource.getValues(context, readerContext);
|
||||||
|
|
||||||
|
return new DocValues() {
|
||||||
|
@Override
|
||||||
|
public byte byteVal(int doc) {
|
||||||
|
return ifVals.boolVal(doc) ? trueVals.byteVal(doc) : falseVals.byteVal(doc);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public short shortVal(int doc) {
|
||||||
|
return ifVals.boolVal(doc) ? trueVals.shortVal(doc) : falseVals.shortVal(doc);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public float floatVal(int doc) {
|
||||||
|
return ifVals.boolVal(doc) ? trueVals.floatVal(doc) : falseVals.floatVal(doc);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int intVal(int doc) {
|
||||||
|
return ifVals.boolVal(doc) ? trueVals.intVal(doc) : falseVals.intVal(doc);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long longVal(int doc) {
|
||||||
|
return ifVals.boolVal(doc) ? trueVals.longVal(doc) : falseVals.longVal(doc);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public double doubleVal(int doc) {
|
||||||
|
return ifVals.boolVal(doc) ? trueVals.doubleVal(doc) : falseVals.doubleVal(doc);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String strVal(int doc) {
|
||||||
|
return ifVals.boolVal(doc) ? trueVals.strVal(doc) : falseVals.strVal(doc);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean boolVal(int doc) {
|
||||||
|
return ifVals.boolVal(doc) ? trueVals.boolVal(doc) : falseVals.boolVal(doc);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean bytesVal(int doc, BytesRef target) {
|
||||||
|
return ifVals.boolVal(doc) ? trueVals.bytesVal(doc, target) : falseVals.bytesVal(doc, target);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object objectVal(int doc) {
|
||||||
|
return ifVals.boolVal(doc) ? trueVals.objectVal(doc) : falseVals.objectVal(doc);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean exists(int doc) {
|
||||||
|
return true; // TODO: flow through to any sub-sources?
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ValueFiller getValueFiller() {
|
||||||
|
// TODO: we need types of trueSource / falseSource to handle this
|
||||||
|
// for now, use float.
|
||||||
|
return super.getValueFiller();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString(int doc) {
|
||||||
|
return "if(" + ifVals.toString(doc) + ',' + trueVals.toString(doc) + ',' + falseVals.toString(doc) + ')';
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String description() {
|
||||||
|
return "if(" + ifSource.description() + ',' + trueSource.description() + ',' + falseSource + ')';
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
int h = ifSource.hashCode();
|
||||||
|
h = h * 31 + trueSource.hashCode();
|
||||||
|
h = h * 31 + falseSource.hashCode();
|
||||||
|
return h;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (!(o instanceof IfFunction)) return false;
|
||||||
|
IfFunction other = (IfFunction)o;
|
||||||
|
return ifSource.equals(other.ifSource)
|
||||||
|
&& trueSource.equals(other.trueSource)
|
||||||
|
&& falseSource.equals(other.falseSource);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void createWeight(Map context, IndexSearcher searcher) throws IOException {
|
||||||
|
ifSource.createWeight(context, searcher);
|
||||||
|
trueSource.createWeight(context, searcher);
|
||||||
|
falseSource.createWeight(context, searcher);
|
||||||
|
}
|
||||||
|
}
|
|
@ -38,6 +38,11 @@ public abstract class LongDocValues extends DocValues {
|
||||||
return (double)longVal(doc);
|
return (double)longVal(doc);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean boolVal(int doc) {
|
||||||
|
return longVal(doc) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String strVal(int doc) {
|
public String strVal(int doc) {
|
||||||
return Long.toString(longVal(doc));
|
return Long.toString(longVal(doc));
|
||||||
|
|
|
@ -0,0 +1,105 @@
|
||||||
|
/**
|
||||||
|
* 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.function;
|
||||||
|
|
||||||
|
import org.apache.lucene.index.IndexReader.AtomicReaderContext;
|
||||||
|
import org.apache.lucene.search.IndexSearcher;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
|
||||||
|
public abstract class MultiBoolFunction extends BoolFunction {
|
||||||
|
protected final List<ValueSource> sources;
|
||||||
|
|
||||||
|
public MultiBoolFunction(List<ValueSource> sources) {
|
||||||
|
this.sources = sources;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract String name();
|
||||||
|
|
||||||
|
protected abstract boolean func(int doc, DocValues[] vals);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BoolDocValues getValues(Map context, AtomicReaderContext readerContext) throws IOException {
|
||||||
|
final DocValues[] vals = new DocValues[sources.size()];
|
||||||
|
int i=0;
|
||||||
|
for (ValueSource source : sources) {
|
||||||
|
vals[i++] = source.getValues(context, readerContext);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new BoolDocValues(this) {
|
||||||
|
@Override
|
||||||
|
public boolean boolVal(int doc) {
|
||||||
|
return func(doc, vals);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString(int doc) {
|
||||||
|
StringBuilder sb = new StringBuilder(name());
|
||||||
|
sb.append('(');
|
||||||
|
boolean first = true;
|
||||||
|
for (DocValues dv : vals) {
|
||||||
|
if (first) {
|
||||||
|
first = false;
|
||||||
|
} else {
|
||||||
|
sb.append(',');
|
||||||
|
}
|
||||||
|
sb.append(dv.toString(doc));
|
||||||
|
}
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String description() {
|
||||||
|
StringBuilder sb = new StringBuilder(name());
|
||||||
|
sb.append('(');
|
||||||
|
boolean first = true;
|
||||||
|
for (ValueSource source : sources) {
|
||||||
|
if (first) {
|
||||||
|
first = false;
|
||||||
|
} else {
|
||||||
|
sb.append(',');
|
||||||
|
}
|
||||||
|
sb.append(source.description());
|
||||||
|
}
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return sources.hashCode() + name().hashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (this.getClass() != o.getClass()) return false;
|
||||||
|
MultiBoolFunction other = (MultiBoolFunction)o;
|
||||||
|
return this.sources.equals(other.sources);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void createWeight(Map context, IndexSearcher searcher) throws IOException {
|
||||||
|
for (ValueSource source : sources) {
|
||||||
|
source.createWeight(context, searcher);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,122 @@
|
||||||
|
package org.apache.solr.search.function;
|
||||||
|
/**
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import org.apache.lucene.index.IndexReader.AtomicReaderContext;
|
||||||
|
import org.apache.lucene.search.IndexSearcher;
|
||||||
|
import org.apache.lucene.util.BytesRef;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
|
||||||
|
public abstract class MultiFunction extends ValueSource {
|
||||||
|
protected final List<ValueSource> sources;
|
||||||
|
|
||||||
|
public MultiFunction(List<ValueSource> sources) {
|
||||||
|
this.sources = sources;
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract protected String name();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String description() {
|
||||||
|
return description(name(), sources);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String description(String name, List<ValueSource> sources) {
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
sb.append(name).append('(');
|
||||||
|
boolean firstTime=true;
|
||||||
|
for (ValueSource source : sources) {
|
||||||
|
if (firstTime) {
|
||||||
|
firstTime=false;
|
||||||
|
} else {
|
||||||
|
sb.append(',');
|
||||||
|
}
|
||||||
|
sb.append(source);
|
||||||
|
}
|
||||||
|
sb.append(')');
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static DocValues[] valsArr(List<ValueSource> sources, Map fcontext, AtomicReaderContext readerContext) throws IOException {
|
||||||
|
final DocValues[] valsArr = new DocValues[sources.size()];
|
||||||
|
int i=0;
|
||||||
|
for (ValueSource source : sources) {
|
||||||
|
valsArr[i++] = source.getValues(fcontext, readerContext);
|
||||||
|
}
|
||||||
|
return valsArr;
|
||||||
|
}
|
||||||
|
|
||||||
|
public class Values extends DocValues {
|
||||||
|
final DocValues[] valsArr;
|
||||||
|
|
||||||
|
public Values(DocValues[] valsArr) {
|
||||||
|
this.valsArr = valsArr;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString(int doc) {
|
||||||
|
return MultiFunction.toString(name(), valsArr, doc);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ValueFiller getValueFiller() {
|
||||||
|
// TODO: need ValueSource.type() to determine correct type
|
||||||
|
return super.getValueFiller();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static String toString(String name, DocValues[] valsArr, int doc) {
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
sb.append(name).append('(');
|
||||||
|
boolean firstTime=true;
|
||||||
|
for (DocValues vals : valsArr) {
|
||||||
|
if (firstTime) {
|
||||||
|
firstTime=false;
|
||||||
|
} else {
|
||||||
|
sb.append(',');
|
||||||
|
}
|
||||||
|
sb.append(vals.toString(doc));
|
||||||
|
}
|
||||||
|
sb.append(')');
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void createWeight(Map context, IndexSearcher searcher) throws IOException {
|
||||||
|
for (ValueSource source : sources)
|
||||||
|
source.createWeight(context, searcher);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return sources.hashCode() + name().hashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (this.getClass() != o.getClass()) return false;
|
||||||
|
MultiFunction other = (MultiFunction)o;
|
||||||
|
return this.sources.equals(other.sources);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,74 @@
|
||||||
|
/**
|
||||||
|
* 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.function;
|
||||||
|
|
||||||
|
import org.apache.lucene.index.IndexReader.AtomicReaderContext;
|
||||||
|
import org.apache.lucene.search.IndexSearcher;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
|
||||||
|
public abstract class SimpleBoolFunction extends BoolFunction {
|
||||||
|
protected final ValueSource source;
|
||||||
|
|
||||||
|
public SimpleBoolFunction(ValueSource source) {
|
||||||
|
this.source = source;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract String name();
|
||||||
|
|
||||||
|
protected abstract boolean func(int doc, DocValues vals);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BoolDocValues getValues(Map context, AtomicReaderContext readerContext) throws IOException {
|
||||||
|
final DocValues vals = source.getValues(context, readerContext);
|
||||||
|
return new BoolDocValues(this) {
|
||||||
|
@Override
|
||||||
|
public boolean boolVal(int doc) {
|
||||||
|
return func(doc, vals);
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public String toString(int doc) {
|
||||||
|
return name() + '(' + vals.toString(doc) + ')';
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String description() {
|
||||||
|
return name() + '(' + source.description() + ')';
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return source.hashCode() + name().hashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (this.getClass() != o.getClass()) return false;
|
||||||
|
SingleFunction other = (SingleFunction)o;
|
||||||
|
return this.source.equals(other.source);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void createWeight(Map context, IndexSearcher searcher) throws IOException {
|
||||||
|
source.createWeight(context, searcher);
|
||||||
|
}
|
||||||
|
}
|
|
@ -21,6 +21,11 @@ public abstract class StrDocValues extends DocValues {
|
||||||
return exists(doc) ? strVal(doc) : null;
|
return exists(doc) ? strVal(doc) : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean boolVal(int doc) {
|
||||||
|
return exists(doc);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString(int doc) {
|
public String toString(int doc) {
|
||||||
return vs.description() + "='" + strVal(doc) + "'";
|
return vs.description() + "='" + strVal(doc) + "'";
|
||||||
|
|
|
@ -78,6 +78,10 @@ public abstract class StringIndexDocValues extends DocValues {
|
||||||
return spareChars.toString();
|
return spareChars.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean boolVal(int doc) {
|
||||||
|
return exists(doc);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public abstract Object objectVal(int doc); // force subclasses to override
|
public abstract Object objectVal(int doc); // force subclasses to override
|
||||||
|
|
|
@ -119,7 +119,29 @@ public class TestQueryTypes extends AbstractSolrTestCase {
|
||||||
assertQ(req( "q", "{!frange v="+f+" l='"+v+"' u='"+v+"'}" )
|
assertQ(req( "q", "{!frange v="+f+" l='"+v+"' u='"+v+"'}" )
|
||||||
,"//result[@numFound='1']"
|
,"//result[@numFound='1']"
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// exists()
|
||||||
|
assertQ(req( "fq","id:999", "q", "{!frange l=1 u=1}if(exists("+f+"),1,0)" )
|
||||||
|
,"//result[@numFound='1']"
|
||||||
|
);
|
||||||
|
|
||||||
|
// boolean value of non-zero values (just leave off the exists from the prev test)
|
||||||
|
assertQ(req( "fq","id:999", "q", "{!frange l=1 u=1}if("+f+",1,0)" )
|
||||||
|
,"//result[@numFound='1']"
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!"id".equals(f)) {
|
||||||
|
assertQ(req( "fq","id:1", "q", "{!frange l=1 u=1}if(exists("+f+"),1,0)" )
|
||||||
|
,"//result[@numFound='0']"
|
||||||
|
);
|
||||||
|
|
||||||
|
// boolean value of zero/missing values (just leave off the exists from the prev test)
|
||||||
|
assertQ(req( "fq","id:1", "q", "{!frange l=1 u=1}if("+f+",1,0)" )
|
||||||
|
,"//result[@numFound='0']"
|
||||||
|
);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
// function query... just make sure it doesn't throw an exception
|
// function query... just make sure it doesn't throw an exception
|
||||||
if ("v_s".equals(f)) continue; // in this context, functions must be able to be interpreted as a float
|
if ("v_s".equals(f)) continue; // in this context, functions must be able to be interpreted as a float
|
||||||
assertQ(req( "q", "+id:999 _val_:\"" + f + "\"")
|
assertQ(req( "q", "+id:999 _val_:\"" + f + "\"")
|
||||||
|
|
|
@ -581,4 +581,56 @@ public class TestFunctionQuery extends SolrTestCaseJ4 {
|
||||||
purgeFieldCache(FieldCache.DEFAULT); // avoid FC insanity
|
purgeFieldCache(FieldCache.DEFAULT); // avoid FC insanity
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testBooleanFunctions() throws Exception {
|
||||||
|
assertU(adoc("id", "1", "text", "hello", "foo_s","A", "foo_ti", "0", "foo_tl","0"));
|
||||||
|
assertU(adoc("id", "2" , "foo_ti","10", "foo_tl","11"));
|
||||||
|
assertU(commit());
|
||||||
|
|
||||||
|
// true and false functions and constants
|
||||||
|
assertJQ(req("q", "id:1", "fl", "t:true(),f:false(),tt:{!func}true,ff:{!func}false")
|
||||||
|
, "/response/docs/[0]=={'t':true,'f':false,'tt':true,'ff':false}");
|
||||||
|
|
||||||
|
// test that exists(query) depends on the query matching the document
|
||||||
|
assertJQ(req("q", "id:1", "fl", "t:exists(query($q1)),f:exists(query($q2))", "q1","text:hello", "q2","text:there")
|
||||||
|
, "/response/docs/[0]=={'t':true,'f':false}");
|
||||||
|
|
||||||
|
// test if()
|
||||||
|
assertJQ(req("q", "id:1", "fl", "a1:if(true,'A','B')", "fl","b1:if(false,'A','B')")
|
||||||
|
, "/response/docs/[0]=={'a1':'A', 'b1':'B'}");
|
||||||
|
|
||||||
|
// test boolean operators
|
||||||
|
assertJQ(req("q", "id:1", "fl", "t1:and(true,true)", "fl","f1:and(true,false)", "fl","f2:and(false,true)", "fl","f3:and(false,false)")
|
||||||
|
, "/response/docs/[0]=={'t1':true, 'f1':false, 'f2':false, 'f3':false}");
|
||||||
|
assertJQ(req("q", "id:1", "fl", "t1:or(true,true)", "fl","t2:or(true,false)", "fl","t3:or(false,true)", "fl","f1:or(false,false)")
|
||||||
|
, "/response/docs/[0]=={'t1':true, 't2':true, 't3':true, 'f1':false}");
|
||||||
|
assertJQ(req("q", "id:1", "fl", "f1:xor(true,true)", "fl","t1:xor(true,false)", "fl","t2:xor(false,true)", "fl","f2:xor(false,false)")
|
||||||
|
, "/response/docs/[0]=={'t1':true, 't2':true, 'f1':false, 'f2':false}");
|
||||||
|
assertJQ(req("q", "id:1", "fl", "t:not(false),f:not(true)")
|
||||||
|
, "/response/docs/[0]=={'t':true, 'f':false}");
|
||||||
|
|
||||||
|
|
||||||
|
// def(), the default function that returns the first value that exists
|
||||||
|
assertJQ(req("q", "id:1", "fl", "x:def(id,123.0), y:def(foo_f,234.0)")
|
||||||
|
, "/response/docs/[0]=={'x':1.0, 'y':234.0}");
|
||||||
|
assertJQ(req("q", "id:1", "fl", "x:def(foo_s,'Q'), y:def(missing_s,'W')")
|
||||||
|
, "/response/docs/[0]=={'x':'A', 'y':'W'}");
|
||||||
|
|
||||||
|
// test constant conversion to boolean
|
||||||
|
assertJQ(req("q", "id:1", "fl", "a:not(0), b:not(1), c:not(0.0), d:not(1.1), e:not('A')")
|
||||||
|
, "/response/docs/[0]=={'a':true, 'b':false, 'c':true, 'd':false, 'e':false}");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testPseudoFieldFunctions() throws Exception {
|
||||||
|
assertU(adoc("id", "1", "text", "hello", "foo_s","A"));
|
||||||
|
assertU(adoc("id", "2"));
|
||||||
|
assertU(commit());
|
||||||
|
|
||||||
|
assertJQ(req("q", "id:1", "fl", "a:1,b:2.0,c:'X',d:{!func}foo_s,e:{!func}bar_s") // if exists() is false, no pseudo-field should be added
|
||||||
|
, "/response/docs/[0]=={'a':1, 'b':2.0,'c':'X','d':'A'}");
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue