@Nullable private Function rewriteAndValidateFields(Function function, Context context) { if (function.arguments().size() == 2) { Symbol left = function.arguments().get(0); Symbol right = function.arguments().get(1); if (left.symbolType() == SymbolType.REFERENCE && right.symbolType().isValueSymbol()) { Reference ref = (Reference) left; if (ref.info().ident().columnIdent().equals(DocSysColumns.ID)) { function.setArgument( 0, new Reference(DocSysColumns.forTable(ref.ident().tableIdent(), DocSysColumns.UID))); function.setArgument( 1, Literal.newLiteral( Uid.createUid( Constants.DEFAULT_MAPPING_TYPE, ValueSymbolVisitor.STRING.process(right)))); } else { String unsupportedMessage = context.unsupportedMessage(ref.info().ident().columnIdent().name()); if (unsupportedMessage != null) { throw new UnsupportedFeatureException(unsupportedMessage); } } } } return function; }
@Override public Query apply(Function input, Context context) { assert input != null; assert input.arguments().size() == 1; BooleanQuery query = new BooleanQuery(); query.add(process(input.arguments().get(0), context), BooleanClause.Occur.MUST_NOT); query.add(Queries.newMatchAllQuery(), BooleanClause.Occur.MUST); return query; }
@Nullable protected Tuple<Reference, Literal> prepare(Function input) { assert input != null; assert input.arguments().size() == 2; Symbol left = input.arguments().get(0); Symbol right = input.arguments().get(1); if (!(left instanceof Reference) || !(right.symbolType().isValueSymbol())) { return null; } assert right.symbolType() == SymbolType.LITERAL; return new Tuple<>((Reference) left, (Literal) right); }
@Override public Query apply(Function input, Context context) throws IOException { List<Symbol> arguments = input.arguments(); assert arguments.size() == 4 : "invalid number of arguments"; assert Symbol.isLiteral(arguments.get(0), DataTypes.OBJECT); assert Symbol.isLiteral(arguments.get(1), DataTypes.STRING); assert Symbol.isLiteral(arguments.get(2), DataTypes.STRING); assert Symbol.isLiteral(arguments.get(3), DataTypes.OBJECT); @SuppressWarnings("unchecked") Map<String, Object> fields = (Map) ((Literal) arguments.get(0)).value(); BytesRef queryString = (BytesRef) ((Literal) arguments.get(1)).value(); BytesRef matchType = (BytesRef) ((Literal) arguments.get(2)).value(); Map options = (Map) ((Literal) arguments.get(3)).value(); checkArgument(queryString != null, "cannot use NULL as query term in match predicate"); MatchQueryBuilder queryBuilder; if (fields.size() == 1) { queryBuilder = new MatchQueryBuilder(context.mapperService, context.indexCache, matchType, options); } else { queryBuilder = new MultiMatchQueryBuilder( context.mapperService, context.indexCache, matchType, options); } return queryBuilder.query(fields, queryString); }
@Override public Query visitFunction(Function function, Context context) { assert function != null; if (fieldIgnored(function, context)) { return Queries.newMatchAllQuery(); } function = rewriteAndValidateFields(function, context); FunctionToQuery toQuery = functions.get(function.info().ident().name()); if (toQuery == null) { return genericFunctionQuery(function, context); } Query query; try { query = toQuery.apply(function, context); } catch (IOException e) { throw ExceptionsHelper.convertToRuntime(e); } catch (UnsupportedOperationException e) { return genericFunctionQuery(function, context); } if (query == null) { query = queryFromInnerFunction(function, context); if (query == null) { return genericFunctionQuery(function, context); } } return query; }
@Override public Query apply(Function input, Context context) { assert input != null; BooleanQuery query = new BooleanQuery(); for (Symbol symbol : input.arguments()) { query.add(process(symbol, context), BooleanClause.Occur.MUST); } return query; }
@Override public Query apply(Function input, Context context) { assert input != null; assert input.arguments().size() == 1; Symbol arg = input.arguments().get(0); if (arg.symbolType() != SymbolType.REFERENCE) { return null; } Reference reference = (Reference) arg; String columnName = reference.info().ident().columnIdent().fqn(); QueryBuilderHelper builderHelper = QueryBuilderHelper.forType(reference.valueType()); BooleanFilter boolFilter = new BooleanFilter(); boolFilter.add( builderHelper.rangeFilter(columnName, null, null, true, true), BooleanClause.Occur.MUST_NOT); return new FilteredQuery(Queries.newMatchAllQuery(), boolFilter); }
@Override public Query apply(Function input, Context context) { assert input != null; BooleanQuery query = new BooleanQuery(); query.setMinimumNumberShouldMatch(1); for (Symbol symbol : input.arguments()) { query.add(process(symbol, context), BooleanClause.Occur.SHOULD); } return query; }
@Override public Query apply(Function function, Context context) throws IOException { Symbol left = function.arguments().get(0); Symbol collectionSymbol = function.arguments().get(1); Preconditions.checkArgument( DataTypes.isCollectionType(collectionSymbol.valueType()), "invalid argument for ANY expression"); if (left.symbolType().isValueSymbol()) { // 1 = any (array_col) - simple eq assert collectionSymbol.symbolType().isReference() : "no reference found in ANY expression"; return applyArrayReference((Reference) collectionSymbol, (Literal) left, context); } else if (collectionSymbol.symbolType().isValueSymbol()) { assert left.symbolType().isReference() : "no reference found in ANY expression"; return applyArrayLiteral((Reference) left, (Literal) collectionSymbol, context); } else { // should never get here - 2 literal arguments must have been normalized away yet return null; } }
private boolean fieldIgnored(Function function, Context context) { if (function.arguments().size() != 2) { return false; } Symbol left = function.arguments().get(0); Symbol right = function.arguments().get(1); if (left.symbolType() == SymbolType.REFERENCE && right.symbolType().isValueSymbol()) { String columnName = ((Reference) left).info().ident().columnIdent().name(); if (Context.FILTERED_FIELDS.contains(columnName)) { context.filteredFieldValues.put(columnName, ((Input) right).value()); return true; } String unsupportedMessage = Context.UNSUPPORTED_FIELDS.get(columnName); if (unsupportedMessage != null) { throw new UnsupportedFeatureException(unsupportedMessage); } } return false; }
RefLiteralPair(Function function) { assert function.arguments().size() == 2 : "function requires 2 arguments"; Symbol left = function.arguments().get(0); Symbol right = function.arguments().get(1); if (left instanceof Reference) { reference = (Reference) left; } else if (right instanceof Reference) { reference = (Reference) right; } else { reference = null; } if (left.symbolType().isValueSymbol()) { input = (Input) left; } else if (right.symbolType().isValueSymbol()) { input = (Input) right; } else { input = null; } }
FunctionLiteralPair(Function outerFunction) { assert outerFunction.arguments().size() == 2 : "function requires 2 arguments"; Symbol left = outerFunction.arguments().get(0); Symbol right = outerFunction.arguments().get(1); functionName = outerFunction.info().ident().name(); if (left instanceof Function) { function = (Function) left; } else if (right instanceof Function) { function = (Function) right; } else { function = null; } if (left.symbolType().isValueSymbol()) { input = (Input) left; } else if (right.symbolType().isValueSymbol()) { input = (Input) right; } else { input = null; } }
private static Filter genericFunctionFilter(Function function, Context context) { if (function.valueType() != DataTypes.BOOLEAN) { raiseUnsupported(function); } // avoid field-cache // reason1: analyzed columns or columns with index off wouldn't work // substr(n, 1, 1) in the case of n => analyzed would throw an error because n would be an // array // reason2: would have to load each value into the field cache function = (Function) DocReferenceConverter.convertIf(function); final CollectInputSymbolVisitor.Context ctx = context.inputSymbolVisitor.extractImplementations(function); assert ctx.topLevelInputs().size() == 1; @SuppressWarnings("unchecked") final Input<Boolean> condition = (Input<Boolean>) ctx.topLevelInputs().get(0); @SuppressWarnings("unchecked") final List<LuceneCollectorExpression> expressions = ctx.docLevelExpressions(); final CollectorContext collectorContext = new CollectorContext( context.mapperService, context.fieldDataService, new CollectorFieldsVisitor(expressions.size())); for (LuceneCollectorExpression expression : expressions) { expression.startCollect(collectorContext); } return new Filter() { @Override public DocIdSet getDocIdSet(AtomicReaderContext context, Bits acceptDocs) throws IOException { for (LuceneCollectorExpression expression : expressions) { expression.setNextReader(context.reader().getContext()); } return BitsFilteredDocIdSet.wrap( new FunctionDocSet( context.reader(), collectorContext.visitor(), condition, expressions, context.reader().maxDoc(), acceptDocs), acceptDocs); } }; }
private Query queryFromInnerFunction(Function function, Context context) { for (Symbol symbol : function.arguments()) { if (symbol.symbolType() == SymbolType.FUNCTION) { String functionName = ((Function) symbol).info().ident().name(); InnerFunctionToQuery functionToQuery = innerFunctions.get(functionName); if (functionToQuery != null) { try { Query query = functionToQuery.apply(function, (Function) symbol, context); if (query != null) { return query; } } catch (IOException e) { throw ExceptionsHelper.convertToRuntime(e); } } } } return null; }
/** * @param parent the outer function. E.g. in the case of * <pre>where distance(p1, POINT (10 20)) > 20</pre> * this would be * <pre>gt( \<inner function\>, 20)</pre> * * @param inner has to be the distance function */ @Override public Query apply(Function parent, Function inner, Context context) { assert inner.info().ident().name().equals(DistanceFunction.NAME); RefLiteralPair distanceRefLiteral = new RefLiteralPair(inner); if (!distanceRefLiteral.isValid()) { // can't use distance filter without literal, fallback to genericFunction return null; } FunctionLiteralPair functionLiteralPair = new FunctionLiteralPair(parent); if (!functionLiteralPair.isValid()) { // must be something like eq(distance(..), non-literal) - fallback to genericFunction return null; } Double distance = DataTypes.DOUBLE.value(functionLiteralPair.input().value()); String fieldName = distanceRefLiteral.reference().info().ident().columnIdent().fqn(); FieldMapper mapper = getGeoPointFieldMapper(fieldName, context.mapperService); GeoPointFieldMapper geoMapper = ((GeoPointFieldMapper) mapper); IndexGeoPointFieldData fieldData = context.fieldDataService.getForField(mapper); Input geoPointInput = distanceRefLiteral.input(); Double[] pointValue = (Double[]) geoPointInput.value(); double lat = pointValue[1]; double lon = pointValue[0]; String parentName = functionLiteralPair.functionName(); Double from = null; Double to = null; boolean includeLower = false; boolean includeUpper = false; switch (parentName) { case EqOperator.NAME: includeLower = true; includeUpper = true; from = distance; to = distance; break; case LteOperator.NAME: includeUpper = true; to = distance; break; case LtOperator.NAME: to = distance; break; case GteOperator.NAME: from = distance; includeLower = true; break; case GtOperator.NAME: from = distance; break; default: // invalid operator? give up return null; } GeoPoint geoPoint = new GeoPoint(lat, lon); Filter filter = new GeoDistanceRangeFilter( geoPoint, from, to, includeLower, includeUpper, GEO_DISTANCE, geoMapper, fieldData, optimizeBox); return new FilteredQuery( Queries.newMatchAllQuery(), context.indexCache.filter().cache(filter)); }