use of org.apache.lucene.queries.function.ValueSource in project lucene-solr by apache.
the class StatsCollectorSupplierFactory method buildNumericSource.
/**
* Recursively parses and breaks down the expression string to build a numeric ValueSource.
*
* @param schema The schema to pull fields from.
* @param expressionString The expression string to build a ValueSource from.
* @return The value source represented by the given expressionString
*/
private static ValueSource buildNumericSource(IndexSchema schema, String expressionString) {
int paren = expressionString.indexOf('(');
String[] arguments;
String operands;
if (paren < 0) {
return buildFieldSource(schema, expressionString, NUMBER_TYPE);
} else {
try {
operands = expressionString.substring(paren + 1, expressionString.lastIndexOf(')')).trim();
} catch (Exception e) {
throw new SolrException(ErrorCode.BAD_REQUEST, "Missing closing parenthesis in [" + expressionString + "]");
}
arguments = ExpressionFactory.getArguments(operands);
}
String operation = expressionString.substring(0, paren).trim();
if (operation.equals(AnalyticsParams.CONSTANT_NUMBER)) {
if (arguments.length != 1) {
throw new SolrException(ErrorCode.BAD_REQUEST, "The constant number declaration [" + expressionString + "] does not have exactly 1 argument.");
}
return new ConstDoubleSource(Double.parseDouble(arguments[0]));
} else if (operation.equals(AnalyticsParams.NEGATE)) {
if (arguments.length != 1) {
throw new SolrException(ErrorCode.BAD_REQUEST, "The negate operation [" + expressionString + "] does not have exactly 1 argument.");
}
ValueSource argSource = buildNumericSource(schema, arguments[0]);
if (argSource == null) {
throw new SolrException(ErrorCode.BAD_REQUEST, "The operation \"" + AnalyticsParams.NEGATE + "\" requires a numeric field or operation as argument. \"" + arguments[0] + "\" is not a numeric field or operation.");
}
return new NegateDoubleFunction(argSource);
} else if (operation.equals(AnalyticsParams.ABSOLUTE_VALUE)) {
if (arguments.length != 1) {
throw new SolrException(ErrorCode.BAD_REQUEST, "The absolute value operation [" + expressionString + "] does not have exactly 1 argument.");
}
ValueSource argSource = buildNumericSource(schema, arguments[0]);
if (argSource == null) {
throw new SolrException(ErrorCode.BAD_REQUEST, "The operation \"" + AnalyticsParams.NEGATE + "\" requires a numeric field or operation as argument. \"" + arguments[0] + "\" is not a numeric field or operation.");
}
return new AbsoluteValueDoubleFunction(argSource);
} else if (operation.equals(AnalyticsParams.FILTER)) {
return buildFilterSource(schema, operands, NUMBER_TYPE);
}
List<ValueSource> subExpressions = new ArrayList<>();
for (String argument : arguments) {
ValueSource argSource = buildNumericSource(schema, argument);
if (argSource == null) {
throw new SolrException(ErrorCode.BAD_REQUEST, "The operation \"" + operation + "\" requires numeric fields or operations as arguments. \"" + argument + "\" is not a numeric field or operation.");
}
subExpressions.add(argSource);
}
if (operation.equals(AnalyticsParams.ADD)) {
return new AddDoubleFunction(subExpressions.toArray(new ValueSource[0]));
} else if (operation.equals(AnalyticsParams.MULTIPLY)) {
return new MultiplyDoubleFunction(subExpressions.toArray(new ValueSource[0]));
} else if (operation.equals(AnalyticsParams.DIVIDE)) {
if (subExpressions.size() != 2) {
throw new SolrException(ErrorCode.BAD_REQUEST, "The divide operation [" + expressionString + "] does not have exactly 2 arguments.");
}
return new DivDoubleFunction(subExpressions.get(0), subExpressions.get(1));
} else if (operation.equals(AnalyticsParams.POWER)) {
if (subExpressions.size() != 2) {
throw new SolrException(ErrorCode.BAD_REQUEST, "The power operation [" + expressionString + "] does not have exactly 2 arguments.");
}
return new PowDoubleFunction(subExpressions.get(0), subExpressions.get(1));
} else if (operation.equals(AnalyticsParams.LOG)) {
if (subExpressions.size() != 2) {
throw new SolrException(ErrorCode.BAD_REQUEST, "The log operation [" + expressionString + "] does not have exactly 2 arguments.");
}
return new LogDoubleFunction(subExpressions.get(0), subExpressions.get(1));
}
if (AnalyticsParams.DATE_OPERATION_SET.contains(operation) || AnalyticsParams.STRING_OPERATION_SET.contains(operation)) {
return null;
}
throw new SolrException(ErrorCode.BAD_REQUEST, "The operation [" + expressionString + "] is not supported.");
}
use of org.apache.lucene.queries.function.ValueSource in project lucene-solr by apache.
the class MultiStringFunction method description.
@Override
public String description() {
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();
}
use of org.apache.lucene.queries.function.ValueSource in project lucene-solr by apache.
the class MultiDoubleFunction method description.
@Override
public String description() {
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();
}
use of org.apache.lucene.queries.function.ValueSource in project lucene-solr by apache.
the class NumericFacets method getCountsSingleValue.
private static NamedList<Integer> getCountsSingleValue(SolrIndexSearcher searcher, DocSet docs, String fieldName, int offset, int limit, int mincount, boolean missing, String sort) throws IOException {
boolean zeros = mincount <= 0;
mincount = Math.max(mincount, 1);
final SchemaField sf = searcher.getSchema().getField(fieldName);
final FieldType ft = sf.getType();
final NumberType numericType = ft.getNumberType();
if (numericType == null) {
throw new IllegalStateException();
}
// We don't return zeros when using PointFields or when index=false
zeros = zeros && !ft.isPointField() && sf.indexed();
final List<LeafReaderContext> leaves = searcher.getIndexReader().leaves();
// 1. accumulate
final HashTable hashTable = new HashTable(true);
final Iterator<LeafReaderContext> ctxIt = leaves.iterator();
LeafReaderContext ctx = null;
NumericDocValues longs = null;
int missingCount = 0;
for (DocIterator docsIt = docs.iterator(); docsIt.hasNext(); ) {
final int doc = docsIt.nextDoc();
if (ctx == null || doc >= ctx.docBase + ctx.reader().maxDoc()) {
do {
ctx = ctxIt.next();
} while (ctx == null || doc >= ctx.docBase + ctx.reader().maxDoc());
assert doc >= ctx.docBase;
switch(numericType) {
case LONG:
case DATE:
case INTEGER:
// Long, Date and Integer
longs = DocValues.getNumeric(ctx.reader(), fieldName);
break;
case FLOAT:
// TODO: this bit flipping should probably be moved to tie-break in the PQ comparator
longs = new FilterNumericDocValues(DocValues.getNumeric(ctx.reader(), fieldName)) {
@Override
public long longValue() throws IOException {
long bits = super.longValue();
if (bits < 0)
bits ^= 0x7fffffffffffffffL;
return bits;
}
};
break;
case DOUBLE:
// TODO: this bit flipping should probably be moved to tie-break in the PQ comparator
longs = new FilterNumericDocValues(DocValues.getNumeric(ctx.reader(), fieldName)) {
@Override
public long longValue() throws IOException {
long bits = super.longValue();
if (bits < 0)
bits ^= 0x7fffffffffffffffL;
return bits;
}
};
break;
default:
throw new AssertionError("Unexpected type: " + numericType);
}
}
int valuesDocID = longs.docID();
if (valuesDocID < doc - ctx.docBase) {
valuesDocID = longs.advance(doc - ctx.docBase);
}
if (valuesDocID == doc - ctx.docBase) {
hashTable.add(doc, longs.longValue(), 1);
} else {
++missingCount;
}
}
// 2. select top-k facet values
final int pqSize = limit < 0 ? hashTable.size : Math.min(offset + limit, hashTable.size);
final PriorityQueue<Entry> pq;
if (FacetParams.FACET_SORT_COUNT.equals(sort) || FacetParams.FACET_SORT_COUNT_LEGACY.equals(sort)) {
pq = new PriorityQueue<Entry>(pqSize) {
@Override
protected boolean lessThan(Entry a, Entry b) {
if (a.count < b.count || (a.count == b.count && a.bits > b.bits)) {
return true;
} else {
return false;
}
}
};
} else {
pq = new PriorityQueue<Entry>(pqSize) {
@Override
protected boolean lessThan(Entry a, Entry b) {
return a.bits > b.bits;
}
};
}
Entry e = null;
for (int i = 0; i < hashTable.bits.length; ++i) {
if (hashTable.counts[i] >= mincount) {
if (e == null) {
e = new Entry();
}
e.bits = hashTable.bits[i];
e.count = hashTable.counts[i];
e.docID = hashTable.docIDs[i];
e = pq.insertWithOverflow(e);
}
}
// 4. build the NamedList
final ValueSource vs = ft.getValueSource(sf, null);
final NamedList<Integer> result = new NamedList<>();
// to be merged with terms from the terms dict
if (!zeros || FacetParams.FACET_SORT_COUNT.equals(sort) || FacetParams.FACET_SORT_COUNT_LEGACY.equals(sort)) {
// Only keep items we're interested in
final Deque<Entry> counts = new ArrayDeque<>();
while (pq.size() > offset) {
counts.addFirst(pq.pop());
}
// Entries from the PQ first, then using the terms dictionary
for (Entry entry : counts) {
final int readerIdx = ReaderUtil.subIndex(entry.docID, leaves);
final FunctionValues values = vs.getValues(Collections.emptyMap(), leaves.get(readerIdx));
result.add(values.strVal(entry.docID - leaves.get(readerIdx).docBase), entry.count);
}
if (zeros && (limit < 0 || result.size() < limit)) {
// need to merge with the term dict
if (!sf.indexed() && !sf.hasDocValues()) {
throw new IllegalStateException("Cannot use " + FacetParams.FACET_MINCOUNT + "=0 on field " + sf.getName() + " which is neither indexed nor docValues");
}
// Add zeros until there are limit results
final Set<String> alreadySeen = new HashSet<>();
while (pq.size() > 0) {
Entry entry = pq.pop();
final int readerIdx = ReaderUtil.subIndex(entry.docID, leaves);
final FunctionValues values = vs.getValues(Collections.emptyMap(), leaves.get(readerIdx));
alreadySeen.add(values.strVal(entry.docID - leaves.get(readerIdx).docBase));
}
for (int i = 0; i < result.size(); ++i) {
alreadySeen.add(result.getName(i));
}
final Terms terms = searcher.getSlowAtomicReader().terms(fieldName);
if (terms != null) {
final String prefixStr = TrieField.getMainValuePrefix(ft);
final BytesRef prefix;
if (prefixStr != null) {
prefix = new BytesRef(prefixStr);
} else {
prefix = new BytesRef();
}
final TermsEnum termsEnum = terms.iterator();
BytesRef term;
switch(termsEnum.seekCeil(prefix)) {
case FOUND:
case NOT_FOUND:
term = termsEnum.term();
break;
case END:
term = null;
break;
default:
throw new AssertionError();
}
final CharsRefBuilder spare = new CharsRefBuilder();
for (int skipped = hashTable.size; skipped < offset && term != null && StringHelper.startsWith(term, prefix); ) {
ft.indexedToReadable(term, spare);
final String termStr = spare.toString();
if (!alreadySeen.contains(termStr)) {
++skipped;
}
term = termsEnum.next();
}
for (; term != null && StringHelper.startsWith(term, prefix) && (limit < 0 || result.size() < limit); term = termsEnum.next()) {
ft.indexedToReadable(term, spare);
final String termStr = spare.toString();
if (!alreadySeen.contains(termStr)) {
result.add(termStr, 0);
}
}
}
}
} else {
// => Merge the PQ and the terms dictionary on the fly
if (!sf.indexed()) {
throw new IllegalStateException("Cannot use " + FacetParams.FACET_SORT + "=" + FacetParams.FACET_SORT_INDEX + " on a field which is not indexed");
}
final Map<String, Integer> counts = new HashMap<>();
while (pq.size() > 0) {
final Entry entry = pq.pop();
final int readerIdx = ReaderUtil.subIndex(entry.docID, leaves);
final FunctionValues values = vs.getValues(Collections.emptyMap(), leaves.get(readerIdx));
counts.put(values.strVal(entry.docID - leaves.get(readerIdx).docBase), entry.count);
}
final Terms terms = searcher.getSlowAtomicReader().terms(fieldName);
if (terms != null) {
final String prefixStr = TrieField.getMainValuePrefix(ft);
final BytesRef prefix;
if (prefixStr != null) {
prefix = new BytesRef(prefixStr);
} else {
prefix = new BytesRef();
}
final TermsEnum termsEnum = terms.iterator();
BytesRef term;
switch(termsEnum.seekCeil(prefix)) {
case FOUND:
case NOT_FOUND:
term = termsEnum.term();
break;
case END:
term = null;
break;
default:
throw new AssertionError();
}
final CharsRefBuilder spare = new CharsRefBuilder();
for (int i = 0; i < offset && term != null && StringHelper.startsWith(term, prefix); ++i) {
term = termsEnum.next();
}
for (; term != null && StringHelper.startsWith(term, prefix) && (limit < 0 || result.size() < limit); term = termsEnum.next()) {
ft.indexedToReadable(term, spare);
final String termStr = spare.toString();
Integer count = counts.get(termStr);
if (count == null) {
count = 0;
}
result.add(termStr, count);
}
}
}
if (missing) {
result.add(null, missingCount);
}
return result;
}
use of org.apache.lucene.queries.function.ValueSource in project lucene-solr by apache.
the class GeoDistValueSourceParser method parse.
@Override
public ValueSource parse(FunctionQParser fp) throws SyntaxError {
// TODO: dispatch through SpatialQueryable in the future?
//note: parseValueSourceList can't handle a field reference to an AbstractSpatialFieldType,
// so those fields are expressly handled via sfield=
List<ValueSource> sources = fp.parseValueSourceList();
// "m" is a multi-value source, "x" is a single-value source
// allow (m,m) (m,x,x) (x,x,m) (x,x,x,x)
// if not enough points are present, "pt" will be checked first, followed by "sfield".
MultiValueSource mv1 = null;
MultiValueSource mv2 = null;
if (sources.size() == 0) {
// nothing to do now
} else if (sources.size() == 1) {
ValueSource vs = sources.get(0);
if (!(vs instanceof MultiValueSource)) {
throw new SyntaxError("geodist - invalid parameters:" + sources);
}
mv1 = (MultiValueSource) vs;
} else if (sources.size() == 2) {
ValueSource vs1 = sources.get(0);
ValueSource vs2 = sources.get(1);
if (vs1 instanceof MultiValueSource && vs2 instanceof MultiValueSource) {
mv1 = (MultiValueSource) vs1;
mv2 = (MultiValueSource) vs2;
} else {
mv1 = makeMV(sources, sources);
}
} else if (sources.size() == 3) {
ValueSource vs1 = sources.get(0);
ValueSource vs2 = sources.get(1);
if (vs1 instanceof MultiValueSource) {
// (m,x,x)
mv1 = (MultiValueSource) vs1;
mv2 = makeMV(sources.subList(1, 3), sources);
} else {
// (x,x,m)
mv1 = makeMV(sources.subList(0, 2), sources);
vs1 = sources.get(2);
if (!(vs1 instanceof MultiValueSource)) {
throw new SyntaxError("geodist - invalid parameters:" + sources);
}
mv2 = (MultiValueSource) vs1;
}
} else if (sources.size() == 4) {
mv1 = makeMV(sources.subList(0, 2), sources);
mv2 = makeMV(sources.subList(2, 4), sources);
} else if (sources.size() > 4) {
throw new SyntaxError("geodist - invalid parameters:" + sources);
}
if (mv1 == null) {
mv1 = parsePoint(fp);
mv2 = parseSfield(fp);
} else if (mv2 == null) {
mv2 = parsePoint(fp);
if (mv2 == null)
mv2 = parseSfield(fp);
}
if (mv1 == null || mv2 == null) {
throw new SyntaxError("geodist - not enough parameters:" + sources);
}
// We have all the parameters at this point, now check if one of the points is constant
//latLon
double[] constants;
constants = getConstants(mv1);
MultiValueSource other = mv2;
if (constants == null) {
constants = getConstants(mv2);
other = mv1;
}
// sfield can only be in mv2, according to the logic above
if (mv2 instanceof SpatialStrategyMultiValueSource) {
if (constants == null)
throw new SyntaxError("When using AbstractSpatialFieldType (e.g. RPT not LatLonType)," + " the point must be supplied as constants");
// note: uses Haversine by default but can be changed via distCalc=...
SpatialStrategy strategy = ((SpatialStrategyMultiValueSource) mv2).strategy;
DistanceUnits distanceUnits = ((SpatialStrategyMultiValueSource) mv2).distanceUnits;
Point queryPoint = strategy.getSpatialContext().makePoint(constants[1], constants[0]);
return strategy.makeDistanceValueSource(queryPoint, distanceUnits.multiplierFromDegreesToThisUnit());
}
if (constants != null && other instanceof VectorValueSource) {
return new HaversineConstFunction(constants[0], constants[1], (VectorValueSource) other);
}
return new HaversineFunction(mv1, mv2, DistanceUtils.EARTH_MEAN_RADIUS_KM, true);
}
Aggregations