Elasticsearch SQL ORM查询案例

bboss

bboss ES SQL是针对es jdbc的替代解决方案

the best elasticsearch highlevel java rest api——-bboss

bboss 提供一组sql和fetchQuery API,可替代官方es jdbc模块;采用bboss即可拥有bboss的客户端自动发现和容灾能力、对es、jdk、spring boot的兼容性能力,又可以拥有es jdbc的所有功能,同时还解决了因为引入es jdbc导致项目对es版本的强依赖和兼容性问题,参考demo:

orm查询
https://gitee.com/bboss/eshelloword-booter/blob/master/src/test/java/org/bboss/elasticsearchtest/sql/SQLOrmTest.java

分页查询
https://gitee.com/bboss/eshelloword-booter/blob/master/src/test/java/org/bboss/elasticsearchtest/sql/SQLPagineTest.java

目前官方es sql提供的功能有限,也可以在bboss中使用Elasticsearch-sql插件提供的功能,下面有专门的章节介绍。

本文详细说明上面的案例:

1 orm查询

1.1 定义orm查询的实体bean

  1. package org.bboss.elasticsearchtest.sql;
  2. import com.frameworkset.orm.annotation.Column;
  3. import java.util.Date;
  4. public class DocObject {
  5. private int isnew;
  6. private Date createtime;
  7. private String content;
  8. private int documentId;
  9. private int channelId;
  10. /**
  11. * 通过column指定索引文档和对象属性的映射关系
  12. * 通过column注解还可以指定日期格式和时区信息
  13. * @Column(name="docInfo.author",dataformat = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'",timezone = "Etc/UTC",locale = "zh")
  14. *
  15. */
  16. @Column(name="docInfo.author")
  17. private String docInfoAuthor;
  18. public int getIsnew() {
  19. return isnew;
  20. }
  21. public void setIsnew(int isnew) {
  22. this.isnew = isnew;
  23. }
  24. public Date getCreatetime() {
  25. return createtime;
  26. }
  27. public void setCreatetime(Date createtime) {
  28. this.createtime = createtime;
  29. }
  30. public String getContent() {
  31. return content;
  32. }
  33. public void setContent(String content) {
  34. this.content = content;
  35. }
  36. public int getDocumentId() {
  37. return documentId;
  38. }
  39. public void setDocumentId(int documentId) {
  40. this.documentId = documentId;
  41. }
  42. public int getChannelId() {
  43. return channelId;
  44. }
  45. public void setChannelId(int channelId) {
  46. this.channelId = channelId;
  47. }
  48. public String getDocInfoAuthor() {
  49. return docInfoAuthor;
  50. }
  51. public void setDocInfoAuthor(String docInfoAuthor) {
  52. this.docInfoAuthor = docInfoAuthor;
  53. }
  54. }

实体定义说明:

通过column指定索引文档和对象属性的映射关系,指定日期格式和时区信息,示例如下:

  1. @Column(name="docInfo.author")
  2. private String docInfoAuthor;

指定属性的映射关系、日期格式和时区信息,示例如下:

@Column(name=”docInfo.author”,dataformat = “yyyy-MM-dd’T’HH:mm:ss.SSS’Z’”,timezone = “Etc/UTC”,locale = “zh”)

1.2 执行orm查询

以rest sql api为例来介绍es 6.3.0的sql orm查询功能

  1. package org.bboss.elasticsearchtest.sql;
  2. import org.frameworkset.elasticsearch.ElasticSearchHelper;
  3. import org.frameworkset.elasticsearch.client.ClientInterface;
  4. import org.frameworkset.elasticsearch.entity.sql.SQLRestResponse;
  5. import org.frameworkset.elasticsearch.entity.sql.SQLRestResponseHandler;
  6. import org.junit.Test;
  7. import java.util.HashMap;
  8. import java.util.List;
  9. import java.util.Map;
  10. /**
  11. * 以rest sql api为例来介绍es 6.3.0的sql orm查询功能
  12. */
  13. public class SQLOrmTest {
  14. /**
  15. * 代码中的sql检索,返回Map类型集合,亦可以返回自定义的对象集合
  16. */
  17. @Test
  18. public void testDemoQuery(){
  19. ClientInterface clientUtil = ElasticSearchHelper.getRestClientUtil();
  20. List<Map> json = clientUtil.sql(Map.class,"{\"query\": \"SELECT * FROM demo\"}");
  21. System.out.println(json);
  22. }
  23. /**
  24. * 代码中的sql检索,返回Map类型集合,亦可以返回自定义的对象集合
  25. */
  26. @Test
  27. public void testMapQuery(){
  28. ClientInterface clientUtil = ElasticSearchHelper.getRestClientUtil();
  29. List<Map> json = clientUtil.sql(Map.class,"{\"query\": \"SELECT * FROM dbclobdemo\"}");
  30. System.out.println(json);
  31. }
  32. /**
  33. * 配置文件中的sql dsl检索,返回Map类型集合,亦可以返回自定义的对象集合
  34. */
  35. @Test
  36. public void testMapSQLQueryFromDSL(){
  37. ClientInterface clientUtil = ElasticSearchHelper.getConfigRestClientUtil("esmapper/sql.xml");//初始化一个加载sql配置文件的es客户端接口
  38. //设置sql查询的参数
  39. Map params = new HashMap();
  40. params.put("channelId",1);
  41. List<Map> json = clientUtil.sql(Map.class,"sqlQuery",params);
  42. System.out.println(json);
  43. }
  44. /**
  45. * 代码中的sql检索,返回Map类型对象,亦可以返回自定义的对象
  46. */
  47. @Test
  48. public void testMapObjectQuery(){
  49. ClientInterface clientUtil = ElasticSearchHelper.getRestClientUtil();
  50. Map json = clientUtil.sqlObject(Map.class,"{\"query\": \"SELECT * FROM dbclobdemo\"}");
  51. System.out.println(json);
  52. }
  53. /**
  54. * 配置文件中的sql dsl检索,返回Map类型对象,亦可以返回自定义的对象
  55. */
  56. @Test
  57. public void testMapObjectSQLQueryFromDSL(){
  58. ClientInterface clientUtil = ElasticSearchHelper.getConfigRestClientUtil("esmapper/sql.xml");//初始化一个加载sql配置文件的es客户端接口
  59. //设置sql查询的参数
  60. Map params = new HashMap();
  61. params.put("channelId",1);
  62. Map json = clientUtil.sqlObject(Map.class,"sqlQuery",params);
  63. System.out.println(json);
  64. }
  65. /**
  66. * 代码中的sql检索,返回DocObject 类型集合
  67. */
  68. @Test
  69. public void testObjectListQuery(){
  70. ClientInterface clientUtil = ElasticSearchHelper.getRestClientUtil();
  71. List<DocObject> json = clientUtil.sql(DocObject.class,"{\"query\": \"SELECT * FROM dbclobdemo\"}");
  72. System.out.println(json);
  73. }
  74. /**
  75. * 配置文件中的sql dsl检索,返回DocObject 类型集合
  76. */
  77. @Test
  78. public void testObjectSQLQueryFromDSL(){
  79. ClientInterface clientUtil = ElasticSearchHelper.getConfigRestClientUtil("esmapper/sql.xml");//初始化一个加载sql配置文件的es客户端接口
  80. //设置sql查询的参数
  81. Map params = new HashMap();
  82. params.put("channelId",1);
  83. List<DocObject> json = clientUtil.sql(DocObject.class,"sqlQuery",params);
  84. System.out.println(json);
  85. }
  86. /**
  87. * 代码中的sql检索,返回DocObject 类型对象
  88. */
  89. @Test
  90. public void testObjectQuery(){
  91. ClientInterface clientUtil = ElasticSearchHelper.getRestClientUtil();
  92. DocObject json = clientUtil.sqlObject(DocObject.class,"{\"query\": \"SELECT * FROM dbclobdemo where documentId = 1\"}");
  93. System.out.println(json);
  94. }
  95. /**
  96. * 配置文件中的sql dsl检索,返回DocObject 类型对象
  97. */
  98. @Test
  99. public void testConditionObjectSQLQueryFromDSL(){
  100. ClientInterface clientUtil = ElasticSearchHelper.getConfigRestClientUtil("esmapper/sql.xml");//初始化一个加载sql配置文件的es客户端接口
  101. //设置sql查询的参数
  102. Map params = new HashMap();
  103. params.put("channelId",1);
  104. DocObject json = clientUtil.sqlObject(DocObject.class,"sqlQuery",params);
  105. System.out.println(json);
  106. }
  107. /**
  108. * sql转换为dsl
  109. */
  110. @Test
  111. public void testTranslate(){
  112. ClientInterface clientUtil = ElasticSearchHelper.getRestClientUtil();
  113. String json = clientUtil.executeHttp("/_xpack/sql/translate",
  114. "{\"query\": \"SELECT * FROM dbclobdemo limit 5\",\"fetch_size\": 5}",
  115. ClientInterface.HTTP_POST
  116. );
  117. System.out.println(json);
  118. }
  119. /**
  120. * 低阶的检索方法
  121. */
  122. @Test
  123. public void testSQLRestResponse(){
  124. ClientInterface clientUtil = ElasticSearchHelper.getRestClientUtil();
  125. SQLRestResponse sqlRestResponse = clientUtil.executeHttp("/_xpack/sql",
  126. "{\"query\": \"SELECT * FROM dbclobdemo where documentId = 1\"}",
  127. ClientInterface.HTTP_POST,
  128. new SQLRestResponseHandler());
  129. System.out.println(sqlRestResponse);
  130. }
  131. }

每个orm查询方法的都有对应的功能注释说明,可以根据需要使用相关的方法。

代码中用到的sql dsl脚本配置文件及内容:esmapper/sql.xml

  1. <properties>
  2. <!--
  3. sql query
  4. -->
  5. <property name="sqlQuery">
  6. <![CDATA[
  7. {"query": "SELECT * FROM dbclobdemo where channelId=#[channelId]"}
  8. ]]>
  9. </property>
  10. <property name="sqlQueryWithStringParam">
  11. <![CDATA[
  12. {"query": "SELECT * FROM dbclobdemo where channelId=#[channelId] and docTitle='#[channelId,quoted=false]'"} ## sql中特定的字符串参数语法
  13. ]]>
  14. </property>
  15. <!--
  16. 分页sql query
  17. 每页显示 fetch_size对应的记录条数
  18. -->
  19. <property name="sqlPagineQuery">
  20. <![CDATA[
  21. {
  22. ## 指示sql语句中的回车换行符会被替换掉开始符,注意dsl注释不能放到sql语句中,否则会有问题,因为sql中的回车换行符会被去掉,导致回车换行符后面的语句变道与注释一行
  23. ## 导致dsl模板解析的时候部分sql段会被去掉
  24. "query": #"""
  25. SELECT * FROM dbclobdemo
  26. where channelId=#[channelId]
  27. """,
  28. ## 指示sql语句中的回车换行符会被替换掉结束符
  29. "fetch_size": #[fetchSize]
  30. }
  31. ]]>
  32. </property>
  33. </properties>

我们将配置文件放到工程resources目录下面即可。sql配置说明:

sql中特定的字符串参数语法

  1. {"query": "SELECT * FROM dbclobdemo where channelId=#[channelId] and docTitle='#[channelId,quoted=false]'"}

我们使用#[xxx]类型变量传递sql参数时,如果是字符串内容会自动在值的两边带上双引号,但是在sql语句是字符串值是用单引号’来标识的,所以通过qutoed=false来指示解析引擎不要在值的两边加双引号,然后在外部手动添加单引号:

‘#[channelId,quoted=false]’

如果sql语句比较长,可能要换行,es暂时不支持多行sql语句的执行,bboss通过下面特定的语法,来包围多行sql,sql解析引擎在第一次解析sql的时候讲其中的多行sql解析为一行:

#“””

“””

例如:

  1. {
  2. ## 指示sql语句中的回车换行符会被替换掉开始符,注意dsl注释不能放到sql语句中,否则会有问题,因为sql中的回车换行符会被去掉,导致回车换行符后面的语句变道与注释一行
  3. ## 导致dsl模板解析的时候部分sql段会被去掉
  4. "query": #"""
  5. SELECT * FROM dbclobdemo
  6. where channelId=#[channelId]
  7. """,
  8. ## 指示sql语句中的回车换行符会被替换掉结束符
  9. "fetch_size": #[fetchSize]
  10. }

1.3 通过fetch_size实现分页查询

  1. package org.bboss.elasticsearchtest.sql;
  2. import org.frameworkset.elasticsearch.ElasticSearchHelper;
  3. import org.frameworkset.elasticsearch.client.ClientInterface;
  4. import org.frameworkset.elasticsearch.entity.sql.SQLResult;
  5. import org.junit.Test;
  6. import java.util.HashMap;
  7. import java.util.List;
  8. import java.util.Map;
  9. public class SQLPagineTest {
  10. /**
  11. * 代码中的sql检索,返回Map类型集合,亦可以返回自定义的对象集合
  12. */
  13. @Test
  14. public void testMapQuery(){
  15. ClientInterface clientUtil = ElasticSearchHelper.getRestClientUtil();
  16. SQLResult<Map> sqlResult = clientUtil.fetchQuery(Map.class,"{\"query\": \"SELECT * FROM dbclobdemo\",\"fetch_size\": 1}");
  17. do{
  18. List<Map> datas = sqlResult.getDatas();
  19. if(datas == null || datas.size() == 0){
  20. break;
  21. }
  22. else{
  23. System.out.println(datas.size());//处理数据
  24. sqlResult = sqlResult.nextPage();//获取下一页数据
  25. }
  26. }while(true);
  27. }
  28. /**
  29. * 配置文件中的sql dsl检索,返回Map类型集合,亦可以返回自定义的对象集合
  30. */
  31. @Test
  32. public void testMapSQLQueryFromDSL(){
  33. ClientInterface clientUtil = ElasticSearchHelper.getConfigRestClientUtil("esmapper/sql.xml");//初始化一个加载sql配置文件的es客户端接口
  34. //设置sql查询的参数
  35. Map params = new HashMap();
  36. params.put("channelId",1);
  37. params.put("fetchSize",1);
  38. SQLResult<Map> sqlResult = clientUtil.fetchQuery(Map.class,"sqlPagineQuery",params);
  39. do{
  40. List<Map> datas = sqlResult.getDatas();
  41. if(datas == null || datas.size() == 0){
  42. break;
  43. }
  44. else{
  45. System.out.println(datas.size());//处理数据
  46. sqlResult = sqlResult.nextPage();//获取下一页数据
  47. }
  48. }while(true);
  49. }
  50. /**
  51. * 代码中的sql检索,返回DocObject 类型集合
  52. */
  53. @Test
  54. public void testObjectListQuery(){
  55. ClientInterface clientUtil = ElasticSearchHelper.getRestClientUtil();
  56. SQLResult<DocObject> sqlResult = clientUtil.fetchQuery(DocObject.class,"{\"query\": \"SELECT * FROM dbclobdemo\",\"fetch_size\": 1}");
  57. do{
  58. List<DocObject> datas = sqlResult.getDatas();
  59. if(datas == null || datas.size() == 0){
  60. break;
  61. }
  62. else{
  63. System.out.println(datas.size());//处理数据
  64. sqlResult = sqlResult.nextPage();//获取下一页数据
  65. }
  66. }while(true);
  67. }
  68. /**
  69. * 配置文件中的sql dsl检索,返回DocObject 类型集合
  70. */
  71. @Test
  72. public void testObjectSQLQueryFromDSL(){
  73. ClientInterface clientUtil = ElasticSearchHelper.getConfigRestClientUtil("esmapper/sql.xml");//初始化一个加载sql配置文件的es客户端接口
  74. //设置sql查询的参数
  75. Map params = new HashMap();
  76. params.put("channelId",1);
  77. params.put("fetchSize",1);
  78. SQLResult<DocObject> sqlResult = clientUtil.fetchQuery(DocObject.class,"sqlPagineQuery",params);
  79. do{
  80. List<DocObject> datas = sqlResult.getDatas();
  81. if(datas == null || datas.size() == 0){
  82. System.out.println(0);//处理数据
  83. break;
  84. }
  85. else{
  86. System.out.println(datas.size());//处理数据
  87. sqlResult = sqlResult.nextPage();//获取下一页数据
  88. }
  89. }while(true);
  90. }
  91. /**
  92. * 配置文件中的sql dsl检索,返回DocObject 类型集合
  93. */
  94. @Test
  95. public void testObjectSQLQueryFromDSL1(){
  96. ClientInterface clientUtil = ElasticSearchHelper.getConfigRestClientUtil("esmapper/sql.xml");//初始化一个加载sql配置文件的es客户端接口
  97. //设置sql查询的参数
  98. Map params = new HashMap();
  99. params.put("channelId",1);
  100. params.put("fetchSize",1);
  101. SQLResult<DocObject> sqlResult = clientUtil.fetchQuery(DocObject.class,"sqlPagineQuery",params);
  102. do{
  103. List<DocObject> datas = sqlResult.getDatas();
  104. if(datas == null || datas.size() == 0){
  105. System.out.println(0);//处理数据
  106. break;
  107. }
  108. else{
  109. System.out.println(datas.size());//处理数据
  110. sqlResult = clientUtil.fetchQueryByCursor(DocObject.class,sqlResult);//获取下一页数据,通过api获取下一页数据
  111. }
  112. }while(true);
  113. }
  114. /**
  115. * 配置文件中的sql dsl检索,返回DocObject 类型集合
  116. * 测试没有返回数据的情况
  117. */
  118. @Test
  119. public void testNodataSQLQueryFromDSL1(){
  120. ClientInterface clientUtil = ElasticSearchHelper.getConfigRestClientUtil("esmapper/sql.xml");//初始化一个加载sql配置文件的es客户端接口
  121. //设置sql查询的参数
  122. Map params = new HashMap();
  123. params.put("channelId",2);
  124. params.put("fetchSize",1);
  125. SQLResult<DocObject> sqlResult = clientUtil.fetchQuery(DocObject.class,"sqlPagineQuery",params);
  126. do{
  127. List<DocObject> datas = sqlResult.getDatas();
  128. if(datas == null || datas.size() == 0){
  129. System.out.println(0);//处理数据
  130. break;
  131. }
  132. else{
  133. System.out.println(datas.size());//处理数据
  134. sqlResult = clientUtil.fetchQueryByCursor(DocObject.class,sqlResult);//获取下一页数据,通过api获取下一页数据
  135. }
  136. }while(true);
  137. }
  138. /**
  139. * 配置文件中的sql dsl检索,返回DocObject 类型集合
  140. */
  141. @Test
  142. public void testObjectSQLQueryFromDSL2(){
  143. ClientInterface clientUtil = ElasticSearchHelper.getConfigRestClientUtil("esmapper/sql.xml");//初始化一个加载sql配置文件的es客户端接口
  144. //设置sql查询的参数
  145. Map params = new HashMap();
  146. params.put("channelId",1);
  147. params.put("fetchSize",1);
  148. SQLResult<DocObject> sqlResult = clientUtil.fetchQuery(DocObject.class,"sqlPagineQuery",params);
  149. do{
  150. List<DocObject> datas = sqlResult.getDatas();
  151. if(datas == null || datas.size() == 0){
  152. System.out.println(0);//处理数据
  153. break;
  154. }
  155. else{
  156. System.out.println(datas.size());//处理数据
  157. sqlResult = clientUtil.fetchQueryByCursor(DocObject.class,sqlResult.getCursor(),sqlResult.getColumns());//获取下一页数据,通过api获取下一页数据
  158. }
  159. }while(true);
  160. }
  161. /**
  162. * 代码中的sql检索,返回 DocObject类型集合
  163. */
  164. @Test
  165. public void testCloseCursor(){
  166. ClientInterface clientUtil = ElasticSearchHelper.getRestClientUtil();
  167. SQLResult<DocObject> sqlResult = clientUtil.fetchQuery(DocObject.class,"{\"query\": \"SELECT * FROM dbclobdemo\",\"fetch_size\": 1}");
  168. List<DocObject> datas = sqlResult.getDatas();
  169. System.out.println(datas.size());//处理数据
  170. System.out.println(sqlResult.closeCursor());//只处理第一页数据,就主动关闭分页游标
  171. }
  172. /**
  173. * 代码中的sql检索,返回DocObject类型集合
  174. */
  175. @Test
  176. public void testCloseCursor1(){
  177. ClientInterface clientUtil = ElasticSearchHelper.getRestClientUtil();
  178. SQLResult<DocObject> sqlResult = clientUtil.fetchQuery(DocObject.class,"{\"query\": \"SELECT * FROM dbclobdemo\",\"fetch_size\": 1}");
  179. List<DocObject> datas = sqlResult.getDatas();
  180. System.out.println(datas.size());//处理数据
  181. String ret = clientUtil.closeSQLCursor(sqlResult.getCursor());
  182. System.out.println(ret);//只处理第一页数据,就主动关闭分页游标
  183. }
  184. }

2 Elasticsearch-sql查询

基于第三方Elasticsearch-sql插件的查询功能的使用方法和bboss提供的查询api使用方法一致,只是检索的rest服务换成/_sql服务即可:

  1. /**
  2. * Elasticsearch-SQL插件功能测试方法
  3. */
  4. public void testESSQL(){
  5. ClientInterface clientUtil = ElasticSearchHelper.getRestClientUtil();
  6. ESDatas<Map> esDatas = //ESDatas包含当前检索的记录集合,最多10条记录,由sql中的limit属性指定
  7. clientUtil.searchList("/_sql",//sql请求
  8. "select * from vem_order_index_2018 limit 0,10", //elasticsearch-sql支持的sql语句
  9. Map.class);//返回的文档封装对象类型
  10. //获取结果对象列表
  11. List<Map> demos = esDatas.getDatas();
  12. //获取总记录数
  13. long totalSize = esDatas.getTotalSize();
  14. System.out.println(totalSize);
  15. }

Elasticsearch-SQL插件sql转dsl功能:

  1. /**
  2. * Elasticsearch-SQL插件功能:sql转dsl
  3. */
  4. @Test
  5. public void testESSQLTranslate(){
  6. ClientInterface clientUtil = ElasticSearchHelper.getRestClientUtil();
  7. String dsl = //将sql转换为dsl
  8. clientUtil.executeHttp("/_sql/_explain",//sql转dsl请求
  9. "select operModule.keyword from dbdemo group by operModule.keyword ",ClientInterface.HTTP_POST);//返回的转换的结果
  10. //获取总记录数
  11. System.out.println(dsl);
  12. }

官方的ES-SQL功能目前还不成熟,必须Elasticsearch 6.3以上的版本,而且收费;Elasticsearch-SQL插件功能全,免费,可以在不同的Elasticsearch版本上运行。

3 开发交流

elasticsearch技术交流:166471282

elasticsearch:

bbossgroups