前言
目前Elasticsearch有好几个客户端工具,最常见的可能是RestHighLevelClient
,但在官网中表示,此工具也已经过时了,取而代之的是elasticsearch-java
,下面是他的一个基本用法,可以看到的是,新工具利用java8的一些特性,让我们写起来更直观,更有层次感。
RestClient restClient = RestClient.builder(
new HttpHost("localhost", 9200)).build();
ElasticsearchTransport transport = new RestClientTransport(
restClient, new JacksonJsonpMapper());
ElasticsearchClient elasticsearchClient = new ElasticsearchClient(transport);
SearchResponse<TbBlog> search = elasticsearchClient.search(s -> s
.index("products")
.query(q -> q
.term(t -> t
.field("name")
.value(v -> v.stringValue("bicycle"))
)),
TbBlog.class);
但是在Kotlin下,更有层次感的写法是这个样子,怎么样,是不是很喜欢。
es {
size = 2
query {
match {
fieldName = "blogTitle"
fieldValue = "Java"
}
}
onHits {
for (hit in it.hits) {
println(hit.sourceAsMap.get("blogTitle"))
}
}
}
上面这段代码核心搜索依然是使用了RestHighLevelClient
,在语法上之所以能这样写,是用了Kotlin的特性,即高阶函数。
高阶函数
高阶函数的定义非常简单:一个函数如果参数类型是函数或者返回值类型是函数,那么这就是一个高阶函数。
如下面的createUser
方法,参数是一个函数,这个函数的输入是一个User对象,并且无返回值,可以理解为,通过invoke
方法调用并传入User实例对象,这个函数将会对这个User进行改变,最终的输出结果为张三
。
class User {
var name: String = ""
}
fun createUser(config: (User) -> Unit) {
val user = User().apply { config.invoke(this) }
println(user.name)
}
fun main() {
createUser {
it.name = "张三"
}
}
但还有一种写法,如下,区别是加了一个.()
。
fun createUser(config: (User).() -> Unit) {
val user = User().apply { config.invoke(this) }
println(user.name)
}
这种写法可以在大括号中使用this,表示传递过来的参数实例,而this
则可以省略。
createUser {
name = "张三"
}
这样就可以嵌套了,完整的查询如下。
abstract class QueryField {
lateinit var fieldName: String
lateinit var fieldValue: String
}
class EsTermConfig : QueryField() {
}
class EsMatchConfig : QueryField() {}
class EsQueryConfig {
var term: EsTermConfig? = null
var match: EsMatchConfig? = null
fun term(esTermConfigFn: (EsTermConfig).() -> Unit) {
EsTermConfig().run {
esTermConfigFn.invoke(this)
term = this
}
}
fun match(esMatchConfigFn: (EsMatchConfig).() -> Unit) {
EsMatchConfig().run {
esMatchConfigFn.invoke(this)
match = this
}
}
}
class EsConfig {
var query: EsQueryConfig? = null
var size = 20
var from = 0
lateinit var responseFn: (SearchResponse) -> Unit
lateinit var hitsResponseFn: (SearchHits) -> Unit
fun query(searchConfig: (EsQueryConfig.() -> Unit)) {
EsQueryConfig().run {
searchConfig.invoke(this)
query = this
}
}
fun onResult(result: (SearchResponse) -> Unit) {
responseFn = result
}
fun onHits(result: (SearchHits) -> Unit) {
hitsResponseFn = result
}
}
fun es(esConfigFn: (EsConfig.() -> Unit)) {
val esConfig = EsConfig()
esConfigFn.invoke(esConfig)
val builder = RestClient.builder(HttpHost("localhost", 9200))
val basicCredentialsProvider = BasicCredentialsProvider()
basicCredentialsProvider.setCredentials(
AuthScope.ANY,
UsernamePasswordCredentials("elastic", "cYjC2iTH4SUjXEWwGAkT")
)
builder.setHttpClientConfigCallback {
it.setDefaultCredentialsProvider(
basicCredentialsProvider
)
}
val restHighLevelClient = RestHighLevelClient(builder)
val searchRequest = SearchRequest()
val searchSourceBuilder = SearchSourceBuilder()
esConfig.query?.term?.run {
searchSourceBuilder.query(QueryBuilders.termQuery(this.fieldName, this.fieldValue))
}
esConfig.query?.match?.run {
searchSourceBuilder.query(QueryBuilders.matchQuery(this.fieldName, this.fieldValue))
}
searchSourceBuilder.from(esConfig.from)
searchSourceBuilder.size(esConfig.size)
searchRequest.source(searchSourceBuilder)
val response = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT)
esConfig.hitsResponseFn.invoke(response.hits)
esConfig.responseFn.invoke(response)
}
fun main() {
es {
size = 2
query {
match {
fieldName = "blogTitle"
fieldValue = "Java"
}
}
onResult {
}
onHits {
for (hit in it.hits) {
println(hit.sourceAsMap.get("blogTitle"))
}
}
}
}
当然这只是一个示例,还没有完成聚合、mulit查询等功能,说不定在以后会单独写一个elasticsearch-kotlin-dsl
得一个库。