/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. The ASF licenses this file to You * under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. For additional information regarding * copyright in this work, please see the NOTICE file in the top level * directory of this distribution. */ package org.apache.usergrid.persistence.query.tree; import java.util.Collection; import java.util.UUID; import org.antlr.runtime.ANTLRStringStream; import org.antlr.runtime.RecognitionException; import org.antlr.runtime.TokenRewriteStream; import org.elasticsearch.index.query.QueryBuilder; import org.junit.Test; import org.apache.usergrid.persistence.core.scope.ApplicationScopeImpl; import org.apache.usergrid.persistence.index.SearchEdge; import org.apache.usergrid.persistence.index.exceptions.QueryParseException; import org.apache.usergrid.persistence.index.impl.SearchEdgeImpl; import org.apache.usergrid.persistence.index.impl.SearchRequestBuilderStrategy; import org.apache.usergrid.persistence.index.query.ParsedQuery; import org.apache.usergrid.persistence.index.query.ParsedQueryBuilder; import org.apache.usergrid.persistence.index.SelectFieldMapping; import org.apache.usergrid.persistence.index.query.tree.AndOperand; import org.apache.usergrid.persistence.index.query.tree.ContainsOperand; import org.apache.usergrid.persistence.index.query.tree.CpQueryFilterLexer; import org.apache.usergrid.persistence.index.query.tree.CpQueryFilterParser; import org.apache.usergrid.persistence.index.query.tree.Equal; import org.apache.usergrid.persistence.index.query.tree.GreaterThan; import org.apache.usergrid.persistence.index.query.tree.GreaterThanEqual; import org.apache.usergrid.persistence.index.query.tree.LessThan; import org.apache.usergrid.persistence.index.query.tree.LessThanEqual; import org.apache.usergrid.persistence.index.query.tree.LongLiteral; import org.apache.usergrid.persistence.index.query.tree.NotOperand; import org.apache.usergrid.persistence.index.query.tree.Operand; import org.apache.usergrid.persistence.index.query.tree.OrOperand; import org.apache.usergrid.persistence.index.query.tree.StringLiteral; import org.apache.usergrid.persistence.index.query.tree.UUIDLiteral; import org.apache.usergrid.persistence.index.query.tree.WithinOperand; import org.apache.usergrid.persistence.model.entity.SimpleId; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; /** @author tnine */ public class GrammarTreeTest { /** Simple test that constructs and AST from the ANTLR generated files */ @Test public void equality() throws RecognitionException { String queryString = "select * where a = 5"; ANTLRStringStream in = new ANTLRStringStream( queryString ); CpQueryFilterLexer lexer = new CpQueryFilterLexer( in ); TokenRewriteStream tokens = new TokenRewriteStream( lexer ); CpQueryFilterParser parser = new CpQueryFilterParser( tokens ); ParsedQuery query = parser.ql().parsedQuery; Operand root = query.getRootOperand(); Equal equal = ( Equal ) root; assertEquals( "a", equal.getProperty().getValue() ); assertEquals( 5, ( ( LongLiteral ) equal.getLiteral() ).getValue().intValue() ); } /** Simple test that constructs and AST from the ANTLR generated files */ @Test public void lessThan() throws RecognitionException { String queryString = "select * where a < 5"; ANTLRStringStream in = new ANTLRStringStream( queryString ); CpQueryFilterLexer lexer = new CpQueryFilterLexer( in ); TokenRewriteStream tokens = new TokenRewriteStream( lexer ); CpQueryFilterParser parser = new CpQueryFilterParser( tokens ); ParsedQuery query = parser.ql().parsedQuery; Operand root = query.getRootOperand(); LessThan equal = ( LessThan ) root; assertEquals( "a", equal.getProperty().getValue() ); assertEquals( 5, ( ( LongLiteral ) equal.getLiteral() ).getValue().intValue() ); queryString = "select * where a lt 5"; in = new ANTLRStringStream( queryString ); lexer = new CpQueryFilterLexer( in ); tokens = new TokenRewriteStream( lexer ); parser = new CpQueryFilterParser( tokens ); query = parser.ql().parsedQuery; root = query.getRootOperand(); equal = ( LessThan ) root; assertEquals( "a", equal.getProperty().getValue() ); assertEquals( 5, ( ( LongLiteral ) equal.getLiteral() ).getValue().intValue() ); } /** Simple test that constructs and AST from the ANTLR generated files */ @Test public void lessThanEqual() throws RecognitionException { String queryString = "select * where a <= 5"; ANTLRStringStream in = new ANTLRStringStream( queryString ); CpQueryFilterLexer lexer = new CpQueryFilterLexer( in ); TokenRewriteStream tokens = new TokenRewriteStream( lexer ); CpQueryFilterParser parser = new CpQueryFilterParser( tokens ); ParsedQuery query = parser.ql().parsedQuery; Operand root = query.getRootOperand(); LessThanEqual equal = ( LessThanEqual ) root; assertEquals( "a", equal.getProperty().getValue() ); assertEquals( 5, ( ( LongLiteral ) equal.getLiteral() ).getValue().intValue() ); queryString = "select * where a lte 5"; in = new ANTLRStringStream( queryString ); lexer = new CpQueryFilterLexer( in ); tokens = new TokenRewriteStream( lexer ); parser = new CpQueryFilterParser( tokens ); query = parser.ql().parsedQuery; root = query.getRootOperand(); equal = ( LessThanEqual ) root; assertEquals( "a", equal.getProperty().getValue() ); assertEquals( 5, ( ( LongLiteral ) equal.getLiteral() ).getValue().intValue() ); } /** Simple test that constructs and AST from the ANTLR generated files */ @Test public void greaterThan() throws RecognitionException { String queryString = "select * where a > 5"; ANTLRStringStream in = new ANTLRStringStream( queryString ); CpQueryFilterLexer lexer = new CpQueryFilterLexer( in ); TokenRewriteStream tokens = new TokenRewriteStream( lexer ); CpQueryFilterParser parser = new CpQueryFilterParser( tokens ); ParsedQuery query = parser.ql().parsedQuery; Operand root = query.getRootOperand(); GreaterThan equal = ( GreaterThan ) root; assertEquals( "a", equal.getProperty().getValue() ); assertEquals( 5, ( ( LongLiteral ) equal.getLiteral() ).getValue().intValue() ); queryString = "select * where a gt 5"; in = new ANTLRStringStream( queryString ); lexer = new CpQueryFilterLexer( in ); tokens = new TokenRewriteStream( lexer ); parser = new CpQueryFilterParser( tokens ); query = parser.ql().parsedQuery; root = query.getRootOperand(); equal = ( GreaterThan ) root; assertEquals( "a", equal.getProperty().getValue() ); assertEquals( 5, ( ( LongLiteral ) equal.getLiteral() ).getValue().intValue() ); } @Test public void partialGrammar() throws Exception{ String queryString; TokenRewriteStream tokens; ParsedQuery query; CpQueryFilterParser parser; CpQueryFilterLexer lexer; ANTLRStringStream in; Operand root; queryString = "where a gte 5"; in = new ANTLRStringStream( queryString ); lexer = new CpQueryFilterLexer( in ); tokens = new TokenRewriteStream( lexer ); parser = new CpQueryFilterParser( tokens ); query = parser.ql().parsedQuery; root = query.getRootOperand(); queryString = "where name = 'bob'"; in = new ANTLRStringStream( queryString ); lexer = new CpQueryFilterLexer( in ); tokens = new TokenRewriteStream( lexer ); parser = new CpQueryFilterParser( tokens ); query = parser.ql().parsedQuery; root = query.getRootOperand(); } /** Simple test that constructs and AST from the ANTLR generated files */ @Test public void greaterThanEqual() throws RecognitionException { String queryString = "select * where a >= 5"; ANTLRStringStream in = new ANTLRStringStream( queryString ); CpQueryFilterLexer lexer = new CpQueryFilterLexer( in ); TokenRewriteStream tokens = new TokenRewriteStream( lexer ); CpQueryFilterParser parser = new CpQueryFilterParser( tokens ); ParsedQuery query = parser.ql().parsedQuery; Operand root = query.getRootOperand(); GreaterThanEqual equal = ( GreaterThanEqual ) root; assertEquals( "a", equal.getProperty().getValue() ); assertEquals( 5, ( ( LongLiteral ) equal.getLiteral() ).getValue().intValue() ); queryString = "select * where a gte 5"; in = new ANTLRStringStream( queryString ); lexer = new CpQueryFilterLexer( in ); tokens = new TokenRewriteStream( lexer ); parser = new CpQueryFilterParser( tokens ); query = parser.ql().parsedQuery; root = query.getRootOperand(); equal = ( GreaterThanEqual ) root; assertEquals( "a", equal.getProperty().getValue() ); assertEquals( 5, ( ( LongLiteral ) equal.getLiteral() ).getValue().intValue() ); } /** Test basic && expression */ @Test public void andExpression() throws RecognitionException { String queryString = "select * where a = 1 and b > 2"; ANTLRStringStream in = new ANTLRStringStream( queryString ); CpQueryFilterLexer lexer = new CpQueryFilterLexer( in ); TokenRewriteStream tokens = new TokenRewriteStream( lexer ); CpQueryFilterParser parser = new CpQueryFilterParser( tokens ); ParsedQuery query = parser.ql().parsedQuery; Operand root = query.getRootOperand(); AndOperand and = ( AndOperand ) root; Equal equal = ( Equal ) and.getLeft(); assertEquals( "a", equal.getProperty().getValue() ); assertEquals( 1, ( ( LongLiteral ) equal.getLiteral() ).getValue().intValue() ); GreaterThan greater = ( GreaterThan ) and.getRight(); assertEquals( "b", greater.getProperty().getValue() ); assertEquals( 2, ( ( LongLiteral ) greater.getLiteral() ).getValue().intValue() ); } /** Test basic || expression */ @Test public void orExpression() throws RecognitionException { String queryString = "select * where a = 1 or b > 2"; ANTLRStringStream in = new ANTLRStringStream( queryString ); CpQueryFilterLexer lexer = new CpQueryFilterLexer( in ); TokenRewriteStream tokens = new TokenRewriteStream( lexer ); CpQueryFilterParser parser = new CpQueryFilterParser( tokens ); ParsedQuery query = parser.ql().parsedQuery; Operand root = query.getRootOperand(); OrOperand and = ( OrOperand ) root; Equal equal = ( Equal ) and.getLeft(); assertEquals( "a", equal.getProperty().getValue() ); assertEquals( 1, ( ( LongLiteral ) equal.getLiteral() ).getValue().intValue() ); GreaterThan greater = ( GreaterThan ) and.getRight(); assertEquals( "b", greater.getProperty().getValue() ); assertEquals( 2, ( ( LongLiteral ) greater.getLiteral() ).getValue().intValue() ); } /** Test basic not expression */ @Test public void notExpression() throws RecognitionException { String queryString = "select * where not a = 1"; ANTLRStringStream in = new ANTLRStringStream( queryString ); CpQueryFilterLexer lexer = new CpQueryFilterLexer( in ); TokenRewriteStream tokens = new TokenRewriteStream( lexer ); CpQueryFilterParser parser = new CpQueryFilterParser( tokens ); ParsedQuery query = parser.ql().parsedQuery; Operand root = query.getRootOperand(); NotOperand not = ( NotOperand ) root; Equal equal = ( Equal ) not.getOperation(); assertEquals( "a", equal.getProperty().getValue() ); assertEquals( 1, ( ( LongLiteral ) equal.getLiteral() ).getValue().intValue() ); } /** Test basic not expression */ @Test public void complexExpression() throws RecognitionException { String queryString = "select * where not a = 1"; ANTLRStringStream in = new ANTLRStringStream( queryString ); CpQueryFilterLexer lexer = new CpQueryFilterLexer( in ); TokenRewriteStream tokens = new TokenRewriteStream( lexer ); CpQueryFilterParser parser = new CpQueryFilterParser( tokens ); ParsedQuery query = parser.ql().parsedQuery; Operand root = query.getRootOperand(); NotOperand not = ( NotOperand ) root; Equal equal = ( Equal ) not.getOperation(); assertEquals( "a", equal.getProperty().getValue() ); assertEquals( 1, ( ( LongLiteral ) equal.getLiteral() ).getValue().intValue() ); } /** Test basic || expression */ @Test public void selectAll() throws RecognitionException { String queryString = "select * where a = 1 or b > 2"; ANTLRStringStream in = new ANTLRStringStream( queryString ); CpQueryFilterLexer lexer = new CpQueryFilterLexer( in ); TokenRewriteStream tokens = new TokenRewriteStream( lexer ); CpQueryFilterParser parser = new CpQueryFilterParser( tokens ); ParsedQuery query = parser.ql().parsedQuery; Collection<SelectFieldMapping> identifiers = query.getSelectFieldMappings(); assertEquals( 0, identifiers.size() ); } @Test public void selectGeo() throws RecognitionException { String queryString = "select * where a within .1 of -40.343666, 175.630917"; ANTLRStringStream in = new ANTLRStringStream( queryString ); CpQueryFilterLexer lexer = new CpQueryFilterLexer( in ); TokenRewriteStream tokens = new TokenRewriteStream( lexer ); CpQueryFilterParser parser = new CpQueryFilterParser( tokens ); ParsedQuery query = parser.ql().parsedQuery; WithinOperand operand = ( WithinOperand ) query.getRootOperand(); assertEquals( "a", operand.getProperty().getValue() ); assertEquals( .1f, operand.getDistance().getFloatValue(), 0 ); assertEquals( -40.343666f, operand.getLatitude().getFloatValue(), 0 ); assertEquals( 175.630917f, operand.getLongitude().getFloatValue(), 0 ); } @Test public void selectGeoWithInt() throws RecognitionException { String queryString = "select * where a within 1 of -40.343666, 175.630917"; ANTLRStringStream in = new ANTLRStringStream( queryString ); CpQueryFilterLexer lexer = new CpQueryFilterLexer( in ); TokenRewriteStream tokens = new TokenRewriteStream( lexer ); CpQueryFilterParser parser = new CpQueryFilterParser( tokens ); ParsedQuery query = parser.ql().parsedQuery; WithinOperand operand = ( WithinOperand ) query.getRootOperand(); assertEquals( "a", operand.getProperty().getValue() ); assertEquals( 1f, operand.getDistance().getFloatValue(), 0 ); assertEquals( -40.343666f, operand.getLatitude().getFloatValue(), 0 ); assertEquals( 175.630917f, operand.getLongitude().getFloatValue(), 0 ); } @Test public void selectGeoWithAnd() throws RecognitionException { String queryString = "select * where location within 20000 of 37,-75 " + "and created > 1407776999925 and created < 1407777000266"; ANTLRStringStream in = new ANTLRStringStream( queryString ); CpQueryFilterLexer lexer = new CpQueryFilterLexer( in ); TokenRewriteStream tokens = new TokenRewriteStream( lexer ); CpQueryFilterParser parser = new CpQueryFilterParser( tokens ); ParsedQuery query = parser.ql().parsedQuery; AndOperand andOp1 = ( AndOperand ) query.getRootOperand(); AndOperand andOp2 = ( AndOperand ) andOp1.getLeft(); WithinOperand withinOperand = ( WithinOperand ) andOp2.getLeft(); assertEquals( "location", withinOperand.getProperty().getValue() ); assertEquals( 20000, withinOperand.getDistance().getFloatValue(), 0 ); assertEquals( 37f, withinOperand.getLatitude().getFloatValue(), 0 ); assertEquals( -75f, withinOperand.getLongitude().getFloatValue(), 0 ); } @Test public void selectDistance() throws RecognitionException { String queryString = "select * where a contains 'foo'"; ANTLRStringStream in = new ANTLRStringStream( queryString ); CpQueryFilterLexer lexer = new CpQueryFilterLexer( in ); TokenRewriteStream tokens = new TokenRewriteStream( lexer ); CpQueryFilterParser parser = new CpQueryFilterParser( tokens ); ParsedQuery query = parser.ql().parsedQuery; ContainsOperand operand = ( ContainsOperand ) query.getRootOperand(); assertEquals( "a", operand.getProperty().getValue() ); assertEquals( "foo", operand.getString().getValue() ); } @Test public void selectField() throws RecognitionException { String queryString = "select c where a = 1 or b > 2"; ANTLRStringStream in = new ANTLRStringStream( queryString ); CpQueryFilterLexer lexer = new CpQueryFilterLexer( in ); TokenRewriteStream tokens = new TokenRewriteStream( lexer ); CpQueryFilterParser parser = new CpQueryFilterParser( tokens ); ParsedQuery query = parser.ql().parsedQuery; Collection<SelectFieldMapping> identifiers = query.getSelectFieldMappings(); final SelectFieldMapping fieldMapping = identifiers.iterator().next(); assertEquals( "c", fieldMapping.getSourceFieldName() ); assertEquals( "c", fieldMapping.getTargetFieldName() ); } @Test public void selectRename() throws RecognitionException { String queryString = "select {source:target} where a = 1 or b > 2"; ANTLRStringStream in = new ANTLRStringStream( queryString ); CpQueryFilterLexer lexer = new CpQueryFilterLexer( in ); TokenRewriteStream tokens = new TokenRewriteStream( lexer ); CpQueryFilterParser parser = new CpQueryFilterParser( tokens ); ParsedQuery query = parser.ql().parsedQuery; Collection<SelectFieldMapping> identifiers = query.getSelectFieldMappings(); final SelectFieldMapping fieldMapping = identifiers.iterator().next(); assertEquals( "source", fieldMapping.getSourceFieldName() ); assertEquals( "target", fieldMapping.getTargetFieldName() ); } @Test public void containsOr() throws Exception { String queryString = "select * where keywords contains 'hot' or title contains 'hot'"; ANTLRStringStream in = new ANTLRStringStream( queryString ); CpQueryFilterLexer lexer = new CpQueryFilterLexer( in ); TokenRewriteStream tokens = new TokenRewriteStream( lexer ); CpQueryFilterParser parser = new CpQueryFilterParser( tokens ); ParsedQuery query = parser.ql().parsedQuery; OrOperand rootNode = ( OrOperand ) query.getRootOperand(); assertNotNull( rootNode ); ContainsOperand left = ( ContainsOperand ) rootNode.getLeft(); assertEquals( "keywords", left.getProperty().getValue() ); assertEquals( "hot", left.getString().getValue() ); assertEquals( "hot", left.getString().getEndValue() ); ContainsOperand right = ( ContainsOperand ) rootNode.getRight(); assertEquals( "title", right.getProperty().getValue() ); assertEquals( "hot", right.getString().getValue() ); assertEquals( "hot", right.getString().getEndValue() ); } @Test public void stringLower() throws Exception { String queryString = "select * where title = 'Hot'"; ANTLRStringStream in = new ANTLRStringStream( queryString ); CpQueryFilterLexer lexer = new CpQueryFilterLexer( in ); TokenRewriteStream tokens = new TokenRewriteStream( lexer ); CpQueryFilterParser parser = new CpQueryFilterParser( tokens ); ParsedQuery query = parser.ql().parsedQuery; Equal rootNode = ( Equal ) query.getRootOperand(); assertEquals( "title", rootNode.getProperty().getValue() ); assertEquals( "hot", ( ( StringLiteral ) rootNode.getLiteral() ).getValue() ); } @Test public void nestedBooleanLogic() throws Exception { String queryString = "select * where field1 = 'foo' AND (field2 = 'bar' OR field2 = 'baz')"; ANTLRStringStream in = new ANTLRStringStream( queryString ); CpQueryFilterLexer lexer = new CpQueryFilterLexer( in ); TokenRewriteStream tokens = new TokenRewriteStream( lexer ); CpQueryFilterParser parser = new CpQueryFilterParser( tokens ); ParsedQuery query = parser.ql().parsedQuery; AndOperand rootNode = ( AndOperand ) query.getRootOperand(); //left should be field1 Equal field1Equal = ( Equal ) rootNode.getLeft(); assertEquals( "field1", field1Equal.getProperty().getValue() ); assertEquals( "foo", ( ( StringLiteral ) field1Equal.getLiteral() ).getValue() ); OrOperand orNode = ( OrOperand ) rootNode.getRight(); Equal field2Bar = ( Equal ) orNode.getLeft(); Equal field2Baz = ( Equal ) orNode.getRight(); assertEquals( "field2", field2Bar.getProperty().getValue() ); assertEquals( "bar", ( ( StringLiteral ) field2Bar.getLiteral() ).getValue() ); assertEquals( "field2", field2Baz.getProperty().getValue() ); assertEquals( "baz", ( ( StringLiteral ) field2Baz.getLiteral() ).getValue() ); } @Test public void uuidParse() throws RecognitionException { String queryString = "select * where title = c6ee8a1c-3ef4-11e2-8861-02e81adcf3d0"; ANTLRStringStream in = new ANTLRStringStream( queryString ); CpQueryFilterLexer lexer = new CpQueryFilterLexer( in ); TokenRewriteStream tokens = new TokenRewriteStream( lexer ); CpQueryFilterParser parser = new CpQueryFilterParser( tokens ); ParsedQuery query = parser.ql().parsedQuery; Equal rootNode = ( Equal ) query.getRootOperand(); assertEquals( "title", rootNode.getProperty().getValue() ); assertEquals( UUID.fromString( "c6ee8a1c-3ef4-11e2-8861-02e81adcf3d0" ), ( ( UUIDLiteral ) rootNode.getLiteral() ).getValue() ); } @Test public void orderByGrammar() throws QueryParseException { String s = "select * where name = 'bob' order by name asc"; ParsedQuery query = ParsedQueryBuilder.build( s ); assertEquals( 1, query.getSortPredicates().size() ); } @Test public void badOrderByGrammar() throws QueryParseException { // from isn't allowed String s = "select * where name = 'bob' order by"; String error = null; try { ParsedQueryBuilder.build ( s ); } catch ( QueryParseException qpe ) { error = qpe.getMessage(); } assertTrue( error.startsWith( "The query cannot be parsed" ) ); } @Test public void badOperand() throws QueryParseException { // from isn't allowed String s = "select * where name != 'bob'"; String error = null; try { ParsedQueryBuilder.build( s ); fail( "should throw an exception" ); } catch ( RuntimeException qpe ) { error = qpe.getMessage(); } assertEquals( "NoViableAltException('!'@[1:1: Tokens : ( T__31 | T__32 | T__33 | T__34 | T__35 | T__36 | T__37 | " + "T__38 | T__39 | T__40 | LT | LTE | EQ | GT | GTE | BOOLEAN | AND | OR | NOT | ASC | DESC |" + " CONTAINS | WITHIN | OF | UUID | ID | LONG | FLOAT | STRING | WS );])", error ); } }