spuerx преди 9 години
родител
ревизия
6f3db6dd0a
променени са 1 файла, в които са добавени 68 реда и са изтрити 534 реда
  1. 68 534
      README.md

+ 68 - 534
README.md

@@ -1,7 +1,7 @@
-SQL to DSL for Elasticsearch
+elasticsearch-query-tookit
 ====================================
 
-elasticsearch-sql2dsl版本
+版本
 -------------
 
 sql2dsl version | ES version
@@ -10,587 +10,121 @@ master | 2.4.4
 2.x    | 2.4.4
 1.x    | 1.4.5
 
-sql2dsl介绍
+介绍
 -------------
-> `elasticsearch-sql2dsl`是一款能将SQL转换为ES的查询语言DSL的工具包,目前只支持比较简单的查询。
+> `elasticsearch-query-tookit`是一款elasticsearch查询编程工具包,提供Java编程接口,支持SQL解析生成DSL,支持JDBC驱动,支持和spring、ibatis集成
 
 
-使用注意
-------
-> * 在SQL的where条件中,原子条件的左边必须为变量名,右边必须为查询参数值
-> * 针对`Inner Doc`,直接引用即可:`a.b.c.d`
-> * 针对`Nested Doc`,需要在内嵌文档加上`$`符号: `$b.c`
+## 一、SQL解析生成DSL使用示例
 
-## Api使用示例
 
-常规sql查询,不带参数
 ```java
-String sql = "select * from index.order where status='SUCCESS' order by nvl(pride, 0) asc limit 0, 20";
+String sql = "select * from index.order where status='SUCCESS' order by nvl(pride, 0) asc routing by 'JD' limit 0, 20";
 
 ElasticSql2DslParser sql2DslParser = new ElasticSql2DslParser();
 //解析SQL
 ElasticSqlParseResult parseResult = sql2DslParser.parse(sql);
-
 //生成DSL,可用于rest api调用
 String dsl = parseResult.toDsl();
 
 //toRequest方法接收一个clinet对象参数,用于生成SearchRequestBuilder
 SearchRequestBuilder searchReq = parseResult.toRequest(esClient);
-
-//执行查询
-SearchResponse response = searchReq.execute().actionGet();
-```
-指定可变参数查询
-```java
-String sql = "select * from index.order where status=? order by nvl(pride, 0) asc limit ?, ?";
-
-ElasticSql2DslParser sql2DslParser = new ElasticSql2DslParser();
-//指定参数,解析SQL
-ElasticSqlParseResult parseResult = sql2DslParser.parse(sql, new Object[]{"SUCCESS", 0, 20});
-
-//生成DSL,可用于rest api调用
-String dsl = parseResult.toDsl();
-
-//toRequest方法接收一个clinet对象参数,用于生成SearchRequestBuilder
-SearchRequestBuilder searchReq = parseResult.toRequest(esClient);
-
 //执行查询
 SearchResponse response = searchReq.execute().actionGet();
 ```
 
-向WHERE条件解析时注册监听
-```java
-String sql = "select * from index.order where status='SUCCESS' and lastUpdateTime > '2017-01-02' order by nvl(pride, 0) asc limit 0, 20";
-
-ElasticSql2DslParser sql2DslParser = new ElasticSql2DslParser();
-
-//指定SQL解析监听器,解析SQL
-ElasticSqlParseResult parseResult = sql2DslParser.parse(sql, new ParseActionListenerAdapter() {
-    @Override
-    public void onAtomConditionParse(ElasticSqlQueryField paramName, Object[] paramValues, SQLConditionOperator operator) {
-        if(paramName.getQueryFieldType() == QueryFieldType.RootDocField && "lastUpdateTime".equals(paramName.getQueryFieldFullName())) {
-            //这里是解析SQL中原子条件时的回调方法
-            //某些按时间划分的索引,可在此解析出SQL中指定的时间范围,并重新设置需要查询的索引
-        }
-    }
-});
-
-//生成DSL,可用于rest api调用
-String dsl = parseResult.toDsl();
-
-//toRequest方法接收一个clinet对象参数,用于生成SearchRequestBuilder
-SearchRequestBuilder searchReq = parseResult.toRequest(esClient);
-
-//执行查询
-SearchResponse response = searchReq.execute().actionGet();
-
-```
-使用routing by指定routing参数
-```bash
-String sql = "select * from index.order where status='SUCCESS' order by nvl(pride, 0) asc routing by 'A','B' limit 0, 20";
-
-ElasticSql2DslParser sql2DslParser = new ElasticSql2DslParser();
-//解析SQL,
-ElasticSqlParseResult parseResult = sql2DslParser.parse(sql);
-
-//toRequest方法会把指定的routing参数放到SearchRequest中
-SearchRequestBuilder searchReq = parseResult.toRequest(esClient);
-```
-
-
-## SQL使用示例
-下面index表示索引名,order表示文档类型名
-```bash
-select * from index.order
-
-# 1. 默认从第0条数据开始,取15条
-# 2. 因为没有带where条件所以查询是match_all
-{
-  "from" : 0,
-  "size" : 15,
-  "query" : {
-    "match_all" : { }
-  }
-}
-```
-
-用关键字limit指定分页参数
-```bash
-select * from index.order limit 0,100
-
-# 分页参数从0开始取100条
-{
-  "from" : 0,
-  "size" : 100,
-  "query" : {
-    "match_all" : { }
-  }
-}
-```
-
-SQL中指定查询条件  status = 'SUCCESS'
-```bash
-select * from index.order where status='SUCCESS' limit 0,100
-
-{
-  "from" : 0,
-  "size" : 100,
-  "query" : {
-    "bool" : {
-      "filter" : {
-        "bool" : {
-          "must" : {
-            "term" : {
-              "status" : "SUCCESS"
-            }
-          }
-        }
-      }
-    }
-  }
-}
-```
-
-范围查询  totalPrice > 1000
-```bash
-select * from index.order where status='SUCCESS' and totalPrice > 1000 limit 0,100
-
-{
-  "from" : 0,
-  "size" : 100,
-  "query" : {
-    "bool" : {
-      "filter" : {
-        "bool" : {
-          "must" : [ {
-            "term" : {
-              "status" : "SUCCESS"
-            }
-          }, {
-            "range" : {
-              "totalPrice" : {
-                "from" : 1000,
-                "to" : null,
-                "include_lower" : false,
-                "include_upper" : true
-              }
-            }
-          } ]
-        }
-      }
-    }
-  }
-}
-```
-
-使用 between...and... 指定条件范围(上下限都取)
-```bash
-select * from index.order where status='SUCCESS' and totalPrice between 1000 and 2000 limit 0,100
 
-{
-  "from" : 0,
-  "size" : 100,
-  "query" : {
-    "bool" : {
-      "filter" : {
-        "bool" : {
-          "must" : [ {
-            "term" : {
-              "status" : "SUCCESS"
-            }
-          }, {
-            "range" : {
-              "totalPrice" : {
-                "from" : 1000,
-                "to" : 2000,
-                "include_lower" : true,
-                "include_upper" : true
-              }
-            }
-          } ]
-        }
-      }
-    }
-  }
-}
-```
-
-
-日期类型范围查询,下面3条SQL等效
+## 二、集成IBatis、Spring
+首先在Spring配置文件中增加如下代码
+1. 指定driverClassName:org.elasticsearch.jdbc.ElasticDriver
+2. 指定连接ES的连接串:jdbc:elastic:192.168.0.109:9300/product_cluster
+3. 创建一个SqlMapClient对象,并指定sqlMapConfig.xml路径
 ```bash
-select * from index.order where status='SUCCESS' and lastUpdateTime > '2017-01-01 00:00:00' limit 0,100
-select * from index.order where status='SUCCESS' and lastUpdateTime > '2017-01-01' limit 0,100
-
-#日期函数可以自定义日期格式
-select * from index.order where status='SUCCESS' and lastUpdateTime > date('yyyy-MM-dd', '2017-01-01') limit 0,100
-
-{
-  "from" : 0,
-  "size" : 100,
-  "query" : {
-    "bool" : {
-      "filter" : {
-        "bool" : {
-          "must" : [ {
-            "term" : {
-              "status" : "SUCCESS"
-            }
-          }, {
-            "range" : {
-              "lastUpdateTime" : {
-                "from" : "2017-01-01T00:00:00.000+0800",
-                "to" : null,
-                "include_lower" : false,
-                "include_upper" : true
-              }
-            }
-          } ]
-        }
-      }
-    }
-  }
-}
+<bean id="elasticDataSource" class="org.elasticsearch.jdbc.ElasticSingleConnectionDataSource" destroy-method="destroy">
+    <property name="driverClassName" value="org.elasticsearch.jdbc.ElasticDriver" />
+    <property name="url" value="jdbc:elastic:192.168.0.109:9300/product_cluster" />
+</bean>
 
+<bean id="sqlMapClient" class="org.springframework.orm.ibatis.SqlMapClientFactoryBean">
+    <property name="dataSource" ref="elasticDataSource" />
+    <property name="configLocation" value="classpath:sqlMapConfig.xml"/>
+</bean>
 ```
 
-排序条件,price升序,publishDate降序
+sqlMapConfig.xml文件内容如下:
 ```bash
-select * from index.order where status='SUCCESS' order by price asc, publishDate desc
+<sqlMapConfig>
+    <settings
+            cacheModelsEnabled="true"
+            lazyLoadingEnabled="true"
+            enhancementEnabled="true"
+            maxSessions="64"
+            maxTransactions="20"
+            maxRequests="128"
+            useStatementNamespaces="true"/>
 
-{
-  "from" : 0,
-  "size" : 15,
-  "query" : {
-    "bool" : {
-      "filter" : {
-        "bool" : {
-          "must" : {
-            "term" : {
-              "status" : "SUCCESS"
-            }
-          }
-        }
-      }
-    }
-  },
-  "sort" : [ {
-    "price" : {
-      "order" : "asc"
-    }
-  }, {
-    "publishDate" : {
-      "order" : "desc"
-    }
-  } ]
-}
+    <sqlMap resource="sqlmap/PRODUCT.xml"/>
 
+</sqlMapConfig>
 ```
 
-使用nvl函数指定默认值
+PRODUCT.xml文件中声明select sql语句
 ```bash
-select * from index.order where status='SUCCESS' order by nvl(price, 0) asc, publishDate desc
-
-{
-  "from" : 0,
-  "size" : 15,
-  "query" : {
-    "bool" : {
-      "filter" : {
-        "bool" : {
-          "must" : {
-            "term" : {
-              "status" : "SUCCESS"
-            }
-          }
-        }
-      }
-    }
-  },
-  "sort" : [ {
-    "price" : {
-      "order" : "asc",
-      "missing" : 0
-    }
-  }, {
-    "publishDate" : {
-      "order" : "desc"
-    }
-  } ]
-}
-
+<sqlMap namespace="PRODUCT">
+    <select id="getProductByCodeAndMatchWord" parameterClass="java.util.Map" resultClass="java.lang.String">
+        SELECT *
+        FROM index.product
+        QUERY match(productName, #matchWord#) or prefix(productName, #prefixWord#, 'boost:2.0f')
+        WHERE productCode = #productCode#
+        AND advicePrice > #advicePrice#
+        AND $$buyers.buyerName IN ('china', 'usa')
+        ROUTING BY #routingVal#
+    </select>
+</sqlMap>
 ```
 
-内嵌文档排序,指定sort_mode(其中$providers 表示内嵌文档),按照 min 方式排序
-```bash
-select * from index.order where status='SUCCESS' order by nvl(product.$providers.sortNo, 0, 'min') asc, publishDate desc
 
-{
-  "from" : 0,
-  "size" : 15,
-  "query" : {
-    "bool" : {
-      "filter" : {
-        "bool" : {
-          "must" : {
-            "term" : {
-              "status" : "SUCCESS"
-            }
-          }
-        }
-      }
-    }
-  },
-  "sort" : [ {
-    "product.providers.sortNo" : {
-      "order" : "asc",
-      "missing" : 0,
-      "mode" : "min",
-      "nested_path" : "product.providers"
-    }
-  }, {
-    "publishDate" : {
-      "order" : "desc"
-    }
-  } ]
-}
-
-```
-
-Inner Doc 查询
+编写对应DAO代码:
 ```bash
-select * from index.order where seller.name='JD' order by id desc
-
-{
-  "from" : 0,
-  "size" : 15,
-  "query" : {
-    "bool" : {
-      "filter" : {
-        "bool" : {
-          "must" : {
-            "term" : {
-              "seller.name" : "JD"
-            }
-          }
-        }
-      }
-    }
-  },
-  "sort" : [ {
-    "id" : {
-      "order" : "desc"
-    }
-  } ]
-}
-
-```
+@Repository
+public class ProductDao {
+    @Autowired
+    @Qualifier("sqlMapClient")
+    private SqlMapClient sqlMapClient;
 
-Nested Doc查询($providers 表示内嵌文档)
-```bash
-select * from index.order where product.$providers.name in ('JD', 'TB') and product.price < 1000 order by id desc
 
-{
-  "from" : 0,
-  "size" : 15,
-  "query" : {
-    "bool" : {
-      "filter" : {
-        "bool" : {
-          "must" : [ {
-            "range" : {
-              "product.price" : {
-                "from" : null,
-                "to" : 1000,
-                "include_lower" : true,
-                "include_upper" : false
-              }
-            }
-          }, {
-            "nested" : {
-              "filter" : {
-                "terms" : {
-                  "product.providers.name" : [ "JD", "TB" ]
-                }
-              },
-              "path" : "product.providers"
-            }
-          } ]
-        }
-      }
-    }
-  },
-  "sort" : [ {
-    "id" : {
-      "order" : "desc"
-    }
-  } ]
-}
-```
+    public List<Product> getProductByCodeAndMatchWord(String matchWord, String productCode) throws SQLException {
+        Map<String, Object> paramMap = Maps.newHashMap();
+        paramMap.put("productCode", productCode);
+        paramMap.put("advicePrice", 1000);
+        paramMap.put("routingVal", "A");
+        paramMap.put("matchWord", matchWord);
+        paramMap.put("prefixWord", matchWord);
+        String responseGson = (String) sqlMapClient.queryForObject("PRODUCT.getProductByCodeAndMatchWord", paramMap);
 
+        JdbcSearchResponseResolver responseResolver = new JdbcSearchResponseResolver(responseGson);
+        JdbcSearchResponse<Product> searchResponse = responseResolver.resolveSearchResponse(Product.class);
 
-组合条件查询
-```bash
-select * from index.order
-where
-(product.$providers.name in ('JD', 'TB') or product.$providers.channel='ONLINE')
-and
-(product.status='OPEN' or product.price < 1000)
-order by id desc
+        return searchResponse.getDocList();
 
-{
-  "from" : 0,
-  "size" : 15,
-  "query" : {
-    "bool" : {
-      "filter" : {
-        "bool" : {
-          "must" : [ {
-            "bool" : {
-              "should" : {
-                "nested" : {
-                  "filter" : {
-                    "bool" : {
-                      "should" : [ {
-                        "terms" : {
-                          "product.providers.name" : [ "JD", "TB" ]
-                        }
-                      }, {
-                        "term" : {
-                          "product.providers.channel" : "ONLINE"
-                        }
-                      } ]
-                    }
-                  },
-                  "path" : "product.providers"
-                }
-              }
-            }
-          }, {
-            "bool" : {
-              "should" : [ {
-                "term" : {
-                  "product.status" : "OPEN"
-                }
-              }, {
-                "range" : {
-                  "product.price" : {
-                    "from" : null,
-                    "to" : 1000,
-                    "include_lower" : true,
-                    "include_upper" : false
-                  }
-                }
-              } ]
-            }
-          } ]
-        }
-      }
     }
-  },
-  "sort" : [ {
-    "id" : {
-      "order" : "desc"
-    }
-  } ]
 }
 ```
-
-查询字段指定
+编写测试方法
 ```bash
-select totalPrice, product.*, product.$seller.* from index.order
-
-{
-  "from" : 0,
-  "size" : 15,
-  "query" : {
-    "match_all" : { }
-  },
-  "_source" : {
-    "includes" : [ "totalPrice", "product.*", "product.seller.*" ],
-    "excludes" : [ ]
-  }
-}
-```
+@Test
+public void testProductQuery() throws Exception {
+    BeanFactory factory = new ClassPathXmlApplicationContext("application-context.xml");
 
+    ProductDao productDao = factory.getBean(ProductDao.class);
 
+    List<Product> productList = productDao.getProductByCodeAndMatchWord("iphone 6s", "IP_6S");
 
-
-聚合统计,目前只支持 terms,range 聚合
-
-```bash
-select min(price),avg(price) from index.product group by terms(category),terms(color),range(price, segment(0,100), segment(100,200), segment(200,300))
-
-
-{
-  "from" : 0,
-  "size" : 15,
-  "query" : {
-    "match_all" : { }
-  },
-  "aggregations" : {
-    "agg_category" : {
-      "terms" : {
-        "field" : "category",
-        "size" : 500,
-        "shard_size" : 1000,
-        "min_doc_count" : 1,
-        "shard_min_doc_count" : 1,
-        "order" : {
-          "_count" : "desc"
-        }
-      },
-      "aggregations" : {
-        "agg_color" : {
-          "terms" : {
-            "field" : "color",
-            "size" : 500,
-            "shard_size" : 1000,
-            "min_doc_count" : 1,
-            "shard_min_doc_count" : 1,
-            "order" : {
-              "_count" : "desc"
-            }
-          },
-          "aggregations" : {
-            "agg_price" : {
-              "range" : {
-                "field" : "price",
-                "ranges" : [ {
-                  "key" : "0-100",
-                  "from" : 0.0,
-                  "to" : 100.0
-                }, {
-                  "key" : "100-200",
-                  "from" : 100.0,
-                  "to" : 200.0
-                }, {
-                  "key" : "200-300",
-                  "from" : 200.0,
-                  "to" : 300.0
-                } ]
-              },
-              "aggregations" : {
-                "min_price" : {
-                  "min" : {
-                    "field" : "price"
-                  }
-                },
-                "avg_price" : {
-                  "avg" : {
-                    "field" : "price"
-                  }
-                }
-              }
-            }
-          }
-        }
-      }
+    for (Product product : productList) {
+        System.out.println(product.getProductName());
     }
-  }
 }
-
 ```
-
 作者:  [@陈楠][1]
 Email: 465360798@qq.com