Java tutorial
/* * Licensed to CRATE Technology GmbH ("Crate") under one or more contributor * license agreements. See the NOTICE file distributed with this work for * additional information regarding copyright ownership. Crate 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. * * However, if you have executed another commercial license agreement * with Crate these terms will supersede the license and you may use the * software solely pursuant to the terms of the relevant commercial agreement. */ package io.crate.executor.transport.task.elasticsearch; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; import io.crate.PartitionName; import io.crate.analyze.WhereClause; import io.crate.metadata.*; import io.crate.operation.operator.*; import io.crate.operation.operator.any.AnyLikeOperator; import io.crate.operation.operator.any.AnyNotLikeOperator; import io.crate.operation.predicate.IsNullPredicate; import io.crate.operation.predicate.NotPredicate; import io.crate.operation.predicate.PredicateModule; import io.crate.operation.predicate.MatchPredicate; import io.crate.operation.scalar.ScalarFunctionModule; import io.crate.operation.scalar.arithmetic.LogFunction; import io.crate.operation.scalar.arithmetic.RoundFunction; import io.crate.operation.scalar.geo.DistanceFunction; import io.crate.operation.scalar.geo.WithinFunction; import io.crate.planner.RowGranularity; import io.crate.planner.node.dml.ESDeleteByQueryNode; import io.crate.planner.node.dql.ESSearchNode; import io.crate.planner.symbol.Function; import io.crate.planner.symbol.Literal; import io.crate.planner.symbol.Reference; import io.crate.planner.symbol.Symbol; import io.crate.types.ArrayType; import io.crate.types.DataType; import io.crate.types.DataTypes; import io.crate.types.SetType; import org.apache.lucene.util.BytesRef; import org.elasticsearch.common.bytes.BytesReference; import org.elasticsearch.common.collect.MapBuilder; import org.elasticsearch.common.collect.Tuple; import org.elasticsearch.common.inject.ModulesBuilder; import org.elasticsearch.common.xcontent.XContentHelper; import org.elasticsearch.common.xcontent.XContentType; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; import java.io.IOException; import java.util.*; import static io.crate.testing.TestingHelpers.createFunction; import static io.crate.testing.TestingHelpers.createReference; import static org.hamcrest.core.Is.is; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertThat; public class ESQueryBuilderTest { Functions functions; static final TableIdent characters = new TableIdent(null, "characters"); static final Reference name_ref = new Reference( new ReferenceInfo(new ReferenceIdent(characters, "name"), RowGranularity.DOC, DataTypes.STRING)); static final Reference age_ref = new Reference( new ReferenceInfo(new ReferenceIdent(characters, "age"), RowGranularity.DOC, DataTypes.INTEGER)); static final Reference weight_ref = new Reference( new ReferenceInfo(new ReferenceIdent(characters, "weight"), RowGranularity.DOC, DataTypes.DOUBLE)); static final Reference float_ref = new Reference( new ReferenceInfo(new ReferenceIdent(characters, "float_ref"), RowGranularity.DOC, DataTypes.FLOAT)); static final Reference long_ref = new Reference( new ReferenceInfo(new ReferenceIdent(characters, "long_ref"), RowGranularity.DOC, DataTypes.LONG)); static final Reference short_ref = new Reference( new ReferenceInfo(new ReferenceIdent(characters, "short_ref"), RowGranularity.DOC, DataTypes.SHORT)); static final Reference isParanoid = new Reference( new ReferenceInfo(new ReferenceIdent(characters, "isParanoid"), RowGranularity.DOC, DataTypes.BOOLEAN)); static final Reference extrafield = new Reference( new ReferenceInfo(new ReferenceIdent(characters, "extrafield"), RowGranularity.DOC, DataTypes.STRING)); static final Reference tagsField = new Reference(new ReferenceInfo(new ReferenceIdent(characters, "tags"), RowGranularity.DOC, new ArrayType(DataTypes.STRING))); static final Reference objectField = new Reference(new ReferenceInfo( new ReferenceIdent(characters, "object_field"), RowGranularity.DOC, DataTypes.OBJECT)); static final Reference nestedField = new Reference( new ReferenceInfo(new ReferenceIdent(characters, "object_field", Arrays.asList("nested")), RowGranularity.DOC, DataTypes.STRING)); private ESQueryBuilder generator; private List<DataType> typeX2(DataType type) { return Arrays.asList(type, type); } @Rule public ExpectedException expectedException = ExpectedException.none(); @Before public void setUp() throws Exception { functions = new ModulesBuilder().add(new OperatorModule()).add(new PredicateModule()) .add(new ScalarFunctionModule()).createInjector().getInstance(Functions.class); generator = new ESQueryBuilder(); } private void xcontentAssert(Function whereClause, String expected) throws IOException { BytesReference reference = generator.convert(new WhereClause(whereClause)); String actual = reference.toUtf8(); assertThat(actual, is(expected)); } @Test public void testConvertNestedAnd() throws Exception { FunctionImplementation eqStringImpl = functions .get(new FunctionIdent(EqOperator.NAME, typeX2(DataTypes.STRING))); FunctionImplementation eqAgeImpl = functions .get(new FunctionIdent(EqOperator.NAME, typeX2(DataTypes.INTEGER))); FunctionImplementation eqLongImpl = functions .get(new FunctionIdent(EqOperator.NAME, typeX2(DataTypes.LONG))); FunctionImplementation andImpl = functions .get(new FunctionIdent(AndOperator.NAME, typeX2(DataTypes.BOOLEAN))); Function eqName = new Function(eqStringImpl.info(), Arrays.<Symbol>asList(name_ref, Literal.newLiteral("Marvin"))); Function eqAge = new Function(eqAgeImpl.info(), Arrays.<Symbol>asList(age_ref, Literal.newLiteral(84))); Function eqLong = new Function(eqLongImpl.info(), Arrays.<Symbol>asList(long_ref, Literal.newLiteral(8L))); Function rightAnd = new Function(andImpl.info(), Arrays.<Symbol>asList(eqAge, eqLong)); Function leftAnd = new Function(andImpl.info(), Arrays.<Symbol>asList(eqName, rightAnd)); xcontentAssert(leftAnd, "{\"query\":{\"bool\":{\"must\":[{\"term\":{\"name\":\"Marvin\"}},{\"bool\":{\"must\":[{\"term\":{\"age\":84}},{\"term\":{\"long_ref\":8}}]}}]}}}"); } @Test public void testWhereWithOr() throws Exception { // where name = marvin and age = 84 and longField = 8 FunctionImplementation eqStringImpl = functions .get(new FunctionIdent(EqOperator.NAME, typeX2(DataTypes.STRING))); FunctionImplementation orImpl = functions .get(new FunctionIdent(OrOperator.NAME, typeX2(DataTypes.BOOLEAN))); Function eqMarvin = new Function(eqStringImpl.info(), Arrays.<Symbol>asList(name_ref, Literal.newLiteral("Marvin"))); Function eqTrillian = new Function(eqStringImpl.info(), Arrays.<Symbol>asList(name_ref, Literal.newLiteral("Trillian"))); Function whereClause = new Function(orImpl.info(), Arrays.<Symbol>asList(eqMarvin, eqTrillian)); xcontentAssert(whereClause, "{\"query\":{\"bool\":{\"minimum_should_match\":1,\"should\":[{\"term\":{\"name\":\"Marvin\"}},{\"term\":{\"name\":\"Trillian\"}}]}}}"); } @Test public void testWhereReferenceEqStringLiteral() throws Exception { FunctionImplementation eqImpl = functions.get(new FunctionIdent(EqOperator.NAME, typeX2(DataTypes.STRING))); Function whereClause = new Function(eqImpl.info(), Arrays.<Symbol>asList(name_ref, Literal.newLiteral("Marvin"))); xcontentAssert(whereClause, "{\"query\":{\"term\":{\"name\":\"Marvin\"}}}"); } @Test public void testWhereReferenceEqIntegerLiteral() throws Exception { FunctionImplementation eqImpl = functions .get(new FunctionIdent(EqOperator.NAME, typeX2(DataTypes.INTEGER))); Function whereClause = new Function(eqImpl.info(), Arrays.<Symbol>asList(age_ref, Literal.newLiteral(40))); xcontentAssert(whereClause, "{\"query\":{\"term\":{\"age\":40}}}"); } @Test public void testWhereReferenceLtDoubleLiteral() throws Exception { FunctionImplementation ltImpl = functions.get(new FunctionIdent(LtOperator.NAME, typeX2(DataTypes.DOUBLE))); Function whereClause = new Function(ltImpl.info(), Arrays.<Symbol>asList(weight_ref, Literal.newLiteral(54.3))); xcontentAssert(whereClause, "{\"query\":{\"range\":{\"weight\":{\"lt\":54.3}}}}"); } @Test public void testWhereReferenceLteFloatLiteral() throws Exception { FunctionImplementation impl = functions.get(new FunctionIdent(LteOperator.NAME, typeX2(DataTypes.FLOAT))); Function whereClause = new Function(impl.info(), Arrays.<Symbol>asList(float_ref, Literal.newLiteral(42.1))); xcontentAssert(whereClause, "{\"query\":{\"range\":{\"float_ref\":{\"lte\":42.1}}}}"); } @Test public void testWhereReferenceGtLong() throws Exception { FunctionImplementation impl = functions.get(new FunctionIdent(GtOperator.NAME, typeX2(DataTypes.LONG))); Function whereClause = new Function(impl.info(), Arrays.<Symbol>asList(long_ref, Literal.newLiteral(8L))); xcontentAssert(whereClause, "{\"query\":{\"range\":{\"long_ref\":{\"gt\":8}}}}"); } @Test public void testWhereReferenceEqShort() throws Exception { FunctionImplementation impl = functions.get(new FunctionIdent(EqOperator.NAME, typeX2(DataTypes.SHORT))); Function whereClause = new Function(impl.info(), Arrays.<Symbol>asList(short_ref, Literal.newLiteral(DataTypes.SHORT, (short) 2))); xcontentAssert(whereClause, "{\"query\":{\"term\":{\"short_ref\":2}}}"); } @Test public void testWhereReferenceEqBoolean() throws Exception { FunctionImplementation impl = functions .get(new FunctionIdent(EqOperator.NAME, typeX2(isParanoid.valueType()))); Function whereClause = new Function(impl.info(), Arrays.<Symbol>asList(isParanoid, Literal.newLiteral(isParanoid.valueType(), true))); xcontentAssert(whereClause, "{\"query\":{\"term\":{\"isParanoid\":true}}}"); } @Test public void testWhereReferenceLikeString() throws Exception { FunctionImplementation impl = functions .get(new FunctionIdent(LikeOperator.NAME, typeX2(name_ref.valueType()))); Function whereClause = new Function(impl.info(), Arrays.<Symbol>asList(name_ref, Literal.newLiteral("%thu%"))); xcontentAssert(whereClause, "{\"query\":{\"wildcard\":{\"name\":\"*thu*\"}}}"); } @Test public void testWhereNotReferenceLikeString() throws Exception { FunctionImplementation notOp = functions .get(new FunctionIdent(NotPredicate.NAME, Arrays.<DataType>asList(DataTypes.BOOLEAN))); FunctionImplementation likeOp = functions .get(new FunctionIdent(LikeOperator.NAME, typeX2(name_ref.valueType()))); Function likeClause = new Function(likeOp.info(), Arrays.<Symbol>asList(name_ref, Literal.newLiteral("%thu%"))); Function whereClause = new Function(notOp.info(), Arrays.<Symbol>asList(likeClause)); xcontentAssert(whereClause, "{\"query\":{\"bool\":{\"must_not\":{\"wildcard\":{\"name\":\"*thu*\"}}}}}"); } @Test public void testWhereReferenceIsNull() throws Exception { FunctionImplementation isNullImpl = functions .get(new FunctionIdent(IsNullPredicate.NAME, Arrays.asList(extrafield.valueType()))); Function isNull = new Function(isNullImpl.info(), Arrays.<Symbol>asList(extrafield)); xcontentAssert(isNull, "{\"query\":{\"filtered\":{\"filter\":{\"missing\":{\"field\":\"extrafield\",\"existence\":true,\"null_value\":true}}}}}"); } @Test public void testWhereReferenceInStringList() throws Exception { // where name in ("alpha", "bravo", "charlie") Reference ref = name_ref; FunctionImplementation inListImpl = functions.get(new FunctionIdent(InOperator.NAME, Arrays.<DataType>asList(DataTypes.STRING, new SetType(DataTypes.STRING)))); ImmutableSet<BytesRef> list = ImmutableSet.of(new BytesRef("alpha"), new BytesRef("bravo"), new BytesRef("charlie")); Literal set = Literal.newLiteral(new SetType(DataTypes.STRING), list); Function inList = new Function(inListImpl.info(), Arrays.<Symbol>asList(ref, set)); BytesReference reference = generator.convert(new WhereClause(inList)); Tuple<XContentType, Map<String, Object>> actualMap = XContentHelper.convertToMap(reference, true); ArrayList<String> actualList = ((ArrayList) ((Map) ((Map) actualMap.v2().get("query")).get("terms")) .get("name")); assertEquals(ImmutableSet.of("alpha", "bravo", "charlie"), new HashSet<>(actualList)); } @Test public void testWhereReferenceMatchString() throws Exception { FunctionIdent functionIdent = new FunctionIdent(MatchPredicate.NAME, ImmutableList.<DataType>of(DataTypes.OBJECT, DataTypes.STRING, DataTypes.STRING, DataTypes.OBJECT)); MatchPredicate matchImpl = (MatchPredicate) functions.get(functionIdent); Function match = new Function(matchImpl.info(), Arrays.<Symbol>asList( Literal.newLiteral(new MapBuilder<String, Object>() .put(name_ref.info().ident().columnIdent().fqn(), null).map()), Literal.newLiteral("arthur"), Literal.newLiteral(MatchPredicate.DEFAULT_MATCH_TYPE), Literal.newLiteral(DataTypes.OBJECT, null))); xcontentAssert(match, "{\"query\":{\"match\":{\"name\":\"arthur\"}}}"); } @Test public void testWhereMultiMatchString() throws Exception { FunctionIdent ident = new FunctionIdent(MatchPredicate.NAME, ImmutableList.<DataType>of(DataTypes.OBJECT, DataTypes.STRING, DataTypes.STRING, DataTypes.OBJECT)); MatchPredicate matchImpl = (MatchPredicate) functions.get(ident); Function match = new Function(matchImpl.info(), Arrays.<Symbol>asList( Literal.newLiteral( new MapBuilder<String, Object>().put(name_ref.info().ident().columnIdent().fqn(), null) .put(extrafield.info().ident().columnIdent().fqn(), 4.5d).map()), Literal.newLiteral("arthur"), Literal.newLiteral(MatchPredicate.DEFAULT_MATCH_TYPE), Literal.newLiteral(new MapBuilder<String, Object>().put("tie_breaker", 0.5) .put("analyzer", "english").map()))); xcontentAssert(match, "{\"query\":{\"multi_match\":{\"type\":\"best_fields\"," + "\"fields\":[\"extrafield^4.5\",\"name\"]," + "\"query\":\"arthur\",\"tie_breaker\":0.5,\"analyzer\":\"english\"}}}"); } @Test public void testMultiMatchType() throws Exception { FunctionIdent ident = new FunctionIdent(MatchPredicate.NAME, ImmutableList.<DataType>of(DataTypes.OBJECT, DataTypes.STRING, DataTypes.STRING, DataTypes.OBJECT)); MatchPredicate matchImpl = (MatchPredicate) functions.get(ident); Function match = new Function(matchImpl.info(), Arrays.<Symbol>asList( Literal.newLiteral( new MapBuilder<String, Object>().put(name_ref.info().ident().columnIdent().fqn(), 0.003d) .put(nestedField.info().ident().columnIdent().fqn(), null).map()), Literal.newLiteral("arthur"), Literal.newLiteral("phrase"), Literal.newLiteral( new MapBuilder<String, Object>().put("fuzziness", 3).put("max_expansions", 6).map()))); xcontentAssert(match, "{\"query\":{\"multi_match\":{\"type\":\"phrase\"," + "\"fields\":[\"name^0.003\",\"object_field.nested\"]," + "\"query\":\"arthur\",\"max_expansions\":6,\"fuzziness\":3}}}"); } @Test public void testMatchNull() throws Exception { expectedException.expect(IllegalArgumentException.class); expectedException.expectMessage("cannot use NULL as query term in match predicate"); FunctionIdent ident = new FunctionIdent(MatchPredicate.NAME, ImmutableList.<DataType>of(DataTypes.OBJECT, DataTypes.STRING, DataTypes.STRING, DataTypes.OBJECT)); MatchPredicate matchImpl = (MatchPredicate) functions.get(ident); Function match = new Function(matchImpl.info(), Arrays.<Symbol>asList( Literal.newLiteral( new MapBuilder<String, Object>().put(name_ref.info().ident().columnIdent().fqn(), 0.003d) .put(nestedField.info().ident().columnIdent().fqn(), null).map()), Literal.newLiteral(DataTypes.STRING, null), Literal.newLiteral(MatchPredicate.DEFAULT_MATCH_TYPE), Literal.newLiteral( new MapBuilder<String, Object>().put("fuzziness", 3).put("max_expansions", 6).map()))); generator.convert(new WhereClause(match)); } @Test public void testWhereReferenceAnyLike() throws Exception { FunctionIdent functionIdent = new FunctionIdent(AnyLikeOperator.NAME, Arrays.<DataType>asList(new ArrayType(DataTypes.STRING), DataTypes.STRING)); FunctionImplementation anyLikeImpl = functions.get(functionIdent); Function anyLike = new Function(anyLikeImpl.info(), Arrays.<Symbol>asList(tagsField, Literal.newLiteral("foo%"))); xcontentAssert(anyLike, "{\"query\":{\"wildcard\":{\"tags\":\"foo*\"}}}"); } @Test public void testWhereReferenceAnyNotLike() throws Exception { FunctionIdent functionIdent = new FunctionIdent(AnyNotLikeOperator.NAME, Arrays.<DataType>asList(new ArrayType(DataTypes.STRING), DataTypes.STRING)); FunctionImplementation anyNotLikeImpl = functions.get(functionIdent); Function anyNotLike = new Function(anyNotLikeImpl.info(), Arrays.<Symbol>asList(tagsField, Literal.newLiteral("foo%"))); xcontentAssert(anyNotLike, "{\"query\":{\"regexp\":{\"tags\":{\"value\":\"~(foo.*)\",\"flags\":\"COMPLEMENT\"}}}}"); } @Test public void testMinScoreIsSet() throws Exception { Reference minScore_ref = new Reference( new ReferenceInfo(new ReferenceIdent(null, "_score"), RowGranularity.DOC, DataTypes.DOUBLE)); Function whereClause = new Function( new FunctionInfo(new FunctionIdent(EqOperator.NAME, Arrays.<DataType>asList(DataTypes.DOUBLE, DataTypes.DOUBLE)), DataTypes.BOOLEAN), Arrays.<Symbol>asList(minScore_ref, Literal.newLiteral(0.4))); ESSearchNode node = new ESSearchNode(new String[] { "something" }, ImmutableList.<Symbol>of(), null, null, null, null, null, new WhereClause(whereClause), null); BytesReference bytesReference = generator.convert(node); assertThat(bytesReference.toUtf8(), is("{\"_source\":false,\"query\":{\"match_all\":{}},\"min_score\":0.4,\"from\":0,\"size\":10000}")); } @Test public void testConvertESSearchNode() throws Exception { FunctionImplementation eqImpl = functions.get(new FunctionIdent(EqOperator.NAME, typeX2(DataTypes.STRING))); Function whereClause = new Function(eqImpl.info(), Arrays.<Symbol>asList(name_ref, Literal.newLiteral("Marvin"))); ESSearchNode searchNode = new ESSearchNode(new String[] { characters.name() }, ImmutableList.<Symbol>of(name_ref), ImmutableList.<Symbol>of(), new boolean[0], new Boolean[0], null, null, new WhereClause(whereClause), null); BytesReference reference = generator.convert(searchNode); String actual = reference.toUtf8(); assertThat(actual, is( "{\"_source\":{\"include\":[\"name\"]},\"query\":{\"term\":{\"name\":\"Marvin\"}},\"from\":0,\"size\":10000}")); } @Test(expected = UnsupportedOperationException.class) public void testQueryWith_Version() throws Exception { FunctionImplementation eqImpl = functions.get(new FunctionIdent(EqOperator.NAME, typeX2(DataTypes.STRING))); Function whereClause = new Function(eqImpl.info(), Arrays.<Symbol>asList(createReference("_version", DataTypes.INTEGER), Literal.newLiteral(4))); generator.convert(new WhereClause(whereClause)); } @Test public void testConvertESDeleteByQueryNode() throws Exception { FunctionImplementation eqImpl = functions.get(new FunctionIdent(EqOperator.NAME, typeX2(DataTypes.STRING))); Function whereClause = new Function(eqImpl.info(), Arrays.<Symbol>asList(name_ref, Literal.newLiteral("Marvin"))); ESDeleteByQueryNode deleteByQueryNode = new ESDeleteByQueryNode(new String[] { characters.name() }, new WhereClause(whereClause)); BytesReference reference = generator.convert(deleteByQueryNode); String actual = reference.toUtf8(); assertThat(actual, is("{\"query\":{\"term\":{\"name\":\"Marvin\"}}}")); } @Test public void testSelect_OnlyVersion() throws Exception { Reference version_ref = createReference("_version", DataTypes.INTEGER); ESSearchNode searchNode = new ESSearchNode(new String[] { characters.name() }, ImmutableList.<Symbol>of(version_ref), null, null, null, null, null, WhereClause.MATCH_ALL, null); BytesReference reference = generator.convert(searchNode); String actual = reference.toUtf8(); assertThat(actual, is("{\"version\":true,\"_source\":false,\"query\":{\"match_all\":{}},\"from\":0,\"size\":10000}")); } @Test public void testCommonAncestors() throws Exception { assertEquals(ImmutableSet.of("a"), ESQueryBuilder.commonAncestors(Arrays.asList("a", "a.b"))); assertEquals(ImmutableSet.of("d", "a", "b"), ESQueryBuilder.commonAncestors(Arrays.asList("a.c", "b", "b.c.d", "a", "a.b", "d"))); assertEquals(ImmutableSet.of("d", "a", "b.c"), ESQueryBuilder.commonAncestors(Arrays.asList("a.c", "b.c", "b.c.d", "a", "a.b", "d"))); } @Test public void testSelect_WholeObjectAndPartial() throws Exception { Reference author = createReference("author", DataTypes.OBJECT); Reference age = createReference(ColumnIdent.getChild(author.info().ident().columnIdent(), "age"), DataTypes.INTEGER); ESSearchNode searchNode = new ESSearchNode(new String[] { characters.name() }, ImmutableList.<Symbol>of(author, age), null, null, null, null, null, WhereClause.MATCH_ALL, null); BytesReference reference = generator.convert(searchNode); String actual = reference.toUtf8(); assertThat(actual, is( "{\"_source\":{\"include\":[\"author\"]},\"query\":{\"match_all\":{}},\"from\":0,\"size\":10000}")); } @Test public void testSelect_excludePartitionedColumns() throws Exception { PartitionName partitionName = new PartitionName(characters.name(), Arrays.asList(new BytesRef("0.5"))); ESSearchNode searchNode = new ESSearchNode(new String[] { partitionName.stringValue() }, ImmutableList.<Symbol>of(name_ref, weight_ref), null, null, null, null, null, WhereClause.MATCH_ALL, Arrays.asList(weight_ref.info())); BytesReference reference = generator.convert(searchNode); String actual = reference.toUtf8(); assertThat(actual, is( "{\"_source\":{\"include\":[\"name\"]},\"query\":{\"match_all\":{}},\"from\":0,\"size\":10000}")); } @Test public void testAnyGreater() throws Exception { // 0.0 < ANY_OF (d_array) DataType doubleArrayType = new ArrayType(DataTypes.DOUBLE); Reference doubleArrayRef = createReference("d_array", doubleArrayType); FunctionImplementation anyGreaterImpl = functions .get(new FunctionIdent("any_>", Arrays.<DataType>asList(doubleArrayType, DataTypes.DOUBLE))); Function whereClause = new Function(anyGreaterImpl.info(), Arrays.<Symbol>asList(doubleArrayRef, Literal.newLiteral(0.0))); xcontentAssert(whereClause, "{\"query\":{\"range\":{\"d_array\":{\"gt\":0.0}}}}"); } @Test public void testAnyGreaterEquals() throws Exception { // 0.0 <= ANY_OF (d_array) DataType doubleArrayType = new ArrayType(DataTypes.DOUBLE); Reference doubleArrayRef = createReference("d_array", doubleArrayType); FunctionImplementation anyGreaterImpl = functions .get(new FunctionIdent("any_>=", Arrays.<DataType>asList(doubleArrayType, DataTypes.DOUBLE))); Function whereClause = new Function(anyGreaterImpl.info(), Arrays.<Symbol>asList(doubleArrayRef, Literal.newLiteral(0.0))); xcontentAssert(whereClause, "{\"query\":{\"range\":{\"d_array\":{\"gte\":0.0}}}}"); } @Test public void testDistanceGteQuery() throws Exception { Function distanceFunction = new Function( new FunctionInfo( new FunctionIdent(DistanceFunction.NAME, Arrays.<DataType>asList(DataTypes.GEO_POINT, DataTypes.GEO_POINT)), DataTypes.DOUBLE), Arrays.<Symbol>asList(createReference("location", DataTypes.GEO_POINT), Literal.newLiteral(DataTypes.GEO_POINT, DataTypes.GEO_POINT.value("POINT (10 20)")))); Function whereClause = new Function( new FunctionInfo(new FunctionIdent(GteOperator.NAME, Arrays.<DataType>asList(DataTypes.DOUBLE, DataTypes.DOUBLE)), DataTypes.BOOLEAN), Arrays.<Symbol>asList(distanceFunction, Literal.newLiteral(20.0d))); xcontentAssert(whereClause, "{\"query\":{\"filtered\":{\"query\":{\"match_all\":{}},\"filter\":{\"geo_distance_range\":{\"location\":[10.0,20.0],\"gte\":20.0}}}}}"); } @Test public void testDistanceGteQuerySwappedArgs() throws Exception { Function distanceFunction = new Function( new FunctionInfo( new FunctionIdent(DistanceFunction.NAME, Arrays.<DataType>asList(DataTypes.GEO_POINT, DataTypes.GEO_POINT)), DataTypes.DOUBLE), Arrays.<Symbol>asList( Literal.newLiteral(DataTypes.GEO_POINT, DataTypes.GEO_POINT.value("POINT (10 20)")), createReference("location", DataTypes.GEO_POINT))); Function whereClause = new Function( new FunctionInfo(new FunctionIdent(GteOperator.NAME, Arrays.<DataType>asList(DataTypes.DOUBLE, DataTypes.DOUBLE)), DataTypes.BOOLEAN), Arrays.<Symbol>asList(Literal.newLiteral(20.d), distanceFunction)); xcontentAssert(whereClause, "{\"query\":{\"filtered\":{\"query\":{\"match_all\":{}},\"filter\":{\"geo_distance_range\":{\"location\":[10.0,20.0],\"gte\":20.0}}}}}"); } @Test public void testDistanceEqQuery() throws Exception { Function distanceFunction = new Function( new FunctionInfo( new FunctionIdent(DistanceFunction.NAME, Arrays.<DataType>asList(DataTypes.GEO_POINT, DataTypes.GEO_POINT)), DataTypes.DOUBLE), Arrays.<Symbol>asList(createReference("location", DataTypes.GEO_POINT), Literal.newLiteral(DataTypes.GEO_POINT, DataTypes.GEO_POINT.value("POINT (10 20)")))); Function whereClause = new Function( new FunctionInfo(new FunctionIdent(EqOperator.NAME, Arrays.<DataType>asList(DataTypes.DOUBLE, DataTypes.DOUBLE)), DataTypes.BOOLEAN), Arrays.<Symbol>asList(distanceFunction, Literal.newLiteral(20.0d))); xcontentAssert(whereClause, "{\"query\":{\"filtered\":{\"query\":{\"match_all\":{}},\"filter\":{\"geo_distance_range\":{\"location\":[10.0,20.0],\"from\":20.0,\"to\":20.0,\"include_upper\":true,\"include_lower\":true}}}}}"); } @Test(expected = IllegalArgumentException.class) public void testWhereDistanceFunctionEqDistanceFunction() throws Exception { /** * distance(p, ...) = distance(p, ...) isn't supported */ Function distanceFunction = new Function( new FunctionInfo( new FunctionIdent(DistanceFunction.NAME, Arrays.<DataType>asList(DataTypes.GEO_POINT, DataTypes.GEO_POINT)), DataTypes.DOUBLE), Arrays.<Symbol>asList(createReference("location", DataTypes.GEO_POINT), Literal.newLiteral(DataTypes.GEO_POINT, DataTypes.GEO_POINT.value("POINT (10 20)")))); Function whereClause = new Function( new FunctionInfo(new FunctionIdent(GteOperator.NAME, Arrays.<DataType>asList(DataTypes.DOUBLE, DataTypes.DOUBLE)), DataTypes.BOOLEAN), Arrays.<Symbol>asList(distanceFunction, distanceFunction)); generator.convert(new WhereClause(whereClause)); } @Test public void testConvertESSearchNodeWithOrderByDistance() throws Exception { Function distanceFunction = new Function( new FunctionInfo( new FunctionIdent(DistanceFunction.NAME, Arrays.<DataType>asList(DataTypes.GEO_POINT, DataTypes.GEO_POINT)), DataTypes.DOUBLE), Arrays.<Symbol>asList(createReference("location", DataTypes.GEO_POINT), Literal.newLiteral(DataTypes.GEO_POINT, DataTypes.GEO_POINT.value("POINT (10 20)")))); ESSearchNode searchNode = new ESSearchNode(new String[] { characters.name() }, ImmutableList.<Symbol>of(name_ref), ImmutableList.<Symbol>of(distanceFunction), new boolean[] { false }, new Boolean[] { null }, null, null, WhereClause.MATCH_ALL, null); BytesReference reference = generator.convert(searchNode); String actual = reference.toUtf8(); assertThat(actual, is( "{\"_source\":{\"include\":[\"name\"]},\"query\":{\"match_all\":{}},\"sort\":[{\"_geo_distance\":{\"location\":[10.0,20.0],\"order\":\"asc\"}}],\"from\":0,\"size\":10000}")); } @Test(expected = IllegalArgumentException.class) public void testConvertESSearchNodeWithOrderByDistanceSwappedArgs() throws Exception { // ref must be the first argument Function distanceFunction = new Function( new FunctionInfo( new FunctionIdent(DistanceFunction.NAME, Arrays.<DataType>asList(DataTypes.GEO_POINT, DataTypes.GEO_POINT)), DataTypes.DOUBLE), Arrays.<Symbol>asList( Literal.newLiteral(DataTypes.GEO_POINT, DataTypes.GEO_POINT.value("POINT (10 20)")), createReference("location", DataTypes.GEO_POINT))); ESSearchNode searchNode = new ESSearchNode(new String[] { characters.name() }, ImmutableList.<Symbol>of(name_ref), ImmutableList.<Symbol>of(distanceFunction), new boolean[] { false }, new Boolean[] { null }, null, null, WhereClause.MATCH_ALL, null); BytesReference reference = generator.convert(searchNode); } @Test(expected = IllegalArgumentException.class) public void testConvertESSearchNodeWithOrderByDistanceTwoReferences() throws Exception { Function distanceFunction = new Function( new FunctionInfo( new FunctionIdent(DistanceFunction.NAME, Arrays.<DataType>asList(DataTypes.GEO_POINT, DataTypes.GEO_POINT)), DataTypes.DOUBLE), Arrays.<Symbol>asList(createReference("location", DataTypes.GEO_POINT), createReference("location2", DataTypes.GEO_POINT))); ESSearchNode searchNode = new ESSearchNode(new String[] { characters.name() }, ImmutableList.<Symbol>of(name_ref), ImmutableList.<Symbol>of(distanceFunction), new boolean[] { false }, new Boolean[] { null }, null, null, WhereClause.MATCH_ALL, null); generator.convert(searchNode); } @Test public void testConvertESSearchNodeWithOrderByScalar() throws Exception { Function scalarFunction = new Function( new FunctionInfo(new FunctionIdent(RoundFunction.NAME, Arrays.<DataType>asList(DataTypes.DOUBLE)), DataTypes.LONG), Arrays.<Symbol>asList(createReference("price", DataTypes.DOUBLE))); ESSearchNode searchNode = new ESSearchNode(new String[] { characters.name() }, ImmutableList.<Symbol>of(name_ref), ImmutableList.<Symbol>of(scalarFunction), new boolean[] { false }, new Boolean[] { null }, null, null, WhereClause.MATCH_ALL, null); BytesReference reference = generator.convert(searchNode); String actual = reference.toUtf8(); assertThat(actual, is("{\"_source\":{\"include\":[\"name\"]},\"query\":{\"match_all\":{}}," + "\"sort\":[{\"_script\":{\"script\":\"numeric_scalar_sort\"," + "\"lang\":\"native\",\"type\":\"number\",\"order\":\"asc\"," + "\"params\":{\"missing\":\"_last\",\"scalar\":" + "{\"scalar_name\":\"round\",\"type\":10,\"args\":[" + "{\"field_name\":\"price\",\"type\":6}]}}}}]," + "\"from\":0,\"size\":10000}")); } @Test public void testWhereClauseWithWithinPolygonQuery() throws Exception { Function withinFunction = createFunction(WithinFunction.NAME, DataTypes.BOOLEAN, createReference("location", DataTypes.GEO_POINT), Literal.newGeoShape("POLYGON (( 5 5, 30 5, 30 30, 5 35, 5 5 ))")); xcontentAssert(withinFunction, "{\"query\":{\"filtered\":{\"query\":{\"match_all\":{}},\"filter\":{\"geo_polygon\":{\"location\":{\"points\":[{\"lon\":5.0,\"lat\":5.0},{\"lon\":30.0,\"lat\":5.0},{\"lon\":30.0,\"lat\":30.0},{\"lon\":5.0,\"lat\":35.0},{\"lon\":5.0,\"lat\":5.0}]}}}}}}"); } @Test public void testWhereClauseWithWithinRectangleQuery() throws Exception { Function withinFunction = createFunction(WithinFunction.NAME, DataTypes.BOOLEAN, createReference("location", DataTypes.GEO_POINT), Literal.newGeoShape("POLYGON (( 5 5, 30 5, 30 30, 5 30, 5 5 ))")); xcontentAssert(withinFunction, "{\"query\":{\"filtered\":{\"query\":{\"match_all\":{}},\"filter\":{\"geo_bounding_box\":{\"location\":{\"top_left\":{\"lon\":5.0,\"lat\":30.0},\"bottom_right\":{\"lon\":30.0,\"lat\":5.0}}}}}}}"); } @Test public void testWhereClauseWithWithinPolygonEqualsTrueQuery() throws Exception { Function withinFunction = createFunction(WithinFunction.NAME, DataTypes.BOOLEAN, createReference("location", DataTypes.GEO_POINT), Literal.newGeoShape("POLYGON (( 5 5, 30 5, 30 30, 5 35, 5 5 ))")); Function eqFunction = createFunction(EqOperator.NAME, DataTypes.BOOLEAN, Literal.newLiteral(true), withinFunction); xcontentAssert(eqFunction, "{\"query\":{\"filtered\":{\"query\":{\"match_all\":{}},\"filter\":{\"geo_polygon\":{\"location\":{\"points\":[{\"lon\":5.0,\"lat\":5.0},{\"lon\":30.0,\"lat\":5.0},{\"lon\":30.0,\"lat\":30.0},{\"lon\":5.0,\"lat\":35.0},{\"lon\":5.0,\"lat\":5.0}]}}}}}}"); } @Test public void testWhereClauseWithWithinEqualsFalseQuery() throws Exception { Function withinFunction = createFunction(WithinFunction.NAME, DataTypes.BOOLEAN, createReference("location", DataTypes.GEO_POINT), Literal.newGeoShape("POLYGON (( 5 5, 30 5, 30 30, 5 30, 5 5 ))")); Function eqFunction = createFunction(EqOperator.NAME, DataTypes.BOOLEAN, withinFunction, Literal.newLiteral(false)); xcontentAssert(eqFunction, "{\"query\":{\"bool\":{\"must_not\":{\"filtered\":{\"query\":{\"match_all\":{}},\"filter\":{\"geo_bounding_box\":{\"location\":{\"top_left\":{\"lon\":5.0,\"lat\":30.0},\"bottom_right\":{\"lon\":30.0,\"lat\":5.0}}}}}}}}}"); } @Test(expected = IllegalArgumentException.class) public void testWithinEqWithin() throws Exception { Function withinFunction = createFunction(WithinFunction.NAME, DataTypes.BOOLEAN, createReference("location", DataTypes.GEO_POINT), Literal.newGeoShape("POLYGON (( 5 5, 30 5, 30 30, 5 30, 5 5 ))")); Function eqFunction = createFunction(EqOperator.NAME, DataTypes.BOOLEAN, withinFunction, withinFunction); generator.convert(new WhereClause(eqFunction)); } @Test public void testWhereNumericScalar() throws Exception { Function scalarFunction = new Function( new FunctionInfo(new FunctionIdent(RoundFunction.NAME, Arrays.<DataType>asList(DataTypes.DOUBLE)), DataTypes.LONG), Arrays.<Symbol>asList(createReference("price", DataTypes.DOUBLE))); Function whereClause = new Function( new FunctionInfo(new FunctionIdent(EqOperator.NAME, Arrays.<DataType>asList(DataTypes.DOUBLE, DataTypes.DOUBLE)), DataTypes.BOOLEAN), Arrays.<Symbol>asList(scalarFunction, Literal.newLiteral(20.0d))); xcontentAssert(whereClause, "{\"query\":{\"filtered\":{\"query\":{\"match_all\":{}}," + "\"filter\":{\"script\":{\"script\":\"numeric_scalar_search\"," + "\"lang\":\"native\",\"params\":" + "{\"op\":\"op_=\",\"args\":[{\"scalar_name\":\"round\"," + "\"type\":10,\"args\":[{\"field_name\":\"price\",\"type\":6}]}," + "{\"value\":20.0,\"type\":6}]}}}}}}"); } @Test public void testWhereNumericScalarWithArguments() throws Exception { Function scalarFunction = new Function( new FunctionInfo(new FunctionIdent(LogFunction.NAME, Arrays.<DataType>asList(DataTypes.DOUBLE, DataTypes.INTEGER)), DataTypes.LONG), Arrays.<Symbol>asList(createReference("price", DataTypes.DOUBLE), Literal.newLiteral(DataTypes.INTEGER, 100))); Function whereClause = new Function( new FunctionInfo(new FunctionIdent(EqOperator.NAME, Arrays.<DataType>asList(DataTypes.DOUBLE, DataTypes.DOUBLE)), DataTypes.BOOLEAN), Arrays.<Symbol>asList(scalarFunction, Literal.newLiteral(20.0))); xcontentAssert(whereClause, "{\"query\":{\"filtered\":{\"query\":{\"match_all\":{}}," + "\"filter\":{\"script\":{\"script\":\"numeric_scalar_search\"," + "\"lang\":\"native\",\"params\":" + "{\"op\":\"op_=\",\"args\":[{\"scalar_name\":\"log\"," + "\"type\":10,\"args\":[" + "{\"field_name\":\"price\",\"type\":6}," + "{\"value\":100,\"type\":9}" + "]},{\"value\":20.0,\"type\":6}]}}}}}}"); } @Test public void testWhereNumericScalarWithArgumentsTwoReferences() throws Exception { Function scalarFunction = new Function( new FunctionInfo(new FunctionIdent(LogFunction.NAME, Arrays.<DataType>asList(DataTypes.DOUBLE, DataTypes.INTEGER)), DataTypes.LONG), Arrays.<Symbol>asList(createReference("price", DataTypes.DOUBLE), createReference("base", DataTypes.INTEGER))); Function whereClause = new Function( new FunctionInfo(new FunctionIdent(EqOperator.NAME, Arrays.<DataType>asList(DataTypes.DOUBLE, DataTypes.DOUBLE)), DataTypes.BOOLEAN), Arrays.<Symbol>asList(scalarFunction, Literal.newLiteral(20.0))); xcontentAssert(whereClause, "{\"query\":{\"filtered\":{\"query\":{\"match_all\":{}}," + "\"filter\":{\"script\":{\"script\":\"numeric_scalar_search\"," + "\"lang\":\"native\",\"params\":" + "{\"op\":\"op_=\",\"args\":[{\"scalar_name\":\"log\"," + "\"type\":10,\"args\":[" + "{\"field_name\":\"price\",\"type\":6}," + "{\"field_name\":\"base\",\"type\":9}" + "]},{\"value\":20.0,\"type\":6}]}}}}}}"); } @Test public void testWhereNumericScalarEqNumericScalar() throws Exception { /** * round(a) = round(b) isn't supported */ Function scalarFunction = new Function( new FunctionInfo(new FunctionIdent(RoundFunction.NAME, Arrays.<DataType>asList(DataTypes.DOUBLE)), DataTypes.LONG), Arrays.<Symbol>asList(createReference("price", DataTypes.DOUBLE))); Function whereClause = new Function( new FunctionInfo(new FunctionIdent(EqOperator.NAME, Arrays.<DataType>asList(DataTypes.DOUBLE, DataTypes.DOUBLE)), DataTypes.BOOLEAN), Arrays.<Symbol>asList(scalarFunction, scalarFunction)); expectedException.expect(IllegalArgumentException.class); expectedException.expectMessage("Can't compare two scalar functions"); generator.convert(new WhereClause(whereClause)); } @Test public void testNestedScalar() throws Exception { /** * round(round(a) isn't supported */ Function scalarFunction = new Function( new FunctionInfo(new FunctionIdent(RoundFunction.NAME, Arrays.<DataType>asList(DataTypes.DOUBLE)), DataTypes.LONG), Arrays.<Symbol>asList( new Function( new FunctionInfo(new FunctionIdent(RoundFunction.NAME, Arrays.<DataType>asList(DataTypes.DOUBLE)), DataTypes.LONG), Arrays.<Symbol>asList(createReference("price", DataTypes.DOUBLE))))); Function whereClause = new Function( new FunctionInfo(new FunctionIdent(EqOperator.NAME, Arrays.<DataType>asList(DataTypes.DOUBLE, DataTypes.DOUBLE)), DataTypes.BOOLEAN), Arrays.<Symbol>asList(scalarFunction, Literal.newLiteral(DataTypes.INTEGER, 100))); expectedException.expect(IllegalArgumentException.class); expectedException.expectMessage("Nested scalar functions are not supported"); generator.convert(new WhereClause(whereClause)); } }