├── CHANGELOG ├── README.md ├── pom.xml └── src ├── assembly └── zip.xml ├── main ├── java │ └── io │ │ └── github │ │ └── iamazy │ │ └── elasticsearch │ │ └── dsl │ │ ├── cons │ │ ├── CoreConstants.java │ │ └── ElasticConstants.java │ │ ├── elastic │ │ └── HighlightBuilders.java │ │ ├── plugin │ │ ├── RestSqlAction.java │ │ └── SqlPlugin.java │ │ ├── sql │ │ ├── druid │ │ │ ├── ElasticSqlExprParser.java │ │ │ ├── ElasticSqlLexer.java │ │ │ ├── ElasticSqlSelectParser.java │ │ │ └── ElasticSqlSelectQueryBlock.java │ │ ├── enums │ │ │ ├── QueryFieldType.java │ │ │ ├── SqlBoolOperator.java │ │ │ ├── SqlConditionOperator.java │ │ │ └── SqlConditionType.java │ │ ├── exception │ │ │ └── ElasticSql2DslException.java │ │ ├── helper │ │ │ ├── ElasticSqlArgConverter.java │ │ │ ├── ElasticSqlDateParseHelper.java │ │ │ └── ElasticSqlMethodInvokeHelper.java │ │ ├── model │ │ │ ├── AggregationQuery.java │ │ │ ├── AtomicQuery.java │ │ │ ├── ElasticDslContext.java │ │ │ ├── ElasticSqlParseResult.java │ │ │ ├── ElasticSqlQueryField.java │ │ │ ├── ElasticSqlQueryFields.java │ │ │ ├── QueryFieldReferenceNode.java │ │ │ ├── QueryFieldReferencePath.java │ │ │ ├── RangeSegment.java │ │ │ └── SqlCondition.java │ │ └── parser │ │ │ ├── ElasticSql2DslParser.java │ │ │ ├── aggs │ │ │ ├── AbstractGroupByMethodAggregationParser.java │ │ │ ├── GroupByAggregationParser.java │ │ │ ├── geo │ │ │ │ └── GeoDistanceAggregationParser.java │ │ │ ├── join │ │ │ │ └── NestedAggregationParser.java │ │ │ └── search │ │ │ │ ├── CardinalityAggregationParser.java │ │ │ │ ├── RangeAggAggregationParser.java │ │ │ │ ├── TermsAggAggregationParser.java │ │ │ │ └── TopHitsAggregationParser.java │ │ │ ├── query │ │ │ ├── exact │ │ │ │ ├── AbstractExactQueryParser.java │ │ │ │ ├── BetweenAndQueryParser.java │ │ │ │ ├── BinaryQueryParser.java │ │ │ │ ├── IConditionExactQueryBuilder.java │ │ │ │ └── InListQueryParser.java │ │ │ └── method │ │ │ │ ├── AbstractFieldSpecificMethodQueryParser.java │ │ │ │ ├── MethodInvocation.java │ │ │ │ ├── MethodQueryParser.java │ │ │ │ ├── ParameterizedMethodQueryParser.java │ │ │ │ ├── expr │ │ │ │ ├── AbstractParameterizedMethodExpression.java │ │ │ │ ├── FieldSpecificMethodExpression.java │ │ │ │ ├── MethodExpression.java │ │ │ │ └── ParameterizedMethodExpression.java │ │ │ │ ├── fulltext │ │ │ │ ├── FullTextQueryParser.java │ │ │ │ ├── MatchAllQueryParser.java │ │ │ │ ├── MatchPhrasePrefixQueryParser.java │ │ │ │ ├── MatchPhraseQueryParser.java │ │ │ │ ├── MatchQueryParser.java │ │ │ │ ├── MultiMatchQueryParser.java │ │ │ │ ├── QueryStringQueryParser.java │ │ │ │ └── SimpleQueryStringQueryParser.java │ │ │ │ ├── join │ │ │ │ ├── HasChildQueryParser.java │ │ │ │ ├── HasParentQueryParser.java │ │ │ │ └── JoinQueryParser.java │ │ │ │ ├── mapping │ │ │ │ └── MappingQueryParser.java │ │ │ │ ├── score │ │ │ │ ├── BoostingQueryParser.java │ │ │ │ ├── FunctionScoreQueryParser.java │ │ │ │ └── ScoreQueryParser.java │ │ │ │ ├── script │ │ │ │ └── ScriptQueryParser.java │ │ │ │ └── term │ │ │ │ ├── FuzzyQueryParser.java │ │ │ │ ├── PrefixQueryParser.java │ │ │ │ ├── RegexpQueryParser.java │ │ │ │ ├── TermLevelAtomicQueryParser.java │ │ │ │ ├── TermQueryParser.java │ │ │ │ ├── TermsQueryParser.java │ │ │ │ └── WildcardQueryParser.java │ │ │ └── sql │ │ │ ├── BoolExpressionParser.java │ │ │ ├── QueryFieldParser.java │ │ │ ├── QueryFromParser.java │ │ │ ├── QueryLimitSizeParser.java │ │ │ ├── QueryMatchConditionParser.java │ │ │ ├── QueryOrderConditionParser.java │ │ │ ├── QueryParser.java │ │ │ ├── QueryRoutingValParser.java │ │ │ ├── QueryScrollParser.java │ │ │ ├── QuerySelectFieldListParser.java │ │ │ ├── QueryWhereConditionParser.java │ │ │ └── sort │ │ │ ├── AbstractMethodSortParser.java │ │ │ ├── ConditionSortBuilder.java │ │ │ ├── MethodSortParser.java │ │ │ ├── NestedSortMethodParser.java │ │ │ ├── NvlMethodSortParser.java │ │ │ ├── ParseSortBuilderHelper.java │ │ │ └── ScriptMethodSortParser.java │ │ └── utils │ │ └── FlatMapUtils.java └── resources │ ├── es-plugin.properties │ ├── plugin-descriptor.properties │ └── plugin-security.policy └── test └── java └── io └── github └── iamazy └── elasticsearch └── dsl └── sql ├── DeleteTest.java ├── DescTest.java ├── NestedAggTest.java ├── ScoreTest.java ├── ScriptQueryTest.java ├── ScrollTest.java └── SqlParserSelectFieldTest.java /CHANGELOG: -------------------------------------------------------------------------------- 1 | 2019-3-6:修复原版Nested类型的nested path识别错误的问题 2 | 3 | 2019-3-7:删除了大部分无用的代码,添加了geo_distance聚类方法 4 | 5 | 2019-3-25: 聚类使用递归实现添加多层嵌套聚类方式([>]表示嵌套聚类[,]表示同级聚类),具体用法见test目录 6 | 7 | 2019-3-26: 添加scroll id深度分页 8 | 9 | 2019-3-28: 更新nested功能,支持双层嵌套类型(再多就要考虑数据结构是否合理了) 10 | 11 | 2019-4-8: 添加高亮显示 12 | 13 | 2019-4-11: 添加Function Score 14 | 15 | 2019-4-24: 将elasticsearch-sql添加为elasticsearch插件 16 | 17 | 2019-4-28: 添加like not like 查询 18 | 19 | 2019-5-5: 添加desc语法获取index(或者index/field)的mapping,无法直接获取实际的mapping,必须结合restClient使用,且desc后面只能加一个index的名称 20 | 21 | 2019-5-8: 添加excludes字段(在字段前加[^]) 22 | 23 | 2019-6-5: 解决了索引名带中划线[-]的bug 24 | -------------------------------------------------------------------------------- /src/assembly/zip.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | sql-plugin 6 | 7 | zip 8 | 9 | false 10 | 11 | 12 | ${project.build.directory} 13 | / 14 | 15 | *.jar 16 | plugin-descriptor.properties 17 | 18 | 19 | *javadoc* 20 | *sources* 21 | 22 | 23 | 24 | 25 | 26 | ${project.basedir}/src/main/resources/plugin-descriptor.properties 27 | 28 | true 29 | 30 | 31 | ${project.basedir}/src/main/resources/plugin-security.policy 32 | 33 | true 34 | 35 | 36 | 37 | 38 | 39 | 40 | true 41 | true 42 | 43 | org.elasticsearch:elasticsearch 44 | org.elasticsearch.client:elasticsearch-rest-high-level-client 45 | org.elasticsearch.client:elasticsearch-rest-client 46 | com.fasterxml.jackson.core:jackson-core 47 | commons-codec:commons-codec 48 | 49 | 50 | 51 | 52 | true 53 | true 54 | 55 | com.alibaba:druid 56 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /src/main/java/io/github/iamazy/elasticsearch/dsl/cons/CoreConstants.java: -------------------------------------------------------------------------------- 1 | package io.github.iamazy.elasticsearch.dsl.cons; 2 | 3 | import com.fasterxml.jackson.databind.ObjectMapper; 4 | import org.bson.codecs.ObjectIdGenerator; 5 | 6 | /** 7 | * @author iamazy 8 | * @date 2019/3/25 9 | **/ 10 | public interface CoreConstants { 11 | 12 | ObjectMapper OBJECT_MAPPER=new ObjectMapper(); 13 | ObjectIdGenerator OBJECT_ID_GENERATOR=new ObjectIdGenerator(); 14 | 15 | String COMMA = ","; 16 | String COLON = ":"; 17 | String DOLLAR = "$"; 18 | String DOT="."; 19 | String GRAVE_ACCENT="`"; 20 | String UP_ARROW="^"; 21 | String POUND="#"; 22 | String HIGHLIGHTER="h#"; 23 | String DEFAULT_ES_DATE_FORMAT = "yyyy-MM-dd'T'HH:mm:ss.SSSZ"; 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/io/github/iamazy/elasticsearch/dsl/cons/ElasticConstants.java: -------------------------------------------------------------------------------- 1 | package io.github.iamazy.elasticsearch.dsl.cons; 2 | 3 | /** 4 | * @author iamazy 5 | */ 6 | public interface ElasticConstants { 7 | 8 | 9 | /** 10 | * Full Text Query Args 11 | */ 12 | String BOOLEAN="boolean"; 13 | String PHRASE="phrase"; 14 | String PHRASE_PREFIX="phrase_prefix"; 15 | String NONE="none"; 16 | String ALL="all"; 17 | String AND="and"; 18 | String NULL="null"; 19 | String OR="or"; 20 | String OPERATOR="operator"; 21 | String MINIMUM_SHOULD_MATCH="minimum_should_match"; 22 | String ANALYZER="analyzer"; 23 | String BOOST="boost"; 24 | String PREFIX_LENGTH="prefix_length"; 25 | String MAX_EXPANSIONS="max_expansions"; 26 | String FUZZY_TRANSPOSITIONS="fuzzy_transpositions"; 27 | String LENIENT="lenient"; 28 | String ZERO_TERMS_QUERY="zero_terms_query"; 29 | String FUZZINESS="fuzziness"; 30 | String SLOP="slop"; 31 | String TYPE="type"; 32 | String FUZZY_REWRITE="fuzzy_rewrite"; 33 | String CUTOFF_FREQUENCY="cutoff_Frequency"; 34 | String USE_DIS_MAX="use_dis_max"; 35 | String TIE_BREAKER="tie_breaker"; 36 | String QUOTE_ANALYZER="quote_analyzer"; 37 | String AUTO_GENERATE_SYNONYMS_PHRASE_QUERIES="auto_generate_synonyms_phrase_queries"; 38 | String MAX_DETERMINIZED_STATES="max_determinized_states"; 39 | String ALLOW_LEADING_WILDCARD="allow_leading_wildcard"; 40 | String ENABLE_POSITION_INCREMENTS="enable_position_increments"; 41 | String FUZZY_PREFIX_LENGTH="fuzzy_prefix_length"; 42 | String FUZZY_MAX_EXPANSIONS="fuzzy_max_expansions"; 43 | String REWRITE="rewrite"; 44 | String PHRASE_SLOP="phrase_slop"; 45 | String ANALYZE_WILDCARD="analyze_wildcard"; 46 | String QUOTE_FIELD_SUFFIX="quote_field_suffix"; 47 | String TIME_ZONE="time_zone"; 48 | String ESCAPE="escape"; 49 | String DEFAULT_OPERATOR="default_operator"; 50 | String FLAGS="flags"; 51 | String FLAGS_VALUE="flags_value"; 52 | String FIELDS="fields"; 53 | String DEFAULT_FIELD="default_field"; 54 | String TRANSPOSITIONS="transpositions"; 55 | 56 | String SCORE_MODE="score_mode"; 57 | String MAX_BOOST="max_boost"; 58 | String MIN_SCORE="min_score"; 59 | String BOOST_MODE="boost_mode"; 60 | 61 | String FILED="field"; 62 | String FACTOR="factor"; 63 | String MODIFIER="modifier"; 64 | String MISSING="missing"; 65 | } -------------------------------------------------------------------------------- /src/main/java/io/github/iamazy/elasticsearch/dsl/elastic/HighlightBuilders.java: -------------------------------------------------------------------------------- 1 | package io.github.iamazy.elasticsearch.dsl.elastic; 2 | 3 | import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder; 4 | import java.util.Set; 5 | 6 | public class HighlightBuilders { 7 | 8 | public static HighlightBuilder highlighter(Set highlighter) { 9 | HighlightBuilder highlightBuilder = new HighlightBuilder().requireFieldMatch(false); 10 | for (String field : highlighter) { 11 | highlightBuilder.field(field, 500, 0); 12 | } 13 | highlightBuilder.preTags(""); 14 | highlightBuilder.postTags(""); 15 | return highlightBuilder; 16 | } 17 | 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/io/github/iamazy/elasticsearch/dsl/plugin/RestSqlAction.java: -------------------------------------------------------------------------------- 1 | package io.github.iamazy.elasticsearch.dsl.plugin; 2 | 3 | import com.carrotsearch.hppc.cursors.ObjectObjectCursor; 4 | import com.google.common.util.concurrent.ThreadFactoryBuilder; 5 | import io.github.iamazy.elasticsearch.dsl.sql.exception.ElasticSql2DslException; 6 | import io.github.iamazy.elasticsearch.dsl.sql.model.ElasticSqlParseResult; 7 | import io.github.iamazy.elasticsearch.dsl.sql.parser.ElasticSql2DslParser; 8 | import org.apache.commons.lang3.StringUtils; 9 | import org.elasticsearch.client.node.NodeClient; 10 | import org.elasticsearch.cluster.metadata.MappingMetaData; 11 | import org.elasticsearch.common.collect.ImmutableOpenMap; 12 | import org.elasticsearch.common.settings.Settings; 13 | import org.elasticsearch.common.xcontent.*; 14 | import org.elasticsearch.rest.*; 15 | 16 | import java.io.IOException; 17 | import java.util.concurrent.*; 18 | 19 | 20 | /** 21 | * @author iamazy 22 | * @date 2019/4/23 23 | * @descrition 24 | **/ 25 | public class RestSqlAction extends BaseRestHandler { 26 | 27 | RestSqlAction(Settings settings, RestController restController) { 28 | super(settings); 29 | restController.registerHandler(RestRequest.Method.POST, "/_isql/_explain", this); 30 | restController.registerHandler(RestRequest.Method.GET, "/_isql/_explain", this); 31 | restController.registerHandler(RestRequest.Method.POST, "/_isql", this); 32 | restController.registerHandler(RestRequest.Method.GET, "/_isql", this); 33 | } 34 | 35 | @Override 36 | public String getName() { 37 | return "isql"; 38 | } 39 | 40 | @Override 41 | protected RestChannelConsumer prepareRequest(RestRequest restRequest, NodeClient nodeClient) { 42 | try (XContentParser parser = restRequest.contentOrSourceParamParser()) { 43 | parser.mapStrings().forEach((k, v) -> restRequest.params().putIfAbsent(k, v)); 44 | } catch (IOException e) { 45 | return channel -> channel.sendResponse(new BytesRestResponse(RestStatus.BAD_REQUEST, XContentType.JSON.mediaType(), "please use json format params, like: {\"sql\":\"select * from test\"}")); 46 | } 47 | try { 48 | String sql = restRequest.param("sql"); 49 | if (StringUtils.isBlank(sql)) { 50 | return channel -> channel.sendResponse(new BytesRestResponse(RestStatus.BAD_REQUEST, XContentType.JSON.mediaType(), "{\"error\":\"sql语句不能为空!!!\"}")); 51 | } 52 | ElasticSql2DslParser sql2DslParser = new ElasticSql2DslParser(); 53 | ElasticSqlParseResult parseResult = sql2DslParser.parse(sql); 54 | XContentBuilder builder = XContentFactory.jsonBuilder().prettyPrint(); 55 | if (restRequest.path().endsWith("/_explain")) { 56 | return channel -> channel.sendResponse(new BytesRestResponse(RestStatus.OK, builder.value(parseResult.toRequest().source()))); 57 | } 58 | else { 59 | if (parseResult.toFieldMapping() != null) { 60 | return channel -> channel.sendResponse(new BytesRestResponse(RestStatus.OK, builder.value(nodeClient.admin().indices().getFieldMappings(parseResult.toFieldMapping()).actionGet()))); 61 | } else if (parseResult.toMapping()!=null) { 62 | ImmutableOpenMap objectObjectCursors = nodeClient.admin().indices().getMappings(parseResult.toMapping()).actionGet().mappings().get(parseResult.getIndices().get(0)); 63 | for (ObjectObjectCursor objectObjectCursor : objectObjectCursors) { 64 | return channel -> channel.sendResponse(new BytesRestResponse(RestStatus.OK, builder.value(objectObjectCursor.value.getSourceAsMap()))); 65 | } 66 | throw new ElasticSql2DslException("sql语句解析失败!!!"); 67 | } else { 68 | return channel -> channel.sendResponse(new BytesRestResponse(RestStatus.OK, builder.value(nodeClient.search(parseResult.toRequest()).actionGet()))); 69 | } 70 | } 71 | } catch (Exception e) { 72 | return channel -> channel.sendResponse(new BytesRestResponse(RestStatus.INTERNAL_SERVER_ERROR, XContentType.JSON.mediaType(), "{\"error\":\"" + e.getMessage() + "\"}")); 73 | } 74 | } 75 | 76 | 77 | } 78 | -------------------------------------------------------------------------------- /src/main/java/io/github/iamazy/elasticsearch/dsl/plugin/SqlPlugin.java: -------------------------------------------------------------------------------- 1 | package io.github.iamazy.elasticsearch.dsl.plugin; 2 | 3 | import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver; 4 | import org.elasticsearch.cluster.node.DiscoveryNodes; 5 | import org.elasticsearch.common.settings.ClusterSettings; 6 | import org.elasticsearch.common.settings.IndexScopedSettings; 7 | import org.elasticsearch.common.settings.Settings; 8 | import org.elasticsearch.common.settings.SettingsFilter; 9 | import org.elasticsearch.plugins.ActionPlugin; 10 | import org.elasticsearch.plugins.Plugin; 11 | import org.elasticsearch.rest.RestController; 12 | import org.elasticsearch.rest.RestHandler; 13 | 14 | import java.util.Collections; 15 | import java.util.List; 16 | import java.util.function.Supplier; 17 | 18 | /** 19 | * @author iamazy 20 | * @date 2019/4/23 21 | * @descrition 22 | **/ 23 | public class SqlPlugin extends Plugin implements ActionPlugin { 24 | 25 | public SqlPlugin(){} 26 | 27 | public String name(){ 28 | return "elasticsearch-sql"; 29 | } 30 | 31 | public String description(){ 32 | return "Use sql to query elasticsearch."; 33 | } 34 | 35 | 36 | @Override 37 | public List getRestHandlers(Settings settings, RestController restController, ClusterSettings clusterSettings, IndexScopedSettings indexScopedSettings, SettingsFilter settingsFilter, IndexNameExpressionResolver indexNameExpressionResolver, Supplier nodesInCluster) { 38 | return Collections.singletonList(new RestSqlAction(settings,restController)); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/io/github/iamazy/elasticsearch/dsl/sql/druid/ElasticSqlExprParser.java: -------------------------------------------------------------------------------- 1 | package io.github.iamazy.elasticsearch.dsl.sql.druid; 2 | 3 | import com.alibaba.druid.sql.ast.SQLExpr; 4 | import com.alibaba.druid.sql.ast.expr.*; 5 | import com.alibaba.druid.sql.parser.*; 6 | import com.google.common.collect.Lists; 7 | import java.util.List; 8 | 9 | /** 10 | * @author iamazy 11 | * @date 2019/2/19 12 | **/ 13 | public class ElasticSqlExprParser extends SQLExprParser { 14 | 15 | public ElasticSqlExprParser(Lexer lexer) { 16 | super(lexer); 17 | } 18 | 19 | public ElasticSqlExprParser(String sql) { 20 | this(new ElasticSqlLexer(sql)); 21 | this.lexer.nextToken(); 22 | } 23 | 24 | @Override 25 | public SQLExpr expr() { 26 | if (this.lexer.token() == Token.STAR) { 27 | this.lexer.nextToken(); 28 | SQLExpr expr = new SQLAllColumnExpr(); 29 | if (this.lexer.token() == Token.DOT) { 30 | this.lexer.nextToken(); 31 | this.accept(Token.STAR); 32 | return new SQLPropertyExpr(expr, "*"); 33 | } else { 34 | return expr; 35 | } 36 | } 37 | else if(this.lexer.token()==Token.CARET){ 38 | this.lexer.nextToken(); 39 | return new SQLIdentifierExpr("^"+expr()); 40 | } 41 | else { 42 | SQLExpr expr = this.primary(); 43 | Token token = this.lexer.token(); 44 | if (token == Token.COMMA) { 45 | return expr; 46 | } else if (token == Token.EQ) { 47 | expr = this.relationalRest(expr); 48 | expr = this.andRest(expr); 49 | expr = this.xorRest(expr); 50 | expr = this.orRest(expr); 51 | return expr; 52 | } else { 53 | return this.exprRest(expr); 54 | } 55 | } 56 | } 57 | 58 | @Override 59 | public SQLSelectParser createSelectParser() { 60 | return new ElasticSqlSelectParser(this); 61 | } 62 | 63 | ElasticSqlSelectQueryBlock.Scroll parseScroll() { 64 | if (lexer.token() == Token.CURSOR && "scroll".equalsIgnoreCase(lexer.stringVal())) { 65 | lexer.nextToken(); 66 | ElasticSqlSelectQueryBlock.Scroll scroll = new ElasticSqlSelectQueryBlock.Scroll(); 67 | accept(Token.BY); 68 | scroll.setExpire(this.expr()); 69 | if (this.lexer.token() == Token.COMMA) { 70 | this.lexer.nextToken(); 71 | scroll.setScrollId(this.expr()); 72 | } 73 | return scroll; 74 | } 75 | return null; 76 | } 77 | 78 | ElasticSqlSelectQueryBlock.Limit parseLimit0() { 79 | if (lexer.token() == Token.LIMIT) { 80 | lexer.nextToken(); 81 | ElasticSqlSelectQueryBlock.Limit limit = new ElasticSqlSelectQueryBlock.Limit(); 82 | SQLExpr temp = this.expr(); 83 | if (lexer.token() == Token.COMMA) { 84 | limit.setOffset(temp); 85 | lexer.nextToken(); 86 | limit.setRowCount(this.expr()); 87 | } else if (identifierEquals("OFFSET")) { 88 | limit.setRowCount(temp); 89 | lexer.nextToken(); 90 | limit.setOffset(this.expr()); 91 | } else { 92 | limit.setRowCount(temp); 93 | } 94 | return limit; 95 | } 96 | return null; 97 | } 98 | 99 | ElasticSqlSelectQueryBlock.Routing parseRoutingBy() { 100 | if (lexer.token() == Token.INDEX && "routing".equalsIgnoreCase(lexer.stringVal())) { 101 | lexer.nextToken(); 102 | accept(Token.BY); 103 | List routingValues = Lists.newLinkedList(); 104 | routingValues.add(this.expr()); 105 | while (lexer.token() == Token.COMMA) { 106 | lexer.nextToken(); 107 | routingValues.add(this.expr()); 108 | } 109 | return new ElasticSqlSelectQueryBlock.Routing(routingValues); 110 | } 111 | return null; 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /src/main/java/io/github/iamazy/elasticsearch/dsl/sql/druid/ElasticSqlLexer.java: -------------------------------------------------------------------------------- 1 | package io.github.iamazy.elasticsearch.dsl.sql.druid; 2 | 3 | import com.alibaba.druid.sql.parser.Keywords; 4 | import com.alibaba.druid.sql.parser.Lexer; 5 | import com.alibaba.druid.sql.parser.Token; 6 | import com.google.common.collect.Maps; 7 | 8 | import java.util.Map; 9 | 10 | /** 11 | * @author iamazy 12 | * @date 2019/2/19 13 | **/ 14 | class ElasticSqlLexer extends Lexer { 15 | private final static Keywords DEFAULT_ELASTIC_SQL_KEYWORDS; 16 | 17 | static { 18 | Map map= Maps.newHashMap(); 19 | map.put("SELECT", Token.SELECT); 20 | map.put("DISTINCT", Token.DISTINCT); 21 | map.put("FROM", Token.FROM); 22 | map.put("WHERE", Token.WHERE); 23 | 24 | map.put("GROUP", Token.GROUP); 25 | map.put("HAVING", Token.HAVING); 26 | 27 | map.put("AND", Token.AND); 28 | map.put("AS", Token.AS); 29 | 30 | map.put("ORDER", Token.ORDER); 31 | map.put("BY", Token.BY); 32 | 33 | map.put("ASC", Token.ASC); 34 | map.put("DESC", Token.DESC); 35 | 36 | map.put("LIMIT", Token.LIMIT); 37 | map.put("IS", Token.IS); 38 | map.put("BETWEEN", Token.BETWEEN); 39 | 40 | map.put("IN", Token.IN); 41 | map.put("EXISTS", Token.EXISTS); 42 | 43 | map.put("LIKE", Token.LIKE); 44 | map.put("NOT", Token.NOT); 45 | 46 | map.put("NULL", Token.NULL); 47 | map.put("OR", Token.OR); 48 | 49 | map.put("XOR", Token.XOR); 50 | map.put("COMMENT", Token.COMMENT); 51 | 52 | map.put("QUERY", Token.INDEX); 53 | map.put("ROUTING", Token.INDEX); 54 | map.put("NESTED", Token.INDEX); 55 | map.put("INNER", Token.INNER); 56 | map.put("JOIN", Token.JOIN); 57 | 58 | map.put("SCROLL",Token.CURSOR); 59 | 60 | 61 | 62 | //delete 63 | map.put("TOP",Token.TOP); 64 | map.put("DELETE",Token.DELETE); 65 | map.put("UPDATE",Token.UPDATE); 66 | map.put("SET",Token.SET); 67 | 68 | DEFAULT_ELASTIC_SQL_KEYWORDS=new Keywords(map); 69 | } 70 | 71 | private ElasticSqlLexer(String input,boolean skipComment){ 72 | super(input,skipComment); 73 | super.keywods=DEFAULT_ELASTIC_SQL_KEYWORDS; 74 | } 75 | 76 | ElasticSqlLexer(String input){ 77 | this(input,false); 78 | } 79 | } 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | -------------------------------------------------------------------------------- /src/main/java/io/github/iamazy/elasticsearch/dsl/sql/druid/ElasticSqlSelectQueryBlock.java: -------------------------------------------------------------------------------- 1 | package io.github.iamazy.elasticsearch.dsl.sql.druid; 2 | 3 | import com.alibaba.druid.sql.ast.SQLExpr; 4 | import com.alibaba.druid.sql.ast.SQLObject; 5 | import com.alibaba.druid.sql.ast.SQLObjectImpl; 6 | import com.alibaba.druid.sql.ast.statement.SQLSelectQueryBlock; 7 | import com.alibaba.druid.sql.visitor.SQLASTVisitor; 8 | import org.apache.commons.collections4.CollectionUtils; 9 | 10 | import java.util.List; 11 | 12 | /** 13 | * @author iamazy 14 | * @date 2019/2/19 15 | **/ 16 | 17 | public class ElasticSqlSelectQueryBlock extends SQLSelectQueryBlock implements SQLObject { 18 | 19 | private Scroll scroll; 20 | private Limit limit; 21 | private Routing routing; 22 | private SQLExpr matchQuery; 23 | 24 | public Scroll getScroll() { 25 | return scroll; 26 | } 27 | 28 | public void setScroll(Scroll scroll) { 29 | this.scroll = scroll; 30 | } 31 | 32 | public Limit getLimit0() { 33 | return limit; 34 | } 35 | 36 | public SQLExpr getMatchQuery() { 37 | return matchQuery; 38 | } 39 | 40 | public void setMatchQuery(SQLExpr matchQuery) { 41 | this.matchQuery = matchQuery; 42 | } 43 | 44 | public void setLimit(Limit limit) { 45 | this.limit = limit; 46 | } 47 | 48 | public Routing getRouting() { 49 | return routing; 50 | } 51 | 52 | public void setRouting(Routing routing) { 53 | this.routing = routing; 54 | } 55 | 56 | public static class Routing extends SQLObjectImpl{ 57 | private List routingValues; 58 | public Routing(List routingValues){ 59 | this.routingValues=routingValues; 60 | if(CollectionUtils.isNotEmpty(routingValues)){ 61 | for(SQLExpr sqlExpr:routingValues){ 62 | sqlExpr.setParent(Routing.this); 63 | } 64 | } 65 | } 66 | 67 | @Override 68 | protected void accept0(SQLASTVisitor sqlastVisitor) { 69 | throw new UnsupportedOperationException("accept0(SQLASTVisitor visitor)"); 70 | } 71 | 72 | public List getRoutingValues(){ 73 | return routingValues; 74 | } 75 | } 76 | 77 | 78 | public static class Scroll extends SQLObjectImpl{ 79 | 80 | private SQLExpr expire; 81 | private SQLExpr scrollId; 82 | 83 | public SQLExpr getExpire() { 84 | return expire; 85 | } 86 | 87 | public SQLExpr getScrollId() { 88 | return scrollId; 89 | } 90 | 91 | public void setExpire(SQLExpr expire) { 92 | if(expire!=null){ 93 | expire.setParent(this); 94 | } 95 | this.expire = expire; 96 | } 97 | 98 | public void setScrollId(SQLExpr scrollId) { 99 | if(scrollId!=null){ 100 | scrollId.setParent(this); 101 | } 102 | this.scrollId = scrollId; 103 | } 104 | 105 | @Override 106 | protected void accept0(SQLASTVisitor sqlastVisitor) { 107 | throw new UnsupportedOperationException("accept0(SQLASTVisitor visitor)"); 108 | } 109 | } 110 | 111 | public static class Limit extends SQLObjectImpl{ 112 | private SQLExpr rowCount; 113 | private SQLExpr offset; 114 | 115 | public SQLExpr getRowCount() { 116 | return rowCount; 117 | } 118 | 119 | public void setRowCount(SQLExpr rowCount) { 120 | if(rowCount!=null){ 121 | rowCount.setParent(this); 122 | } 123 | this.rowCount = rowCount; 124 | } 125 | 126 | public SQLExpr getOffset(){ 127 | return offset; 128 | } 129 | 130 | public void setOffset(SQLExpr offset) { 131 | if(offset!=null){ 132 | offset.setParent(this); 133 | } 134 | this.offset = offset; 135 | } 136 | 137 | @Override 138 | protected void accept0(SQLASTVisitor sqlastVisitor) { 139 | throw new UnsupportedOperationException("accept0(SQLASTVisitor visitor)"); 140 | } 141 | } 142 | } 143 | -------------------------------------------------------------------------------- /src/main/java/io/github/iamazy/elasticsearch/dsl/sql/enums/QueryFieldType.java: -------------------------------------------------------------------------------- 1 | package io.github.iamazy.elasticsearch.dsl.sql.enums; 2 | 3 | /** 4 | * @author iamazy 5 | * @date 2019/2/19 6 | **/ 7 | public enum QueryFieldType { 8 | MatchAllField, 9 | SqlSelectField, 10 | RootDocField, 11 | InnerDocField, 12 | NestedDocField 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/io/github/iamazy/elasticsearch/dsl/sql/enums/SqlBoolOperator.java: -------------------------------------------------------------------------------- 1 | package io.github.iamazy.elasticsearch.dsl.sql.enums; 2 | 3 | /** 4 | * @author iamazy 5 | * @date 2019/2/19 6 | * @descrition 7 | **/ 8 | public enum SqlBoolOperator { 9 | AND,OR 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/io/github/iamazy/elasticsearch/dsl/sql/enums/SqlConditionOperator.java: -------------------------------------------------------------------------------- 1 | package io.github.iamazy.elasticsearch.dsl.sql.enums; 2 | 3 | /** 4 | * @author iamazy 5 | * @date 2019/2/19 6 | * @descrition 7 | **/ 8 | public enum SqlConditionOperator { 9 | Equality, 10 | NotEqual, 11 | GreaterThan, 12 | GreaterThanOrEqual, 13 | LessThan, 14 | LessThanOrEqual, 15 | IsNull, 16 | IsNotNull, 17 | In, 18 | NotIn, 19 | BetweenAnd, 20 | Like, 21 | NotLike 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/io/github/iamazy/elasticsearch/dsl/sql/enums/SqlConditionType.java: -------------------------------------------------------------------------------- 1 | package io.github.iamazy.elasticsearch.dsl.sql.enums; 2 | 3 | /** 4 | * @author iamazy 5 | * @date 2019/2/19 6 | * @descrition 7 | **/ 8 | public enum SqlConditionType { 9 | Atom,Combine 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/io/github/iamazy/elasticsearch/dsl/sql/exception/ElasticSql2DslException.java: -------------------------------------------------------------------------------- 1 | package io.github.iamazy.elasticsearch.dsl.sql.exception; 2 | 3 | import com.alibaba.druid.sql.parser.ParserException; 4 | 5 | /** 6 | * @author iamazy 7 | * @date 2019/2/19 8 | * @descrition 9 | **/ 10 | public class ElasticSql2DslException extends RuntimeException { 11 | public ElasticSql2DslException(String message) { 12 | super(message); 13 | } 14 | 15 | public ElasticSql2DslException(ParserException ex) { 16 | super(ex); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/io/github/iamazy/elasticsearch/dsl/sql/helper/ElasticSqlArgConverter.java: -------------------------------------------------------------------------------- 1 | package io.github.iamazy.elasticsearch.dsl.sql.helper; 2 | 3 | import com.alibaba.druid.sql.ast.SQLExpr; 4 | import com.alibaba.druid.sql.ast.expr.*; 5 | import io.github.iamazy.elasticsearch.dsl.sql.exception.ElasticSql2DslException; 6 | 7 | import java.util.List; 8 | 9 | /** 10 | * @author iamazy 11 | * @date 2019/2/19 12 | * @descrition 13 | **/ 14 | public class ElasticSqlArgConverter { 15 | private ElasticSqlArgConverter(){} 16 | 17 | public static Object[] convertSqlArgs(List exprList) { 18 | Object[] values = new Object[exprList.size()]; 19 | for (int idx = 0; idx < exprList.size(); idx++) { 20 | values[idx] = convertSqlArg(exprList.get(idx), true); 21 | } 22 | return values; 23 | } 24 | 25 | public static Object convertSqlArg(SQLExpr expr) { 26 | return convertSqlArg(expr, true); 27 | } 28 | 29 | public static Object convertSqlArg(SQLExpr expr, boolean recognizeDateArg) { 30 | if (expr instanceof SQLIntegerExpr) { 31 | return ((SQLIntegerExpr) expr).getNumber().longValue(); 32 | } 33 | if (expr instanceof SQLNumberExpr) { 34 | return ((SQLNumberExpr) expr).getNumber().doubleValue(); 35 | } 36 | if (expr instanceof SQLCharExpr) { 37 | Object textObject = ((SQLCharExpr) expr).getValue(); 38 | 39 | if (recognizeDateArg && (textObject instanceof String) && ElasticSqlDateParseHelper.isDateArgStringValue((String) textObject)) { 40 | return ElasticSqlDateParseHelper.formatDefaultEsDateStringValue((String) textObject); 41 | } 42 | return textObject; 43 | } 44 | if (expr instanceof SQLMethodInvokeExpr) { 45 | SQLMethodInvokeExpr methodExpr = (SQLMethodInvokeExpr) expr; 46 | if (ElasticSqlDateParseHelper.isDateMethod(methodExpr)) { 47 | ElasticSqlMethodInvokeHelper.checkDateMethod(methodExpr); 48 | String patternArg = (String) ElasticSqlArgConverter.convertSqlArg(methodExpr.getParameters().get(0), false); 49 | String timeValArg = (String) ElasticSqlArgConverter.convertSqlArg(methodExpr.getParameters().get(1), false); 50 | return ElasticSqlDateParseHelper.formatDefaultEsDate(patternArg, timeValArg); 51 | } 52 | return methodExpr; 53 | } 54 | if(expr instanceof SQLBinaryOpExpr){ 55 | return expr; 56 | } 57 | throw new ElasticSql2DslException( 58 | String.format("[syntax error] Arg type[%s] can not support.", 59 | expr.toString())); 60 | } 61 | } -------------------------------------------------------------------------------- /src/main/java/io/github/iamazy/elasticsearch/dsl/sql/helper/ElasticSqlDateParseHelper.java: -------------------------------------------------------------------------------- 1 | package io.github.iamazy.elasticsearch.dsl.sql.helper; 2 | 3 | import com.alibaba.druid.sql.ast.expr.SQLMethodInvokeExpr; 4 | import io.github.iamazy.elasticsearch.dsl.cons.CoreConstants; 5 | import io.github.iamazy.elasticsearch.dsl.sql.exception.ElasticSql2DslException; 6 | 7 | import java.text.ParseException; 8 | import java.text.SimpleDateFormat; 9 | import java.util.Date; 10 | import java.util.regex.Pattern; 11 | 12 | /** 13 | * @author iamazy 14 | * @date 2019/2/19 15 | * @descrition 16 | **/ 17 | class ElasticSqlDateParseHelper { 18 | private static final Pattern SQL_DATE_REGEX_PATTERN_01 = Pattern.compile("\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2}"); 19 | private static final Pattern SQL_DATE_REGEX_PATTERN_02 = Pattern.compile("\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}"); 20 | private static final Pattern SQL_DATE_REGEX_PATTERN_03 = Pattern.compile("\\d{4}-\\d{2}-\\d{2}"); 21 | 22 | static boolean isDateMethod(SQLMethodInvokeExpr dateMethodExpr) { 23 | return ElasticSqlMethodInvokeHelper.isMethodOf(ElasticSqlMethodInvokeHelper.DATE_METHOD, dateMethodExpr.getMethodName()); 24 | } 25 | 26 | static boolean isDateArgStringValue(String date) { 27 | return SqlDateRegex.DATE_REGEX_01.getPattern().matcher(date).matches() 28 | || SqlDateRegex.DATE_REGEX_02.getPattern().matcher(date).matches() 29 | || SqlDateRegex.DATE_REGEX_03.getPattern().matcher(date).matches(); 30 | } 31 | 32 | static boolean isDateArgObjectValue(Object date) { 33 | return date instanceof Date; 34 | } 35 | 36 | static String formatDefaultEsDateStringValue(String date) { 37 | if (SqlDateRegex.DATE_REGEX_01.getPattern().matcher(date).matches()) { 38 | return formatDefaultEsDate(SqlDateRegex.DATE_REGEX_01.getPatternString(), date); 39 | } 40 | if (SqlDateRegex.DATE_REGEX_02.getPattern().matcher(date).matches()) { 41 | return formatDefaultEsDate(SqlDateRegex.DATE_REGEX_02.getPatternString(), date); 42 | } 43 | if (SqlDateRegex.DATE_REGEX_03.getPattern().matcher(date).matches()) { 44 | return formatDefaultEsDate(SqlDateRegex.DATE_REGEX_03.getPatternString(), date); 45 | } 46 | throw new ElasticSql2DslException( 47 | String.format("[syntax error] Can't support such date type: %s", date)); 48 | } 49 | 50 | static String formatDefaultEsDateObjectValue(Object date) { 51 | if (date instanceof Date) { 52 | SimpleDateFormat dateFormat = new SimpleDateFormat(CoreConstants.DEFAULT_ES_DATE_FORMAT); 53 | return dateFormat.format(date); 54 | } 55 | throw new ElasticSql2DslException( 56 | String.format("[syntax error] Sql cannot support such date type: %s", date.getClass())); 57 | } 58 | 59 | static String formatDefaultEsDate(String patternArg, String timeValArg) { 60 | try { 61 | SimpleDateFormat dateFormat = new SimpleDateFormat(patternArg); 62 | Date date = dateFormat.parse(timeValArg); 63 | 64 | dateFormat = new SimpleDateFormat(CoreConstants.DEFAULT_ES_DATE_FORMAT); 65 | return dateFormat.format(date); 66 | } 67 | catch (ParseException pex) { 68 | throw new ElasticSql2DslException("[syntax error] Parse time arg error: " + timeValArg); 69 | } 70 | } 71 | 72 | private enum SqlDateRegex { 73 | DATE_REGEX_01 { 74 | @Override 75 | Pattern getPattern() { 76 | return SQL_DATE_REGEX_PATTERN_01; 77 | } 78 | 79 | @Override 80 | String getPatternString() { 81 | return "yyyy-MM-dd HH:mm:ss"; 82 | } 83 | }, 84 | DATE_REGEX_02 { 85 | @Override 86 | Pattern getPattern() { 87 | return SQL_DATE_REGEX_PATTERN_02; 88 | } 89 | 90 | @Override 91 | String getPatternString() { 92 | return "yyyy-MM-dd HH:mm"; 93 | } 94 | }, 95 | DATE_REGEX_03 { 96 | @Override 97 | Pattern getPattern() { 98 | return SQL_DATE_REGEX_PATTERN_03; 99 | } 100 | 101 | @Override 102 | String getPatternString() { 103 | return "yyyy-MM-dd"; 104 | } 105 | }; 106 | 107 | abstract Pattern getPattern(); 108 | 109 | abstract String getPatternString(); 110 | } 111 | } 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | -------------------------------------------------------------------------------- /src/main/java/io/github/iamazy/elasticsearch/dsl/sql/helper/ElasticSqlMethodInvokeHelper.java: -------------------------------------------------------------------------------- 1 | package io.github.iamazy.elasticsearch.dsl.sql.helper; 2 | 3 | import com.alibaba.druid.sql.ast.SQLExpr; 4 | import com.alibaba.druid.sql.ast.expr.SQLAggregateExpr; 5 | import com.alibaba.druid.sql.ast.expr.SQLCharExpr; 6 | import com.alibaba.druid.sql.ast.expr.SQLMethodInvokeExpr; 7 | import com.alibaba.druid.sql.ast.expr.SQLVariantRefExpr; 8 | import com.google.common.collect.ImmutableList; 9 | import io.github.iamazy.elasticsearch.dsl.sql.exception.ElasticSql2DslException; 10 | import org.apache.commons.collections4.CollectionUtils; 11 | import org.apache.commons.lang3.StringUtils; 12 | 13 | import java.util.List; 14 | 15 | /** 16 | * @author iamazy 17 | * @date 2019/2/19 18 | * @descrition 19 | **/ 20 | public class ElasticSqlMethodInvokeHelper { 21 | static final List DATE_METHOD = ImmutableList.of("date", "to_date", "toDate"); 22 | 23 | 24 | private static final List AGG_RANGE_METHOD = ImmutableList.of("range", "range_agg"); 25 | private static final List AGG_DATE_HISTOGRAM_METHOD = ImmutableList.of("histogram", "histogram_agg"); 26 | private static final List AGG_RANGE_SEGMENT_METHOD = ImmutableList.of("segment", "segment_agg"); 27 | 28 | public static final String AGG_MIN_METHOD = "min"; 29 | public static final String AGG_MAX_METHOD = "max"; 30 | public static final String AGG_AVG_METHOD = "avg"; 31 | public static final String AGG_SUM_METHOD = "sum"; 32 | 33 | public static Boolean isMethodOf(List methodAlias, String method) { 34 | if (CollectionUtils.isEmpty(methodAlias)) { 35 | return Boolean.FALSE; 36 | } 37 | for (String alias : methodAlias) { 38 | if (alias.equalsIgnoreCase(method)) { 39 | return Boolean.TRUE; 40 | } 41 | } 42 | return Boolean.FALSE; 43 | } 44 | 45 | public static Boolean isMethodOf(String methodAlias, String method) { 46 | if (StringUtils.isBlank(methodAlias)) { 47 | return Boolean.FALSE; 48 | } 49 | return methodAlias.equalsIgnoreCase(method); 50 | } 51 | 52 | public static void checkDateHistogramAggMethod(SQLMethodInvokeExpr aggInvokeExpr) { 53 | if (!isMethodOf(AGG_DATE_HISTOGRAM_METHOD, aggInvokeExpr.getMethodName())) { 54 | throw new ElasticSql2DslException("[syntax error] Sql not support method:" + aggInvokeExpr.getMethodName()); 55 | } 56 | } 57 | 58 | public static void checkRangeAggMethod(SQLMethodInvokeExpr aggInvokeExpr) { 59 | if (!isMethodOf(AGG_RANGE_METHOD, aggInvokeExpr.getMethodName())) { 60 | throw new ElasticSql2DslException("[syntax error] Sql not support method:" + aggInvokeExpr.getMethodName()); 61 | } 62 | } 63 | 64 | public static void checkRangeItemAggMethod(SQLMethodInvokeExpr aggInvokeExpr) { 65 | if (!isMethodOf(AGG_RANGE_SEGMENT_METHOD, aggInvokeExpr.getMethodName())) { 66 | throw new ElasticSql2DslException("[syntax error] Sql not support method:" + aggInvokeExpr.getMethodName()); 67 | } 68 | } 69 | 70 | public static void checkStatAggMethod(SQLAggregateExpr statAggExpr) { 71 | if (!AGG_MIN_METHOD.equalsIgnoreCase(statAggExpr.getMethodName()) && 72 | !AGG_MAX_METHOD.equalsIgnoreCase(statAggExpr.getMethodName()) && 73 | !AGG_AVG_METHOD.equalsIgnoreCase(statAggExpr.getMethodName()) && 74 | !AGG_SUM_METHOD.equalsIgnoreCase(statAggExpr.getMethodName())) { 75 | throw new ElasticSql2DslException("[syntax error] Sql not support method:" + statAggExpr.getMethodName()); 76 | } 77 | } 78 | 79 | public static void checkDateMethod(SQLMethodInvokeExpr dateInvokeExpr) { 80 | if (!isMethodOf(DATE_METHOD, dateInvokeExpr.getMethodName())) { 81 | throw new ElasticSql2DslException("[syntax error] Sql not support method:" + dateInvokeExpr.getMethodName()); 82 | } 83 | 84 | if (CollectionUtils.isEmpty(dateInvokeExpr.getParameters()) || dateInvokeExpr.getParameters().size() != 2) { 85 | throw new ElasticSql2DslException(String.format("[syntax error] There is no %s args method named date", 86 | dateInvokeExpr.getParameters() != null ? dateInvokeExpr.getParameters().size() : 0)); 87 | } 88 | 89 | SQLExpr patternArg = dateInvokeExpr.getParameters().get(0); 90 | SQLExpr timeValArg = dateInvokeExpr.getParameters().get(1); 91 | 92 | if (!(patternArg instanceof SQLCharExpr) && !(patternArg instanceof SQLVariantRefExpr)) { 93 | throw new ElasticSql2DslException("[syntax error] The first arg of date method should be a time pattern"); 94 | } 95 | 96 | if (!(timeValArg instanceof SQLCharExpr) && !(timeValArg instanceof SQLVariantRefExpr)) { 97 | throw new ElasticSql2DslException("[syntax error] The second arg of date method should be a string of time"); 98 | } 99 | } 100 | } 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | -------------------------------------------------------------------------------- /src/main/java/io/github/iamazy/elasticsearch/dsl/sql/model/AggregationQuery.java: -------------------------------------------------------------------------------- 1 | package io.github.iamazy.elasticsearch.dsl.sql.model; 2 | 3 | import lombok.Getter; 4 | import org.elasticsearch.search.aggregations.AggregationBuilder; 5 | 6 | /** 7 | * @author iamazy 8 | * @date 2019/3/7 9 | * @descrition 10 | **/ 11 | @Getter 12 | public class AggregationQuery { 13 | 14 | private AggregationBuilder aggregationBuilder; 15 | 16 | public AggregationQuery(AggregationBuilder aggregationBuilder){ 17 | this.aggregationBuilder=aggregationBuilder; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/io/github/iamazy/elasticsearch/dsl/sql/model/AtomicQuery.java: -------------------------------------------------------------------------------- 1 | package io.github.iamazy.elasticsearch.dsl.sql.model; 2 | 3 | 4 | import lombok.Getter; 5 | import lombok.Setter; 6 | import org.elasticsearch.index.query.QueryBuilder; 7 | 8 | import java.util.ArrayList; 9 | import java.util.HashSet; 10 | import java.util.Set; 11 | 12 | /** 13 | * @author iamazy 14 | * @date 2019/2/19 15 | * @descrition 16 | **/ 17 | @Getter 18 | public class AtomicQuery { 19 | 20 | @Setter 21 | private Set highlighter=new HashSet<>(0); 22 | 23 | private QueryBuilder queryBuilder; 24 | private boolean isNestedQuery; 25 | private ArrayList nestedQueryPath; 26 | 27 | public AtomicQuery(QueryBuilder queryBuilder){ 28 | this.queryBuilder=queryBuilder; 29 | this.isNestedQuery=false; 30 | } 31 | 32 | public AtomicQuery(QueryBuilder queryBuilder,ArrayList nestedQueryPath){ 33 | this.queryBuilder=queryBuilder; 34 | this.isNestedQuery=true; 35 | this.nestedQueryPath=nestedQueryPath; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/io/github/iamazy/elasticsearch/dsl/sql/model/ElasticDslContext.java: -------------------------------------------------------------------------------- 1 | package io.github.iamazy.elasticsearch.dsl.sql.model; 2 | 3 | import com.alibaba.druid.sql.ast.SQLObject; 4 | import lombok.Getter; 5 | 6 | /** 7 | * @author iamazy 8 | * @date 2019/2/19 9 | * @descrition 10 | **/ 11 | 12 | @Getter 13 | public class ElasticDslContext { 14 | 15 | private SQLObject sqlObject; 16 | private ElasticSqlParseResult parseResult; 17 | 18 | public ElasticDslContext(SQLObject sqlObject){ 19 | 20 | this.sqlObject =sqlObject; 21 | parseResult=new ElasticSqlParseResult(); 22 | } 23 | 24 | 25 | @Override 26 | public String toString() { 27 | return parseResult.toDsl(parseResult.toRequest()); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/io/github/iamazy/elasticsearch/dsl/sql/model/ElasticSqlQueryField.java: -------------------------------------------------------------------------------- 1 | package io.github.iamazy.elasticsearch.dsl.sql.model; 2 | 3 | import io.github.iamazy.elasticsearch.dsl.sql.enums.QueryFieldType; 4 | import lombok.AllArgsConstructor; 5 | import lombok.Getter; 6 | import lombok.Setter; 7 | 8 | import java.util.ArrayList; 9 | 10 | /** 11 | * @author iamazy 12 | * @date 2019/2/19 13 | * @descrition 14 | **/ 15 | @Getter 16 | @AllArgsConstructor 17 | public class ElasticSqlQueryField { 18 | /** 19 | * 最多支持两层nested类型,再多就要考虑数据结构是否合理了 20 | */ 21 | private ArrayList nestedDocContextPath; 22 | private String simpleQueryFieldName; 23 | @Setter 24 | private String queryFieldFullName; 25 | private QueryFieldType queryFieldType; 26 | 27 | public ElasticSqlQueryField(ArrayList nestedDocContextPath,QueryFieldType queryFieldType){ 28 | this.nestedDocContextPath=nestedDocContextPath; 29 | this.queryFieldType=queryFieldType; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/io/github/iamazy/elasticsearch/dsl/sql/model/ElasticSqlQueryFields.java: -------------------------------------------------------------------------------- 1 | package io.github.iamazy.elasticsearch.dsl.sql.model; 2 | 3 | import io.github.iamazy.elasticsearch.dsl.sql.enums.QueryFieldType; 4 | 5 | import java.util.ArrayList; 6 | 7 | /** 8 | * @author iamazy 9 | * @date 2019/2/19 10 | * @descrition 11 | **/ 12 | public class ElasticSqlQueryFields { 13 | private ElasticSqlQueryFields(){} 14 | 15 | public static ElasticSqlQueryField newSqlSelectField(String fullPathFieldName){ 16 | return new ElasticSqlQueryField(null,fullPathFieldName,fullPathFieldName, QueryFieldType.SqlSelectField); 17 | } 18 | 19 | public static ElasticSqlQueryField newMatchAllRootDocField(){ 20 | return new ElasticSqlQueryField(null,"*","*",QueryFieldType.MatchAllField); 21 | } 22 | 23 | public static ElasticSqlQueryField newRootDocQueryField(String rootDocFieldName){ 24 | return new ElasticSqlQueryField(null,rootDocFieldName,rootDocFieldName,QueryFieldType.RootDocField); 25 | } 26 | 27 | public static ElasticSqlQueryField newInnerDocQueryField(String innerDocFieldPrefix,String innerDocFieldName){ 28 | String innerDocQueryFieldFullName=String.format("%s.%s",innerDocFieldPrefix,innerDocFieldName); 29 | return new ElasticSqlQueryField(null,innerDocFieldName,innerDocQueryFieldFullName,QueryFieldType.InnerDocField); 30 | } 31 | 32 | public static ElasticSqlQueryField newNestedDocQueryField(ArrayList nestedDocContextPath, String simpleQueryFieldName){ 33 | return new ElasticSqlQueryField(nestedDocContextPath,simpleQueryFieldName,simpleQueryFieldName,QueryFieldType.NestedDocField); 34 | } 35 | } 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | -------------------------------------------------------------------------------- /src/main/java/io/github/iamazy/elasticsearch/dsl/sql/model/QueryFieldReferenceNode.java: -------------------------------------------------------------------------------- 1 | package io.github.iamazy.elasticsearch.dsl.sql.model; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Getter; 5 | import lombok.Setter; 6 | 7 | /** 8 | * @author iamazy 9 | * @date 2019/2/19 10 | * @descrition 11 | **/ 12 | 13 | @Getter 14 | @AllArgsConstructor 15 | public class QueryFieldReferenceNode { 16 | @Setter 17 | private boolean isNestedDocReference; 18 | private String referenceNodeName; 19 | 20 | public QueryFieldReferenceNode(String referenceNodeName, boolean isNestedDocReference) { 21 | this.isNestedDocReference = isNestedDocReference; 22 | this.referenceNodeName = referenceNodeName; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/io/github/iamazy/elasticsearch/dsl/sql/model/QueryFieldReferencePath.java: -------------------------------------------------------------------------------- 1 | package io.github.iamazy.elasticsearch.dsl.sql.model; 2 | 3 | import com.google.common.collect.ImmutableList; 4 | import com.google.common.collect.Lists; 5 | import org.apache.commons.collections4.CollectionUtils; 6 | 7 | import java.util.Collections; 8 | import java.util.List; 9 | 10 | /** 11 | * @author iamazy 12 | * @date 2019/2/19 13 | * @descrition 14 | **/ 15 | public class QueryFieldReferencePath { 16 | private List referenceNodes; 17 | public void addReferenceNode(QueryFieldReferenceNode referenceNode){ 18 | if(referenceNodes==null){ 19 | referenceNodes= Lists.newLinkedList(); 20 | } 21 | referenceNodes.add(referenceNode); 22 | } 23 | 24 | public List getReferenceNodes(){ 25 | if(CollectionUtils.isEmpty(referenceNodes)){ 26 | return Collections.emptyList(); 27 | } 28 | return ImmutableList.copyOf(referenceNodes); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/io/github/iamazy/elasticsearch/dsl/sql/model/RangeSegment.java: -------------------------------------------------------------------------------- 1 | package io.github.iamazy.elasticsearch.dsl.sql.model; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Getter; 5 | 6 | /** 7 | * @author iamazy 8 | * @date 2019/2/19 9 | * @descrition 10 | **/ 11 | @Getter 12 | @AllArgsConstructor 13 | public class RangeSegment { 14 | 15 | private Object from; 16 | private Object to; 17 | private SegmentType segmentType; 18 | 19 | 20 | 21 | public enum SegmentType{ 22 | /** 23 | * 时间类型 24 | */ 25 | Date, 26 | /** 27 | * 数字类型 28 | */ 29 | Numeric, 30 | /** 31 | * Ip类型 32 | */ 33 | Ip, 34 | /** 35 | * 地理类型 36 | */ 37 | Geo 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/io/github/iamazy/elasticsearch/dsl/sql/model/SqlCondition.java: -------------------------------------------------------------------------------- 1 | package io.github.iamazy.elasticsearch.dsl.sql.model; 2 | 3 | import com.google.common.collect.Lists; 4 | import io.github.iamazy.elasticsearch.dsl.sql.enums.SqlBoolOperator; 5 | import io.github.iamazy.elasticsearch.dsl.sql.enums.SqlConditionType; 6 | import lombok.Getter; 7 | 8 | import java.util.List; 9 | 10 | /** 11 | * @author iamazy 12 | * @date 2019/2/19 13 | * @descrition 14 | **/ 15 | 16 | @Getter 17 | public class SqlCondition { 18 | private SqlConditionType conditionType; 19 | private SqlBoolOperator operator; 20 | private List queryList; 21 | 22 | public SqlCondition(AtomicQuery query){ 23 | this(query,SqlConditionType.Atom); 24 | } 25 | 26 | public SqlCondition(List queryList,SqlBoolOperator operator){ 27 | this.queryList=queryList; 28 | this.conditionType=SqlConditionType.Combine; 29 | this.operator=operator; 30 | } 31 | 32 | public SqlCondition(AtomicQuery query,SqlConditionType conditionType){ 33 | this.queryList= Lists.newArrayList(query); 34 | this.conditionType=conditionType; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/io/github/iamazy/elasticsearch/dsl/sql/parser/ElasticSql2DslParser.java: -------------------------------------------------------------------------------- 1 | package io.github.iamazy.elasticsearch.dsl.sql.parser; 2 | 3 | import com.alibaba.druid.sql.ast.SQLExpr; 4 | import com.alibaba.druid.sql.ast.SQLLimit; 5 | import com.alibaba.druid.sql.ast.expr.SQLIntegerExpr; 6 | import com.alibaba.druid.sql.ast.expr.SQLQueryExpr; 7 | import com.alibaba.druid.sql.ast.statement.SQLDeleteStatement; 8 | import com.alibaba.druid.sql.parser.ParserException; 9 | import com.alibaba.druid.sql.parser.SQLStatementParser; 10 | import com.alibaba.druid.sql.parser.Token; 11 | import com.google.common.collect.ImmutableList; 12 | import io.github.iamazy.elasticsearch.dsl.sql.druid.ElasticSqlExprParser; 13 | import io.github.iamazy.elasticsearch.dsl.sql.druid.ElasticSqlSelectQueryBlock; 14 | import io.github.iamazy.elasticsearch.dsl.sql.exception.ElasticSql2DslException; 15 | import io.github.iamazy.elasticsearch.dsl.sql.parser.aggs.GroupByAggregationParser; 16 | import io.github.iamazy.elasticsearch.dsl.sql.parser.query.method.mapping.MappingQueryParser; 17 | import io.github.iamazy.elasticsearch.dsl.sql.parser.sql.*; 18 | import io.github.iamazy.elasticsearch.dsl.sql.model.ElasticDslContext; 19 | import io.github.iamazy.elasticsearch.dsl.sql.model.ElasticSqlParseResult; 20 | import org.elasticsearch.action.admin.indices.mapping.get.GetFieldMappingsRequest; 21 | import org.elasticsearch.action.admin.indices.mapping.get.GetMappingsRequest; 22 | 23 | 24 | import java.util.Arrays; 25 | import java.util.List; 26 | 27 | /** 28 | * @author iamazy 29 | */ 30 | public class ElasticSql2DslParser { 31 | 32 | public ElasticSqlParseResult parse(String sql) throws ElasticSql2DslException { 33 | SQLQueryExpr queryExpr; 34 | try { 35 | SQLStatementParser sqlStatementParser = new SQLStatementParser(sql); 36 | Token token = sqlStatementParser.getLexer().token(); 37 | switch (token) { 38 | case DELETE: { 39 | SQLDeleteStatement sqlDeleteStatement = sqlStatementParser.parseDeleteStatement(); 40 | SQLLimit sqlLimit = sqlStatementParser.getExprParser().parseLimit(); 41 | ElasticDslContext elasticDslContext = new ElasticDslContext(sqlDeleteStatement); 42 | for (QueryParser sqlParser : buildSqlDeleteParserChain()) { 43 | sqlParser.parse(elasticDslContext); 44 | } 45 | //此处设置的是DeleteByQueryRequest的Size,将DeleteByQueryRequest中的SearchRequest的DSL打印出来的size是1000,不是这个值,不要搞混淆 46 | elasticDslContext.getParseResult().setSize(((SQLIntegerExpr) sqlLimit.getRowCount()).getNumber().intValue()); 47 | return elasticDslContext.getParseResult(); 48 | } 49 | case DESC:{ 50 | Object mappingsRequest = MappingQueryParser.parse(sql); 51 | ElasticDslContext elasticDslContext=new ElasticDslContext(null); 52 | if(mappingsRequest instanceof GetMappingsRequest) { 53 | GetMappingsRequest getMappingsRequest=(GetMappingsRequest) mappingsRequest; 54 | elasticDslContext.getParseResult().setMappingsRequest(getMappingsRequest); 55 | elasticDslContext.getParseResult().setIndices(Arrays.asList(getMappingsRequest.indices())); 56 | }else if(mappingsRequest instanceof GetFieldMappingsRequest) { 57 | GetFieldMappingsRequest getFieldMappingsRequest=(GetFieldMappingsRequest) mappingsRequest; 58 | elasticDslContext.getParseResult().setFieldMappingsRequest((GetFieldMappingsRequest) mappingsRequest); 59 | elasticDslContext.getParseResult().setIndices(Arrays.asList(getFieldMappingsRequest.indices())); 60 | }else{ 61 | throw new ElasticSql2DslException("[syntax error] not support desc like this syntax"); 62 | } 63 | return elasticDslContext.getParseResult(); 64 | } 65 | case SELECT: 66 | default: { 67 | ElasticSqlExprParser elasticSqlExprParser = new ElasticSqlExprParser(sql); 68 | SQLExpr sqlQueryExpr = elasticSqlExprParser.expr(); 69 | check(elasticSqlExprParser, sqlQueryExpr); 70 | queryExpr = (SQLQueryExpr) sqlQueryExpr; 71 | ElasticDslContext elasticDslContext = new ElasticDslContext(queryExpr); 72 | if (queryExpr.getSubQuery().getQuery() instanceof ElasticSqlSelectQueryBlock) { 73 | for (QueryParser sqlParser : buildSqlSelectParserChain()) { 74 | sqlParser.parse(elasticDslContext); 75 | } 76 | } else { 77 | throw new ElasticSql2DslException("[syntax error] Sql only support Select,Delete,Desc Sql"); 78 | } 79 | return elasticDslContext.getParseResult(); 80 | } 81 | } 82 | 83 | } catch (ParserException ex) { 84 | throw new ElasticSql2DslException(ex); 85 | } 86 | 87 | } 88 | 89 | private void check(ElasticSqlExprParser sqlExprParser, SQLExpr sqlQueryExpr) { 90 | if (sqlExprParser.getLexer().token() != Token.EOF) { 91 | throw new ElasticSql2DslException("[syntax error] Sql last token is not EOF"); 92 | } 93 | 94 | if (!(sqlQueryExpr instanceof SQLQueryExpr)) { 95 | throw new ElasticSql2DslException("[syntax error] Sql is not select druid"); 96 | } 97 | } 98 | 99 | private List buildSqlSelectParserChain( ) { 100 | //SQL解析器的顺序不能改变 101 | return ImmutableList.of( 102 | //解析SQL指定的索引和文档类型 103 | new QueryFromParser(), 104 | //解析SQL查询指定的match条件 105 | new QueryMatchConditionParser(), 106 | //解析SQL查询指定的where条件 107 | new QueryWhereConditionParser(), 108 | //解析SQL排序条件 109 | new QueryOrderConditionParser(), 110 | //解析路由参数 111 | new QueryRoutingValParser(), 112 | //解析分组统计 113 | new GroupByAggregationParser(), 114 | //解析SQL查询指定的字段 115 | new QuerySelectFieldListParser(), 116 | //解析Scroll By字段 117 | new QueryScrollParser(), 118 | //解析SQL的分页条数 119 | new QueryLimitSizeParser() 120 | ); 121 | } 122 | 123 | private List buildSqlDeleteParserChain() { 124 | //SQL解析器的顺序不能改变 125 | return ImmutableList.of( 126 | //解析SQL指定的索引和文档类型 127 | new QueryFromParser(), 128 | //解析SQL查询指定的where条件 129 | new QueryWhereConditionParser(), 130 | //解析路由参数 131 | new QueryRoutingValParser() 132 | ); 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /src/main/java/io/github/iamazy/elasticsearch/dsl/sql/parser/aggs/AbstractGroupByMethodAggregationParser.java: -------------------------------------------------------------------------------- 1 | package io.github.iamazy.elasticsearch.dsl.sql.parser.aggs; 2 | 3 | import io.github.iamazy.elasticsearch.dsl.sql.exception.ElasticSql2DslException; 4 | import io.github.iamazy.elasticsearch.dsl.sql.parser.query.method.MethodInvocation; 5 | import io.github.iamazy.elasticsearch.dsl.sql.parser.query.method.expr.MethodExpression; 6 | import io.github.iamazy.elasticsearch.dsl.sql.model.AggregationQuery; 7 | 8 | /** 9 | * @author iamazy 10 | * @date 2019/3/7 11 | * @descrition 12 | **/ 13 | public abstract class AbstractGroupByMethodAggregationParser implements MethodExpression { 14 | /** 15 | * 解析聚合函数 16 | * 17 | * @param invocation 18 | * @return 19 | * @throws ElasticSql2DslException 20 | */ 21 | public abstract AggregationQuery parseAggregationMethod(MethodInvocation invocation) throws ElasticSql2DslException; 22 | 23 | @Override 24 | public void checkMethodInvocation(MethodInvocation invocation) throws ElasticSql2DslException { 25 | if (!isMatchMethodInvocation(invocation)) { 26 | throw new ElasticSql2DslException("[syntax error] Sql not support group by method:" + invocation.getMethodName()); 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/io/github/iamazy/elasticsearch/dsl/sql/parser/aggs/join/NestedAggregationParser.java: -------------------------------------------------------------------------------- 1 | package io.github.iamazy.elasticsearch.dsl.sql.parser.aggs.join; 2 | 3 | import com.alibaba.druid.sql.ast.SQLExpr; 4 | import com.google.common.collect.ImmutableList; 5 | import io.github.iamazy.elasticsearch.dsl.sql.enums.QueryFieldType; 6 | import io.github.iamazy.elasticsearch.dsl.sql.exception.ElasticSql2DslException; 7 | import io.github.iamazy.elasticsearch.dsl.sql.parser.query.method.MethodInvocation; 8 | import io.github.iamazy.elasticsearch.dsl.sql.helper.ElasticSqlMethodInvokeHelper; 9 | import io.github.iamazy.elasticsearch.dsl.sql.model.AggregationQuery; 10 | import io.github.iamazy.elasticsearch.dsl.sql.model.ElasticSqlQueryField; 11 | import io.github.iamazy.elasticsearch.dsl.sql.parser.aggs.AbstractGroupByMethodAggregationParser; 12 | import io.github.iamazy.elasticsearch.dsl.sql.parser.sql.QueryFieldParser; 13 | import org.elasticsearch.search.aggregations.AggregationBuilder; 14 | import org.elasticsearch.search.aggregations.AggregationBuilders; 15 | import org.elasticsearch.search.aggregations.bucket.nested.NestedAggregationBuilder; 16 | 17 | import java.util.List; 18 | 19 | public class NestedAggregationParser extends AbstractGroupByMethodAggregationParser { 20 | 21 | 22 | private static final List AGG_NESTED_METHOD = ImmutableList.of("nested","nested_agg"); 23 | 24 | 25 | @Override 26 | public AggregationQuery parseAggregationMethod(MethodInvocation invocation) throws ElasticSql2DslException { 27 | SQLExpr nested=invocation.getFirstParameter(); 28 | AggregationBuilder nestedBuilder=parseNestedAggregation(invocation.getQueryAs(),nested); 29 | return new AggregationQuery(nestedBuilder); 30 | } 31 | 32 | @Override 33 | public List defineMethodNames() { 34 | return AGG_NESTED_METHOD; 35 | } 36 | 37 | @Override 38 | public boolean isMatchMethodInvocation(MethodInvocation invocation) { 39 | int paramCount=invocation.getParameterCount(); 40 | if(paramCount!=1){ 41 | return false; 42 | } 43 | return ElasticSqlMethodInvokeHelper.isMethodOf(defineMethodNames(), invocation.getMethodName()); 44 | } 45 | 46 | 47 | private AggregationBuilder parseNestedAggregation(String queryAs, SQLExpr nestedExpr){ 48 | QueryFieldParser queryFieldParser=new QueryFieldParser(); 49 | ElasticSqlQueryField elasticSqlQueryField = queryFieldParser.parseConditionQueryField(nestedExpr, queryAs); 50 | if(elasticSqlQueryField.getQueryFieldType()== QueryFieldType.RootDocField||elasticSqlQueryField.getQueryFieldType()==QueryFieldType.InnerDocField){ 51 | return createNestedBuilder(elasticSqlQueryField.getQueryFieldFullName()); 52 | }else if(elasticSqlQueryField.getQueryFieldType()==QueryFieldType.NestedDocField){ 53 | throw new ElasticSql2DslException("[syntax error] can not support aggregation defined by dollar[$]"); 54 | } 55 | else { 56 | throw new ElasticSql2DslException(String.format("[syntax error] can not support nested aggregation for field type[%s]", elasticSqlQueryField.getQueryFieldType())); 57 | } 58 | } 59 | 60 | 61 | private NestedAggregationBuilder createNestedBuilder(String fieldName) { 62 | return AggregationBuilders.nested(fieldName + "_nested",fieldName); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/main/java/io/github/iamazy/elasticsearch/dsl/sql/parser/aggs/search/CardinalityAggregationParser.java: -------------------------------------------------------------------------------- 1 | package io.github.iamazy.elasticsearch.dsl.sql.parser.aggs.search; 2 | 3 | import com.alibaba.druid.sql.ast.SQLExpr; 4 | import com.google.common.collect.ImmutableList; 5 | import io.github.iamazy.elasticsearch.dsl.sql.enums.QueryFieldType; 6 | import io.github.iamazy.elasticsearch.dsl.sql.exception.ElasticSql2DslException; 7 | import io.github.iamazy.elasticsearch.dsl.sql.parser.query.method.MethodInvocation; 8 | import io.github.iamazy.elasticsearch.dsl.sql.helper.ElasticSqlArgConverter; 9 | import io.github.iamazy.elasticsearch.dsl.sql.helper.ElasticSqlMethodInvokeHelper; 10 | import io.github.iamazy.elasticsearch.dsl.sql.model.AggregationQuery; 11 | import io.github.iamazy.elasticsearch.dsl.sql.model.ElasticSqlQueryField; 12 | import io.github.iamazy.elasticsearch.dsl.sql.parser.aggs.AbstractGroupByMethodAggregationParser; 13 | import io.github.iamazy.elasticsearch.dsl.sql.parser.sql.QueryFieldParser; 14 | import org.elasticsearch.search.aggregations.AggregationBuilder; 15 | import org.elasticsearch.search.aggregations.AggregationBuilders; 16 | import org.elasticsearch.search.aggregations.metrics.CardinalityAggregationBuilder; 17 | 18 | import java.util.List; 19 | 20 | /** 21 | * @author iamazy 22 | * @date 2019/3/7 23 | * @descrition 24 | **/ 25 | public class CardinalityAggregationParser extends AbstractGroupByMethodAggregationParser { 26 | 27 | private static final List AGG_CARDINALITY_METHOD = ImmutableList.of("cardinality"); 28 | 29 | @Override 30 | public AggregationQuery parseAggregationMethod(MethodInvocation invocation) throws ElasticSql2DslException { 31 | SQLExpr cardinality = invocation.getFirstParameter(); 32 | SQLExpr precisionThreshold = null; 33 | if (invocation.getParameterCount() == 2) { 34 | precisionThreshold = invocation.getParameter(1); 35 | } 36 | AggregationBuilder cardinalityBuilder = parseCardinalityAggregation(invocation.getQueryAs(), cardinality, precisionThreshold); 37 | return new AggregationQuery(cardinalityBuilder); 38 | } 39 | 40 | @Override 41 | public List defineMethodNames() { 42 | return AGG_CARDINALITY_METHOD; 43 | } 44 | 45 | @Override 46 | public boolean isMatchMethodInvocation(MethodInvocation invocation) { 47 | int paramCount = invocation.getParameterCount(); 48 | if (paramCount != 1 && paramCount != 2) { 49 | return false; 50 | } 51 | return ElasticSqlMethodInvokeHelper.isMethodOf(defineMethodNames(), invocation.getMethodName()); 52 | } 53 | 54 | 55 | private AggregationBuilder parseCardinalityAggregation(String queryAs, SQLExpr cardinalityFieldExpr, SQLExpr precisionThreshold) { 56 | QueryFieldParser queryFieldParser = new QueryFieldParser(); 57 | ElasticSqlQueryField queryField = queryFieldParser.parseConditionQueryField(cardinalityFieldExpr, queryAs); 58 | if (queryField.getQueryFieldType() == QueryFieldType.RootDocField || queryField.getQueryFieldType() == QueryFieldType.InnerDocField) { 59 | if (precisionThreshold != null) { 60 | Number threshold = (Number) ElasticSqlArgConverter.convertSqlArg(precisionThreshold); 61 | return createCardinalityBuilder(queryField.getQueryFieldFullName(), threshold.longValue()); 62 | } 63 | return createCardinalityBuilder(queryField.getQueryFieldFullName()); 64 | } else if (queryField.getQueryFieldType() == QueryFieldType.NestedDocField) { 65 | throw new ElasticSql2DslException("[syntax error] can not support aggregation defined by dollar[$]"); 66 | } else { 67 | throw new ElasticSql2DslException(String.format("[syntax error] can not support cardinality aggregation for field type[%s]", queryField.getQueryFieldType())); 68 | } 69 | 70 | } 71 | 72 | private CardinalityAggregationBuilder createCardinalityBuilder(String fieldName, long threshold) { 73 | return AggregationBuilders.cardinality(fieldName + "_cardinality").field(fieldName).precisionThreshold(threshold); 74 | } 75 | 76 | private CardinalityAggregationBuilder createCardinalityBuilder(String fieldName) { 77 | return AggregationBuilders.cardinality(fieldName + "_cardinality").field(fieldName); 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/main/java/io/github/iamazy/elasticsearch/dsl/sql/parser/aggs/search/RangeAggAggregationParser.java: -------------------------------------------------------------------------------- 1 | package io.github.iamazy.elasticsearch.dsl.sql.parser.aggs.search; 2 | 3 | import com.alibaba.druid.sql.ast.SQLExpr; 4 | import com.alibaba.druid.sql.ast.expr.SQLMethodInvokeExpr; 5 | import com.google.common.collect.ImmutableList; 6 | import com.google.common.collect.Lists; 7 | import io.github.iamazy.elasticsearch.dsl.sql.enums.QueryFieldType; 8 | import io.github.iamazy.elasticsearch.dsl.sql.exception.ElasticSql2DslException; 9 | import io.github.iamazy.elasticsearch.dsl.sql.parser.query.method.MethodInvocation; 10 | import io.github.iamazy.elasticsearch.dsl.sql.helper.ElasticSqlArgConverter; 11 | import io.github.iamazy.elasticsearch.dsl.sql.helper.ElasticSqlMethodInvokeHelper; 12 | import io.github.iamazy.elasticsearch.dsl.sql.model.AggregationQuery; 13 | import io.github.iamazy.elasticsearch.dsl.sql.model.ElasticSqlQueryField; 14 | import io.github.iamazy.elasticsearch.dsl.sql.model.RangeSegment; 15 | import io.github.iamazy.elasticsearch.dsl.sql.parser.aggs.AbstractGroupByMethodAggregationParser; 16 | import io.github.iamazy.elasticsearch.dsl.sql.parser.aggs.GroupByAggregationParser; 17 | import io.github.iamazy.elasticsearch.dsl.sql.parser.sql.QueryFieldParser; 18 | import org.elasticsearch.search.aggregations.AggregationBuilder; 19 | import org.elasticsearch.search.aggregations.AggregationBuilders; 20 | import org.elasticsearch.search.aggregations.bucket.range.AbstractRangeBuilder; 21 | import org.elasticsearch.search.aggregations.bucket.range.DateRangeAggregationBuilder; 22 | import org.elasticsearch.search.aggregations.bucket.range.RangeAggregationBuilder; 23 | import org.joda.time.DateTime; 24 | 25 | import java.time.ZonedDateTime; 26 | import java.util.List; 27 | 28 | /** 29 | * @author iamazy 30 | * @date 2019/3/7 31 | * @descrition 32 | **/ 33 | public class RangeAggAggregationParser extends AbstractGroupByMethodAggregationParser { 34 | 35 | private static final List AGG_RANGE_METHOD = ImmutableList.of("range", "range_agg"); 36 | 37 | private static ZonedDateTime getDateRangeVal(String date) { 38 | return ZonedDateTime.parse(date); 39 | } 40 | 41 | 42 | 43 | @Override 44 | public AggregationQuery parseAggregationMethod(MethodInvocation invocation) throws ElasticSql2DslException { 45 | List rangeSegments = parseRangeSegments(invocation.getMethodInvokeExpr()); 46 | SQLExpr rangeFieldExpr = invocation.getMethodInvokeExpr().getParameters().get(0); 47 | 48 | return new AggregationQuery(parseRangeAggregation(invocation.getQueryAs(), rangeFieldExpr, rangeSegments)); 49 | } 50 | 51 | @Override 52 | public List defineMethodNames() { 53 | return AGG_RANGE_METHOD; 54 | } 55 | 56 | @Override 57 | public boolean isMatchMethodInvocation(MethodInvocation invocation) { 58 | return ElasticSqlMethodInvokeHelper.isMethodOf(defineMethodNames(), invocation.getMethodName()); 59 | } 60 | 61 | 62 | private AggregationBuilder parseRangeAggregation(String queryAs, SQLExpr rangeFieldExpr, List rangeSegments) { 63 | 64 | QueryFieldParser queryFieldParser = new QueryFieldParser(); 65 | 66 | ElasticSqlQueryField queryField = queryFieldParser.parseConditionQueryField(rangeFieldExpr, queryAs); 67 | if (queryField.getQueryFieldType() == QueryFieldType.RootDocField || queryField.getQueryFieldType() == QueryFieldType.InnerDocField) { 68 | return createRangeBuilder(queryField.getQueryFieldFullName(), rangeSegments); 69 | } 70 | else if(queryField.getQueryFieldType()==QueryFieldType.NestedDocField){ 71 | throw new ElasticSql2DslException("[syntax error] can not support aggregation defined by dollar[$]"); 72 | } 73 | else{ 74 | throw new ElasticSql2DslException(String.format("[syntax error] can not support range aggregation for field type[%s]", queryField.getQueryFieldType())); 75 | } 76 | 77 | 78 | } 79 | 80 | private List parseRangeSegments(SQLMethodInvokeExpr rangeMethodExpr) { 81 | List rangeSegmentList = Lists.newArrayList(); 82 | for (int pIdx = 1; pIdx < rangeMethodExpr.getParameters().size(); pIdx++) { 83 | SQLMethodInvokeExpr segMethodExpr = (SQLMethodInvokeExpr) rangeMethodExpr.getParameters().get(pIdx); 84 | 85 | ElasticSqlMethodInvokeHelper.checkRangeItemAggMethod(segMethodExpr); 86 | 87 | Object from = ElasticSqlArgConverter.convertSqlArg(segMethodExpr.getParameters().get(0), true); 88 | Object to = ElasticSqlArgConverter.convertSqlArg(segMethodExpr.getParameters().get(1), true); 89 | 90 | rangeSegmentList.add(new RangeSegment(from, to, 91 | from instanceof Number ? RangeSegment.SegmentType.Numeric : RangeSegment.SegmentType.Date)); 92 | } 93 | return rangeSegmentList; 94 | } 95 | 96 | private AbstractRangeBuilder createRangeBuilder(String rangeFieldName, List rangeSegments) { 97 | AbstractRangeBuilder rangeBuilder = null; 98 | RangeSegment.SegmentType segType = rangeSegments.get(0).getSegmentType(); 99 | 100 | if (segType == RangeSegment.SegmentType.Numeric) { 101 | RangeAggregationBuilder numericRangeBuilder = AggregationBuilders.range(GroupByAggregationParser.AGG_BUCKET_KEY_PREFIX + rangeFieldName+"_range").field(rangeFieldName); 102 | for (RangeSegment segment : rangeSegments) { 103 | String key = String.format("%s-%s", segment.getFrom().toString(), segment.getTo().toString()); 104 | numericRangeBuilder.addRange(key, Double.valueOf(segment.getFrom().toString()), Double.valueOf(segment.getTo().toString())); 105 | } 106 | rangeBuilder = numericRangeBuilder; 107 | } 108 | 109 | if (segType == RangeSegment.SegmentType.Date) { 110 | 111 | DateRangeAggregationBuilder dateRangeBuilder = AggregationBuilders.dateRange(GroupByAggregationParser.AGG_BUCKET_KEY_PREFIX + rangeFieldName+"_range").field(rangeFieldName); 112 | for (RangeSegment segment : rangeSegments) { 113 | ZonedDateTime fromDate = getDateRangeVal(segment.getFrom().toString()); 114 | ZonedDateTime toDate = getDateRangeVal(segment.getTo().toString()); 115 | 116 | String key = String.format("[%s]-[%s]", formatDateRangeAggKey(fromDate), formatDateRangeAggKey(toDate)); 117 | dateRangeBuilder.addRange(key, fromDate, toDate); 118 | } 119 | rangeBuilder = dateRangeBuilder; 120 | } 121 | return rangeBuilder; 122 | } 123 | 124 | private String formatDateRangeAggKey(ZonedDateTime date) { 125 | final String dateRangeKeyPattern = "yyyy-MM-dd HH:mm:ss"; 126 | return new DateTime(date).toString(dateRangeKeyPattern); 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /src/main/java/io/github/iamazy/elasticsearch/dsl/sql/parser/aggs/search/TermsAggAggregationParser.java: -------------------------------------------------------------------------------- 1 | package io.github.iamazy.elasticsearch.dsl.sql.parser.aggs.search; 2 | 3 | import com.alibaba.druid.sql.ast.SQLExpr; 4 | import com.google.common.collect.ImmutableList; 5 | import io.github.iamazy.elasticsearch.dsl.sql.enums.QueryFieldType; 6 | import io.github.iamazy.elasticsearch.dsl.sql.exception.ElasticSql2DslException; 7 | import io.github.iamazy.elasticsearch.dsl.sql.parser.query.method.MethodInvocation; 8 | import io.github.iamazy.elasticsearch.dsl.sql.helper.ElasticSqlArgConverter; 9 | import io.github.iamazy.elasticsearch.dsl.sql.helper.ElasticSqlMethodInvokeHelper; 10 | import io.github.iamazy.elasticsearch.dsl.sql.model.AggregationQuery; 11 | import io.github.iamazy.elasticsearch.dsl.sql.model.ElasticSqlQueryField; 12 | import io.github.iamazy.elasticsearch.dsl.sql.parser.aggs.AbstractGroupByMethodAggregationParser; 13 | import io.github.iamazy.elasticsearch.dsl.sql.parser.aggs.GroupByAggregationParser; 14 | import io.github.iamazy.elasticsearch.dsl.sql.parser.sql.QueryFieldParser; 15 | import org.elasticsearch.search.aggregations.AggregationBuilder; 16 | import org.elasticsearch.search.aggregations.AggregationBuilders; 17 | import org.elasticsearch.search.aggregations.BucketOrder; 18 | import org.elasticsearch.search.aggregations.bucket.terms.TermsAggregationBuilder; 19 | 20 | import java.util.List; 21 | 22 | /** 23 | * @author iamazy 24 | * @date 2019/3/7 25 | * @descrition 26 | **/ 27 | public class TermsAggAggregationParser extends AbstractGroupByMethodAggregationParser { 28 | 29 | private static final List AGG_TERMS_METHOD = ImmutableList.of("terms", "terms_agg"); 30 | 31 | @Override 32 | public AggregationQuery parseAggregationMethod(MethodInvocation invocation) throws ElasticSql2DslException { 33 | SQLExpr termsFieldExpr = invocation.getFirstParameter(); 34 | SQLExpr shardSizeExpr = null; 35 | if (invocation.getParameterCount() == 2) { 36 | shardSizeExpr = invocation.getParameters().get(1); 37 | } 38 | AggregationBuilder termsBuilder = parseTermsAggregation(invocation.getQueryAs(), termsFieldExpr, shardSizeExpr); 39 | return new AggregationQuery(termsBuilder); 40 | } 41 | 42 | @Override 43 | public List defineMethodNames() { 44 | return AGG_TERMS_METHOD; 45 | } 46 | 47 | @Override 48 | public boolean isMatchMethodInvocation(MethodInvocation invocation) { 49 | return ElasticSqlMethodInvokeHelper.isMethodOf(defineMethodNames(), invocation.getMethodName()); 50 | } 51 | 52 | private AggregationBuilder parseTermsAggregation(String queryAs, SQLExpr termsFieldExpr, SQLExpr shardSizeExpr) { 53 | QueryFieldParser queryFieldParser = new QueryFieldParser(); 54 | ElasticSqlQueryField queryField = queryFieldParser.parseConditionQueryField(termsFieldExpr, queryAs); 55 | if (queryField.getQueryFieldType() == QueryFieldType.RootDocField || queryField.getQueryFieldType() == QueryFieldType.InnerDocField) { 56 | if (shardSizeExpr != null) { 57 | Number termBuckets = (Number) ElasticSqlArgConverter.convertSqlArg(shardSizeExpr); 58 | return createTermsBuilder(queryField.getQueryFieldFullName(), termBuckets.intValue()); 59 | } 60 | return createTermsBuilder(queryField.getQueryFieldFullName()); 61 | } else if (queryField.getQueryFieldType() == QueryFieldType.NestedDocField) { 62 | throw new ElasticSql2DslException("[syntax error] can not support aggregation defined by dollar[$]"); 63 | } else { 64 | throw new ElasticSql2DslException(String.format("[syntax error] can not support terms aggregation for field type[%s]", queryField.getQueryFieldType())); 65 | } 66 | } 67 | 68 | private TermsAggregationBuilder createTermsBuilder(String termsFieldName, int termBuckets) { 69 | return AggregationBuilders.terms(GroupByAggregationParser.AGG_BUCKET_KEY_PREFIX + termsFieldName + "_terms") 70 | .field(termsFieldName) 71 | .minDocCount(1).shardMinDocCount(1) 72 | .shardSize(termBuckets << 1).size(termBuckets).order(BucketOrder.count(false)); 73 | } 74 | 75 | private TermsAggregationBuilder createTermsBuilder(String termsFieldName) { 76 | return createTermsBuilder(termsFieldName, GroupByAggregationParser.MAX_GROUP_BY_SIZE); 77 | } 78 | 79 | } 80 | -------------------------------------------------------------------------------- /src/main/java/io/github/iamazy/elasticsearch/dsl/sql/parser/aggs/search/TopHitsAggregationParser.java: -------------------------------------------------------------------------------- 1 | package io.github.iamazy.elasticsearch.dsl.sql.parser.aggs.search; 2 | 3 | import com.alibaba.druid.sql.ast.expr.SQLCharExpr; 4 | import com.alibaba.druid.sql.ast.expr.SQLIntegerExpr; 5 | import com.alibaba.druid.sql.ast.expr.SQLMethodInvokeExpr; 6 | import com.google.common.collect.ImmutableList; 7 | import io.github.iamazy.elasticsearch.dsl.sql.exception.ElasticSql2DslException; 8 | import io.github.iamazy.elasticsearch.dsl.sql.parser.query.method.MethodInvocation; 9 | import io.github.iamazy.elasticsearch.dsl.sql.helper.ElasticSqlMethodInvokeHelper; 10 | import io.github.iamazy.elasticsearch.dsl.sql.model.AggregationQuery; 11 | import io.github.iamazy.elasticsearch.dsl.sql.parser.aggs.AbstractGroupByMethodAggregationParser; 12 | import org.elasticsearch.search.aggregations.AggregationBuilder; 13 | import org.elasticsearch.search.aggregations.AggregationBuilders; 14 | import org.elasticsearch.search.aggregations.metrics.TopHitsAggregationBuilder; 15 | 16 | import java.util.List; 17 | 18 | /** 19 | * @author iamazy 20 | * @date 2019/3/7 21 | * @descrition 22 | **/ 23 | public class TopHitsAggregationParser extends AbstractGroupByMethodAggregationParser { 24 | 25 | private static final List AGG_TOPHITS_METHOD = ImmutableList.of("topHits", "top_hits"); 26 | 27 | @Override 28 | public AggregationQuery parseAggregationMethod(MethodInvocation invocation) throws ElasticSql2DslException { 29 | return new AggregationQuery(parseTopHitsAggregation(invocation.getMethodInvokeExpr())); 30 | } 31 | 32 | @Override 33 | public List defineMethodNames() { 34 | return AGG_TOPHITS_METHOD; 35 | } 36 | 37 | @Override 38 | public boolean isMatchMethodInvocation(MethodInvocation invocation) { 39 | return ElasticSqlMethodInvokeHelper.isMethodOf(defineMethodNames(), invocation.getMethodName()); 40 | } 41 | 42 | private AggregationBuilder parseTopHitsAggregation(SQLMethodInvokeExpr topHitsFieldExpr) { 43 | if (topHitsFieldExpr.getParameters().size() == 1) { 44 | if (topHitsFieldExpr.getParameters().get(0) instanceof SQLIntegerExpr) { 45 | SQLIntegerExpr sizeExpr = (SQLIntegerExpr) topHitsFieldExpr.getParameters().get(0); 46 | return createTopHitsBuilder(sizeExpr.getNumber().intValue()); 47 | } else { 48 | throw new ElasticSql2DslException("[syntax error] can not support top_hits aggregation for only one field which is not integer type"); 49 | } 50 | } else if (topHitsFieldExpr.getParameters().size() == 2 && topHitsFieldExpr.getParameters().get(0) instanceof SQLCharExpr 51 | && topHitsFieldExpr.getParameters().get(1) instanceof SQLIntegerExpr) { 52 | SQLCharExpr nameExpr = (SQLCharExpr) topHitsFieldExpr.getParameters().get(0); 53 | SQLIntegerExpr sizeExpr = (SQLIntegerExpr) topHitsFieldExpr.getParameters().get(1); 54 | return createTopHitsBuilder(nameExpr.getText(), sizeExpr.getNumber().intValue()); 55 | } else if (topHitsFieldExpr.getParameters().size() == 3 && topHitsFieldExpr.getParameters().get(0) instanceof SQLCharExpr 56 | && topHitsFieldExpr.getParameters().get(1) instanceof SQLIntegerExpr && topHitsFieldExpr.getParameters().get(2) instanceof SQLIntegerExpr) { 57 | SQLCharExpr nameExpr = (SQLCharExpr) topHitsFieldExpr.getParameters().get(0); 58 | SQLIntegerExpr sizeExpr = (SQLIntegerExpr) topHitsFieldExpr.getParameters().get(1); 59 | SQLIntegerExpr fromExpr = (SQLIntegerExpr) topHitsFieldExpr.getParameters().get(2); 60 | return createTopHitsBuilder(nameExpr.getText(), sizeExpr.getNumber().intValue(), fromExpr.getNumber().intValue()); 61 | } else { 62 | throw new ElasticSql2DslException("[syntax error] can not support top_hits aggregation for field count bigger than 2"); 63 | } 64 | } 65 | 66 | private TopHitsAggregationBuilder createTopHitsBuilder(int size) { 67 | return AggregationBuilders.topHits("agg_top_hits").size(size); 68 | } 69 | 70 | private TopHitsAggregationBuilder createTopHitsBuilder(String name, int size) { 71 | return AggregationBuilders.topHits(name).size(size); 72 | } 73 | 74 | private TopHitsAggregationBuilder createTopHitsBuilder(String name, int size, int from) { 75 | return AggregationBuilders.topHits(name).size(size).from(from); 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/main/java/io/github/iamazy/elasticsearch/dsl/sql/parser/query/exact/AbstractExactQueryParser.java: -------------------------------------------------------------------------------- 1 | package io.github.iamazy.elasticsearch.dsl.sql.parser.query.exact; 2 | 3 | import com.alibaba.druid.sql.ast.SQLExpr; 4 | import io.github.iamazy.elasticsearch.dsl.cons.CoreConstants; 5 | import io.github.iamazy.elasticsearch.dsl.sql.enums.QueryFieldType; 6 | import io.github.iamazy.elasticsearch.dsl.sql.enums.SqlConditionOperator; 7 | import io.github.iamazy.elasticsearch.dsl.sql.exception.ElasticSql2DslException; 8 | import io.github.iamazy.elasticsearch.dsl.sql.model.AtomicQuery; 9 | import io.github.iamazy.elasticsearch.dsl.sql.model.ElasticSqlQueryField; 10 | import io.github.iamazy.elasticsearch.dsl.sql.parser.sql.QueryFieldParser; 11 | import org.apache.commons.lang3.StringUtils; 12 | import org.elasticsearch.index.query.QueryBuilder; 13 | 14 | public abstract class AbstractExactQueryParser { 15 | 16 | 17 | AtomicQuery parseCondition(SQLExpr queryFieldExpr, SqlConditionOperator operator, Object[] params, String queryAs, IConditionExactQueryBuilder queryBuilder) { 18 | 19 | QueryFieldParser queryFieldParser = new QueryFieldParser(); 20 | ElasticSqlQueryField queryField = queryFieldParser.parseConditionQueryField(queryFieldExpr, queryAs); 21 | if(CoreConstants.HIGHLIGHTER.equalsIgnoreCase(queryField.getQueryFieldFullName())){ 22 | throw new ElasticSql2DslException("[syntax error] the query field can not equals to 'h#'"); 23 | } 24 | AtomicQuery atomQuery = null; 25 | String field=null; 26 | boolean highlighter=false; 27 | if(queryField.getQueryFieldFullName().startsWith(CoreConstants.HIGHLIGHTER)){ 28 | field=queryField.getQueryFieldFullName().substring(CoreConstants.HIGHLIGHTER.length()); 29 | queryField.setQueryFieldFullName(field); 30 | highlighter=true; 31 | } 32 | if (queryField.getQueryFieldType() == QueryFieldType.RootDocField || queryField.getQueryFieldType() == QueryFieldType.InnerDocField) { 33 | QueryBuilder originalQuery = queryBuilder.buildQuery(queryField.getQueryFieldFullName(), operator, params); 34 | atomQuery = new AtomicQuery(originalQuery); 35 | } 36 | else if (queryField.getQueryFieldType() == QueryFieldType.NestedDocField) { 37 | QueryBuilder originalQuery = queryBuilder.buildQuery(queryField.getQueryFieldFullName(), operator, params); 38 | atomQuery = new AtomicQuery(originalQuery, queryField.getNestedDocContextPath()); 39 | } 40 | 41 | if (atomQuery == null) { 42 | throw new ElasticSql2DslException(String.format("[syntax error] where condition field can not support type[%s]", queryField.getQueryFieldType())); 43 | } 44 | 45 | if(highlighter&& StringUtils.isNotBlank(field)) { 46 | atomQuery.getHighlighter().add(field); 47 | } 48 | 49 | return atomQuery; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/main/java/io/github/iamazy/elasticsearch/dsl/sql/parser/query/exact/BetweenAndQueryParser.java: -------------------------------------------------------------------------------- 1 | package io.github.iamazy.elasticsearch.dsl.sql.parser.query.exact; 2 | 3 | import com.alibaba.druid.sql.ast.expr.SQLBetweenExpr; 4 | import io.github.iamazy.elasticsearch.dsl.sql.enums.SqlConditionOperator; 5 | import io.github.iamazy.elasticsearch.dsl.sql.exception.ElasticSql2DslException; 6 | import io.github.iamazy.elasticsearch.dsl.sql.helper.ElasticSqlArgConverter; 7 | import io.github.iamazy.elasticsearch.dsl.sql.model.AtomicQuery; 8 | import org.elasticsearch.index.query.QueryBuilders; 9 | 10 | 11 | /** 12 | * @author iamazy 13 | */ 14 | public class BetweenAndQueryParser extends AbstractExactQueryParser { 15 | 16 | 17 | public AtomicQuery parseBetweenAndQuery(SQLBetweenExpr betweenAndExpr, String queryAs) { 18 | Object from = ElasticSqlArgConverter.convertSqlArg(betweenAndExpr.getBeginExpr()); 19 | Object to = ElasticSqlArgConverter.convertSqlArg(betweenAndExpr.getEndExpr()); 20 | 21 | if (from == null || to == null) { 22 | throw new ElasticSql2DslException("[syntax error] Between Expr only support one of [number,date] arg type"); 23 | } 24 | 25 | return parseCondition(betweenAndExpr.getTestExpr(), SqlConditionOperator.BetweenAnd, new Object[]{from, to}, queryAs, (queryFieldName, operator, rightParamValues) -> QueryBuilders.rangeQuery(queryFieldName).gte(rightParamValues[0]).lte(rightParamValues[1])); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/io/github/iamazy/elasticsearch/dsl/sql/parser/query/exact/IConditionExactQueryBuilder.java: -------------------------------------------------------------------------------- 1 | package io.github.iamazy.elasticsearch.dsl.sql.parser.query.exact; 2 | 3 | import io.github.iamazy.elasticsearch.dsl.sql.enums.SqlConditionOperator; 4 | import org.elasticsearch.index.query.QueryBuilder; 5 | 6 | /** 7 | * @author iamazy 8 | */ 9 | @FunctionalInterface 10 | public interface IConditionExactQueryBuilder { 11 | QueryBuilder buildQuery(String queryFieldName, SqlConditionOperator operator, Object[] rightParamValues); 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/io/github/iamazy/elasticsearch/dsl/sql/parser/query/exact/InListQueryParser.java: -------------------------------------------------------------------------------- 1 | package io.github.iamazy.elasticsearch.dsl.sql.parser.query.exact; 2 | 3 | import com.alibaba.druid.sql.ast.expr.SQLInListExpr; 4 | import io.github.iamazy.elasticsearch.dsl.sql.enums.SqlConditionOperator; 5 | import io.github.iamazy.elasticsearch.dsl.sql.exception.ElasticSql2DslException; 6 | import io.github.iamazy.elasticsearch.dsl.sql.helper.ElasticSqlArgConverter; 7 | import io.github.iamazy.elasticsearch.dsl.sql.model.AtomicQuery; 8 | import org.apache.commons.collections4.CollectionUtils; 9 | import org.elasticsearch.index.query.QueryBuilders; 10 | 11 | 12 | /** 13 | * @author iamazy 14 | */ 15 | public class InListQueryParser extends AbstractExactQueryParser { 16 | 17 | 18 | public AtomicQuery parseInListQuery(SQLInListExpr inListQueryExpr, String queryAs) { 19 | if (CollectionUtils.isEmpty(inListQueryExpr.getTargetList())) { 20 | throw new ElasticSql2DslException("[syntax error] In list expr target list cannot be blank"); 21 | } 22 | 23 | Object[] targetInList = ElasticSqlArgConverter.convertSqlArgs(inListQueryExpr.getTargetList()); 24 | SqlConditionOperator operator = inListQueryExpr.isNot() ? SqlConditionOperator.NotIn : SqlConditionOperator.In; 25 | 26 | return parseCondition(inListQueryExpr.getExpr(), operator, targetInList, queryAs, (queryFieldName, operator1, rightParamValues) -> { 27 | if (SqlConditionOperator.NotIn == operator1) { 28 | return QueryBuilders.boolQuery().mustNot(QueryBuilders.termsQuery(queryFieldName, rightParamValues)); 29 | } 30 | else { 31 | return QueryBuilders.termsQuery(queryFieldName, rightParamValues); 32 | } 33 | }); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/io/github/iamazy/elasticsearch/dsl/sql/parser/query/method/AbstractFieldSpecificMethodQueryParser.java: -------------------------------------------------------------------------------- 1 | package io.github.iamazy.elasticsearch.dsl.sql.parser.query.method; 2 | 3 | import io.github.iamazy.elasticsearch.dsl.sql.enums.QueryFieldType; 4 | import io.github.iamazy.elasticsearch.dsl.sql.exception.ElasticSql2DslException; 5 | import io.github.iamazy.elasticsearch.dsl.sql.parser.query.method.expr.FieldSpecificMethodExpression; 6 | import io.github.iamazy.elasticsearch.dsl.sql.model.AtomicQuery; 7 | import io.github.iamazy.elasticsearch.dsl.sql.model.ElasticSqlQueryField; 8 | import io.github.iamazy.elasticsearch.dsl.sql.parser.sql.QueryFieldParser; 9 | import org.elasticsearch.index.query.QueryBuilder; 10 | 11 | import java.util.Map; 12 | 13 | public abstract class AbstractFieldSpecificMethodQueryParser extends ParameterizedMethodQueryParser implements FieldSpecificMethodExpression { 14 | 15 | 16 | public AbstractFieldSpecificMethodQueryParser() { } 17 | 18 | protected abstract QueryBuilder buildQuery(MethodInvocation invocation, String fieldName, Map extraParams); 19 | 20 | @Override 21 | protected String defineExtraParamString(MethodInvocation invocation) { 22 | //ignore extra params, subclass can override if necessary 23 | return null; 24 | } 25 | 26 | @Override 27 | protected AtomicQuery parseMethodQueryWithExtraParams(MethodInvocation invocation, Map extraParamMap) throws ElasticSql2DslException { 28 | QueryFieldParser queryFieldParser = new QueryFieldParser(); 29 | ElasticSqlQueryField queryField = queryFieldParser.parseConditionQueryField(defineFieldExpr(invocation), invocation.getQueryAs()); 30 | 31 | AtomicQuery atomicQuery = null; 32 | if (queryField.getQueryFieldType() == QueryFieldType.RootDocField || queryField.getQueryFieldType() == QueryFieldType.InnerDocField) { 33 | QueryBuilder originalQuery = buildQuery(invocation, queryField.getQueryFieldFullName(), extraParamMap); 34 | atomicQuery = new AtomicQuery(originalQuery); 35 | } 36 | 37 | if (queryField.getQueryFieldType() == QueryFieldType.NestedDocField) { 38 | QueryBuilder originalQuery = buildQuery(invocation, queryField.getQueryFieldFullName(), extraParamMap); 39 | atomicQuery = new AtomicQuery(originalQuery, queryField.getNestedDocContextPath()); 40 | } 41 | 42 | if (atomicQuery == null) { 43 | throw new ElasticSql2DslException( 44 | String.format("[syntax error] query field can not support type[%s]", queryField.getQueryFieldType())); 45 | } 46 | 47 | return atomicQuery; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/main/java/io/github/iamazy/elasticsearch/dsl/sql/parser/query/method/MethodInvocation.java: -------------------------------------------------------------------------------- 1 | package io.github.iamazy.elasticsearch.dsl.sql.parser.query.method; 2 | 3 | import com.alibaba.druid.sql.ast.SQLExpr; 4 | import com.alibaba.druid.sql.ast.expr.SQLMethodInvokeExpr; 5 | import io.github.iamazy.elasticsearch.dsl.sql.helper.ElasticSqlArgConverter; 6 | import lombok.Getter; 7 | import org.apache.commons.lang3.StringUtils; 8 | 9 | import java.util.List; 10 | 11 | /** 12 | * @author iamazy 13 | */ 14 | public class MethodInvocation { 15 | @Getter 16 | private final SQLMethodInvokeExpr methodInvokeExpr; 17 | private final String queryAs; 18 | 19 | public MethodInvocation(SQLMethodInvokeExpr methodInvokeExpr, String queryAs) { 20 | if (methodInvokeExpr == null) { 21 | throw new IllegalArgumentException("method invoke expression can not be null"); 22 | } 23 | this.methodInvokeExpr = methodInvokeExpr; 24 | this.queryAs = queryAs; 25 | } 26 | 27 | public String getQueryAs() { 28 | return queryAs; 29 | } 30 | 31 | 32 | public String getMethodName() { 33 | return methodInvokeExpr.getMethodName(); 34 | } 35 | 36 | public List getParameters() { 37 | return methodInvokeExpr.getParameters(); 38 | } 39 | 40 | public int getParameterCount() { 41 | return methodInvokeExpr.getParameters().size(); 42 | } 43 | 44 | public SQLExpr getFirstParameter() { 45 | return getParameter(0); 46 | } 47 | 48 | public SQLExpr getParameter(int index) { 49 | return methodInvokeExpr.getParameters().get(index); 50 | } 51 | 52 | public Object getParameterAsObject(int index) { 53 | SQLExpr paramExpr = methodInvokeExpr.getParameters().get(index); 54 | return ElasticSqlArgConverter.convertSqlArg(paramExpr, false); 55 | } 56 | 57 | public String getParameterAsFormatDate(int index) { 58 | SQLExpr paramExpr = methodInvokeExpr.getParameters().get(index); 59 | return ElasticSqlArgConverter.convertSqlArg(paramExpr, true).toString(); 60 | } 61 | 62 | public String getParameterAsString(int index) { 63 | return getParameterAsObject(index).toString(); 64 | } 65 | 66 | public String getLastParameterAsString() { 67 | if(getParameterCount()-1>=0) { 68 | return getParameterAsObject(getParameterCount() - 1).toString(); 69 | } 70 | return StringUtils.EMPTY; 71 | } 72 | 73 | public Double getParameterAsDouble(int index) { 74 | return (Double) getParameterAsObject(index); 75 | } 76 | 77 | public Long getParameterAsLong(int index) { 78 | return (Long) getParameterAsObject(index); 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /src/main/java/io/github/iamazy/elasticsearch/dsl/sql/parser/query/method/MethodQueryParser.java: -------------------------------------------------------------------------------- 1 | package io.github.iamazy.elasticsearch.dsl.sql.parser.query.method; 2 | 3 | 4 | import io.github.iamazy.elasticsearch.dsl.sql.exception.ElasticSql2DslException; 5 | import io.github.iamazy.elasticsearch.dsl.sql.parser.query.method.expr.MethodExpression; 6 | import io.github.iamazy.elasticsearch.dsl.sql.model.AtomicQuery; 7 | 8 | public interface MethodQueryParser extends MethodExpression { 9 | AtomicQuery parseMethodQuery(MethodInvocation invocation) throws ElasticSql2DslException; 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/io/github/iamazy/elasticsearch/dsl/sql/parser/query/method/ParameterizedMethodQueryParser.java: -------------------------------------------------------------------------------- 1 | package io.github.iamazy.elasticsearch.dsl.sql.parser.query.method; 2 | 3 | 4 | import com.alibaba.druid.sql.ast.SQLExpr; 5 | import io.github.iamazy.elasticsearch.dsl.cons.CoreConstants; 6 | import io.github.iamazy.elasticsearch.dsl.sql.druid.ElasticSqlExprParser; 7 | import io.github.iamazy.elasticsearch.dsl.sql.exception.ElasticSql2DslException; 8 | import io.github.iamazy.elasticsearch.dsl.sql.parser.query.method.expr.AbstractParameterizedMethodExpression; 9 | import io.github.iamazy.elasticsearch.dsl.sql.helper.ElasticSqlMethodInvokeHelper; 10 | import io.github.iamazy.elasticsearch.dsl.sql.model.AtomicQuery; 11 | import org.apache.commons.lang3.StringUtils; 12 | 13 | import java.util.Map; 14 | 15 | /** 16 | * @author iamazy 17 | */ 18 | public abstract class ParameterizedMethodQueryParser extends AbstractParameterizedMethodExpression implements MethodQueryParser { 19 | 20 | @Override 21 | protected abstract String defineExtraParamString(MethodInvocation invocation); 22 | 23 | protected abstract AtomicQuery parseMethodQueryWithExtraParams( 24 | MethodInvocation invocation, Map extraParamMap) throws ElasticSql2DslException; 25 | 26 | @Override 27 | public boolean isMatchMethodInvocation(MethodInvocation invocation) { 28 | return ElasticSqlMethodInvokeHelper.isMethodOf(defineMethodNames(), invocation.getMethodName()); 29 | } 30 | 31 | @Override 32 | public AtomicQuery parseMethodQuery(MethodInvocation invocation) throws ElasticSql2DslException { 33 | if (!isMatchMethodInvocation(invocation)) { 34 | throw new ElasticSql2DslException( 35 | String.format("[syntax error] Expected method name is one of [%s],but get [%s]", 36 | defineMethodNames(), invocation.getMethodName())); 37 | } 38 | checkMethodInvocation(invocation); 39 | 40 | Map extraParamMap = generateParameterMap(invocation); 41 | 42 | boolean highlighter = false; 43 | String field = null; 44 | for (int i = 0; i < invocation.getParameterCount(); i++) { 45 | SQLExpr expr = invocation.getParameter(i); 46 | if (StringUtils.isNotBlank(expr.toString())) { 47 | if (CoreConstants.HIGHLIGHTER.equalsIgnoreCase(expr.toString())) { 48 | throw new ElasticSql2DslException("[syntax error] the query field can not equals to 'h#'"); 49 | } 50 | if (expr.toString().startsWith(CoreConstants.HIGHLIGHTER)) { 51 | field = expr.toString().substring(CoreConstants.HIGHLIGHTER.length()); 52 | ElasticSqlExprParser elasticSqlExprParser = new ElasticSqlExprParser(field); 53 | SQLExpr sqlExpr = elasticSqlExprParser.expr(); 54 | invocation.getParameters().set(i, sqlExpr); 55 | highlighter = true; 56 | } 57 | 58 | } 59 | } 60 | AtomicQuery atomicQuery = parseMethodQueryWithExtraParams(invocation, extraParamMap); 61 | if (highlighter && StringUtils.isNotBlank(field)) { 62 | if (atomicQuery.isNestedQuery()) { 63 | if (field.startsWith(CoreConstants.DOLLAR)) { 64 | field = field.substring(CoreConstants.DOLLAR.length()); 65 | } 66 | field = field.replace(CoreConstants.DOLLAR, CoreConstants.DOT); 67 | atomicQuery.getHighlighter().add(field); 68 | } else { 69 | atomicQuery.getHighlighter().add(field); 70 | } 71 | } 72 | return atomicQuery; 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/main/java/io/github/iamazy/elasticsearch/dsl/sql/parser/query/method/expr/AbstractParameterizedMethodExpression.java: -------------------------------------------------------------------------------- 1 | package io.github.iamazy.elasticsearch.dsl.sql.parser.query.method.expr; 2 | 3 | import com.google.common.collect.Maps; 4 | import io.github.iamazy.elasticsearch.dsl.sql.exception.ElasticSql2DslException; 5 | import io.github.iamazy.elasticsearch.dsl.sql.parser.query.method.MethodInvocation; 6 | import org.apache.commons.collections4.MapUtils; 7 | import org.apache.commons.lang3.StringUtils; 8 | import org.apache.commons.lang3.math.NumberUtils; 9 | 10 | import java.util.Collections; 11 | import java.util.Map; 12 | 13 | import static io.github.iamazy.elasticsearch.dsl.cons.CoreConstants.COLON; 14 | import static io.github.iamazy.elasticsearch.dsl.cons.CoreConstants.COMMA; 15 | 16 | /** 17 | * @author iamazy 18 | */ 19 | public abstract class AbstractParameterizedMethodExpression implements ParameterizedMethodExpression { 20 | 21 | 22 | 23 | protected abstract String defineExtraParamString(MethodInvocation invocation); 24 | 25 | @Override 26 | public Map generateParameterMap(MethodInvocation invocation) { 27 | String extraParamString = defineExtraParamString(invocation); 28 | 29 | if (StringUtils.isBlank(extraParamString)) { 30 | return Collections.emptyMap(); 31 | } 32 | 33 | Map extraParamMap = Maps.newHashMap(); 34 | for (String paramPair : extraParamString.split(COMMA)) { 35 | String[] paramPairArr = paramPair.split(COLON); 36 | if (paramPairArr.length == 2) { 37 | extraParamMap.put(paramPairArr[0].trim(), paramPairArr[1].trim()); 38 | } 39 | else { 40 | throw new ElasticSql2DslException("Failed to parse query method extra param string!"); 41 | } 42 | } 43 | return extraParamMap; 44 | } 45 | 46 | public Map generateRawTypeParameterMap(MethodInvocation invocation) { 47 | Map extraParamMap = generateParameterMap(invocation); 48 | if (MapUtils.isNotEmpty(extraParamMap)) { 49 | return Maps.transformEntries(extraParamMap, (key, value) -> NumberUtils.isNumber(value) ? NumberUtils.createNumber(value) : value); 50 | } 51 | 52 | return Collections.emptyMap(); 53 | } 54 | 55 | protected Boolean isExtraParamsString(String extraParams) { 56 | if (StringUtils.isBlank(extraParams)) { 57 | return Boolean.FALSE; 58 | } 59 | for (String paramPair : extraParams.split(COMMA)) { 60 | String[] paramPairArr = paramPair.split(COLON); 61 | if (paramPairArr.length != 2) { 62 | return Boolean.FALSE; 63 | } 64 | } 65 | return Boolean.TRUE; 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/main/java/io/github/iamazy/elasticsearch/dsl/sql/parser/query/method/expr/FieldSpecificMethodExpression.java: -------------------------------------------------------------------------------- 1 | package io.github.iamazy.elasticsearch.dsl.sql.parser.query.method.expr; 2 | 3 | import com.alibaba.druid.sql.ast.SQLExpr; 4 | import io.github.iamazy.elasticsearch.dsl.sql.parser.query.method.MethodInvocation; 5 | 6 | 7 | public interface FieldSpecificMethodExpression extends MethodExpression { 8 | SQLExpr defineFieldExpr(MethodInvocation invocation); 9 | } 10 | -------------------------------------------------------------------------------- /src/main/java/io/github/iamazy/elasticsearch/dsl/sql/parser/query/method/expr/MethodExpression.java: -------------------------------------------------------------------------------- 1 | package io.github.iamazy.elasticsearch.dsl.sql.parser.query.method.expr; 2 | 3 | 4 | import io.github.iamazy.elasticsearch.dsl.sql.exception.ElasticSql2DslException; 5 | import io.github.iamazy.elasticsearch.dsl.sql.parser.query.method.MethodInvocation; 6 | 7 | import java.util.List; 8 | 9 | public interface MethodExpression { 10 | 11 | List defineMethodNames(); 12 | 13 | boolean isMatchMethodInvocation(MethodInvocation invocation); 14 | 15 | void checkMethodInvocation(MethodInvocation invocation) throws ElasticSql2DslException; 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/io/github/iamazy/elasticsearch/dsl/sql/parser/query/method/expr/ParameterizedMethodExpression.java: -------------------------------------------------------------------------------- 1 | package io.github.iamazy.elasticsearch.dsl.sql.parser.query.method.expr; 2 | 3 | 4 | import io.github.iamazy.elasticsearch.dsl.sql.parser.query.method.MethodInvocation; 5 | 6 | import java.util.Map; 7 | 8 | public interface ParameterizedMethodExpression extends MethodExpression { 9 | Map generateParameterMap(MethodInvocation methodInvocation); 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/io/github/iamazy/elasticsearch/dsl/sql/parser/query/method/fulltext/FullTextQueryParser.java: -------------------------------------------------------------------------------- 1 | package io.github.iamazy.elasticsearch.dsl.sql.parser.query.method.fulltext; 2 | 3 | import com.alibaba.druid.sql.ast.expr.SQLMethodInvokeExpr; 4 | import com.google.common.collect.ImmutableList; 5 | import io.github.iamazy.elasticsearch.dsl.sql.exception.ElasticSql2DslException; 6 | import io.github.iamazy.elasticsearch.dsl.sql.model.AtomicQuery; 7 | import io.github.iamazy.elasticsearch.dsl.sql.parser.query.method.MethodInvocation; 8 | import io.github.iamazy.elasticsearch.dsl.sql.parser.query.method.MethodQueryParser; 9 | 10 | 11 | import java.util.List; 12 | 13 | public class FullTextQueryParser { 14 | 15 | private final List methodQueryParsers; 16 | 17 | public FullTextQueryParser() { 18 | methodQueryParsers = ImmutableList.of( 19 | new MatchPhraseQueryParser(), 20 | new MatchPhrasePrefixQueryParser(), 21 | new MatchQueryParser(), 22 | new MultiMatchQueryParser(), 23 | new QueryStringQueryParser(), 24 | new SimpleQueryStringQueryParser(), 25 | new MatchAllQueryParser() 26 | ); 27 | } 28 | 29 | public Boolean isFulltextAtomQuery(MethodInvocation invocation) { 30 | return methodQueryParsers.stream().anyMatch(methodQueryParser -> methodQueryParser.isMatchMethodInvocation(invocation)); 31 | } 32 | 33 | public AtomicQuery parseFullTextAtomQuery(SQLMethodInvokeExpr methodQueryExpr, String queryAs) { 34 | MethodInvocation methodInvocation = new MethodInvocation(methodQueryExpr, queryAs); 35 | MethodQueryParser matchAtomQueryParser = getQueryParser(methodInvocation); 36 | return matchAtomQueryParser.parseMethodQuery(methodInvocation); 37 | } 38 | 39 | private MethodQueryParser getQueryParser(MethodInvocation invocation) { 40 | for (MethodQueryParser methodQueryParser : methodQueryParsers) { 41 | if (methodQueryParser.isMatchMethodInvocation(invocation)) { 42 | return methodQueryParser; 43 | } 44 | } 45 | throw new ElasticSql2DslException( 46 | String.format("[syntax error] Can not support method query expr[%s] condition", invocation.getMethodName())); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/main/java/io/github/iamazy/elasticsearch/dsl/sql/parser/query/method/fulltext/MatchAllQueryParser.java: -------------------------------------------------------------------------------- 1 | package io.github.iamazy.elasticsearch.dsl.sql.parser.query.method.fulltext; 2 | 3 | import com.google.common.collect.ImmutableList; 4 | import io.github.iamazy.elasticsearch.dsl.sql.exception.ElasticSql2DslException; 5 | import io.github.iamazy.elasticsearch.dsl.sql.model.AtomicQuery; 6 | import io.github.iamazy.elasticsearch.dsl.sql.parser.query.method.ParameterizedMethodQueryParser; 7 | import io.github.iamazy.elasticsearch.dsl.sql.parser.query.method.MethodInvocation; 8 | import org.apache.commons.lang3.StringUtils; 9 | import org.elasticsearch.index.query.QueryBuilders; 10 | 11 | import java.util.List; 12 | import java.util.Map; 13 | 14 | /** 15 | * @author iamazy 16 | * @date 2019/3/4 17 | * @descrition 18 | **/ 19 | public class MatchAllQueryParser extends ParameterizedMethodQueryParser { 20 | 21 | private static final List MATCH_ALL_METHOD = ImmutableList.of("matchAll", "match_all", "matchAllQuery","match_all_query"); 22 | 23 | 24 | @Override 25 | protected String defineExtraParamString(MethodInvocation invocation) { 26 | return isExtraParamsString(invocation.getLastParameterAsString()) 27 | ? invocation.getLastParameterAsString(): StringUtils.EMPTY; 28 | } 29 | 30 | @Override 31 | protected AtomicQuery parseMethodQueryWithExtraParams(MethodInvocation invocation, Map extraParamMap) throws ElasticSql2DslException { 32 | return new AtomicQuery(QueryBuilders.matchAllQuery()); 33 | } 34 | 35 | @Override 36 | public List defineMethodNames() { 37 | return MATCH_ALL_METHOD; 38 | } 39 | 40 | @Override 41 | public void checkMethodInvocation(MethodInvocation invocation) throws ElasticSql2DslException { 42 | int paramCount=invocation.getParameterCount(); 43 | if(paramCount!=0){ 44 | throw new ElasticSql2DslException( 45 | String.format("[syntax error] The method named [%s] params must be empty", invocation.getMethodName())); 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/main/java/io/github/iamazy/elasticsearch/dsl/sql/parser/query/method/fulltext/MatchPhrasePrefixQueryParser.java: -------------------------------------------------------------------------------- 1 | package io.github.iamazy.elasticsearch.dsl.sql.parser.query.method.fulltext; 2 | 3 | import com.alibaba.druid.sql.ast.SQLExpr; 4 | import com.google.common.collect.ImmutableList; 5 | import io.github.iamazy.elasticsearch.dsl.sql.exception.ElasticSql2DslException; 6 | import io.github.iamazy.elasticsearch.dsl.sql.parser.query.method.AbstractFieldSpecificMethodQueryParser; 7 | import io.github.iamazy.elasticsearch.dsl.sql.parser.query.method.MethodInvocation; 8 | import io.github.iamazy.elasticsearch.dsl.cons.ElasticConstants; 9 | import org.apache.commons.collections4.MapUtils; 10 | import org.apache.commons.lang3.StringUtils; 11 | import org.elasticsearch.index.query.*; 12 | 13 | import java.util.List; 14 | import java.util.Map; 15 | 16 | /** 17 | * @author iamazy 18 | * @date 2019/2/20 19 | * @descrition 20 | **/ 21 | public class MatchPhrasePrefixQueryParser extends AbstractFieldSpecificMethodQueryParser { 22 | 23 | private static final List MATCH_PHRASE_PREFIX_METHOD = ImmutableList.of("match_phrase_prefix", "match_phrase_prefix_query", "matchPhrasePrefixQuery"); 24 | 25 | @Override 26 | protected QueryBuilder buildQuery(MethodInvocation invocation, String fieldName, Map extraParams) { 27 | String text=invocation.getParameterAsString(1); 28 | MatchPhrasePrefixQueryBuilder matchPhrasePrefixQueryBuilder= QueryBuilders.matchPhrasePrefixQuery(fieldName,text); 29 | setExtraMatchQueryParam(matchPhrasePrefixQueryBuilder,extraParams); 30 | return matchPhrasePrefixQueryBuilder; 31 | } 32 | 33 | @Override 34 | protected String defineExtraParamString(MethodInvocation invocation) { 35 | int extraParamIdx = 2; 36 | 37 | return (invocation.getParameterCount() == extraParamIdx + 1) 38 | ? invocation.getParameterAsString(extraParamIdx) : StringUtils.EMPTY; 39 | } 40 | 41 | @Override 42 | public SQLExpr defineFieldExpr(MethodInvocation invocation) { 43 | return invocation.getParameter(0); 44 | } 45 | 46 | @Override 47 | public List defineMethodNames() { 48 | return MATCH_PHRASE_PREFIX_METHOD; 49 | } 50 | 51 | @Override 52 | public void checkMethodInvocation(MethodInvocation invocation) throws ElasticSql2DslException { 53 | if (invocation.getParameterCount() != 2 && invocation.getParameterCount() != 3) { 54 | throw new ElasticSql2DslException( 55 | String.format("[syntax error] There's no %s args method named [%s].", 56 | invocation.getParameterCount(), invocation.getMethodName())); 57 | } 58 | 59 | String text = invocation.getParameterAsString(1); 60 | if (StringUtils.isEmpty(text)) { 61 | throw new ElasticSql2DslException("[syntax error] Match search text can not be blank!"); 62 | } 63 | 64 | if (invocation.getParameterCount() == 3) { 65 | String extraParamString = defineExtraParamString(invocation); 66 | if (StringUtils.isEmpty(extraParamString)) { 67 | throw new ElasticSql2DslException("[syntax error] The extra param of match method can not be blank"); 68 | } 69 | } 70 | } 71 | 72 | private void setExtraMatchQueryParam(MatchPhrasePrefixQueryBuilder matchQuery, Map extraParamMap) { 73 | if (MapUtils.isEmpty(extraParamMap)) { 74 | return; 75 | } 76 | 77 | if (extraParamMap.containsKey(ElasticConstants.ANALYZER)) { 78 | String val = extraParamMap.get(ElasticConstants.ANALYZER); 79 | matchQuery.analyzer(val); 80 | } 81 | 82 | if (extraParamMap.containsKey(ElasticConstants.BOOST)) { 83 | String val = extraParamMap.get(ElasticConstants.BOOST); 84 | matchQuery.boost(Float.valueOf(val)); 85 | } 86 | 87 | if (extraParamMap.containsKey(ElasticConstants.MAX_EXPANSIONS)) { 88 | String val = extraParamMap.get(ElasticConstants.MAX_EXPANSIONS); 89 | matchQuery.maxExpansions(Integer.valueOf(val)); 90 | } 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /src/main/java/io/github/iamazy/elasticsearch/dsl/sql/parser/query/method/fulltext/MatchPhraseQueryParser.java: -------------------------------------------------------------------------------- 1 | package io.github.iamazy.elasticsearch.dsl.sql.parser.query.method.fulltext; 2 | 3 | import com.alibaba.druid.sql.ast.SQLExpr; 4 | import com.google.common.collect.ImmutableList; 5 | import io.github.iamazy.elasticsearch.dsl.sql.exception.ElasticSql2DslException; 6 | import io.github.iamazy.elasticsearch.dsl.sql.parser.query.method.AbstractFieldSpecificMethodQueryParser; 7 | import io.github.iamazy.elasticsearch.dsl.sql.parser.query.method.MethodInvocation; 8 | import io.github.iamazy.elasticsearch.dsl.cons.ElasticConstants; 9 | import org.apache.commons.collections4.MapUtils; 10 | import org.apache.commons.lang3.StringUtils; 11 | import org.elasticsearch.index.query.*; 12 | import org.elasticsearch.index.search.MatchQuery; 13 | 14 | import java.util.List; 15 | import java.util.Map; 16 | 17 | /** 18 | * @author iamazy 19 | * @date 2019/2/20 20 | * @descrition 21 | **/ 22 | public class MatchPhraseQueryParser extends AbstractFieldSpecificMethodQueryParser { 23 | 24 | 25 | private static final List MATCH_PHRASE_METHOD = ImmutableList.of("match_phrase", "match_phrase_query", "matchPhraseQuery"); 26 | 27 | @Override 28 | protected QueryBuilder buildQuery(MethodInvocation invocation, String fieldName, Map extraParams) { 29 | String text=invocation.getParameterAsString(1); 30 | MatchPhraseQueryBuilder matchPhraseQueryBuilder= QueryBuilders.matchPhraseQuery(fieldName,text); 31 | setExtraMatchQueryParam(matchPhraseQueryBuilder,extraParams); 32 | return matchPhraseQueryBuilder; 33 | } 34 | 35 | @Override 36 | protected String defineExtraParamString(MethodInvocation invocation) { 37 | int extraParamIdx = 2; 38 | 39 | return (invocation.getParameterCount() == extraParamIdx + 1) 40 | ? invocation.getParameterAsString(extraParamIdx) : StringUtils.EMPTY; 41 | } 42 | 43 | @Override 44 | public SQLExpr defineFieldExpr(MethodInvocation invocation) { 45 | return invocation.getParameter(0); 46 | } 47 | 48 | @Override 49 | public List defineMethodNames() { 50 | return MATCH_PHRASE_METHOD; 51 | } 52 | 53 | @Override 54 | public void checkMethodInvocation(MethodInvocation invocation) throws ElasticSql2DslException { 55 | if (invocation.getParameterCount() != 2 && invocation.getParameterCount() != 3) { 56 | throw new ElasticSql2DslException( 57 | String.format("[syntax error] There's no %s args method named [%s].", 58 | invocation.getParameterCount(), invocation.getMethodName())); 59 | } 60 | 61 | String text = invocation.getParameterAsString(1); 62 | if (StringUtils.isEmpty(text)) { 63 | throw new ElasticSql2DslException("[syntax error] Match search text can not be blank!"); 64 | } 65 | 66 | if (invocation.getParameterCount() == 3) { 67 | String extraParamString = defineExtraParamString(invocation); 68 | if (StringUtils.isEmpty(extraParamString)) { 69 | throw new ElasticSql2DslException("[syntax error] The extra param of match method can not be blank"); 70 | } 71 | } 72 | } 73 | 74 | private void setExtraMatchQueryParam(MatchPhraseQueryBuilder queryBuilder, Map extraParamMap) { 75 | if (MapUtils.isEmpty(extraParamMap)) { 76 | return; 77 | } 78 | if (extraParamMap.containsKey(ElasticConstants.ANALYZER)) { 79 | String val = extraParamMap.get(ElasticConstants.ANALYZER); 80 | queryBuilder.analyzer(val); 81 | } 82 | 83 | if (extraParamMap.containsKey(ElasticConstants.BOOST)) { 84 | String val = extraParamMap.get(ElasticConstants.BOOST); 85 | queryBuilder.boost(Float.valueOf(val)); 86 | } 87 | 88 | if (extraParamMap.containsKey(ElasticConstants.SLOP)) { 89 | String val = extraParamMap.get(ElasticConstants.SLOP); 90 | queryBuilder.slop(Integer.valueOf(val)); 91 | } 92 | 93 | if(extraParamMap.containsKey(ElasticConstants.ZERO_TERMS_QUERY)){ 94 | String val=extraParamMap.get(ElasticConstants.ZERO_TERMS_QUERY).toLowerCase(); 95 | switch (val){ 96 | case ElasticConstants.ALL:{ 97 | queryBuilder.zeroTermsQuery(MatchQuery.ZeroTermsQuery.ALL); 98 | break; 99 | } 100 | case ElasticConstants.NONE:{ 101 | queryBuilder.zeroTermsQuery(MatchQuery.ZeroTermsQuery.NONE); 102 | break; 103 | } 104 | case ElasticConstants.NULL:{ 105 | queryBuilder.zeroTermsQuery(MatchQuery.ZeroTermsQuery.NULL); 106 | break; 107 | } 108 | default:{ 109 | break; 110 | } 111 | } 112 | 113 | } 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /src/main/java/io/github/iamazy/elasticsearch/dsl/sql/parser/query/method/fulltext/SimpleQueryStringQueryParser.java: -------------------------------------------------------------------------------- 1 | package io.github.iamazy.elasticsearch.dsl.sql.parser.query.method.fulltext; 2 | 3 | import com.google.common.collect.ImmutableList; 4 | import com.google.common.collect.Lists; 5 | import io.github.iamazy.elasticsearch.dsl.sql.exception.ElasticSql2DslException; 6 | import io.github.iamazy.elasticsearch.dsl.sql.parser.query.method.MethodInvocation; 7 | import io.github.iamazy.elasticsearch.dsl.sql.parser.query.method.ParameterizedMethodQueryParser; 8 | import io.github.iamazy.elasticsearch.dsl.sql.model.AtomicQuery; 9 | import io.github.iamazy.elasticsearch.dsl.cons.ElasticConstants; 10 | import org.apache.commons.collections4.MapUtils; 11 | import org.apache.commons.lang3.StringUtils; 12 | import org.elasticsearch.index.query.Operator; 13 | import org.elasticsearch.index.query.QueryBuilders; 14 | import org.elasticsearch.index.query.SimpleQueryStringBuilder; 15 | import org.elasticsearch.index.query.SimpleQueryStringFlag; 16 | 17 | import java.util.List; 18 | import java.util.Map; 19 | 20 | import static io.github.iamazy.elasticsearch.dsl.cons.CoreConstants.COLON; 21 | import static io.github.iamazy.elasticsearch.dsl.cons.CoreConstants.COMMA; 22 | 23 | public class SimpleQueryStringQueryParser extends ParameterizedMethodQueryParser { 24 | 25 | private static List SIMPLE_QUERY_STRING_METHOD = ImmutableList.of("simpleQueryString", "simple_query_string"); 26 | 27 | @Override 28 | public List defineMethodNames() { 29 | return SIMPLE_QUERY_STRING_METHOD; 30 | } 31 | 32 | @Override 33 | protected String defineExtraParamString(MethodInvocation invocation) { 34 | return isExtraParamsString(invocation.getLastParameterAsString()) 35 | ? invocation.getLastParameterAsString() : StringUtils.EMPTY; 36 | } 37 | 38 | @Override 39 | protected AtomicQuery parseMethodQueryWithExtraParams(MethodInvocation invocation, Map extraParamMap) throws ElasticSql2DslException { 40 | String text = invocation.getParameterAsString(0); 41 | SimpleQueryStringBuilder simpleQueryString = QueryBuilders.simpleQueryStringQuery(text); 42 | 43 | String queryFields ; 44 | if (invocation.getParameterCount() == 3) { 45 | queryFields = invocation.getParameterAsString(1); 46 | 47 | if (StringUtils.isNotBlank(queryFields)) { 48 | String[] tArr = queryFields.split(COLON); 49 | if (ElasticConstants.FIELDS.equalsIgnoreCase(tArr[0])) { 50 | for (String fieldItem : tArr[1].split(COMMA)) { 51 | simpleQueryString.field(fieldItem); 52 | } 53 | } 54 | } 55 | } 56 | 57 | if (MapUtils.isNotEmpty(extraParamMap)) { 58 | setExtraMatchQueryParam(simpleQueryString, extraParamMap); 59 | } 60 | 61 | return new AtomicQuery(simpleQueryString); 62 | } 63 | 64 | @Override 65 | public void checkMethodInvocation(MethodInvocation invocation) throws ElasticSql2DslException { 66 | int paramCount = invocation.getParameterCount(); 67 | if (paramCount != 1 && paramCount != 2 && paramCount != 3) { 68 | throw new ElasticSql2DslException( 69 | String.format("[syntax error] There's no %s args method named [%s].", 70 | invocation.getParameterCount(), invocation.getMethodName())); 71 | } 72 | 73 | String text = invocation.getParameterAsString(0); 74 | 75 | if (StringUtils.isEmpty(text)) { 76 | throw new ElasticSql2DslException("[syntax error] Search text can not be blank!"); 77 | } 78 | 79 | if (paramCount == 3) { 80 | String strFields = invocation.getParameterAsString(1); 81 | 82 | if (StringUtils.isEmpty(text)) { 83 | throw new ElasticSql2DslException("[syntax error] Search fields can not be empty!"); 84 | } 85 | String[] tArr = strFields.split(COLON); 86 | 87 | if (tArr.length != 2) { 88 | throw new ElasticSql2DslException("[syntax error] queryString method args error"); 89 | } 90 | 91 | if (Boolean.FALSE == ElasticConstants.FIELDS.equalsIgnoreCase(tArr[0])) { 92 | throw new ElasticSql2DslException("[syntax error] Search fields name should one of [fields]"); 93 | } 94 | } 95 | } 96 | 97 | private void setExtraMatchQueryParam(SimpleQueryStringBuilder simpleStringQuery, Map extraParamMap) { 98 | if (MapUtils.isEmpty(extraParamMap)) { 99 | return; 100 | } 101 | 102 | if (extraParamMap.containsKey(ElasticConstants.MINIMUM_SHOULD_MATCH)) { 103 | String val = extraParamMap.get(ElasticConstants.MINIMUM_SHOULD_MATCH); 104 | simpleStringQuery.minimumShouldMatch(val); 105 | } 106 | 107 | if (extraParamMap.containsKey(ElasticConstants.ANALYZER)) { 108 | String val = extraParamMap.get(ElasticConstants.ANALYZER); 109 | simpleStringQuery.analyzer(val); 110 | } 111 | 112 | if (extraParamMap.containsKey(ElasticConstants.BOOST)) { 113 | String val = extraParamMap.get(ElasticConstants.BOOST); 114 | simpleStringQuery.boost(Float.valueOf(val)); 115 | } 116 | 117 | if (extraParamMap.containsKey(ElasticConstants.ANALYZE_WILDCARD)) { 118 | String val = extraParamMap.get(ElasticConstants.ANALYZE_WILDCARD); 119 | simpleStringQuery.analyzeWildcard(Boolean.parseBoolean(val)); 120 | } 121 | 122 | if (extraParamMap.containsKey(ElasticConstants.FLAGS)) { 123 | String[] flags = extraParamMap.get(ElasticConstants.FLAGS).split("\\|"); 124 | List flagList = Lists.newLinkedList(); 125 | for (String flag : flags) { 126 | flagList.add(SimpleQueryStringFlag.valueOf(flag.toUpperCase())); 127 | } 128 | simpleStringQuery.flags(flagList.toArray(new SimpleQueryStringFlag[0])); 129 | } 130 | 131 | 132 | if (extraParamMap.containsKey(ElasticConstants.DEFAULT_OPERATOR)) { 133 | String val = extraParamMap.get(ElasticConstants.DEFAULT_OPERATOR); 134 | 135 | if (ElasticConstants.AND.equalsIgnoreCase(val)) { 136 | simpleStringQuery.defaultOperator(Operator.AND); 137 | } 138 | if (ElasticConstants.OR.equalsIgnoreCase(val)) { 139 | simpleStringQuery.defaultOperator(Operator.OR); 140 | } 141 | } 142 | } 143 | } 144 | -------------------------------------------------------------------------------- /src/main/java/io/github/iamazy/elasticsearch/dsl/sql/parser/query/method/join/HasChildQueryParser.java: -------------------------------------------------------------------------------- 1 | package io.github.iamazy.elasticsearch.dsl.sql.parser.query.method.join; 2 | 3 | import com.alibaba.druid.sql.ast.SQLExpr; 4 | import com.google.common.collect.ImmutableList; 5 | import io.github.iamazy.elasticsearch.dsl.sql.exception.ElasticSql2DslException; 6 | import io.github.iamazy.elasticsearch.dsl.sql.helper.ElasticSqlMethodInvokeHelper; 7 | import io.github.iamazy.elasticsearch.dsl.sql.model.AtomicQuery; 8 | import io.github.iamazy.elasticsearch.dsl.sql.parser.query.method.MethodInvocation; 9 | import io.github.iamazy.elasticsearch.dsl.sql.parser.query.method.MethodQueryParser; 10 | import io.github.iamazy.elasticsearch.dsl.sql.parser.sql.BoolExpressionParser; 11 | import org.apache.lucene.search.join.ScoreMode; 12 | import org.elasticsearch.index.query.BoolQueryBuilder; 13 | import org.elasticsearch.join.query.HasChildQueryBuilder; 14 | import org.elasticsearch.join.query.JoinQueryBuilders; 15 | 16 | 17 | import java.util.List; 18 | 19 | /** 20 | * has_child(childType, filterExpression, minChildren, maxChildren) 21 | *

22 | * has_child('collection_plan', principal > 1000) 23 | *

24 | * has_child('collection_plan', principal > 1000, 1, 4) 25 | */ 26 | public class HasChildQueryParser implements MethodQueryParser { 27 | 28 | private static List HAS_CHILD_METHOD = ImmutableList.of("has_child", "hasChild", "has_child_query", "hasChildQuery"); 29 | 30 | @Override 31 | public List defineMethodNames() { 32 | return HAS_CHILD_METHOD; 33 | } 34 | 35 | @Override 36 | public boolean isMatchMethodInvocation(MethodInvocation invocation) { 37 | return ElasticSqlMethodInvokeHelper.isMethodOf(defineMethodNames(), invocation.getMethodName()); 38 | } 39 | 40 | @Override 41 | public void checkMethodInvocation(MethodInvocation invocation) throws ElasticSql2DslException { 42 | if (invocation.getParameterCount() != 2 && invocation.getParameterCount() != 4) { 43 | throw new ElasticSql2DslException( 44 | String.format("[syntax error] There's no %s args method named [%s].", 45 | invocation.getParameterCount(), invocation.getMethodName())); 46 | } 47 | } 48 | 49 | @Override 50 | public AtomicQuery parseMethodQuery(MethodInvocation invocation) throws ElasticSql2DslException { 51 | String childType = invocation.getParameterAsString(0); 52 | SQLExpr filter = invocation.getParameter(1); 53 | 54 | BoolExpressionParser boolExpressionParser = new BoolExpressionParser(); 55 | String queryAs = invocation.getQueryAs(); 56 | 57 | BoolQueryBuilder filterBuilder = boolExpressionParser.parseBoolQueryExpr(filter, queryAs); 58 | HasChildQueryBuilder hasChildQueryBuilder = JoinQueryBuilders.hasChildQuery(childType, filterBuilder, ScoreMode.None); 59 | 60 | if (invocation.getParameterCount() == 4) { 61 | Long minChildren = invocation.getParameterAsLong(2); 62 | Long maxChildren = invocation.getParameterAsLong(3); 63 | 64 | hasChildQueryBuilder.minMaxChildren(minChildren.intValue(), maxChildren.intValue()); 65 | } 66 | 67 | return new AtomicQuery(hasChildQueryBuilder); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/main/java/io/github/iamazy/elasticsearch/dsl/sql/parser/query/method/join/HasParentQueryParser.java: -------------------------------------------------------------------------------- 1 | package io.github.iamazy.elasticsearch.dsl.sql.parser.query.method.join; 2 | 3 | import com.alibaba.druid.sql.ast.SQLExpr; 4 | import com.google.common.collect.ImmutableList; 5 | import io.github.iamazy.elasticsearch.dsl.sql.exception.ElasticSql2DslException; 6 | import io.github.iamazy.elasticsearch.dsl.sql.helper.ElasticSqlMethodInvokeHelper; 7 | import io.github.iamazy.elasticsearch.dsl.sql.model.AtomicQuery; 8 | import io.github.iamazy.elasticsearch.dsl.sql.parser.query.method.MethodQueryParser; 9 | import io.github.iamazy.elasticsearch.dsl.sql.parser.query.method.MethodInvocation; 10 | import io.github.iamazy.elasticsearch.dsl.sql.parser.sql.BoolExpressionParser; 11 | import org.elasticsearch.index.query.BoolQueryBuilder; 12 | import org.elasticsearch.join.query.HasParentQueryBuilder; 13 | import org.elasticsearch.join.query.JoinQueryBuilders; 14 | 15 | import java.util.List; 16 | 17 | /** 18 | * has_parent(parentType, filterExpression) 19 | *

20 | * has_parent('investment', principal > 100 and status='SUCCESS') 21 | * 22 | */ 23 | public class HasParentQueryParser implements MethodQueryParser { 24 | 25 | private static List HAS_PARENT_METHOD = ImmutableList.of("has_parent", "hasParent", "has_parent_query", "hasParentQuery"); 26 | 27 | @Override 28 | public List defineMethodNames() { 29 | return HAS_PARENT_METHOD; 30 | } 31 | 32 | @Override 33 | public boolean isMatchMethodInvocation(MethodInvocation invocation) { 34 | return ElasticSqlMethodInvokeHelper.isMethodOf(defineMethodNames(), invocation.getMethodName()); 35 | } 36 | 37 | @Override 38 | public void checkMethodInvocation(MethodInvocation invocation) throws ElasticSql2DslException { 39 | if (invocation.getParameterCount() != 2) { 40 | throw new ElasticSql2DslException( 41 | String.format("[syntax error] There's no %s args method named [%s].", 42 | invocation.getParameterCount(), invocation.getMethodName())); 43 | } 44 | } 45 | 46 | @Override 47 | public AtomicQuery parseMethodQuery(MethodInvocation invocation) throws ElasticSql2DslException { 48 | String parentType = invocation.getParameterAsString(0); 49 | SQLExpr filter = invocation.getParameter(1); 50 | 51 | BoolExpressionParser boolExpressionParser = new BoolExpressionParser(); 52 | String queryAs = invocation.getQueryAs(); 53 | 54 | BoolQueryBuilder filterBuilder = boolExpressionParser.parseBoolQueryExpr(filter, queryAs); 55 | HasParentQueryBuilder hasParentQueryBuilder = JoinQueryBuilders.hasParentQuery(parentType, filterBuilder, false); 56 | 57 | return new AtomicQuery(hasParentQueryBuilder); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/main/java/io/github/iamazy/elasticsearch/dsl/sql/parser/query/method/join/JoinQueryParser.java: -------------------------------------------------------------------------------- 1 | package io.github.iamazy.elasticsearch.dsl.sql.parser.query.method.join; 2 | 3 | import com.alibaba.druid.sql.ast.expr.SQLMethodInvokeExpr; 4 | import com.google.common.collect.ImmutableList; 5 | import io.github.iamazy.elasticsearch.dsl.sql.exception.ElasticSql2DslException; 6 | import io.github.iamazy.elasticsearch.dsl.sql.model.AtomicQuery; 7 | import io.github.iamazy.elasticsearch.dsl.sql.parser.query.method.MethodInvocation; 8 | import io.github.iamazy.elasticsearch.dsl.sql.parser.query.method.MethodQueryParser; 9 | 10 | import java.util.List; 11 | 12 | public class JoinQueryParser { 13 | 14 | private final List joinQueryParsers; 15 | 16 | public JoinQueryParser() { 17 | joinQueryParsers = ImmutableList.of( 18 | new HasParentQueryParser(), 19 | new HasChildQueryParser() 20 | ); 21 | } 22 | 23 | public Boolean isJoinAtomQuery(MethodInvocation invocation) { 24 | return joinQueryParsers.stream().anyMatch(methodQueryParser -> methodQueryParser.isMatchMethodInvocation(invocation)); 25 | } 26 | 27 | public AtomicQuery parseJoinAtomQuery(SQLMethodInvokeExpr methodQueryExpr, String queryAs) { 28 | MethodInvocation methodInvocation = new MethodInvocation(methodQueryExpr, queryAs); 29 | MethodQueryParser joinAtomQueryParser = getQueryParser(methodInvocation); 30 | joinAtomQueryParser.checkMethodInvocation(methodInvocation); 31 | return joinAtomQueryParser.parseMethodQuery(methodInvocation); 32 | } 33 | 34 | private MethodQueryParser getQueryParser(MethodInvocation methodInvocation) { 35 | for (MethodQueryParser joinQueryParserItem : joinQueryParsers) { 36 | if (joinQueryParserItem.isMatchMethodInvocation(methodInvocation)) { 37 | return joinQueryParserItem; 38 | } 39 | } 40 | throw new ElasticSql2DslException( 41 | String.format("[syntax error] Can not support join query expr[%s] condition", 42 | methodInvocation.getMethodName())); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/io/github/iamazy/elasticsearch/dsl/sql/parser/query/method/mapping/MappingQueryParser.java: -------------------------------------------------------------------------------- 1 | package io.github.iamazy.elasticsearch.dsl.sql.parser.query.method.mapping; 2 | 3 | 4 | import io.github.iamazy.elasticsearch.dsl.sql.exception.ElasticSql2DslException; 5 | import org.apache.commons.lang3.StringUtils; 6 | import org.elasticsearch.action.admin.indices.mapping.get.GetFieldMappingsRequest; 7 | import org.elasticsearch.action.admin.indices.mapping.get.GetMappingsRequest; 8 | 9 | /** 10 | * @author iamazy 11 | * @date 2019/5/5 12 | * @descrition 13 | **/ 14 | public class MappingQueryParser { 15 | 16 | public static Object parse(String sql) { 17 | String[] descItems = StringUtils.split(sql, " "); 18 | GetFieldMappingsRequest getFieldMappingsRequest = new GetFieldMappingsRequest(); 19 | GetMappingsRequest getMappingsRequest = new GetMappingsRequest(); 20 | if (descItems.length == 2) { 21 | String[] items = descItems[1].split("/"); 22 | switch (items.length) { 23 | case 2: { 24 | getFieldMappingsRequest.indices(items[0]); 25 | getFieldMappingsRequest.fields(items[1]); 26 | return getFieldMappingsRequest; 27 | } 28 | case 1: 29 | default: { 30 | getMappingsRequest.indices(items[0]); 31 | return getMappingsRequest; 32 | } 33 | } 34 | } else { 35 | throw new ElasticSql2DslException("[syntax error] desc must have table name or table/field name"); 36 | } 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/io/github/iamazy/elasticsearch/dsl/sql/parser/query/method/score/BoostingQueryParser.java: -------------------------------------------------------------------------------- 1 | package io.github.iamazy.elasticsearch.dsl.sql.parser.query.method.score; 2 | 3 | import com.alibaba.druid.sql.ast.SQLExpr; 4 | import com.alibaba.druid.sql.ast.expr.SQLNumberExpr; 5 | import com.google.common.collect.ImmutableList; 6 | import io.github.iamazy.elasticsearch.dsl.sql.exception.ElasticSql2DslException; 7 | import io.github.iamazy.elasticsearch.dsl.sql.helper.ElasticSqlMethodInvokeHelper; 8 | import io.github.iamazy.elasticsearch.dsl.sql.model.AtomicQuery; 9 | import io.github.iamazy.elasticsearch.dsl.sql.parser.query.method.MethodInvocation; 10 | import io.github.iamazy.elasticsearch.dsl.sql.parser.query.method.MethodQueryParser; 11 | import io.github.iamazy.elasticsearch.dsl.sql.parser.sql.BoolExpressionParser; 12 | import org.elasticsearch.index.query.BoolQueryBuilder; 13 | import org.elasticsearch.index.query.BoostingQueryBuilder; 14 | import org.elasticsearch.index.query.QueryBuilders; 15 | 16 | import java.util.List; 17 | 18 | /** 19 | * @author iamazy 20 | * @date 2019/4/9 21 | * @descrition 22 | **/ 23 | public class BoostingQueryParser implements MethodQueryParser { 24 | 25 | private static List BOOSTING_METHOD = ImmutableList.of("boosting"); 26 | 27 | @Override 28 | public AtomicQuery parseMethodQuery(MethodInvocation invocation) throws ElasticSql2DslException { 29 | 30 | SQLExpr positiveExpr = invocation.getParameter(0); 31 | SQLExpr negativeExpr=invocation.getParameter(1); 32 | Float negativeBoost=((SQLNumberExpr) invocation.getParameter(2)).getNumber().floatValue(); 33 | BoolExpressionParser boolExpressionParser = new BoolExpressionParser(); 34 | BoolQueryBuilder positiveQuery = boolExpressionParser.parseBoolQueryExpr(positiveExpr, invocation.getQueryAs()); 35 | BoolQueryBuilder negativeQuery=boolExpressionParser.parseBoolQueryExpr(negativeExpr,invocation.getQueryAs()); 36 | BoostingQueryBuilder boostingQueryBuilder = QueryBuilders.boostingQuery(positiveQuery,negativeQuery).negativeBoost(negativeBoost); 37 | AtomicQuery atomicQuery= new AtomicQuery(boostingQueryBuilder); 38 | atomicQuery.getHighlighter().addAll(boolExpressionParser.getHighlighter()); 39 | return atomicQuery; 40 | 41 | } 42 | 43 | @Override 44 | public List defineMethodNames() { 45 | return BOOSTING_METHOD; 46 | } 47 | 48 | @Override 49 | public boolean isMatchMethodInvocation(MethodInvocation invocation) { 50 | return ElasticSqlMethodInvokeHelper.isMethodOf(defineMethodNames(), invocation.getMethodName()); 51 | } 52 | 53 | @Override 54 | public void checkMethodInvocation(MethodInvocation invocation) throws ElasticSql2DslException { 55 | if (invocation.getParameterCount() != 3) { 56 | throw new ElasticSql2DslException( 57 | String.format("[syntax error] There's no %s args method named [%s].", 58 | invocation.getParameterCount(), invocation.getMethodName())); 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/main/java/io/github/iamazy/elasticsearch/dsl/sql/parser/query/method/score/ScoreQueryParser.java: -------------------------------------------------------------------------------- 1 | package io.github.iamazy.elasticsearch.dsl.sql.parser.query.method.score; 2 | 3 | import com.alibaba.druid.sql.ast.expr.SQLMethodInvokeExpr; 4 | import com.google.common.collect.ImmutableList; 5 | import io.github.iamazy.elasticsearch.dsl.sql.exception.ElasticSql2DslException; 6 | import io.github.iamazy.elasticsearch.dsl.sql.model.AtomicQuery; 7 | import io.github.iamazy.elasticsearch.dsl.sql.parser.query.method.MethodInvocation; 8 | import io.github.iamazy.elasticsearch.dsl.sql.parser.query.method.MethodQueryParser; 9 | import lombok.Data; 10 | 11 | import java.util.List; 12 | 13 | 14 | /** 15 | * @author iamazy 16 | * @date 2019/4/9 17 | * @descrition 18 | **/ 19 | @Data 20 | public class ScoreQueryParser { 21 | 22 | private final List scoreQueryParsers; 23 | 24 | public ScoreQueryParser() { 25 | scoreQueryParsers = ImmutableList.of( 26 | new BoostingQueryParser(), 27 | new FunctionScoreQueryParser() 28 | ); 29 | } 30 | 31 | public Boolean isScoreAtomQuery(MethodInvocation invocation) { 32 | return scoreQueryParsers.stream().anyMatch(methodQueryParser -> methodQueryParser.isMatchMethodInvocation(invocation)); 33 | } 34 | 35 | public AtomicQuery parseScoreAtomQuery(SQLMethodInvokeExpr methodQueryExpr, String queryAs) { 36 | MethodInvocation methodInvocation = new MethodInvocation(methodQueryExpr, queryAs); 37 | MethodQueryParser joinAtomQueryParser = getQueryParser(methodInvocation); 38 | joinAtomQueryParser.checkMethodInvocation(methodInvocation); 39 | return joinAtomQueryParser.parseMethodQuery(methodInvocation); 40 | } 41 | 42 | private MethodQueryParser getQueryParser(MethodInvocation methodInvocation) { 43 | for (MethodQueryParser scoreQueryParserItem : scoreQueryParsers) { 44 | if (scoreQueryParserItem.isMatchMethodInvocation(methodInvocation)) { 45 | return scoreQueryParserItem; 46 | } 47 | } 48 | throw new ElasticSql2DslException( 49 | String.format("[syntax error] Can not support score query expr[%s] condition", 50 | methodInvocation.getMethodName())); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/main/java/io/github/iamazy/elasticsearch/dsl/sql/parser/query/method/script/ScriptQueryParser.java: -------------------------------------------------------------------------------- 1 | package io.github.iamazy.elasticsearch.dsl.sql.parser.query.method.script; 2 | 3 | import com.google.common.collect.ImmutableList; 4 | import io.github.iamazy.elasticsearch.dsl.sql.exception.ElasticSql2DslException; 5 | import io.github.iamazy.elasticsearch.dsl.sql.model.AtomicQuery; 6 | import io.github.iamazy.elasticsearch.dsl.sql.parser.query.method.MethodInvocation; 7 | import io.github.iamazy.elasticsearch.dsl.sql.parser.query.method.ParameterizedMethodQueryParser; 8 | import org.apache.commons.collections4.MapUtils; 9 | import org.apache.commons.lang3.StringUtils; 10 | import org.elasticsearch.index.query.QueryBuilders; 11 | import org.elasticsearch.script.Script; 12 | 13 | import java.util.List; 14 | import java.util.Map; 15 | 16 | public class ScriptQueryParser extends ParameterizedMethodQueryParser { 17 | 18 | private static List SCRIPT_METHOD = ImmutableList.of("script_query", "scriptQuery"); 19 | 20 | @Override 21 | public List defineMethodNames() { 22 | return SCRIPT_METHOD; 23 | } 24 | 25 | @Override 26 | protected String defineExtraParamString(MethodInvocation invocation) { 27 | int extraParamIdx = 1; 28 | 29 | return (invocation.getParameterCount() == extraParamIdx + 1) 30 | ? invocation.getParameterAsString(extraParamIdx) : StringUtils.EMPTY; 31 | } 32 | 33 | @Override 34 | public void checkMethodInvocation(MethodInvocation invocation) throws ElasticSql2DslException { 35 | if (invocation.getParameterCount() != 1 && invocation.getParameterCount() != 2) { 36 | throw new ElasticSql2DslException( 37 | String.format("[syntax error] There's no %s args method named [%s].", 38 | invocation.getParameterCount(), invocation.getMethodName())); 39 | } 40 | 41 | String script = invocation.getParameterAsString(0); 42 | if (StringUtils.isEmpty(script)) { 43 | throw new ElasticSql2DslException("[syntax error] Script can not be blank!"); 44 | } 45 | } 46 | 47 | @Override 48 | protected AtomicQuery parseMethodQueryWithExtraParams(MethodInvocation invocation, Map extraParamMap) throws ElasticSql2DslException { 49 | String script = invocation.getParameterAsString(0); 50 | 51 | if (MapUtils.isNotEmpty(extraParamMap)) { 52 | Map scriptParamMap = generateRawTypeParameterMap(invocation); 53 | return new AtomicQuery(QueryBuilders.scriptQuery( 54 | new Script(Script.DEFAULT_SCRIPT_TYPE, Script.DEFAULT_SCRIPT_LANG, script, scriptParamMap)) 55 | ); 56 | } 57 | return new AtomicQuery(QueryBuilders.scriptQuery(new Script(script))); 58 | } 59 | 60 | 61 | } 62 | -------------------------------------------------------------------------------- /src/main/java/io/github/iamazy/elasticsearch/dsl/sql/parser/query/method/term/FuzzyQueryParser.java: -------------------------------------------------------------------------------- 1 | package io.github.iamazy.elasticsearch.dsl.sql.parser.query.method.term; 2 | 3 | import com.alibaba.druid.sql.ast.SQLExpr; 4 | import com.google.common.collect.ImmutableList; 5 | import io.github.iamazy.elasticsearch.dsl.sql.exception.ElasticSql2DslException; 6 | import io.github.iamazy.elasticsearch.dsl.cons.ElasticConstants; 7 | import io.github.iamazy.elasticsearch.dsl.sql.parser.query.method.AbstractFieldSpecificMethodQueryParser; 8 | import io.github.iamazy.elasticsearch.dsl.sql.parser.query.method.MethodInvocation; 9 | import org.apache.commons.collections4.MapUtils; 10 | import org.apache.commons.lang3.StringUtils; 11 | import org.elasticsearch.common.unit.Fuzziness; 12 | import org.elasticsearch.index.query.FuzzyQueryBuilder; 13 | import org.elasticsearch.index.query.QueryBuilder; 14 | import org.elasticsearch.index.query.QueryBuilders; 15 | 16 | import java.util.List; 17 | import java.util.Map; 18 | 19 | public class FuzzyQueryParser extends AbstractFieldSpecificMethodQueryParser { 20 | 21 | private static final List FUZZY_QUERY_METHOD = ImmutableList.of("fuzzy", "fuzzy_query", "fuzzyQuery"); 22 | 23 | FuzzyQueryParser() { 24 | super(); 25 | } 26 | 27 | @Override 28 | public List defineMethodNames() { 29 | return FUZZY_QUERY_METHOD; 30 | } 31 | 32 | @Override 33 | public SQLExpr defineFieldExpr(MethodInvocation invocation) { 34 | return invocation.getParameter(0); 35 | } 36 | 37 | @Override 38 | protected String defineExtraParamString(MethodInvocation invocation) { 39 | int extraParamIdx = 2; 40 | 41 | return (invocation.getParameterCount() == extraParamIdx + 1) 42 | ? invocation.getParameterAsString(extraParamIdx) : StringUtils.EMPTY; 43 | } 44 | 45 | @Override 46 | public void checkMethodInvocation(MethodInvocation invocation) { 47 | if (invocation.getParameterCount() != 2 && invocation.getParameterCount() != 3) { 48 | throw new ElasticSql2DslException( 49 | String.format("[syntax error] There's no %s args method named [%s].", 50 | invocation.getParameterCount(), invocation.getMethodName())); 51 | } 52 | 53 | String text = invocation.getParameterAsString(1); 54 | if (StringUtils.isEmpty(text)) { 55 | throw new ElasticSql2DslException("[syntax error] Fuzzy search text can not be blank!"); 56 | } 57 | 58 | if (invocation.getParameterCount() == 3) { 59 | String extraParamString = defineExtraParamString(invocation); 60 | if (StringUtils.isEmpty(extraParamString)) { 61 | throw new ElasticSql2DslException("[syntax error] The extra param of fuzzy method can not be blank"); 62 | } 63 | } 64 | } 65 | 66 | @Override 67 | protected QueryBuilder buildQuery(MethodInvocation invocation, String fieldName, Map extraParams) { 68 | String text = invocation.getParameterAsString(1); 69 | FuzzyQueryBuilder fuzzyQuery = QueryBuilders.fuzzyQuery(fieldName, text); 70 | 71 | setExtraMatchQueryParam(fuzzyQuery, extraParams); 72 | return fuzzyQuery; 73 | } 74 | 75 | private void setExtraMatchQueryParam(FuzzyQueryBuilder fuzzyQuery, Map extraParamMap) { 76 | if (MapUtils.isEmpty(extraParamMap)) { 77 | return; 78 | } 79 | if (extraParamMap.containsKey(ElasticConstants.BOOST)) { 80 | String val = extraParamMap.get(ElasticConstants.BOOST); 81 | fuzzyQuery.boost(Float.valueOf(val)); 82 | } 83 | if (extraParamMap.containsKey(ElasticConstants.TRANSPOSITIONS)) { 84 | String val = extraParamMap.get(ElasticConstants.TRANSPOSITIONS); 85 | fuzzyQuery.transpositions(Boolean.parseBoolean(val)); 86 | } 87 | if (extraParamMap.containsKey(ElasticConstants.PREFIX_LENGTH)) { 88 | String val = extraParamMap.get(ElasticConstants.PREFIX_LENGTH); 89 | fuzzyQuery.prefixLength(Integer.valueOf(val)); 90 | } 91 | if (extraParamMap.containsKey(ElasticConstants.MAX_EXPANSIONS)) { 92 | String val = extraParamMap.get(ElasticConstants.MAX_EXPANSIONS); 93 | fuzzyQuery.maxExpansions(Integer.valueOf(val)); 94 | } 95 | if (extraParamMap.containsKey(ElasticConstants.REWRITE)) { 96 | String val = extraParamMap.get(ElasticConstants.REWRITE); 97 | fuzzyQuery.rewrite(val); 98 | } 99 | 100 | if (extraParamMap.containsKey(ElasticConstants.FUZZINESS)) { 101 | String val = extraParamMap.get(ElasticConstants.FUZZINESS).toLowerCase(); 102 | 103 | switch (val){ 104 | case "0": 105 | case "zero":{ 106 | fuzzyQuery.fuzziness(Fuzziness.ZERO); 107 | break; 108 | } 109 | case "1": 110 | case "one":{ 111 | fuzzyQuery.fuzziness(Fuzziness.ONE); 112 | break; 113 | } 114 | case "2": 115 | case "two":{ 116 | fuzzyQuery.fuzziness(Fuzziness.TWO); 117 | break; 118 | } 119 | default:{ 120 | fuzzyQuery.fuzziness(Fuzziness.AUTO); 121 | } 122 | } 123 | } 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /src/main/java/io/github/iamazy/elasticsearch/dsl/sql/parser/query/method/term/PrefixQueryParser.java: -------------------------------------------------------------------------------- 1 | package io.github.iamazy.elasticsearch.dsl.sql.parser.query.method.term; 2 | 3 | import com.alibaba.druid.sql.ast.SQLExpr; 4 | import com.google.common.collect.ImmutableList; 5 | import io.github.iamazy.elasticsearch.dsl.sql.exception.ElasticSql2DslException; 6 | import io.github.iamazy.elasticsearch.dsl.cons.ElasticConstants; 7 | import io.github.iamazy.elasticsearch.dsl.sql.parser.query.method.AbstractFieldSpecificMethodQueryParser; 8 | import io.github.iamazy.elasticsearch.dsl.sql.parser.query.method.MethodInvocation; 9 | import org.apache.commons.collections4.MapUtils; 10 | import org.apache.commons.lang3.StringUtils; 11 | import org.elasticsearch.index.query.PrefixQueryBuilder; 12 | import org.elasticsearch.index.query.QueryBuilder; 13 | import org.elasticsearch.index.query.QueryBuilders; 14 | 15 | import java.util.List; 16 | import java.util.Map; 17 | 18 | /** 19 | * @author iamazy 20 | */ 21 | public class PrefixQueryParser extends AbstractFieldSpecificMethodQueryParser { 22 | 23 | private static List PREFIX_QUERY_METHOD = ImmutableList.of("prefix", "prefix_query", "prefixQuery"); 24 | 25 | @Override 26 | public List defineMethodNames() { 27 | return PREFIX_QUERY_METHOD; 28 | } 29 | 30 | @Override 31 | public SQLExpr defineFieldExpr(MethodInvocation invocation) { 32 | return invocation.getParameter(0); 33 | } 34 | 35 | @Override 36 | protected String defineExtraParamString(MethodInvocation invocation) { 37 | int extraParamIdx = 2; 38 | 39 | return (invocation.getParameterCount() == extraParamIdx + 1) 40 | ? invocation.getParameterAsString(extraParamIdx) : StringUtils.EMPTY; 41 | } 42 | 43 | @Override 44 | public void checkMethodInvocation(MethodInvocation invocation) { 45 | if (invocation.getParameterCount() != 2 && invocation.getParameterCount() != 3) { 46 | throw new ElasticSql2DslException( 47 | String.format("[syntax error] There's no %s args method named [%s].", 48 | invocation.getParameterCount(), invocation.getMethodName())); 49 | } 50 | 51 | String text = invocation.getParameterAsString(1); 52 | if (StringUtils.isEmpty(text)) { 53 | throw new ElasticSql2DslException("[syntax error] Prefix search text can not be blank!"); 54 | } 55 | 56 | if (invocation.getParameterCount() == 3) { 57 | String extraParamString = defineExtraParamString(invocation); 58 | if (StringUtils.isEmpty(extraParamString)) { 59 | throw new ElasticSql2DslException("[syntax error] The extra param of prefix method can not be blank"); 60 | } 61 | } 62 | } 63 | 64 | @Override 65 | protected QueryBuilder buildQuery(MethodInvocation invocation, String fieldName, Map extraParams) { 66 | String text = invocation.getParameterAsString(1); 67 | PrefixQueryBuilder prefixQuery = QueryBuilders.prefixQuery(fieldName, text); 68 | 69 | setExtraMatchQueryParam(prefixQuery, extraParams); 70 | return prefixQuery; 71 | } 72 | 73 | private void setExtraMatchQueryParam(PrefixQueryBuilder prefixQuery, Map extraParamMap) { 74 | if (MapUtils.isEmpty(extraParamMap)) { 75 | return; 76 | } 77 | if (extraParamMap.containsKey(ElasticConstants.BOOST)) { 78 | String val = extraParamMap.get(ElasticConstants.BOOST); 79 | prefixQuery.boost(Float.valueOf(val)); 80 | } 81 | if (extraParamMap.containsKey(ElasticConstants.REWRITE)) { 82 | String val = extraParamMap.get(ElasticConstants.REWRITE); 83 | prefixQuery.rewrite(val); 84 | } 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /src/main/java/io/github/iamazy/elasticsearch/dsl/sql/parser/query/method/term/RegexpQueryParser.java: -------------------------------------------------------------------------------- 1 | package io.github.iamazy.elasticsearch.dsl.sql.parser.query.method.term; 2 | 3 | import com.alibaba.druid.sql.ast.SQLExpr; 4 | import com.google.common.collect.ImmutableList; 5 | import com.google.common.collect.Lists; 6 | import io.github.iamazy.elasticsearch.dsl.sql.exception.ElasticSql2DslException; 7 | import io.github.iamazy.elasticsearch.dsl.cons.ElasticConstants; 8 | import io.github.iamazy.elasticsearch.dsl.sql.parser.query.method.AbstractFieldSpecificMethodQueryParser; 9 | import io.github.iamazy.elasticsearch.dsl.sql.parser.query.method.MethodInvocation; 10 | import org.apache.commons.collections4.MapUtils; 11 | import org.apache.commons.lang3.StringUtils; 12 | import org.elasticsearch.index.query.QueryBuilder; 13 | import org.elasticsearch.index.query.QueryBuilders; 14 | import org.elasticsearch.index.query.RegexpFlag; 15 | import org.elasticsearch.index.query.RegexpQueryBuilder; 16 | 17 | 18 | import java.util.List; 19 | import java.util.Map; 20 | 21 | public class RegexpQueryParser extends AbstractFieldSpecificMethodQueryParser { 22 | 23 | private static List REGEXP_QUERY_METHOD = ImmutableList.of("regexp", "regexp_query", "regexpQuery"); 24 | 25 | RegexpQueryParser() { 26 | 27 | } 28 | 29 | @Override 30 | public List defineMethodNames() { 31 | return REGEXP_QUERY_METHOD; 32 | } 33 | 34 | @Override 35 | public SQLExpr defineFieldExpr(MethodInvocation invocation) { 36 | return invocation.getParameter(0); 37 | } 38 | 39 | @Override 40 | protected String defineExtraParamString(MethodInvocation invocation) { 41 | int extraParamIdx = 2; 42 | 43 | return (invocation.getParameterCount() == extraParamIdx + 1) 44 | ? invocation.getParameterAsString(extraParamIdx) : StringUtils.EMPTY; 45 | } 46 | 47 | @Override 48 | public void checkMethodInvocation(MethodInvocation invocation) throws ElasticSql2DslException { 49 | if (invocation.getParameterCount() != 2 && invocation.getParameterCount() != 3) { 50 | throw new ElasticSql2DslException( 51 | String.format("[syntax error] There's no %s args method named [%s].", 52 | invocation.getParameterCount(), invocation.getMethodName())); 53 | } 54 | 55 | String text = invocation.getParameterAsString(1); 56 | if (StringUtils.isEmpty(text)) { 57 | throw new ElasticSql2DslException("[syntax error] Regexp search text can not be blank!"); 58 | } 59 | 60 | if (invocation.getParameterCount() == 3) { 61 | String extraParamString = defineExtraParamString(invocation); 62 | if (StringUtils.isEmpty(extraParamString)) { 63 | throw new ElasticSql2DslException("[syntax error] The extra param of regexp method can not be blank"); 64 | } 65 | } 66 | } 67 | 68 | @Override 69 | protected QueryBuilder buildQuery(MethodInvocation invocation, String fieldName, Map extraParams) { 70 | String text = invocation.getParameterAsString(1); 71 | RegexpQueryBuilder regexpQuery = QueryBuilders.regexpQuery(fieldName, text); 72 | 73 | setExtraMatchQueryParam(regexpQuery, extraParams); 74 | return regexpQuery; 75 | } 76 | 77 | 78 | private void setExtraMatchQueryParam(RegexpQueryBuilder regexpQuery, Map extraParamMap) { 79 | if (MapUtils.isEmpty(extraParamMap)) { 80 | return; 81 | } 82 | if (extraParamMap.containsKey(ElasticConstants.BOOST)) { 83 | String val = extraParamMap.get(ElasticConstants.BOOST); 84 | regexpQuery.boost(Float.valueOf(val)); 85 | } 86 | if (extraParamMap.containsKey(ElasticConstants.REWRITE)) { 87 | String val = extraParamMap.get(ElasticConstants.REWRITE); 88 | regexpQuery.rewrite(val); 89 | } 90 | if (extraParamMap.containsKey(ElasticConstants.MAX_DETERMINIZED_STATES)) { 91 | String val = extraParamMap.get(ElasticConstants.MAX_DETERMINIZED_STATES); 92 | regexpQuery.maxDeterminizedStates(Integer.valueOf(val)); 93 | } 94 | if (extraParamMap.containsKey(ElasticConstants.FLAGS)) { 95 | String[] flags = extraParamMap.get(ElasticConstants.FLAGS).split("\\|"); 96 | List flagList = Lists.newLinkedList(); 97 | for (String flag : flags) { 98 | flagList.add(RegexpFlag.valueOf(flag.toUpperCase())); 99 | } 100 | regexpQuery.flags(flagList.toArray(new RegexpFlag[0])); 101 | } 102 | if (extraParamMap.containsKey(ElasticConstants.FLAGS_VALUE)) { 103 | String[] flags = extraParamMap.get(ElasticConstants.FLAGS_VALUE).split("\\|"); 104 | List flagList = Lists.newLinkedList(); 105 | for (String flag : flags) { 106 | flagList.add(RegexpFlag.valueOf(flag.toUpperCase())); 107 | } 108 | regexpQuery.flags(flagList.toArray(new RegexpFlag[0])); 109 | } 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /src/main/java/io/github/iamazy/elasticsearch/dsl/sql/parser/query/method/term/TermLevelAtomicQueryParser.java: -------------------------------------------------------------------------------- 1 | package io.github.iamazy.elasticsearch.dsl.sql.parser.query.method.term; 2 | 3 | import com.alibaba.druid.sql.ast.expr.SQLMethodInvokeExpr; 4 | import com.google.common.collect.ImmutableList; 5 | import io.github.iamazy.elasticsearch.dsl.sql.exception.ElasticSql2DslException; 6 | import io.github.iamazy.elasticsearch.dsl.sql.model.AtomicQuery; 7 | import io.github.iamazy.elasticsearch.dsl.sql.parser.query.method.MethodQueryParser; 8 | import io.github.iamazy.elasticsearch.dsl.sql.parser.query.method.MethodInvocation; 9 | 10 | 11 | import java.util.List; 12 | 13 | public class TermLevelAtomicQueryParser { 14 | 15 | private final List methodQueryParsers; 16 | 17 | public TermLevelAtomicQueryParser() { 18 | methodQueryParsers = ImmutableList.of( 19 | new PrefixQueryParser(), 20 | new TermQueryParser(), 21 | new TermsQueryParser(), 22 | new WildcardQueryParser(), 23 | new RegexpQueryParser(), 24 | new FuzzyQueryParser() 25 | ); 26 | } 27 | 28 | public Boolean isTermLevelAtomQuery(MethodInvocation invocation) { 29 | return methodQueryParsers.stream().anyMatch(methodQueryParser -> methodQueryParser.isMatchMethodInvocation(invocation)); 30 | } 31 | 32 | public AtomicQuery parseTermLevelAtomQuery(SQLMethodInvokeExpr methodQueryExpr, String queryAs) { 33 | MethodInvocation methodInvocation = new MethodInvocation(methodQueryExpr, queryAs); 34 | MethodQueryParser matchAtomQueryParser = getQueryParser(methodInvocation); 35 | return matchAtomQueryParser.parseMethodQuery(methodInvocation); 36 | } 37 | 38 | private MethodQueryParser getQueryParser(MethodInvocation methodInvocation) { 39 | for (MethodQueryParser methodQueryParserItem : methodQueryParsers) { 40 | if (methodQueryParserItem.isMatchMethodInvocation(methodInvocation)) { 41 | return methodQueryParserItem; 42 | } 43 | } 44 | throw new ElasticSql2DslException( 45 | String.format("[syntax error] Can not support method query expr[%s] condition", 46 | methodInvocation.getMethodName())); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/main/java/io/github/iamazy/elasticsearch/dsl/sql/parser/query/method/term/TermQueryParser.java: -------------------------------------------------------------------------------- 1 | package io.github.iamazy.elasticsearch.dsl.sql.parser.query.method.term; 2 | 3 | import com.alibaba.druid.sql.ast.SQLExpr; 4 | import com.google.common.collect.ImmutableList; 5 | import io.github.iamazy.elasticsearch.dsl.sql.exception.ElasticSql2DslException; 6 | import io.github.iamazy.elasticsearch.dsl.cons.ElasticConstants; 7 | import io.github.iamazy.elasticsearch.dsl.sql.parser.query.method.AbstractFieldSpecificMethodQueryParser; 8 | import io.github.iamazy.elasticsearch.dsl.sql.parser.query.method.MethodInvocation; 9 | import org.apache.commons.collections4.MapUtils; 10 | import org.apache.commons.lang3.StringUtils; 11 | import org.elasticsearch.index.query.QueryBuilder; 12 | import org.elasticsearch.index.query.QueryBuilders; 13 | import org.elasticsearch.index.query.TermQueryBuilder; 14 | 15 | import java.util.List; 16 | import java.util.Map; 17 | 18 | public class TermQueryParser extends AbstractFieldSpecificMethodQueryParser { 19 | 20 | private static List TERM_QUERY_METHOD = ImmutableList.of("term", "term_query", "termQuery"); 21 | 22 | @Override 23 | public List defineMethodNames() { 24 | return TERM_QUERY_METHOD; 25 | } 26 | 27 | @Override 28 | public SQLExpr defineFieldExpr(MethodInvocation invocation) { 29 | return invocation.getParameter(0); 30 | } 31 | 32 | @Override 33 | protected String defineExtraParamString(MethodInvocation invocation) { 34 | int extraParamIdx = 2; 35 | 36 | return (invocation.getParameterCount() == extraParamIdx + 1) 37 | ? invocation.getParameterAsString(extraParamIdx) : StringUtils.EMPTY; 38 | } 39 | 40 | @Override 41 | public void checkMethodInvocation(MethodInvocation invocation) throws ElasticSql2DslException { 42 | if (invocation.getParameterCount() != 2 && invocation.getParameterCount() != 3) { 43 | throw new ElasticSql2DslException( 44 | String.format("[syntax error] There's no %s args method named [%s].", 45 | invocation.getParameterCount(), invocation.getMethodName())); 46 | } 47 | 48 | String text = invocation.getParameterAsString(1); 49 | if (StringUtils.isEmpty(text)) { 50 | throw new ElasticSql2DslException("[syntax error] Term search text can not be blank!"); 51 | } 52 | 53 | if (invocation.getParameterCount() == 3) { 54 | String extraParamString = defineExtraParamString(invocation); 55 | if (StringUtils.isEmpty(extraParamString)) { 56 | throw new ElasticSql2DslException("[syntax error] The extra param of term method can not be blank"); 57 | } 58 | } 59 | } 60 | 61 | @Override 62 | protected QueryBuilder buildQuery(MethodInvocation invocation, String fieldName, Map extraParams) { 63 | String text = invocation.getParameterAsString(1); 64 | 65 | TermQueryBuilder termQuery = QueryBuilders.termQuery(fieldName, text); 66 | setExtraMatchQueryParam(termQuery, extraParams); 67 | 68 | return termQuery; 69 | } 70 | 71 | private void setExtraMatchQueryParam(TermQueryBuilder termQuery, Map extraParamMap) { 72 | if (MapUtils.isEmpty(extraParamMap)) { 73 | return; 74 | } 75 | if (extraParamMap.containsKey(ElasticConstants.BOOST)) { 76 | String val = extraParamMap.get(ElasticConstants.BOOST); 77 | termQuery.boost(Float.valueOf(val)); 78 | } 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /src/main/java/io/github/iamazy/elasticsearch/dsl/sql/parser/query/method/term/TermsQueryParser.java: -------------------------------------------------------------------------------- 1 | package io.github.iamazy.elasticsearch.dsl.sql.parser.query.method.term; 2 | 3 | import com.alibaba.druid.sql.ast.SQLExpr; 4 | import com.google.common.collect.ImmutableList; 5 | import com.google.common.collect.Lists; 6 | import io.github.iamazy.elasticsearch.dsl.sql.exception.ElasticSql2DslException; 7 | import io.github.iamazy.elasticsearch.dsl.sql.parser.query.method.AbstractFieldSpecificMethodQueryParser; 8 | import io.github.iamazy.elasticsearch.dsl.cons.ElasticConstants; 9 | import io.github.iamazy.elasticsearch.dsl.sql.parser.query.method.MethodInvocation; 10 | import org.apache.commons.collections4.MapUtils; 11 | import org.apache.commons.lang3.StringUtils; 12 | import org.elasticsearch.index.query.QueryBuilder; 13 | import org.elasticsearch.index.query.QueryBuilders; 14 | import org.elasticsearch.index.query.TermsQueryBuilder; 15 | 16 | import java.util.List; 17 | import java.util.Map; 18 | 19 | public class TermsQueryParser extends AbstractFieldSpecificMethodQueryParser { 20 | 21 | private static List TERMS_QUERY_METHOD = ImmutableList.of("terms", "terms_query", "termsQuery"); 22 | 23 | 24 | @Override 25 | public List defineMethodNames() { 26 | return TERMS_QUERY_METHOD; 27 | } 28 | 29 | @Override 30 | public SQLExpr defineFieldExpr(MethodInvocation invocation) { 31 | return invocation.getParameter(0); 32 | } 33 | 34 | @Override 35 | protected String defineExtraParamString(MethodInvocation invocation) { 36 | String extraParamString = invocation.getLastParameterAsString(); 37 | if (isExtraParamsString(extraParamString)) { 38 | return extraParamString; 39 | } 40 | return StringUtils.EMPTY; 41 | } 42 | 43 | @Override 44 | public void checkMethodInvocation(MethodInvocation invocation) { 45 | if (invocation.getParameterCount() <= 1) { 46 | throw new ElasticSql2DslException( 47 | String.format("[syntax error] There's no %s args method named [%s].", 48 | invocation.getParameterCount(), invocation.getMethodName())); 49 | } 50 | 51 | int paramCount = invocation.getParameterCount(); 52 | 53 | for (int idx = 1; idx < paramCount - 1; idx++) { 54 | String text = invocation.getParameterAsString(idx); 55 | if (StringUtils.isEmpty(text)) { 56 | throw new ElasticSql2DslException("[syntax error] Terms text can not be blank!"); 57 | } 58 | } 59 | } 60 | 61 | @Override 62 | protected QueryBuilder buildQuery(MethodInvocation invocation, String fieldName, Map extraParams) { 63 | int paramCount = invocation.getParameterCount(); 64 | 65 | List termTextList = Lists.newArrayList(); 66 | for (int idx = 1; idx < paramCount - 1; idx++) { 67 | String text = invocation.getParameterAsString(idx); 68 | termTextList.add(text); 69 | } 70 | 71 | String lastParamText = invocation.getLastParameterAsString(); 72 | if (!isExtraParamsString(lastParamText)) { 73 | termTextList.add(lastParamText); 74 | } 75 | 76 | TermsQueryBuilder termsQuery = QueryBuilders.termsQuery(fieldName, termTextList); 77 | setExtraMatchQueryParam(termsQuery, extraParams); 78 | return termsQuery; 79 | } 80 | 81 | private void setExtraMatchQueryParam(TermsQueryBuilder termsQuery, Map extraParamMap) { 82 | if (MapUtils.isEmpty(extraParamMap)) { 83 | return; 84 | } 85 | if (extraParamMap.containsKey(ElasticConstants.BOOST)) { 86 | String val = extraParamMap.get(ElasticConstants.BOOST); 87 | termsQuery.boost(Float.valueOf(val)); 88 | } 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /src/main/java/io/github/iamazy/elasticsearch/dsl/sql/parser/query/method/term/WildcardQueryParser.java: -------------------------------------------------------------------------------- 1 | package io.github.iamazy.elasticsearch.dsl.sql.parser.query.method.term; 2 | 3 | import com.alibaba.druid.sql.ast.SQLExpr; 4 | import com.google.common.collect.ImmutableList; 5 | import io.github.iamazy.elasticsearch.dsl.sql.exception.ElasticSql2DslException; 6 | import io.github.iamazy.elasticsearch.dsl.sql.parser.query.method.AbstractFieldSpecificMethodQueryParser; 7 | import io.github.iamazy.elasticsearch.dsl.cons.ElasticConstants; 8 | import io.github.iamazy.elasticsearch.dsl.sql.parser.query.method.MethodInvocation; 9 | import org.apache.commons.collections4.MapUtils; 10 | import org.apache.commons.lang3.StringUtils; 11 | import org.elasticsearch.index.query.QueryBuilder; 12 | import org.elasticsearch.index.query.QueryBuilders; 13 | import org.elasticsearch.index.query.WildcardQueryBuilder; 14 | 15 | 16 | import java.util.List; 17 | import java.util.Map; 18 | 19 | public class WildcardQueryParser extends AbstractFieldSpecificMethodQueryParser { 20 | 21 | private static List WILDCARD_QUERY_METHOD = ImmutableList.of("wildcard", "wildcard_query", "wildcardQuery"); 22 | 23 | @Override 24 | public List defineMethodNames() { 25 | return WILDCARD_QUERY_METHOD; 26 | } 27 | 28 | @Override 29 | protected String defineExtraParamString(MethodInvocation invocation) { 30 | int extraParamIdx = 2; 31 | 32 | return (invocation.getParameterCount() == extraParamIdx + 1) 33 | ? invocation.getParameterAsString(extraParamIdx) : StringUtils.EMPTY; 34 | } 35 | 36 | @Override 37 | public SQLExpr defineFieldExpr(MethodInvocation invocation) { 38 | return invocation.getParameter(0); 39 | } 40 | 41 | @Override 42 | public void checkMethodInvocation(MethodInvocation invocation) throws ElasticSql2DslException { 43 | if (invocation.getParameterCount() != 2 && invocation.getParameterCount() != 3) { 44 | throw new ElasticSql2DslException( 45 | String.format("[syntax error] There's no %s args method named [%s].", 46 | invocation.getParameterCount(), invocation.getMethodName())); 47 | } 48 | 49 | String text = invocation.getParameterAsString(1); 50 | if (StringUtils.isEmpty(text)) { 51 | throw new ElasticSql2DslException("[syntax error] Wildcard search text can not be blank!"); 52 | } 53 | 54 | if (invocation.getParameterCount() == 3) { 55 | String extraParamString = defineExtraParamString(invocation); 56 | if (StringUtils.isEmpty(extraParamString)) { 57 | throw new ElasticSql2DslException("[syntax error] The extra param of wildcard method can not be blank"); 58 | } 59 | } 60 | } 61 | 62 | @Override 63 | protected QueryBuilder buildQuery(MethodInvocation invocation, String fieldName, Map extraParams) { 64 | String text = invocation.getParameterAsString(1); 65 | WildcardQueryBuilder wildcardQuery = QueryBuilders.wildcardQuery(fieldName, text); 66 | 67 | setExtraMatchQueryParam(wildcardQuery, extraParams); 68 | return wildcardQuery; 69 | } 70 | 71 | private void setExtraMatchQueryParam(WildcardQueryBuilder wildcardQuery, Map extraParamMap) { 72 | if (MapUtils.isEmpty(extraParamMap)) { 73 | return; 74 | } 75 | if (extraParamMap.containsKey(ElasticConstants.BOOST)) { 76 | String val = extraParamMap.get(ElasticConstants.BOOST); 77 | wildcardQuery.boost(Float.valueOf(val)); 78 | } 79 | if (extraParamMap.containsKey(ElasticConstants.REWRITE)) { 80 | String val = extraParamMap.get(ElasticConstants.REWRITE); 81 | wildcardQuery.rewrite(val); 82 | } 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /src/main/java/io/github/iamazy/elasticsearch/dsl/sql/parser/sql/QueryFromParser.java: -------------------------------------------------------------------------------- 1 | package io.github.iamazy.elasticsearch.dsl.sql.parser.sql; 2 | 3 | import com.alibaba.druid.sql.ast.expr.SQLIdentifierExpr; 4 | import com.alibaba.druid.sql.ast.expr.SQLPropertyExpr; 5 | import com.alibaba.druid.sql.ast.expr.SQLQueryExpr; 6 | import com.alibaba.druid.sql.ast.statement.SQLDeleteStatement; 7 | import com.alibaba.druid.sql.ast.statement.SQLExprTableSource; 8 | import com.google.common.collect.Lists; 9 | import io.github.iamazy.elasticsearch.dsl.sql.druid.ElasticSqlSelectQueryBlock; 10 | import io.github.iamazy.elasticsearch.dsl.sql.exception.ElasticSql2DslException; 11 | import io.github.iamazy.elasticsearch.dsl.sql.model.ElasticDslContext; 12 | 13 | 14 | public class QueryFromParser implements QueryParser { 15 | 16 | @Override 17 | public void parse(ElasticDslContext dslContext) { 18 | SQLExprTableSource tableSource; 19 | if (dslContext.getSqlObject() instanceof SQLDeleteStatement) { 20 | SQLDeleteStatement sqlDeleteStatement = (SQLDeleteStatement) dslContext.getSqlObject(); 21 | tableSource = sqlDeleteStatement.getExprTableSource(); 22 | extractFrom(tableSource,dslContext); 23 | } 24 | 25 | if (dslContext.getSqlObject() instanceof SQLQueryExpr) { 26 | ElasticSqlSelectQueryBlock queryBlock = (ElasticSqlSelectQueryBlock) ((SQLQueryExpr) dslContext.getSqlObject()).getSubQuery().getQuery(); 27 | if (queryBlock.getFrom() instanceof SQLExprTableSource) { 28 | tableSource = (SQLExprTableSource) queryBlock.getFrom(); 29 | extractFrom(tableSource,dslContext); 30 | } 31 | 32 | } 33 | } 34 | 35 | 36 | private void extractFrom(SQLExprTableSource tableSource,ElasticDslContext dslContext){ 37 | dslContext.getParseResult().setQueryAs(tableSource.getAlias()); 38 | 39 | if (tableSource.getExpr() instanceof SQLIdentifierExpr) { 40 | String index = ((SQLIdentifierExpr) tableSource.getExpr()).getName(); 41 | dslContext.getParseResult().setIndices(Lists.newArrayList(index)); 42 | return; 43 | } 44 | 45 | if (tableSource.getExpr() instanceof SQLPropertyExpr) { 46 | SQLPropertyExpr idxExpr = (SQLPropertyExpr) tableSource.getExpr(); 47 | 48 | if (!(idxExpr.getOwner() instanceof SQLIdentifierExpr)) { 49 | throw new ElasticSql2DslException("[syntax error] From table should like [index].[type]"); 50 | } 51 | 52 | String index = ((SQLIdentifierExpr) idxExpr.getOwner()).getName(); 53 | dslContext.getParseResult().setIndices(Lists.newArrayList(index)); 54 | dslContext.getParseResult().setType(idxExpr.getName()); 55 | return; 56 | } 57 | 58 | throw new ElasticSql2DslException("[syntax error] From table should like [index].[type]"); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/main/java/io/github/iamazy/elasticsearch/dsl/sql/parser/sql/QueryLimitSizeParser.java: -------------------------------------------------------------------------------- 1 | package io.github.iamazy.elasticsearch.dsl.sql.parser.sql; 2 | 3 | import com.alibaba.druid.sql.ast.SQLExpr; 4 | import com.alibaba.druid.sql.ast.expr.SQLIntegerExpr; 5 | import com.alibaba.druid.sql.ast.expr.SQLQueryExpr; 6 | import com.alibaba.druid.sql.ast.expr.SQLVariantRefExpr; 7 | import io.github.iamazy.elasticsearch.dsl.sql.druid.ElasticSqlSelectQueryBlock; 8 | import io.github.iamazy.elasticsearch.dsl.sql.exception.ElasticSql2DslException; 9 | import io.github.iamazy.elasticsearch.dsl.sql.helper.ElasticSqlArgConverter; 10 | import io.github.iamazy.elasticsearch.dsl.sql.model.ElasticDslContext; 11 | 12 | 13 | public class QueryLimitSizeParser implements QueryParser { 14 | 15 | 16 | 17 | public QueryLimitSizeParser() { } 18 | 19 | @Override 20 | public void parse(ElasticDslContext dslContext) { 21 | 22 | if (dslContext.getSqlObject() instanceof SQLQueryExpr) { 23 | ElasticSqlSelectQueryBlock queryBlock = (ElasticSqlSelectQueryBlock) ((SQLQueryExpr) dslContext.getSqlObject()).getSubQuery().getQuery(); 24 | if (queryBlock.getLimit0() != null) { 25 | Integer from = parseLimitInteger(queryBlock.getLimit0().getOffset()); 26 | dslContext.getParseResult().setFrom(from); 27 | 28 | Integer size = parseLimitInteger(queryBlock.getLimit0().getRowCount()); 29 | dslContext.getParseResult().setSize(size); 30 | } else { 31 | dslContext.getParseResult().setFrom(0); 32 | dslContext.getParseResult().setSize(15); 33 | } 34 | } 35 | } 36 | 37 | private Integer parseLimitInteger(SQLExpr limitInt) { 38 | if (limitInt instanceof SQLIntegerExpr) { 39 | return ((SQLIntegerExpr) limitInt).getNumber().intValue(); 40 | } else if (limitInt instanceof SQLVariantRefExpr) { 41 | SQLVariantRefExpr varLimitExpr = (SQLVariantRefExpr) limitInt; 42 | Object targetVal = ElasticSqlArgConverter.convertSqlArg(varLimitExpr); 43 | if (!(targetVal instanceof Integer)) { 44 | throw new ElasticSql2DslException("[syntax error] Sql limit expr should be a non-negative number"); 45 | } 46 | return Integer.valueOf(targetVal.toString()); 47 | } else { 48 | throw new ElasticSql2DslException("[syntax error] Sql limit expr should be a non-negative number"); 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/main/java/io/github/iamazy/elasticsearch/dsl/sql/parser/sql/QueryMatchConditionParser.java: -------------------------------------------------------------------------------- 1 | package io.github.iamazy.elasticsearch.dsl.sql.parser.sql; 2 | 3 | 4 | import com.alibaba.druid.sql.ast.expr.SQLQueryExpr; 5 | import io.github.iamazy.elasticsearch.dsl.sql.druid.ElasticSqlSelectQueryBlock; 6 | import io.github.iamazy.elasticsearch.dsl.sql.model.ElasticDslContext; 7 | import org.elasticsearch.index.query.BoolQueryBuilder; 8 | 9 | /** 10 | * @author iamazy 11 | */ 12 | public class QueryMatchConditionParser extends BoolExpressionParser implements QueryParser{ 13 | 14 | @Override 15 | public void parse(ElasticDslContext dslContext) { 16 | 17 | if(dslContext.getSqlObject() instanceof SQLQueryExpr) { 18 | ElasticSqlSelectQueryBlock queryBlock = (ElasticSqlSelectQueryBlock) ((SQLQueryExpr) dslContext.getSqlObject()).getSubQuery().getQuery(); 19 | if (queryBlock.getMatchQuery() != null) { 20 | String queryAs = dslContext.getParseResult().getQueryAs(); 21 | BoolQueryBuilder matchQuery = parseBoolQueryExpr(queryBlock.getMatchQuery(), queryAs); 22 | dslContext.getParseResult().setMatchCondition(matchQuery); 23 | dslContext.getParseResult().getHighlighter().addAll(this.getHighlighter()); 24 | } 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/io/github/iamazy/elasticsearch/dsl/sql/parser/sql/QueryOrderConditionParser.java: -------------------------------------------------------------------------------- 1 | package io.github.iamazy.elasticsearch.dsl.sql.parser.sql; 2 | 3 | import com.alibaba.druid.sql.ast.SQLOrderBy; 4 | import com.alibaba.druid.sql.ast.SQLOrderingSpecification; 5 | import com.alibaba.druid.sql.ast.expr.SQLMethodInvokeExpr; 6 | import com.alibaba.druid.sql.ast.expr.SQLQueryExpr; 7 | import com.alibaba.druid.sql.ast.statement.SQLSelectOrderByItem; 8 | import com.google.common.collect.ImmutableList; 9 | import com.google.common.collect.Lists; 10 | 11 | import io.github.iamazy.elasticsearch.dsl.sql.druid.ElasticSqlSelectQueryBlock; 12 | import io.github.iamazy.elasticsearch.dsl.sql.exception.ElasticSql2DslException; 13 | import io.github.iamazy.elasticsearch.dsl.sql.parser.query.method.MethodInvocation; 14 | import io.github.iamazy.elasticsearch.dsl.sql.parser.sql.sort.*; 15 | import io.github.iamazy.elasticsearch.dsl.sql.model.ElasticDslContext; 16 | import io.github.iamazy.elasticsearch.dsl.sql.model.ElasticSqlQueryField; 17 | import org.apache.commons.collections4.CollectionUtils; 18 | import org.elasticsearch.search.sort.SortBuilder; 19 | import org.elasticsearch.search.sort.SortBuilders; 20 | import org.elasticsearch.search.sort.SortOrder; 21 | 22 | 23 | import java.util.List; 24 | 25 | public class QueryOrderConditionParser implements QueryParser { 26 | 27 | 28 | private List methodSortParsers; 29 | 30 | public QueryOrderConditionParser() { 31 | 32 | methodSortParsers = ImmutableList.of( 33 | new NvlMethodSortParser(), 34 | new ScriptMethodSortParser(), 35 | new NestedSortMethodParser() 36 | ); 37 | } 38 | 39 | @Override 40 | public void parse(ElasticDslContext dslContext) { 41 | ElasticSqlSelectQueryBlock queryBlock = (ElasticSqlSelectQueryBlock) ((SQLQueryExpr)dslContext.getSqlObject()).getSubQuery().getQuery(); 42 | SQLOrderBy sqlOrderBy = queryBlock.getOrderBy(); 43 | if (sqlOrderBy != null && CollectionUtils.isNotEmpty(sqlOrderBy.getItems())) { 44 | List orderByList = Lists.newLinkedList(); 45 | 46 | String queryAs = dslContext.getParseResult().getQueryAs(); 47 | 48 | for (SQLSelectOrderByItem orderByItem : sqlOrderBy.getItems()) { 49 | SortBuilder orderBy = parseOrderCondition(orderByItem, queryAs); 50 | if (orderBy != null) { 51 | orderByList.add(orderBy); 52 | } 53 | } 54 | dslContext.getParseResult().setOrderBy(orderByList); 55 | } 56 | } 57 | 58 | private SortBuilder parseOrderCondition(SQLSelectOrderByItem orderByItem, String queryAs) { 59 | 60 | SortOrder order = orderByItem.getType() == SQLOrderingSpecification.ASC ? SortOrder.ASC : SortOrder.DESC; 61 | 62 | if (ParseSortBuilderHelper.isFieldExpr(orderByItem.getExpr())) { 63 | QueryFieldParser fieldParser = new QueryFieldParser(); 64 | ElasticSqlQueryField sortField = fieldParser.parseConditionQueryField(orderByItem.getExpr(), queryAs); 65 | return ParseSortBuilderHelper.parseBasedOnFieldSortBuilder(sortField, queryFieldName -> SortBuilders.fieldSort(queryFieldName).order(order)); 66 | } 67 | 68 | if (ParseSortBuilderHelper.isMethodInvokeExpr(orderByItem.getExpr())) { 69 | MethodInvocation sortMethodInvocation = new MethodInvocation((SQLMethodInvokeExpr) orderByItem.getExpr(), queryAs); 70 | for (MethodSortParser methodSortParser : methodSortParsers) { 71 | if (methodSortParser.isMatchMethodInvocation(sortMethodInvocation)) { 72 | return methodSortParser.parseMethodSortBuilder(sortMethodInvocation, order); 73 | } 74 | } 75 | } 76 | 77 | throw new ElasticSql2DslException("[syntax error] can not support sort type: " + orderByItem.getExpr().getClass()); 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/main/java/io/github/iamazy/elasticsearch/dsl/sql/parser/sql/QueryParser.java: -------------------------------------------------------------------------------- 1 | package io.github.iamazy.elasticsearch.dsl.sql.parser.sql; 2 | 3 | 4 | import io.github.iamazy.elasticsearch.dsl.sql.model.ElasticDslContext; 5 | 6 | @FunctionalInterface 7 | public interface QueryParser { 8 | void parse(ElasticDslContext dslContext); 9 | } 10 | -------------------------------------------------------------------------------- /src/main/java/io/github/iamazy/elasticsearch/dsl/sql/parser/sql/QueryRoutingValParser.java: -------------------------------------------------------------------------------- 1 | package io.github.iamazy.elasticsearch.dsl.sql.parser.sql; 2 | 3 | import com.alibaba.druid.sql.ast.SQLExpr; 4 | import com.alibaba.druid.sql.ast.expr.SQLCharExpr; 5 | import com.alibaba.druid.sql.ast.expr.SQLQueryExpr; 6 | import com.alibaba.druid.sql.ast.expr.SQLVariantRefExpr; 7 | import com.google.common.collect.Lists; 8 | import io.github.iamazy.elasticsearch.dsl.sql.druid.ElasticSqlSelectQueryBlock; 9 | import io.github.iamazy.elasticsearch.dsl.sql.exception.ElasticSql2DslException; 10 | import io.github.iamazy.elasticsearch.dsl.sql.helper.ElasticSqlArgConverter; 11 | import io.github.iamazy.elasticsearch.dsl.sql.model.ElasticDslContext; 12 | import org.apache.commons.collections4.CollectionUtils; 13 | 14 | 15 | import java.util.List; 16 | 17 | public class QueryRoutingValParser implements QueryParser { 18 | 19 | 20 | @Override 21 | public void parse(ElasticDslContext dslContext) { 22 | 23 | if(dslContext.getSqlObject() instanceof SQLQueryExpr) { 24 | ElasticSqlSelectQueryBlock queryBlock = (ElasticSqlSelectQueryBlock) ((SQLQueryExpr) dslContext.getSqlObject()).getSubQuery().getQuery(); 25 | if (queryBlock.getRouting() != null && CollectionUtils.isNotEmpty(queryBlock.getRouting().getRoutingValues())) { 26 | List routingStringValues = Lists.newLinkedList(); 27 | for (SQLExpr routingVal : queryBlock.getRouting().getRoutingValues()) { 28 | if (routingVal instanceof SQLCharExpr) { 29 | routingStringValues.add(((SQLCharExpr) routingVal).getText()); 30 | } else if (routingVal instanceof SQLVariantRefExpr) { 31 | Object targetVal = ElasticSqlArgConverter.convertSqlArg(routingVal); 32 | routingStringValues.add(targetVal.toString()); 33 | } else { 34 | throw new ElasticSql2DslException("[syntax error] Index routing val must be a string"); 35 | } 36 | } 37 | dslContext.getParseResult().setRoutingBy(routingStringValues); 38 | } 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/io/github/iamazy/elasticsearch/dsl/sql/parser/sql/QueryScrollParser.java: -------------------------------------------------------------------------------- 1 | package io.github.iamazy.elasticsearch.dsl.sql.parser.sql; 2 | 3 | import com.alibaba.druid.sql.ast.SQLExpr; 4 | import com.alibaba.druid.sql.ast.expr.SQLCharExpr; 5 | import com.alibaba.druid.sql.ast.expr.SQLQueryExpr; 6 | import io.github.iamazy.elasticsearch.dsl.sql.druid.ElasticSqlSelectQueryBlock; 7 | import io.github.iamazy.elasticsearch.dsl.sql.exception.ElasticSql2DslException; 8 | import io.github.iamazy.elasticsearch.dsl.sql.model.ElasticDslContext; 9 | 10 | 11 | /** 12 | * @author iamazy 13 | * @date 2019/3/25 14 | * @descrition 15 | **/ 16 | public class QueryScrollParser implements QueryParser { 17 | 18 | public QueryScrollParser() { } 19 | 20 | @Override 21 | public void parse(ElasticDslContext dslContext) { 22 | 23 | if (dslContext.getSqlObject() instanceof SQLQueryExpr) { 24 | ElasticSqlSelectQueryBlock queryBlock = (ElasticSqlSelectQueryBlock) ((SQLQueryExpr) dslContext.getSqlObject()).getSubQuery().getQuery(); 25 | if (queryBlock.getScroll() != null) { 26 | String expire = parseScroll(queryBlock.getScroll().getExpire()); 27 | dslContext.getParseResult().setScrollExpire(expire); 28 | if(queryBlock.getScroll().getScrollId()!=null) { 29 | String scrollId = parseScroll(queryBlock.getScroll().getScrollId()); 30 | dslContext.getParseResult().setScrollId(scrollId); 31 | } 32 | } 33 | } 34 | } 35 | 36 | private String parseScroll(SQLExpr sqlExpr) { 37 | if (sqlExpr instanceof SQLCharExpr) { 38 | return ((SQLCharExpr) sqlExpr).getText(); 39 | } else { 40 | throw new ElasticSql2DslException("[syntax error] Sql scroll expr should be a string expr"); 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/io/github/iamazy/elasticsearch/dsl/sql/parser/sql/QuerySelectFieldListParser.java: -------------------------------------------------------------------------------- 1 | package io.github.iamazy.elasticsearch.dsl.sql.parser.sql; 2 | 3 | import com.alibaba.druid.sql.ast.SQLExpr; 4 | import com.alibaba.druid.sql.ast.expr.SQLAggregateExpr; 5 | import com.alibaba.druid.sql.ast.expr.SQLQueryExpr; 6 | import com.alibaba.druid.sql.ast.statement.SQLSelectItem; 7 | import com.google.common.collect.Lists; 8 | import io.github.iamazy.elasticsearch.dsl.sql.druid.ElasticSqlSelectQueryBlock; 9 | import io.github.iamazy.elasticsearch.dsl.sql.enums.QueryFieldType; 10 | import io.github.iamazy.elasticsearch.dsl.sql.exception.ElasticSql2DslException; 11 | import io.github.iamazy.elasticsearch.dsl.sql.helper.ElasticSqlMethodInvokeHelper; 12 | import io.github.iamazy.elasticsearch.dsl.sql.model.ElasticDslContext; 13 | import io.github.iamazy.elasticsearch.dsl.sql.model.ElasticSqlQueryField; 14 | import org.apache.commons.collections4.CollectionUtils; 15 | import org.elasticsearch.search.aggregations.AbstractAggregationBuilder; 16 | import org.elasticsearch.search.aggregations.AggregationBuilder; 17 | import org.elasticsearch.search.aggregations.AggregationBuilders; 18 | 19 | import java.util.List; 20 | 21 | public class QuerySelectFieldListParser implements QueryParser { 22 | 23 | 24 | @Override 25 | public void parse(ElasticDslContext dslContext) { 26 | ElasticSqlSelectQueryBlock queryBlock = (ElasticSqlSelectQueryBlock) ((SQLQueryExpr)dslContext.getSqlObject()).getSubQuery().getQuery(); 27 | 28 | List selectFields = Lists.newLinkedList(); 29 | QueryFieldParser queryFieldParser = new QueryFieldParser(); 30 | String queryAs = dslContext.getParseResult().getQueryAs(); 31 | 32 | List aggregations = Lists.newLinkedList(); 33 | for (SQLSelectItem selectField : queryBlock.getSelectList()) { 34 | 35 | // agg method 36 | if (selectField.getExpr() instanceof SQLAggregateExpr) { 37 | 38 | SQLAggregateExpr aggExpr = (SQLAggregateExpr) selectField.getExpr(); 39 | SQLExpr aggFieldExpr = aggExpr.getArguments().get(0); 40 | 41 | ElasticSqlQueryField aggField = queryFieldParser.parseConditionQueryField(aggFieldExpr, queryAs); 42 | AbstractAggregationBuilder statsAgg = parseStatsAggregation(aggExpr, aggField.getQueryFieldFullName()); 43 | 44 | aggregations.add(statsAgg); 45 | continue; 46 | } 47 | 48 | // select field 49 | ElasticSqlQueryField sqlSelectField = queryFieldParser.parseSelectQueryField(selectField.getExpr(), queryAs); 50 | 51 | if (sqlSelectField.getQueryFieldType() == QueryFieldType.SqlSelectField) { 52 | selectFields.add(sqlSelectField.getQueryFieldFullName()); 53 | } 54 | } 55 | 56 | if (CollectionUtils.isNotEmpty(aggregations)) { 57 | List groupByList = dslContext.getParseResult().getGroupBy(); 58 | 59 | if (CollectionUtils.isNotEmpty(groupByList)) { 60 | AggregationBuilder lastLevelAggItem = groupByList.get(groupByList.size() - 1); 61 | for (AggregationBuilder aggItem : aggregations) { 62 | lastLevelAggItem.subAggregation(aggItem); 63 | } 64 | } 65 | else { 66 | dslContext.getParseResult().setGroupBy(aggregations); 67 | } 68 | } 69 | 70 | dslContext.getParseResult().setQueryFieldList(selectFields); 71 | } 72 | 73 | private AbstractAggregationBuilder parseStatsAggregation(SQLAggregateExpr aggExpr, String fieldName) { 74 | ElasticSqlMethodInvokeHelper.checkStatAggMethod(aggExpr); 75 | 76 | String methodName = aggExpr.getMethodName(); 77 | if (ElasticSqlMethodInvokeHelper.AGG_MIN_METHOD.equalsIgnoreCase(methodName)) { 78 | return AggregationBuilders.min(String.format("%s_%s", ElasticSqlMethodInvokeHelper.AGG_MIN_METHOD, fieldName)).field(fieldName); 79 | } 80 | 81 | if (ElasticSqlMethodInvokeHelper.AGG_MAX_METHOD.equalsIgnoreCase(methodName)) { 82 | return AggregationBuilders.max(String.format("%s_%s", ElasticSqlMethodInvokeHelper.AGG_MAX_METHOD, fieldName)).field(fieldName); 83 | } 84 | 85 | if (ElasticSqlMethodInvokeHelper.AGG_AVG_METHOD.equalsIgnoreCase(methodName)) { 86 | return AggregationBuilders.avg(String.format("%s_%s", ElasticSqlMethodInvokeHelper.AGG_AVG_METHOD, fieldName)).field(fieldName); 87 | } 88 | 89 | if (ElasticSqlMethodInvokeHelper.AGG_SUM_METHOD.equalsIgnoreCase(methodName)) { 90 | return AggregationBuilders.sum(String.format("%s_%s", ElasticSqlMethodInvokeHelper.AGG_SUM_METHOD, fieldName)).field(fieldName); 91 | } 92 | throw new ElasticSql2DslException(String.format("[syntax error] UnSupport agg method call[%s]", methodName)); 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /src/main/java/io/github/iamazy/elasticsearch/dsl/sql/parser/sql/QueryWhereConditionParser.java: -------------------------------------------------------------------------------- 1 | package io.github.iamazy.elasticsearch.dsl.sql.parser.sql; 2 | 3 | import com.alibaba.druid.sql.ast.SQLExpr; 4 | import com.alibaba.druid.sql.ast.expr.SQLQueryExpr; 5 | import com.alibaba.druid.sql.ast.statement.SQLDeleteStatement; 6 | import io.github.iamazy.elasticsearch.dsl.sql.druid.ElasticSqlSelectQueryBlock; 7 | import io.github.iamazy.elasticsearch.dsl.sql.model.ElasticDslContext; 8 | import org.elasticsearch.index.query.BoolQueryBuilder; 9 | 10 | 11 | public class QueryWhereConditionParser extends BoolExpressionParser implements QueryParser{ 12 | 13 | 14 | @Override 15 | public void parse(ElasticDslContext dslContext) { 16 | 17 | if(dslContext.getSqlObject() instanceof SQLDeleteStatement){ 18 | SQLDeleteStatement sqlDeleteStatement = (SQLDeleteStatement) dslContext.getSqlObject(); 19 | String queryAs=dslContext.getParseResult().getQueryAs(); 20 | SQLExpr sqlExpr=sqlDeleteStatement.getWhere(); 21 | BoolQueryBuilder matchQuery=parseBoolQueryExpr(sqlExpr,queryAs); 22 | dslContext.getParseResult().setMatchCondition(matchQuery); 23 | } 24 | if(dslContext.getSqlObject() instanceof SQLQueryExpr) { 25 | ElasticSqlSelectQueryBlock queryBlock = (ElasticSqlSelectQueryBlock) ((SQLQueryExpr) dslContext.getSqlObject()).getSubQuery().getQuery(); 26 | 27 | if (queryBlock.getWhere() != null) { 28 | String queryAs = dslContext.getParseResult().getQueryAs(); 29 | BoolQueryBuilder whereQuery = parseBoolQueryExpr(queryBlock.getWhere(), queryAs); 30 | dslContext.getParseResult().setWhereCondition(whereQuery); 31 | dslContext.getParseResult().getHighlighter().addAll(this.getHighlighter()); 32 | } 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/io/github/iamazy/elasticsearch/dsl/sql/parser/sql/sort/AbstractMethodSortParser.java: -------------------------------------------------------------------------------- 1 | package io.github.iamazy.elasticsearch.dsl.sql.parser.sql.sort; 2 | 3 | 4 | import io.github.iamazy.elasticsearch.dsl.sql.exception.ElasticSql2DslException; 5 | import io.github.iamazy.elasticsearch.dsl.sql.parser.query.method.MethodInvocation; 6 | import io.github.iamazy.elasticsearch.dsl.sql.parser.query.method.expr.AbstractParameterizedMethodExpression; 7 | import io.github.iamazy.elasticsearch.dsl.sql.helper.ElasticSqlMethodInvokeHelper; 8 | import org.apache.commons.lang3.StringUtils; 9 | import org.elasticsearch.search.sort.SortBuilder; 10 | import org.elasticsearch.search.sort.SortOrder; 11 | 12 | import java.util.Map; 13 | 14 | public abstract class AbstractMethodSortParser extends AbstractParameterizedMethodExpression implements MethodSortParser { 15 | 16 | protected abstract SortBuilder parseMethodSortBuilder( 17 | MethodInvocation invocation, SortOrder order, Map extraParamMap) throws ElasticSql2DslException; 18 | 19 | @Override 20 | protected String defineExtraParamString(MethodInvocation invocation) { 21 | return StringUtils.EMPTY; 22 | } 23 | 24 | @Override 25 | public void checkMethodInvocation(MethodInvocation invocation) throws ElasticSql2DslException { 26 | 27 | } 28 | 29 | @Override 30 | public boolean isMatchMethodInvocation(MethodInvocation invocation) { 31 | return ElasticSqlMethodInvokeHelper.isMethodOf(defineMethodNames(), invocation.getMethodName()); 32 | } 33 | 34 | @Override 35 | public SortBuilder parseMethodSortBuilder(MethodInvocation invocation, SortOrder order) throws ElasticSql2DslException { 36 | if (!isMatchMethodInvocation(invocation)) { 37 | throw new ElasticSql2DslException( 38 | String.format("[syntax error] Expected method name is one of [%s],but get [%s]", 39 | defineMethodNames(), invocation.getMethodName())); 40 | } 41 | checkMethodInvocation(invocation); 42 | 43 | Map extraParamMap = generateRawTypeParameterMap(invocation); 44 | return parseMethodSortBuilder(invocation, order, extraParamMap); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/io/github/iamazy/elasticsearch/dsl/sql/parser/sql/sort/ConditionSortBuilder.java: -------------------------------------------------------------------------------- 1 | package io.github.iamazy.elasticsearch.dsl.sql.parser.sql.sort; 2 | 3 | import org.elasticsearch.search.sort.FieldSortBuilder; 4 | 5 | public interface ConditionSortBuilder { 6 | FieldSortBuilder buildSort(String idfName); 7 | } -------------------------------------------------------------------------------- /src/main/java/io/github/iamazy/elasticsearch/dsl/sql/parser/sql/sort/MethodSortParser.java: -------------------------------------------------------------------------------- 1 | package io.github.iamazy.elasticsearch.dsl.sql.parser.sql.sort; 2 | 3 | import io.github.iamazy.elasticsearch.dsl.sql.exception.ElasticSql2DslException; 4 | import io.github.iamazy.elasticsearch.dsl.sql.parser.query.method.MethodInvocation; 5 | import io.github.iamazy.elasticsearch.dsl.sql.parser.query.method.expr.MethodExpression; 6 | import org.elasticsearch.search.sort.SortBuilder; 7 | import org.elasticsearch.search.sort.SortOrder; 8 | 9 | 10 | public interface MethodSortParser extends MethodExpression { 11 | SortBuilder parseMethodSortBuilder(MethodInvocation invocation, SortOrder order) throws ElasticSql2DslException; 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/io/github/iamazy/elasticsearch/dsl/sql/parser/sql/sort/NestedSortMethodParser.java: -------------------------------------------------------------------------------- 1 | package io.github.iamazy.elasticsearch.dsl.sql.parser.sql.sort; 2 | 3 | import com.alibaba.druid.sql.ast.SQLExpr; 4 | import com.alibaba.druid.sql.ast.expr.SQLCharExpr; 5 | import com.google.common.collect.ImmutableList; 6 | import io.github.iamazy.elasticsearch.dsl.sql.exception.ElasticSql2DslException; 7 | import io.github.iamazy.elasticsearch.dsl.sql.parser.query.method.MethodInvocation; 8 | import io.github.iamazy.elasticsearch.dsl.sql.model.ElasticSqlQueryField; 9 | import io.github.iamazy.elasticsearch.dsl.sql.parser.sql.BoolExpressionParser; 10 | import io.github.iamazy.elasticsearch.dsl.sql.parser.sql.QueryFieldParser; 11 | import org.elasticsearch.index.query.BoolQueryBuilder; 12 | import org.elasticsearch.search.sort.*; 13 | 14 | 15 | import java.util.List; 16 | import java.util.Map; 17 | 18 | /** 19 | * nested sort(nestedField, sortMode, missingValue, defaultValue, filterExpression) 20 | *

21 | * order by nested_sort($repaymentRecords.principal, 'min', 0, repaymentRecords.status='DONE') asc 22 | */ 23 | public class NestedSortMethodParser extends AbstractMethodSortParser { 24 | 25 | public static final List NESTED_SORT_METHOD = ImmutableList.of("nested_sort", "nestedSort"); 26 | 27 | @Override 28 | public List defineMethodNames() { 29 | return NESTED_SORT_METHOD; 30 | } 31 | 32 | @Override 33 | public void checkMethodInvocation(MethodInvocation nestedSortMethodInvocation) throws ElasticSql2DslException { 34 | if (!isMatchMethodInvocation(nestedSortMethodInvocation)) { 35 | throw new ElasticSql2DslException( 36 | String.format("[syntax error] No suck sort method[%s]", nestedSortMethodInvocation.getMethodName())); 37 | } 38 | 39 | if (nestedSortMethodInvocation.getParameterCount() > 4) { 40 | throw new ElasticSql2DslException( 41 | String.format("[syntax error] There is no %s args method named nested_sort", 42 | nestedSortMethodInvocation.getParameterCount())); 43 | } 44 | 45 | SQLExpr sortModArg = nestedSortMethodInvocation.getParameter(1); 46 | if (!(sortModArg instanceof SQLCharExpr)) { 47 | throw new ElasticSql2DslException("[syntax error] The second arg of nested_sort method should be string"); 48 | } 49 | 50 | String sortModeText = ((SQLCharExpr) sortModArg).getText(); 51 | SortMode.fromString(sortModeText); 52 | } 53 | 54 | @Override 55 | protected SortBuilder parseMethodSortBuilder(MethodInvocation invocation, SortOrder order, Map extraParamMap) throws ElasticSql2DslException { 56 | String sortMode = invocation.getParameterAsString(1); 57 | Object defaultSortVal = invocation.getParameterAsObject(2); 58 | 59 | boolean hasFilterExpr = invocation.getParameterCount() == 4; 60 | 61 | QueryFieldParser queryFieldParser = new QueryFieldParser(); 62 | ElasticSqlQueryField sortField = queryFieldParser.parseConditionQueryField(invocation.getParameter(0), invocation.getQueryAs()); 63 | 64 | return ParseSortBuilderHelper.parseBasedOnFieldSortBuilder(sortField, nestedFieldName -> { 65 | BoolQueryBuilder filter = null; 66 | if (hasFilterExpr) { 67 | SQLExpr filterExpr = invocation.getParameter(3); 68 | 69 | BoolExpressionParser boolExpressionParser = new BoolExpressionParser(); 70 | 71 | String queryAs = invocation.getQueryAs(); 72 | 73 | filter = boolExpressionParser.parseBoolQueryExpr(filterExpr, queryAs); 74 | } 75 | 76 | if(sortField.getNestedDocContextPath().size()==1) { 77 | return SortBuilders.fieldSort(nestedFieldName) 78 | .missing(defaultSortVal).sortMode(SortMode.fromString(sortMode)) 79 | .setNestedSort(new NestedSortBuilder(sortField.getNestedDocContextPath().get(0)).setFilter(hasFilterExpr ? filter : null)) 80 | .order(order); 81 | }else if(sortField.getNestedDocContextPath().size()==2){ 82 | return SortBuilders.fieldSort(nestedFieldName) 83 | .missing(defaultSortVal).sortMode(SortMode.fromString(sortMode)) 84 | .setNestedSort(new NestedSortBuilder(sortField.getNestedDocContextPath().get(0)).setNestedSort(new NestedSortBuilder(sortField.getNestedDocContextPath().get(1)).setFilter(hasFilterExpr ? filter : null))) 85 | .order(order); 86 | }else{ 87 | throw new ElasticSql2DslException("[syntax error] can not support sql for 3 more nested sort aggregation"); 88 | } 89 | }); 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /src/main/java/io/github/iamazy/elasticsearch/dsl/sql/parser/sql/sort/NvlMethodSortParser.java: -------------------------------------------------------------------------------- 1 | package io.github.iamazy.elasticsearch.dsl.sql.parser.sql.sort; 2 | 3 | import com.alibaba.druid.sql.ast.SQLExpr; 4 | import com.alibaba.druid.sql.ast.expr.*; 5 | import com.google.common.collect.ImmutableList; 6 | import io.github.iamazy.elasticsearch.dsl.sql.exception.ElasticSql2DslException; 7 | import io.github.iamazy.elasticsearch.dsl.sql.parser.query.method.MethodInvocation; 8 | import io.github.iamazy.elasticsearch.dsl.sql.model.ElasticSqlQueryField; 9 | import io.github.iamazy.elasticsearch.dsl.sql.parser.sql.QueryFieldParser; 10 | import org.elasticsearch.search.sort.*; 11 | 12 | import java.util.List; 13 | import java.util.Map; 14 | 15 | /** 16 | * nvl(rootDocField, defaultValue) 17 | *

18 | * order by nvl(price, 0) asc 19 | * 20 | * @author chennan 21 | */ 22 | public class NvlMethodSortParser extends AbstractMethodSortParser { 23 | 24 | public static final List NVL_METHOD = ImmutableList.of("nvl", "is_null", "isnull"); 25 | 26 | @Override 27 | public List defineMethodNames() { 28 | return NVL_METHOD; 29 | } 30 | 31 | @Override 32 | public void checkMethodInvocation(MethodInvocation nvlMethodInvocation) throws ElasticSql2DslException { 33 | if (!isMatchMethodInvocation(nvlMethodInvocation)) { 34 | throw new ElasticSql2DslException("[syntax error] Sql sort condition only support nvl method invoke"); 35 | } 36 | 37 | int methodParameterCount = nvlMethodInvocation.getParameterCount(); 38 | if (methodParameterCount == 0 || methodParameterCount >= 3) { 39 | throw new ElasticSql2DslException(String.format("[syntax error] There is no %s args method named nvl", methodParameterCount)); 40 | } 41 | 42 | SQLExpr fieldArg = nvlMethodInvocation.getParameter(0); 43 | SQLExpr valueArg = nvlMethodInvocation.getParameter(1); 44 | 45 | if (!(fieldArg instanceof SQLPropertyExpr) && !(fieldArg instanceof SQLIdentifierExpr)) { 46 | throw new ElasticSql2DslException("[syntax error] The first arg of nvl method should be field param name"); 47 | } 48 | 49 | if (!(valueArg instanceof SQLCharExpr) && !(valueArg instanceof SQLIntegerExpr) && !(valueArg instanceof SQLNumberExpr)) { 50 | throw new ElasticSql2DslException("[syntax error] The second arg of nvl method should be number or string"); 51 | } 52 | } 53 | 54 | @Override 55 | protected SortBuilder parseMethodSortBuilder( 56 | MethodInvocation sortMethodInvocation, SortOrder order, Map extraParamMap) throws ElasticSql2DslException { 57 | 58 | String queryAs = sortMethodInvocation.getQueryAs(); 59 | SQLExpr fieldExpr = sortMethodInvocation.getParameter(0); 60 | Object valueArg = sortMethodInvocation.getParameterAsObject(1); 61 | 62 | QueryFieldParser queryFieldParser = new QueryFieldParser(); 63 | ElasticSqlQueryField sortField = queryFieldParser.parseConditionQueryField(fieldExpr, queryAs); 64 | 65 | return ParseSortBuilderHelper.parseBasedOnFieldSortBuilder(sortField, idfName -> { 66 | FieldSortBuilder fieldSortBuilder = SortBuilders.fieldSort(idfName).order(order).missing(valueArg); 67 | 68 | if (sortMethodInvocation.getParameterCount() == 3) { 69 | String sortModeText = sortMethodInvocation.getParameterAsString(2); 70 | fieldSortBuilder.sortMode(SortMode.fromString(sortModeText)); 71 | } 72 | return fieldSortBuilder; 73 | }); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/main/java/io/github/iamazy/elasticsearch/dsl/sql/parser/sql/sort/ParseSortBuilderHelper.java: -------------------------------------------------------------------------------- 1 | package io.github.iamazy.elasticsearch.dsl.sql.parser.sql.sort; 2 | 3 | import com.alibaba.druid.sql.ast.SQLExpr; 4 | import com.alibaba.druid.sql.ast.expr.SQLIdentifierExpr; 5 | import com.alibaba.druid.sql.ast.expr.SQLMethodInvokeExpr; 6 | import com.alibaba.druid.sql.ast.expr.SQLPropertyExpr; 7 | import io.github.iamazy.elasticsearch.dsl.sql.enums.QueryFieldType; 8 | import io.github.iamazy.elasticsearch.dsl.sql.exception.ElasticSql2DslException; 9 | import io.github.iamazy.elasticsearch.dsl.sql.model.ElasticSqlQueryField; 10 | import org.elasticsearch.search.sort.FieldSortBuilder; 11 | import org.elasticsearch.search.sort.NestedSortBuilder; 12 | import org.elasticsearch.search.sort.SortBuilder; 13 | 14 | 15 | public class ParseSortBuilderHelper { 16 | 17 | public static boolean isFieldExpr(SQLExpr expr) { 18 | return expr instanceof SQLPropertyExpr || expr instanceof SQLIdentifierExpr; 19 | } 20 | 21 | public static boolean isMethodInvokeExpr(SQLExpr expr) { 22 | return expr instanceof SQLMethodInvokeExpr; 23 | } 24 | 25 | public static SortBuilder parseBasedOnFieldSortBuilder(ElasticSqlQueryField sortField, ConditionSortBuilder sortBuilder) { 26 | SortBuilder rtnSortBuilder = null; 27 | if (sortField.getQueryFieldType() == QueryFieldType.RootDocField || sortField.getQueryFieldType() == QueryFieldType.InnerDocField) { 28 | rtnSortBuilder = sortBuilder.buildSort(sortField.getQueryFieldFullName()); 29 | } 30 | 31 | if (sortField.getQueryFieldType() == QueryFieldType.NestedDocField) { 32 | FieldSortBuilder originalSort = sortBuilder.buildSort(sortField.getQueryFieldFullName()); 33 | if(sortField.getNestedDocContextPath().size()==1) { 34 | originalSort.setNestedSort(new NestedSortBuilder(sortField.getNestedDocContextPath().get(0))); 35 | }else if(sortField.getNestedDocContextPath().size()==2){ 36 | originalSort.setNestedSort(new NestedSortBuilder(sortField.getNestedDocContextPath().get(0)).setNestedSort(new NestedSortBuilder(sortField.getNestedDocContextPath().get(1)))); 37 | } 38 | rtnSortBuilder = originalSort; 39 | } 40 | 41 | if (rtnSortBuilder == null) { 42 | throw new ElasticSql2DslException(String.format("[syntax error] sort condition field can not support type[%s]", sortField.getQueryFieldType())); 43 | } 44 | 45 | return rtnSortBuilder; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/main/java/io/github/iamazy/elasticsearch/dsl/sql/parser/sql/sort/ScriptMethodSortParser.java: -------------------------------------------------------------------------------- 1 | package io.github.iamazy.elasticsearch.dsl.sql.parser.sql.sort; 2 | 3 | import com.google.common.collect.ImmutableList; 4 | import io.github.iamazy.elasticsearch.dsl.sql.exception.ElasticSql2DslException; 5 | import io.github.iamazy.elasticsearch.dsl.sql.parser.query.method.MethodInvocation; 6 | import org.apache.commons.collections4.MapUtils; 7 | import org.apache.commons.lang3.StringUtils; 8 | import org.elasticsearch.script.Script; 9 | import org.elasticsearch.search.sort.ScriptSortBuilder; 10 | import org.elasticsearch.search.sort.SortBuilder; 11 | import org.elasticsearch.search.sort.SortBuilders; 12 | import org.elasticsearch.search.sort.SortOrder; 13 | 14 | 15 | import java.util.List; 16 | import java.util.Map; 17 | 18 | public class ScriptMethodSortParser extends AbstractMethodSortParser { 19 | 20 | public static final List SCRIPT_SORT_METHOD = ImmutableList.of("script_sort", "scriptSort"); 21 | 22 | @Override 23 | public List defineMethodNames() { 24 | return SCRIPT_SORT_METHOD; 25 | } 26 | 27 | @Override 28 | protected String defineExtraParamString(MethodInvocation invocation) { 29 | if (invocation.getParameterCount() == 3) { 30 | return invocation.getParameterAsString(2); 31 | } 32 | return StringUtils.EMPTY; 33 | } 34 | 35 | @Override 36 | public void checkMethodInvocation(MethodInvocation nvlMethodInvocation) throws ElasticSql2DslException { 37 | if (!isMatchMethodInvocation(nvlMethodInvocation)) { 38 | throw new ElasticSql2DslException("[syntax error] Sql sort condition only support script_query method invoke"); 39 | } 40 | 41 | int methodParameterCount = nvlMethodInvocation.getParameterCount(); 42 | if (methodParameterCount != 2 && methodParameterCount != 3) { 43 | throw new ElasticSql2DslException(String.format("[syntax error] There is no %s args method named script_sort", methodParameterCount)); 44 | } 45 | } 46 | 47 | @Override 48 | protected SortBuilder parseMethodSortBuilder( 49 | MethodInvocation scriptSortMethodInvocation, SortOrder order, Map extraParamMap) throws ElasticSql2DslException { 50 | 51 | String strScript = scriptSortMethodInvocation.getParameterAsString(0); 52 | String type = scriptSortMethodInvocation.getParameterAsString(1); 53 | ScriptSortBuilder.ScriptSortType scriptSortType = ScriptSortBuilder.ScriptSortType.fromString(type); 54 | 55 | if (MapUtils.isNotEmpty(extraParamMap)) { 56 | Map scriptParamMap = generateRawTypeParameterMap(scriptSortMethodInvocation); 57 | Script scriptObject = new Script(Script.DEFAULT_SCRIPT_TYPE, Script.DEFAULT_SCRIPT_LANG, strScript, scriptParamMap); 58 | return SortBuilders.scriptSort(scriptObject, scriptSortType).order(order); 59 | } 60 | 61 | return SortBuilders.scriptSort(new Script(strScript), scriptSortType).order(order); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/main/java/io/github/iamazy/elasticsearch/dsl/utils/FlatMapUtils.java: -------------------------------------------------------------------------------- 1 | package io.github.iamazy.elasticsearch.dsl.utils; 2 | 3 | import io.github.iamazy.elasticsearch.dsl.cons.CoreConstants; 4 | import org.apache.commons.lang3.StringUtils; 5 | 6 | import java.util.HashMap; 7 | import java.util.Map; 8 | 9 | /** 10 | * @author iamazy 11 | * @date 2019/4/11 12 | * @descrition 13 | **/ 14 | @SuppressWarnings("unchecked") 15 | public class FlatMapUtils { 16 | 17 | public static Map flat(Map map, String parentKey) { 18 | String parent = parentKey; 19 | Map dataInfo = new HashMap<>(0); 20 | for (Map.Entry entry : map.entrySet()) { 21 | if (!(entry.getValue() instanceof Map)) { 22 | if (StringUtils.isNotBlank(parent)) { 23 | dataInfo.put(parent + CoreConstants.DOT + entry.getKey(), entry.getValue() != null ? entry.getValue().toString() : StringUtils.EMPTY); 24 | } else { 25 | dataInfo.put(entry.getKey(), entry.getValue() != null ? entry.getValue().toString() : StringUtils.EMPTY); 26 | } 27 | } else { 28 | Map childMap = (Map) entry.getValue(); 29 | if (StringUtils.isNotBlank(parent)) { 30 | parent = parent + CoreConstants.DOT + entry.getKey(); 31 | } else { 32 | parent = entry.getKey(); 33 | } 34 | dataInfo.putAll(flat(childMap, parent)); 35 | if(parent.contains(CoreConstants.DOT)) { 36 | parent = parent.substring(0, parent.lastIndexOf(CoreConstants.DOT)); 37 | }else{ 38 | parent=StringUtils.EMPTY; 39 | } 40 | 41 | } 42 | } 43 | return dataInfo; 44 | } 45 | 46 | 47 | public static void flatPut(String key, Object value, Map map) { 48 | if (key.contains(CoreConstants.DOT)) { 49 | String firstItem = key.substring(0, key.indexOf(CoreConstants.DOT)); 50 | String restItems = key.substring(key.indexOf(CoreConstants.DOT) + 1); 51 | if (map.containsKey(firstItem)) { 52 | flatPut(restItems, value, (Map) map.get(firstItem)); 53 | } else { 54 | Map temp = new HashMap<>(0); 55 | map.put(firstItem, temp); 56 | flatPut(restItems, value, (Map) map.get(firstItem)); 57 | } 58 | } else { 59 | map.put(key, value); 60 | } 61 | } 62 | 63 | public static Object flatGet(String key, Map map) { 64 | if (key.contains(CoreConstants.DOT)) { 65 | String firstItem = key.substring(0, key.indexOf(CoreConstants.DOT)); 66 | String restItems = key.substring(key.indexOf(CoreConstants.DOT) + 1); 67 | if (map.containsKey(firstItem)) { 68 | return flatGet(restItems, (Map) map.get(firstItem)); 69 | } else { 70 | return null; 71 | } 72 | } else { 73 | return map.get(key); 74 | } 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/main/resources/es-plugin.properties: -------------------------------------------------------------------------------- 1 | plugin=io.github.iamazy.elasticsearch.dsl.plugin.SqlPlugin 2 | version=${project.version} -------------------------------------------------------------------------------- /src/main/resources/plugin-descriptor.properties: -------------------------------------------------------------------------------- 1 | # elasticsearch-sql项目的描述 2 | description=${project.description} 3 | 4 | # elasticsearch-sql项目自己的版本,和es的版本没有关系 5 | version=${project.version} 6 | 7 | # 插件的名字 8 | name=${elasticsearch.plugin.name} 9 | 10 | classname=${elasticsearch.plugin.classname} 11 | 12 | java.version=1.8 13 | 14 | # es的版本,ctrl+鼠标点击 可以跳到pom文件看该参数的值 15 | elasticsearch.version=${elasticsearch.version} -------------------------------------------------------------------------------- /src/main/resources/plugin-security.policy: -------------------------------------------------------------------------------- 1 | grant { 2 | // needed because of the hot reload functionality 3 | permission java.net.SocketPermission "*", "connect,resolve"; 4 | }; -------------------------------------------------------------------------------- /src/test/java/io/github/iamazy/elasticsearch/dsl/sql/DeleteTest.java: -------------------------------------------------------------------------------- 1 | package io.github.iamazy.elasticsearch.dsl.sql; 2 | 3 | import io.github.iamazy.elasticsearch.dsl.sql.model.ElasticSqlParseResult; 4 | import io.github.iamazy.elasticsearch.dsl.sql.parser.ElasticSql2DslParser; 5 | import org.junit.Test; 6 | 7 | /** 8 | * @author iamazy 9 | * @date 2019/3/4 10 | * @descrition 11 | **/ 12 | public class DeleteTest { 13 | 14 | @Test 15 | public void delete(){ 16 | 17 | String sql="DELETE from fruits where match_all() limit 1100"; 18 | ElasticSql2DslParser elasticSql2DslParser=new ElasticSql2DslParser(); 19 | ElasticSqlParseResult elasticSqlParseResult = elasticSql2DslParser.parse(sql); 20 | 21 | System.out.println(elasticSqlParseResult.toPrettyDsl(elasticSqlParseResult.toDelRequest().getSearchRequest())); 22 | } 23 | 24 | @Test 25 | public void query(){ 26 | String sql="SELECT * FROM product.apple QUERY term(productName, 'iphone6s', 'boost:2.0f')"; 27 | ElasticSql2DslParser elasticSql2DslParser=new ElasticSql2DslParser(); 28 | ElasticSqlParseResult elasticSqlParseResult = elasticSql2DslParser.parse(sql); 29 | 30 | System.out.println(elasticSqlParseResult.toPrettyDsl(elasticSqlParseResult.toRequest())); 31 | } 32 | 33 | 34 | 35 | 36 | } 37 | -------------------------------------------------------------------------------- /src/test/java/io/github/iamazy/elasticsearch/dsl/sql/DescTest.java: -------------------------------------------------------------------------------- 1 | package io.github.iamazy.elasticsearch.dsl.sql; 2 | 3 | import io.github.iamazy.elasticsearch.dsl.sql.model.ElasticSqlParseResult; 4 | import io.github.iamazy.elasticsearch.dsl.sql.parser.ElasticSql2DslParser; 5 | import org.junit.Test; 6 | 7 | /** 8 | * @author iamazy 9 | * @date 2019/5/5 10 | * @descrition 11 | **/ 12 | public class DescTest { 13 | 14 | @Test 15 | public void test3(){ 16 | String sql="desc device_info"; 17 | ElasticSql2DslParser sql2DslParser=new ElasticSql2DslParser(); 18 | ElasticSqlParseResult parseResult = sql2DslParser.parse(sql); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/test/java/io/github/iamazy/elasticsearch/dsl/sql/NestedAggTest.java: -------------------------------------------------------------------------------- 1 | package io.github.iamazy.elasticsearch.dsl.sql; 2 | 3 | import io.github.iamazy.elasticsearch.dsl.sql.model.ElasticSqlParseResult; 4 | import io.github.iamazy.elasticsearch.dsl.sql.parser.ElasticSql2DslParser; 5 | import org.junit.Test; 6 | 7 | /** 8 | * @author iamazy 9 | * @date 2019/3/25 10 | * @descrition 11 | **/ 12 | public class NestedAggTest { 13 | 14 | 15 | @Test 16 | public void nested2Agg(){ 17 | String nested="select * from product where $product$apple.type='AirPod' group by nested(product)>(nested(product.apple)>(terms(product.apple.type, 20),terms(product.apple.name,2))) limit 0,0"; 18 | ElasticSql2DslParser elasticSql2DslParser=new ElasticSql2DslParser(); 19 | ElasticSqlParseResult elasticSqlParseResult = elasticSql2DslParser.parse(nested); 20 | System.out.println(elasticSqlParseResult.toPrettyDsl(elasticSqlParseResult.toRequest())); 21 | } 22 | 23 | @Test 24 | public void nested3(){ 25 | String sql="select * from regions group by terms(country)>terms(province)"; 26 | ElasticSql2DslParser elasticSql2DslParser=new ElasticSql2DslParser(); 27 | ElasticSqlParseResult elasticSqlParseResult = elasticSql2DslParser.parse(sql); 28 | System.out.println(elasticSqlParseResult.toPrettyDsl(elasticSqlParseResult.toRequest())); 29 | } 30 | 31 | @Test 32 | public void geoDistanceAgg(){ 33 | String sql="select * from regions group by geo_distance(distance#km,origin(0.0,11.0),range(1.0,3.0))"; 34 | ElasticSql2DslParser elasticSql2DslParser=new ElasticSql2DslParser(); 35 | ElasticSqlParseResult elasticSqlParseResult = elasticSql2DslParser.parse(sql); 36 | System.out.println(elasticSqlParseResult.toPrettyDsl(elasticSqlParseResult.toRequest())); 37 | } 38 | 39 | @Test 40 | public void agg(){ 41 | String sql="select * from apple group by terms(productName,10)>(terms(provider,10),cardinality(provider))"; 42 | ElasticSql2DslParser elasticSql2DslParser=new ElasticSql2DslParser(); 43 | ElasticSqlParseResult elasticSqlParseResult = elasticSql2DslParser.parse(sql); 44 | System.out.println(elasticSqlParseResult.toPrettyDsl(elasticSqlParseResult.toRequest())); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/test/java/io/github/iamazy/elasticsearch/dsl/sql/ScoreTest.java: -------------------------------------------------------------------------------- 1 | package io.github.iamazy.elasticsearch.dsl.sql; 2 | 3 | import io.github.iamazy.elasticsearch.dsl.sql.model.ElasticSqlParseResult; 4 | import io.github.iamazy.elasticsearch.dsl.sql.parser.ElasticSql2DslParser; 5 | import org.junit.Test; 6 | 7 | public class ScoreTest { 8 | 9 | 10 | @Test 11 | public void boostingTest(){ 12 | String sql="select * from fruit query boosting(h#name='apple',h#weight>100,0.2)"; 13 | ElasticSql2DslParser sql2DslParser=new ElasticSql2DslParser(); 14 | ElasticSqlParseResult parseResult = sql2DslParser.parse(sql); 15 | System.out.println(parseResult.toPrettyDsl(parseResult.toRequest())); 16 | } 17 | 18 | @Test 19 | public void functionScoreTest(){ 20 | String sql="select * from fruit query function_score(h#name='a',script_score(h#naame='ddd','fsdfsdf0','a:1,b:2'),random_score(age>90,101092339,'date'),weight(a>1,3),weight(b<4,4),weight(c='aa',5),weight(d is not null,9))"; 21 | ElasticSql2DslParser sql2DslParser=new ElasticSql2DslParser(); 22 | ElasticSqlParseResult parseResult = sql2DslParser.parse(sql); 23 | System.out.println(parseResult.toPrettyDsl(parseResult.toRequest())); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/test/java/io/github/iamazy/elasticsearch/dsl/sql/ScriptQueryTest.java: -------------------------------------------------------------------------------- 1 | package io.github.iamazy.elasticsearch.dsl.sql; 2 | 3 | import io.github.iamazy.elasticsearch.dsl.sql.model.ElasticSqlParseResult; 4 | import io.github.iamazy.elasticsearch.dsl.sql.parser.ElasticSql2DslParser; 5 | import org.junit.Test; 6 | 7 | /** 8 | * @author iamazy 9 | * @date 2019/3/6 10 | * @descrition 11 | **/ 12 | public class ScriptQueryTest { 13 | 14 | 15 | @Test 16 | public void scriptTest(){ 17 | String sql="select * from search where script_query('if (ctx._source.user == \"kimchy\") {ctx._source.likes++;}','name:iamazy,age:23,gender:male')"; 18 | ElasticSql2DslParser elasticSql2DslParser=new ElasticSql2DslParser(); 19 | ElasticSqlParseResult elasticSqlParseResult = elasticSql2DslParser.parse(sql); 20 | System.out.println(elasticSqlParseResult.toPrettyDsl(elasticSqlParseResult.toRequest())); 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /src/test/java/io/github/iamazy/elasticsearch/dsl/sql/ScrollTest.java: -------------------------------------------------------------------------------- 1 | package io.github.iamazy.elasticsearch.dsl.sql; 2 | 3 | import io.github.iamazy.elasticsearch.dsl.sql.model.ElasticSqlParseResult; 4 | import io.github.iamazy.elasticsearch.dsl.sql.parser.ElasticSql2DslParser; 5 | import org.junit.Test; 6 | 7 | /** 8 | * @author iamazy 9 | * @date 2019/3/25 10 | * @descrition 11 | **/ 12 | public class ScrollTest { 13 | 14 | 15 | @Test 16 | public void scroll(){ 17 | 18 | //scroll by 前面表示scroll id过期时间,后面表示scroll id 19 | String sql="select * from search order by lastModified routing by 'fdsfdsfdf' scroll by '2121m','fdsfdsfdsfsdfdsf' limit 20,10"; 20 | ElasticSql2DslParser elasticSql2DslParser=new ElasticSql2DslParser(); 21 | ElasticSqlParseResult elasticSqlParseResult = elasticSql2DslParser.parse(sql); 22 | 23 | System.out.println(elasticSqlParseResult.toPrettyDsl(elasticSqlParseResult.toRequest())); 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /src/test/java/io/github/iamazy/elasticsearch/dsl/sql/SqlParserSelectFieldTest.java: -------------------------------------------------------------------------------- 1 | package io.github.iamazy.elasticsearch.dsl.sql; 2 | 3 | import io.github.iamazy.elasticsearch.dsl.sql.model.ElasticSqlParseResult; 4 | import io.github.iamazy.elasticsearch.dsl.sql.parser.ElasticSql2DslParser; 5 | import org.junit.Test; 6 | 7 | import java.util.Arrays; 8 | 9 | /** 10 | * @author iamazy 11 | * @date 2019/2/20 12 | * @descrition 13 | **/ 14 | public class SqlParserSelectFieldTest { 15 | 16 | @Test 17 | public void testParseFromMethodSource(){ 18 | String sql="select * from fruit query match(h#name,'苹果','prefix_length:21,boost:2.0f') and term(weight,80)"; 19 | ElasticSql2DslParser sql2DslParser=new ElasticSql2DslParser(); 20 | ElasticSqlParseResult parseResult = sql2DslParser.parse(sql); 21 | System.out.println(parseResult.toPrettyDsl(parseResult.toRequest())); 22 | } 23 | 24 | @Test 25 | public void testHasParent(){ 26 | String sql="select * from fruit where has_parent('vegetable',weight between 100 and 400)"; 27 | ElasticSql2DslParser sql2DslParser=new ElasticSql2DslParser(); 28 | ElasticSqlParseResult parseResult = sql2DslParser.parse(sql); 29 | System.out.println(parseResult.toPrettyDsl(parseResult.toRequest())); 30 | } 31 | 32 | @Test 33 | public void testHasChild(){ 34 | String sql="select `name*`,^age from fruit where has_child('apple',price in (10,20,30),1,4)"; 35 | ElasticSql2DslParser sql2DslParser=new ElasticSql2DslParser(); 36 | ElasticSqlParseResult parseResult = sql2DslParser.parse(sql); 37 | System.out.println(parseResult.toPrettyDsl(parseResult.toRequest())); 38 | } 39 | 40 | 41 | /** 42 | * 高亮显示 在字段前面用h#标识 43 | */ 44 | @Test 45 | public void testParseFlatTermsAgg(){ 46 | String sql="select * from fruit where match(h#$aaaaa.bb,'fdsfdsfdsf') and fuzzy(h#bbb,'fdsfdf') and h#name is not null and color is not null group by terms(weight,5000),terms(category,100) limit 0,0"; 47 | ElasticSql2DslParser sql2DslParser=new ElasticSql2DslParser(); 48 | ElasticSqlParseResult parseResult = sql2DslParser.parse(sql); 49 | System.out.println(parseResult.toPrettyDsl(parseResult.toRequest())); 50 | } 51 | 52 | 53 | @Test 54 | public void testHighlighter(){ 55 | String sql="select * from fruit where h#$macInfo.name='0xsdfs' limit 0,0"; 56 | ElasticSql2DslParser sql2DslParser=new ElasticSql2DslParser(); 57 | ElasticSqlParseResult parseResult = sql2DslParser.parse(sql); 58 | System.out.println(parseResult.toPrettyDsl(parseResult.toRequest())); 59 | } 60 | 61 | @Test 62 | public void testHighlighter2(){ 63 | String sql="select * from fruit where match_phrase(name,'苹果') or match_phrase(h#$aaa$bbb.mac,'0x10192j') order by lastModified desc limit 0,10"; 64 | ElasticSql2DslParser sql2DslParser=new ElasticSql2DslParser(); 65 | ElasticSqlParseResult parseResult = sql2DslParser.parse(sql); 66 | System.out.println(parseResult.toPrettyDsl(parseResult.toRequest())); 67 | } 68 | 69 | @Test 70 | public void test2(){ 71 | String sql="select * from fruit where query_string('h#苹果','fields:weight*,name','analyzer:ik_smart,phrase_slop:1')"; 72 | ElasticSql2DslParser sql2DslParser=new ElasticSql2DslParser(); 73 | ElasticSqlParseResult parseResult = sql2DslParser.parse(sql); 74 | System.out.println(parseResult.toPrettyDsl(parseResult.toRequest())); 75 | } 76 | 77 | @Test 78 | public void test3(){ 79 | String sql="select * from `apple-aaa-01-.*` order by minPrice desc, advicePrice asc"; 80 | ElasticSql2DslParser sql2DslParser=new ElasticSql2DslParser(); 81 | ElasticSqlParseResult parseResult = sql2DslParser.parse(sql); 82 | System.out.println(parseResult.toPrettyDsl(parseResult.toRequest())); 83 | System.out.println(Arrays.toString(parseResult.toRequest().indices())); 84 | } 85 | 86 | 87 | } 88 | --------------------------------------------------------------------------------