package ameba.db.ebean.filter; import ameba.core.ws.rs.ParamConverters; import ameba.db.dsl.ExprTransformer; import ameba.db.dsl.QueryExprMeta; import ameba.db.dsl.QueryExprMeta.Val; import ameba.db.dsl.QuerySyntaxException; import ameba.db.dsl.Transformed; import ameba.db.ebean.EbeanUtils; import ameba.exception.UnprocessableEntityException; import ameba.i18n.Messages; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import io.ebean.*; import io.ebean.plugin.BeanType; import io.ebean.search.*; import io.ebeaninternal.api.SpiEbeanServer; import io.ebeaninternal.api.SpiExpressionFactory; import io.ebeaninternal.api.SpiExpressionValidation; import io.ebeaninternal.api.SpiQuery; import java.math.BigDecimal; import java.util.List; import java.util.Map; import java.util.Set; /** * <p>CommonExprTransformer class.</p> * * @author icode * */ public class CommonExprTransformer implements ExprTransformer<Expression, EbeanExprInvoker> { private static ExpressionFactory factory(EbeanExprInvoker invoker) { EbeanServer server = invoker.getServer(); return server.getExpressionFactory(); } /** * <p>fillArgs.</p> * * @param operator a {@link java.lang.String} object. * @param args an array of {@link ameba.db.dsl.QueryExprMeta.Val} objects. * @param et a {@link io.ebean.ExpressionList} object. */ public static void fillArgs(String operator, Val<Expression>[] args, ExpressionList<?> et) { for (Val<Expression> val : args) { if (val.object() instanceof Expression) { et.add(val.expr()); } else { throw new QuerySyntaxException(Messages.get("dsl.arguments.error3", operator)); } } } /** * <p>junction.</p> * * @param operator a {@link java.lang.String} object. * @param args an array of {@link ameba.db.dsl.QueryExprMeta.Val} objects. * @param type a {@link io.ebean.Junction.Type} object. * @param invoker a {@link ameba.db.ebean.filter.EbeanExprInvoker} object. * @param checkCount a int. * @return a {@link io.ebean.Junction} object. */ public static Junction<?> junction(String operator, Val<Expression>[] args, Junction.Type type, EbeanExprInvoker invoker, int checkCount) { if (args.length > checkCount) { Query query = invoker.getQuery(); Junction junction = factory(invoker).junction(type, query, query.where()); fillArgs(operator, args, junction); return junction; } throw new QuerySyntaxException(Messages.get("dsl.arguments.error2", operator, checkCount)); } /** * <p>filter.</p> * * @param field a {@link java.lang.String} object. * @param operator a {@link java.lang.String} object. * @param args an array of {@link ameba.db.dsl.QueryExprMeta.Val} objects. * @param invoker a {@link ameba.db.ebean.filter.EbeanExprInvoker} object. * @return a {@link io.ebean.Expression} object. */ public static Expression filter(String field, String operator, Val<Expression>[] args, EbeanExprInvoker invoker) { if (args.length > 0) { SpiExpressionFactory queryEf = (SpiExpressionFactory) invoker.getServer().getExpressionFactory(); ExpressionFactory filterEf = queryEf.createExpressionFactory(); SpiQuery<?> query = invoker.getQuery(); FilterExpression filter = new FilterExpression<>(field, filterEf, query); fillArgs(operator, args, filter); try { BeanType type = query.getBeanDescriptor().getBeanTypeAtPath(field); SpiExpressionValidation validation = new SpiExpressionValidation(type); filter.validate(validation); Set<String> invalid = validation.getUnknownProperties(); if (invalid != null && !invalid.isEmpty()) { UnprocessableEntityException.throwQuery(invalid); } return filter; } catch (Exception e) { UnprocessableEntityException.throwQuery("[" + field + "]"); } } throw new QuerySyntaxException(Messages.get("dsl.arguments.error0", operator)); } /** * <p>select.</p> * * @param field a {@link java.lang.String} object. * @param operator a {@link java.lang.String} object. * @param args an array of {@link ameba.db.dsl.QueryExprMeta.Val} objects. * @param invoker a {@link ameba.db.ebean.filter.EbeanExprInvoker} object. * @return a {@link io.ebean.Expression} object. */ public static Expression select(String field, String operator, Val<Expression>[] args, EbeanExprInvoker invoker) { if (args.length > 0) { EbeanServer server = invoker.getServer(); Class type = Filters.getBeanTypeByName(field, (SpiEbeanServer) server); if (type == null) { throw new QuerySyntaxException(Messages.get("dsl.bean.type.err", field)); } Query q = Filters.createQuery(type, server); ExpressionList<?> et = q.select(args[0].string()).where(); for (int i = 1; i < args.length; i++) { Object o = args[i].object(); if (o instanceof HavingExpression) { ExpressionList having = q.having(); ((HavingExpression) o).expressionList.forEach(having::add); } else if (o instanceof DistinctExpression) { et.setDistinct(((DistinctExpression) o).distinct); } else if (o instanceof Expression) { et.add(args[i].expr()); } else { throw new QuerySyntaxException(Messages.get("dsl.arguments.error3", operator)); } } EbeanUtils.checkQuery(q, invoker.getLocator()); return QueryExpression.of(q); } throw new QuerySyntaxException(Messages.get("dsl.arguments.error0", operator)); } /** * <p>having.</p> * * @param operator a {@link java.lang.String} object. * @param args an array of {@link ameba.db.dsl.QueryExprMeta.Val} objects. * @return a {@link io.ebean.Expression} object. */ public static Expression having(String operator, Val<Expression>[] args) { if (args.length > 0) { return HavingExpression.of(transformArgsToList(operator, args)); } throw new QuerySyntaxException(Messages.get("dsl.arguments.error0", operator)); } /** * <p>in.</p> * * @param field a {@link java.lang.String} object. * @param operator a {@link java.lang.String} object. * @param args an array of {@link ameba.db.dsl.QueryExprMeta.Val} objects. * @param invoker a {@link ameba.db.ebean.filter.EbeanExprInvoker} object. * @return a {@link io.ebean.Expression} object. */ public static Expression in(String field, String operator, Val<Expression>[] args, EbeanExprInvoker invoker) { if (args.length == 1) { if (args[0].object() instanceof QueryExpression) { return factory(invoker).in(field, ((QueryExpression<?>) args[0].object()).query); } throw new QuerySyntaxException(Messages.get("dsl.arguments.error3", operator)); } return factory(invoker).in(field, args); } /** * <p>notIn.</p> * * @param field a {@link java.lang.String} object. * @param operator a {@link java.lang.String} object. * @param args an array of {@link ameba.db.dsl.QueryExprMeta.Val} objects. * @param invoker a {@link ameba.db.ebean.filter.EbeanExprInvoker} object. * @return a {@link io.ebean.Expression} object. */ public static Expression notIn(String field, String operator, Val<Expression>[] args, EbeanExprInvoker invoker) { if (args.length == 1) { if (args[0].object() instanceof QueryExpression) { return factory(invoker).notIn(field, ((QueryExpression<?>) args[0].object()).query); } throw new QuerySyntaxException(Messages.get("dsl.arguments.error3", operator)); } return factory(invoker).notIn(field, args); } /** * <p>exists.</p> * * @param operator a {@link java.lang.String} object. * @param args an array of {@link ameba.db.dsl.QueryExprMeta.Val} objects. * @param invoker a {@link ameba.db.ebean.filter.EbeanExprInvoker} object. * @return a {@link io.ebean.Expression} object. */ public static Expression exists(String operator, Val<Expression>[] args, EbeanExprInvoker invoker) { if (args.length == 1) { if (args[0].object() instanceof QueryExpression) { return factory(invoker).exists(((QueryExpression<?>) args[0].object()).query); } throw new QuerySyntaxException(Messages.get("dsl.arguments.error3", operator)); } throw new QuerySyntaxException(Messages.get("dsl.arguments.error0", operator)); } /** * <p>notExists.</p> * * @param operator a {@link java.lang.String} object. * @param args an array of {@link ameba.db.dsl.QueryExprMeta.Val} objects. * @param invoker a {@link ameba.db.ebean.filter.EbeanExprInvoker} object. * @return a {@link io.ebean.Expression} object. */ public static Expression notExists(String operator, Val<Expression>[] args, EbeanExprInvoker invoker) { if (args.length == 1) { if (args[0].object() instanceof QueryExpression) { return factory(invoker).notExists(((QueryExpression<?>) args[0].object()).query); } throw new QuerySyntaxException(Messages.get("dsl.arguments.error3", operator)); } throw new QuerySyntaxException(Messages.get("dsl.arguments.error0", operator)); } /** * <p>match.</p> * * @param field a {@link java.lang.String} object. * @param operator a {@link java.lang.String} object. * @param args an array of {@link ameba.db.dsl.QueryExprMeta.Val} objects. * @param invoker a {@link ameba.db.ebean.filter.EbeanExprInvoker} object. * @return a {@link io.ebean.Expression} object. */ public static Expression match(String field, String operator, Val<Expression>[] args, EbeanExprInvoker invoker) { if (field != null) { //field.match(text) //field.match(text,option(opAnd, phrase)) if (args.length < 2) { throw new QuerySyntaxException(Messages.get("dsl.arguments.error2", operator, 1)); } Match match = null; if (args[1].object() instanceof TextOptionsExpression) { match = new Match(); Map<String, Val<Expression>> ops = ((TextOptionsExpression) args[1].object()).options; for (String o : ops.keySet()) { switch (o) { case "phrase": match.phrase(); break; case "phrasePre": match.phrasePrefix(); break; case "opAnd": match.opAnd(); break; case "opOr": match.opOr(); break; case "terms": match.zeroTerms(ops.get(o).string()); break; case "cutoff": match.cutoffFrequency(ops.get(o).doubleV()); break; case "maxExp": match.maxExpansions(ops.get(o).integer()); break; case "analyzer": match.analyzer(ops.get(o).string()); break; case "boost": match.boost(ops.get(o).doubleV()); break; case "minMatch": match.minShouldMatch(ops.get(o).string()); break; case "rewrite": match.rewrite(ops.get(o).string()); break; } } } return factory(invoker).textMatch(field, args[0].string(), match); } else if (args.length > 1) { MultiMatch match; if (args.length == 2) { //match(text, options(fields(f1,f2,f3),opOr)) if (args[1].object() instanceof TextOptionsExpression) { Map<String, Val<Expression>> ops = ((TextOptionsExpression) args[1].expr()).options; Val<Expression> val = ops.get("fields"); if (val == null) { throw new QuerySyntaxException( Messages.get("dsl.arguments.error4", "option", 1, "fields") ); } match = MultiMatch.fields(((TextFieldsExpression) val.expr()).fields); for (String o : ops.keySet()) { switch (o) { case "type": { Val v = ops.get(o); match.type((MultiMatch.Type) v.enumV(MultiMatch.Type.class)); break; } case "tie": match.tieBreaker(ops.get(o).doubleV()); break; case "opAnd": match.opAnd(); break; case "opOr": match.opOr(); break; case "terms": match.zeroTerms(ops.get(o).string()); break; case "cutoff": match.cutoffFrequency(ops.get(o).doubleV()); break; case "maxExp": match.maxExpansions(ops.get(o).integer()); break; case "analyzer": match.analyzer(ops.get(o).string()); break; case "boost": match.boost(ops.get(o).doubleV()); break; case "minMatch": match.minShouldMatch(ops.get(o).string()); break; case "rewrite": match.rewrite(ops.get(o).string()); break; } } } else { throw new QuerySyntaxException(Messages.get("dsl.arguments.error4", operator, 1, "option")); } } else { //match(text, field0, field1, field2) String[] fields = new String[args.length - 1]; for (int i = 1; i < args.length; i++) { fields[i - 1] = args[i].string(); } match = MultiMatch.fields(fields); } return factory(invoker).textMultiMatch(args[0].string(), match); } else { throw new QuerySyntaxException(Messages.get("dsl.arguments.error2", operator, 2)); } } /** * <p>textQueryString.</p> * * @param operator a {@link java.lang.String} object. * @param args an array of {@link ameba.db.dsl.QueryExprMeta.Val} objects. * @param invoker a {@link ameba.db.ebean.filter.EbeanExprInvoker} object. * @return a {@link io.ebean.Expression} object. */ public static Expression textQueryString(String operator, Val<Expression>[] args, EbeanExprInvoker invoker) { checkTextOptions(operator, args); Map<String, Val<Expression>> ops = ((TextOptionsExpression) args[1].expr()).options; Val<Expression> val = ops.get("fields"); if (val == null) { throw new QuerySyntaxException( Messages.get("dsl.arguments.error4", "option", 1, "fields") ); } TextQueryString queryString = TextQueryString.fields(((TextFieldsExpression) val.expr()).fields); for (String k : ops.keySet()) { switch (k) { case "opAnd": queryString.opAnd(); break; case "opOr": queryString.opOr(); break; case "locale": queryString.locale(ops.get(k).string()); break; case "lenient": queryString.lenient(ops.get(k).bool()); break; case "minMatch": queryString.minShouldMatch(ops.get(k).string()); break; case "analyzer": queryString.analyzer(ops.get(k).string()); break; case "disMax": queryString.useDisMax(ops.get(k).bool()); break; case "tie": queryString.tieBreaker(ops.get(k).doubleV()); break; case "defaultField": queryString.defaultField(ops.get(k).string()); break; case "leadingWildcard": queryString.allowLeadingWildcard(ops.get(k).bool()); break; case "lowerExp": queryString.lowercaseExpandedTerms(ops.get(k).bool()); break; case "fuzzyMaxExp": queryString.fuzzyMaxExpansions(ops.get(k).integer()); break; case "fuzziness": queryString.fuzziness(ops.get(k).string()); break; case "fuzzyPreLen": queryString.fuzzyPrefixLength(ops.get(k).integer()); break; case "phraseSlop": queryString.phraseSlop(ops.get(k).doubleV()); break; case "boost": queryString.boost(ops.get(k).doubleV()); break; case "analyzeWildcard": queryString.analyzeWildcard(ops.get(k).bool()); break; case "autoPhrase": queryString.autoGeneratePhraseQueries(ops.get(k).bool()); break; case "timeZone": queryString.timeZone(ops.get(k).string()); break; case "rewrite": queryString.rewrite(ops.get(k).string()); break; } } return factory(invoker).textQueryString(args[0].string(), queryString); } /** * <p>textCommonTerms.</p> * * @param operator a {@link java.lang.String} object. * @param args an array of {@link ameba.db.dsl.QueryExprMeta.Val} objects. * @param invoker a {@link ameba.db.ebean.filter.EbeanExprInvoker} object. * @return a {@link io.ebean.Expression} object. */ public static Expression textCommonTerms(String operator, Val<Expression>[] args, EbeanExprInvoker invoker) { checkTextOptions(operator, args); Map<String, Val<Expression>> ops = ((TextOptionsExpression) args[1].expr()).options; TextCommonTerms common = new TextCommonTerms(); for (String k : ops.keySet()) { switch (k) { case "cutoff": common.cutoffFrequency(ops.get(k).doubleV()); break; case "lowFreqAnd": common.lowFreqOperatorAnd(ops.get(k).bool()); break; case "highFreqAnd": common.highFreqOperatorAnd(ops.get(k).bool()); break; case "minMatch": common.minShouldMatch(ops.get(k).string()); break; case "minMatchLowFreq": common.minShouldMatchLowFreq(ops.get(k).string()); break; case "minMatchHighFreq": common.minShouldMatchHighFreq(ops.get(k).string()); break; } } return factory(invoker).textCommonTerms(args[0].string(), common); } private static void checkTextOptions(String operator, Val<Expression>[] args) { if (args.length == 2) { throw new QuerySyntaxException(Messages.get("dsl.arguments.error2", operator)); } if (!(args[1].object() instanceof TextOptionsExpression)) { throw new QuerySyntaxException(Messages.get("dsl.arguments.error4", operator, 1, "option")); } } private static void checkOneArgLength(String operator, Val<Expression>[] args) { if (args.length != 1) { throw new QuerySyntaxException(Messages.get("dsl.arguments.error0", operator)); } } /** * <p>textSimple.</p> * * @param operator a {@link java.lang.String} object. * @param args an array of {@link ameba.db.dsl.QueryExprMeta.Val} objects. * @param invoker a {@link ameba.db.ebean.filter.EbeanExprInvoker} object. * @return a {@link io.ebean.Expression} object. */ public static Expression textSimple(String operator, Val<Expression>[] args, EbeanExprInvoker invoker) { checkTextOptions(operator, args); Map<String, Val<Expression>> ops = ((TextOptionsExpression) args[1].expr()).options; TextSimple simple = new TextSimple(); for (String k : ops.keySet()) { switch (k) { case "fields": simple.fields(((TextFieldsExpression) ops.get(k).expr()).fields); break; case "opAnd": simple.opAnd(); break; case "opOr": simple.opOr(); break; case "analyzer": simple.analyzer(ops.get(k).string()); break; case "flags": simple.flags(ops.get(k).string()); break; case "lowerExp": simple.lowercaseExpandedTerms(ops.get(k).bool()); break; case "analyzeWildcard": simple.analyzeWildcard(ops.get(k).bool()); break; case "locale": simple.locale(ops.get(k).string()); break; case "lenient": simple.lenient(ops.get(k).bool()); break; case "minMatch": simple.minShouldMatch(ops.get(k).string()); break; } } return factory(invoker).textSimple(args[0].string(), simple); } /** * <p>text.</p> * * @param operator a {@link java.lang.String} object. * @param args an array of {@link ameba.db.dsl.QueryExprMeta.Val} objects. * @return a {@link io.ebean.Expression} object. */ public static Expression text(String operator, Val<Expression>[] args) { if (args.length < 1) { throw new QuerySyntaxException(Messages.get("dsl.arguments.error2", operator, 0)); } return TextExpression.of(transformArgsToList(operator, args)); } /** * <p>map.</p> * * @param operator a {@link java.lang.String} object. * @param arg a {@link ameba.db.dsl.QueryExprMeta.Val} object. * @param parent a {@link ameba.db.dsl.QueryExprMeta} object. * @return a {@link io.ebean.Expression} object. */ public static Expression map(String operator, Val<Expression> arg, QueryExprMeta parent) { if (parent == null) { throw new QuerySyntaxException(Messages.get("dsl.arguments.error5", operator)); } else if (!parent.operator().equals("option")) { throw new QuerySyntaxException(Messages.get("dsl.arguments.error6", operator, "option")); } return MapExpression.of(operator, arg); } /** * <p>fields.</p> * * @param operator a {@link java.lang.String} object. * @param args an array of {@link ameba.db.dsl.QueryExprMeta.Val} objects. * @param parent a {@link ameba.db.dsl.QueryExprMeta} object. * @return a {@link io.ebean.Expression} object. */ public static Expression fields(String operator, Val<Expression>[] args, QueryExprMeta parent) { if (args.length < 1) { throw new QuerySyntaxException(Messages.get("dsl.arguments.error2", operator, 0)); } if (parent == null) { throw new QuerySyntaxException(Messages.get("dsl.arguments.error5", operator)); } else if (!parent.operator().equals("option")) { throw new QuerySyntaxException(Messages.get("dsl.arguments.error6", operator, "option")); } return TextFieldsExpression.of(args); } /** * <p>options.</p> * * @param operator a {@link java.lang.String} object. * @param args an array of {@link ameba.db.dsl.QueryExprMeta.Val} objects. * @param parent a {@link ameba.db.dsl.QueryExprMeta} object. * @return a {@link io.ebean.Expression} object. */ public static Expression options(String operator, Val<Expression>[] args, QueryExprMeta parent) { if (args.length < 1) { throw new QuerySyntaxException(Messages.get("dsl.arguments.error2", operator, 0)); } if (parent == null) { throw new QuerySyntaxException(Messages.get("dsl.arguments.error5", operator)); } TextOptionsExpression op = new TextOptionsExpression(); for (Val v : args) { if (v.object() instanceof MapExpression) { MapExpression map = ((MapExpression) v.expr()); op.options.put(map.key, map.value); } else { op.options.put(v.string(), null); } } return op; } /** * <p>distinct.</p> * * @param args an array of {@link ameba.db.dsl.QueryExprMeta.Val} objects. * @return a {@link io.ebean.Expression} object. */ public static Expression distinct(Val<Expression>[] args) { boolean dis = true; if (args.length > 1) { Object o = args[0].object(); if (o instanceof Boolean || o instanceof BigDecimal) { dis = args[0].bool(); } } return new DistinctExpression(dis); } /** * <p>transformArgs.</p> * * @param args an array of {@link ameba.db.dsl.QueryExprMeta.Val} objects. * @return array {@link java.lang.Object} object. */ public static Object[] transformArgs(Val<Expression>[] args) { Object[] objects = new Object[args.length]; for (int i = 0; i < args.length; i++) { objects[i] = args[i].object(); } return objects; } /** * <p>transformArgsToList.</p> * * @param operator a {@link java.lang.String} object. * @param args an array of {@link ameba.db.dsl.QueryExprMeta.Val} objects. * @return a {@link java.util.List} object. */ public static List<Expression> transformArgsToList(String operator, Val<Expression>[] args) { List<Expression> et = Lists.newArrayListWithCapacity(args.length); for (Val<Expression> e : args) { if (e.object() instanceof Expression) { et.add(e.expr()); } else { throw new QuerySyntaxException(Messages.get("dsl.arguments.error3", operator)); } } return et; } /** {@inheritDoc} */ @Override public Transformed<Val<Expression>> transform(String field, String operator, Val<Expression>[] args, EbeanExprInvoker invoker, QueryExprMeta parent) { EbeanServer server = invoker.getServer(); ExpressionFactory factory = server.getExpressionFactory(); Object expr = null; switch (operator) { case "eq": checkOneArgLength(operator, args); expr = factory.eq(field, args[0].object()); break; case "ne": checkOneArgLength(operator, args); expr = factory.ne(field, args[0].object()); break; case "ieq": checkOneArgLength(operator, args); expr = factory.ieq(field, args[0].string()); break; case "between": if (args.length != 2) { throw new QuerySyntaxException(Messages.get("dsl.arguments.error1", operator)); } expr = factory.between(field, args[0].object(), args[1].object()); break; case "gt": checkOneArgLength(operator, args); expr = factory.gt(field, args[0].object()); break; case "ge": checkOneArgLength(operator, args); expr = factory.ge(field, args[0].object()); break; case "lt": checkOneArgLength(operator, args); expr = factory.lt(field, args[0].object()); break; case "le": checkOneArgLength(operator, args); expr = factory.le(field, args[0].object()); break; case "isNull": expr = factory.isNull(field); break; case "notNull": expr = factory.isNotNull(field); break; case "startsWith": checkOneArgLength(operator, args); expr = factory.startsWith(field, args[0].string()); break; case "istartsWith": checkOneArgLength(operator, args); expr = factory.istartsWith(field, args[0].string()); break; case "endsWith": checkOneArgLength(operator, args); expr = factory.endsWith(field, args[0].string()); break; case "iendsWith": checkOneArgLength(operator, args); expr = factory.iendsWith(field, args[0].string()); break; case "contains": checkOneArgLength(operator, args); expr = factory.contains(field, args[0].string()); break; case "icontains": checkOneArgLength(operator, args); expr = factory.icontains(field, args[0].string()); break; case "empty": expr = factory.isEmpty(field); break; case "notEmpty": expr = factory.isNotEmpty(field); break; case "id": checkOneArgLength(operator, args); expr = factory.idEq(args[0].object()); break; case "idIn": expr = factory.idIn(transformArgs(args)); break; case "date": checkOneArgLength(operator, args); if (parent == null) { throw new QuerySyntaxException(Messages.get("dsl.arguments.error5", operator)); } expr = ParamConverters.parseDate(args[0].object().toString()); break; case "having": expr = having(operator, args); break; case "in": expr = in(field, operator, args, invoker); break; case "notIn": expr = notIn(field, operator, args, invoker); break; case "exists": expr = exists(operator, args, invoker); break; case "notExists": expr = notExists(operator, args, invoker); break; case "not": expr = junction(operator, args, Junction.Type.NOT, invoker, 0); break; case "and": expr = junction(operator, args, Junction.Type.AND, invoker, 1); break; case "or": expr = junction(operator, args, Junction.Type.OR, invoker, 1); break; case "must": expr = junction(operator, args, Junction.Type.MUST, invoker, 0); break; case "should": expr = junction(operator, args, Junction.Type.SHOULD, invoker, 0); break; case "mustNot": expr = junction(operator, args, Junction.Type.MUST_NOT, invoker, 0); break; case "filter": expr = filter(field, operator, args, invoker); break; case "select": expr = select(field, operator, args, invoker); break; case "distinct": expr = distinct(args); break; case "text": expr = text(operator, args); break; case "match": expr = match(field, operator, args, invoker); break; case "simple": expr = textSimple(operator, args, invoker); break; case "query": expr = textQueryString(operator, args, invoker); break; case "common": expr = textCommonTerms(operator, args, invoker); break; case "option": expr = options(operator, args, parent); break; case "fields": expr = fields(operator, args, parent); break; case "phrase": case "phrasePre": case "opAnd": case "opOr": expr = map(operator, null, parent); break; case "type": case "tie": case "terms": case "cutoff": case "maxExp": case "analyzer": case "boost": case "minMatch": case "rewrite": case "disMax": case "defaultField": case "leadingWildcard": case "lowerExp": case "fuzzyMaxExp": case "fuzziness": case "fuzzyPreLen": case "phraseSlop": case "analyzeWildcard": case "autoPhrase": case "timeZone": case "lowFreqAnd": case "highFreqAnd": case "minMatchLowFreq": case "minMatchHighFreq": checkOneArgLength(operator, args); expr = map(operator, args[0], parent); break; } if (expr != null) return Transformed.succ(this, Val.<Expression>ofObject(expr)); else return Transformed.fail(this); } private static class TextOptionsExpression implements Expression { private Map<String, Val<Expression>> options = Maps.newLinkedHashMap(); } static class MapExpression implements Expression { private String key; private Val<Expression> value; MapExpression(String key, Val<Expression> value) { this.key = key; this.value = value; } public static MapExpression of(String key, Val<Expression> v) { return new MapExpression(key, v); } } private static class TextFieldsExpression implements Expression { private String[] fields; public static TextFieldsExpression of(Val<Expression>[] args) { TextFieldsExpression e = new TextFieldsExpression(); e.fields = new String[args.length]; for (int i = 0; i < args.length; i++) { e.fields[i] = args[i].string(); } return e; } } private static class QueryExpression<T> implements Expression { private Query<T> query; QueryExpression(Query<T> query) { this.query = query; } public static <T> QueryExpression<T> of(Query<T> query) { return new QueryExpression<>(query); } } static class TextExpression implements Expression { private List<Expression> expressionList; TextExpression(List<Expression> expressionList) { this.expressionList = expressionList; } public static TextExpression of(List<Expression> expressionList) { return new TextExpression(expressionList); } public List<Expression> getExpressionList() { return expressionList; } } static class DistinctExpression implements Expression { boolean distinct; public DistinctExpression(boolean distinct) { this.distinct = distinct; } } static class HavingExpression implements Expression { private List<Expression> expressionList; HavingExpression(List<Expression> expressionList) { this.expressionList = expressionList; } public static HavingExpression of(List<Expression> expressionList) { return new HavingExpression(expressionList); } public List<Expression> getExpressionList() { return expressionList; } } }