elastic4s是elasticsearch一个第三方开发的scala语言终端工具库(Elastic4s is a concise, idiomatic, reactive, type safe Scala client for Elasticsearch.)。scala用户可以用elastic4s提供的DSL用编程代码形式来构建ES服务请求。与字符型json文本直接编写请求不同的是:在编译DSL编写的ES服务请求时可以发现无论是语法上或者语意上的错误。一般来讲:elastic4s的程序流程相对直接、简单,如下:

  client.execute {
indexInto("books" ).fields("title" -> "重庆火锅的十种吃法", "content" -> "在这部书里描述了火锅的各种烹饪方式")
}.await val response = client.execute {
search("books").matchQuery("title", "火锅")
}.await ... ...

一项ES操作服务从构建请求到具体运行都是在execute(T)这个函数里进行的。值得注意的是这个T类型在上面的例子里可以是IndexRequest或者SearchRequest,如下:

   def indexInto(index: Index): IndexRequest
...
def search(index: String): SearchRequest

实际上execute(T)的T代表elastic4s支持的所有ES操作类型。这种方法实现有赖于scala的typeclass模式。我们先看看execute函数定义:

  // Executes the given request type T, and returns an effect of Response[U]
// where U is particular to the request type.
// For example a search request will return a Response[SearchResponse].
def execute[T, U, F[_]](t: T)(implicit
executor: Executor[F],
functor: Functor[F],
handler: Handler[T, U],
manifest: Manifest[U]): F[Response[U]] = {
val request = handler.build(t)
val f = executor.exec(client, request)
functor.map(f) { resp =>
handler.responseHandler.handle(resp) match {
case Right(u) => RequestSuccess(resp.statusCode, resp.entity.map(_.content), resp.headers, u)
case Left(error) => RequestFailure(resp.statusCode, resp.entity.map(_.content), resp.headers, error)
}
}
}

这个函数比较重要的功能之一应该是构建服务请求了。这个功能是通过handler.build(t)实现的。handler: Handler[T,U]是个隐式参数,它就是一个typeclass:

/**
* A [[Handler]] is a typeclass used by the [[ElasticClient]] in order to
* create [[ElasticRequest]] instances which are sent to the elasticsearch
* server, as well as returning a [[ResponseHandler]] which handles the
* response from the server.
*
* @tparam T the type of the request object handled by this handler
* @tparam U the type of the response object returned by this handler
*/
abstract class Handler[T, U: Manifest] extends Logging {
def responseHandler: ResponseHandler[U] = ResponseHandler.default[U]
def build(t: T): ElasticRequest
}

这个抽象类中有两个函数,其中一个就是build(t: T):ElasticRequest,也是个抽象方法,必须在构建实例时实现。在execute(T)中handler是一个隐式参数,也就是说如果在调用这个函数的可视域内能发现Handler[T,U]实例,则可获取handler,然后可调用handler.build(t)来构建请求。这个T类型是即是调用execute(T)这个T类型了,上面说过T可以是ES的任何操作类型,也就是说如果这些操作类型都继承了Handler[T,U],那么必须按照要求实现build(t:T)来构建该操作类型所需的服务请求ElasticRequest。下面就是例子里两个操作类型需要的隐式实例:

 implicit object IndexHandler extends Handler[IndexRequest, IndexResponse] {

    override def responseHandler: ResponseHandler[IndexResponse] = new ResponseHandler[IndexResponse] {
override def handle(response: HttpResponse): Either[ElasticError, IndexResponse] = response.statusCode match {
case | => Right(ResponseHandler.fromResponse[IndexResponse](response))
case | | | | => Left(ElasticError.parse(response))
case _ => sys.error(response.toString)
}
} override def build(request: IndexRequest): ElasticRequest = { val (method, endpoint) = request.id match {
case Some(id) =>
"PUT" -> s"/${URLEncoder.encode(request.index.name, StandardCharsets.UTF_8.name())}/_doc/${URLEncoder.encode(id.toString, StandardCharsets.UTF_8.name())}"
case None =>
"POST" -> s"/${URLEncoder.encode(request.index.name, StandardCharsets.UTF_8.name())}/_doc"
} val params = scala.collection.mutable.Map.empty[String, String]
request.createOnly.foreach(
createOnly =>
if (createOnly)
params.put("op_type", "create")
)
request.routing.foreach(params.put("routing", _))
request.parent.foreach(params.put("parent", _))
request.timeout.foreach(params.put("timeout", _))
request.pipeline.foreach(params.put("pipeline", _))
request.refresh.map(RefreshPolicyHttpValue.apply).foreach(params.put("refresh", _))
request.version.map(_.toString).foreach(params.put("version", _))
request.ifPrimaryTerm.map(_.toString).foreach(params.put("if_primary_term", _))
request.ifSeqNo.map(_.toString).foreach(params.put("if_seq_no", _))
request.versionType.map(VersionTypeHttpString.apply).foreach(params.put("version_type", _)) val body = IndexContentBuilder(request)
val entity = ByteArrayEntity(body.getBytes, Some("application/json")) logger.debug(s"Endpoint=$endpoint")
ElasticRequest(method, endpoint, params.toMap, entity)
}
} ... implicit object SearchHandler extends Handler[SearchRequest, SearchResponse] { override def build(request: SearchRequest): ElasticRequest = { val endpoint =
if (request.indexes.values.isEmpty)
"/_all/_search"
else
"/" + request.indexes.values
.map(URLEncoder.encode(_, "UTF-8"))
.mkString(",") + "/_search" val params = scala.collection.mutable.Map.empty[String, String]
request.requestCache.map(_.toString).foreach(params.put("request_cache", _))
request.searchType
.filter(_ != SearchType.DEFAULT)
.map(SearchTypeHttpParameters.convert)
.foreach(params.put("search_type", _))
request.routing.map(_.toString).foreach(params.put("routing", _))
request.pref.foreach(params.put("preference", _))
request.keepAlive.foreach(params.put("scroll", _))
request.allowPartialSearchResults.map(_.toString).foreach(params.put("allow_partial_search_results", _))
request.batchedReduceSize.map(_.toString).foreach(params.put("batched_reduce_size", _)) request.indicesOptions.foreach { opts =>
IndicesOptionsParams(opts).foreach { case (key, value) => params.put(key, value) }
} request.typedKeys.map(_.toString).foreach(params.put("typed_keys", _)) val body = request.source.getOrElse(SearchBodyBuilderFn(request).string())
ElasticRequest("POST", endpoint, params.toMap, HttpEntity(body, "application/json"))
}
}

以上IndexHandler, SearchHandler就是针对index,search操作的Handler[T,U]隐式实例。它们的build(t:T)函数分别按传入的T类型参数构建了各自要求格式的服务请求。

我总是觉着:不一定所有类型的服务请求都适合用DSL来构建,比如多层逻辑条件的json,可能不容易用DSL来实现(我个人的顾虑)。那么应该有个接口直接json文本嵌入request-entity。elastic4s在各种操作类型的服务请求类型如IndexRequest, SearchRequest,BulkRequest等提供了source:Option[String]字段接收json文本,如下:

case class IndexRequest(index: Index,
...
source: Option[String] = None)
extends BulkCompatibleRequest {
...
def source(json: String): IndexRequest = copy(source = json.some) ...
} case class SearchRequest(indexes: Indexes,
...
source: Option[String] = None,
...
typedKeys: Option[Boolean] = None) {
...
/**
* Sets the source of the request as a json string. Note, if you use this method
* any other body-level settings will be ignored.
*
* HTTP query-parameter settings can still be used, eg limit, routing, search type etc.
*
* Unlike rawQuery, source is parsed at the "root" level
* Query must be valid json beginning with '{' and ending with '}'.
* Field names must be double quoted.
*
* NOTE: This method only works with the HTTP client.
*
* Example:
* {{{
* search in "*" limit 5 source {
* """{ "query": { "prefix": { "bands": { "prefix": "coldplay", "boost": 5.0, "rewrite": "yes" } } } }"""
* } searchType SearchType.Scan
* }}}
*/
def source(json: String): SearchRequest = copy(source = json.some) ... }

现在,我们可以直接用json文本了:

  val json =
"""
|{
| "query" : {
| "match" : {"title" : "火锅"}
| }
|}
|""".stripMargin
val response = client.execute {
search("books").source(json) // .matchQuery("title", "火锅")
}.await

search(3)- elastic4s-QueryDSL的更多相关文章

  1. search(9)- elastic4s logback-appender

    前面写了个cassandra-appender,一个基于cassandra的logback插件.正是cassandra的分布式数据库属性才合适作为akka-cluster-sharding分布式应用的 ...

  2. 【CF528D】Fuzzy Search(FFT)

    [CF528D]Fuzzy Search(FFT) 题面 给定两个只含有\(A,T,G,C\)的\(DNA\)序列 定义一个字符\(c\)可以被匹配为:它对齐的字符,在距离\(K\)以内,存在一个字符 ...

  3. search(0)- 企业搜索,写在前面

    计划研究一下搜索search,然后写个学习过程系列博客.开动之前先说说学习搜索的目的:不是想开发个什么搜索引擎,而是想用现成的搜索引擎在传统信息系统中引进搜索的概念和方法.对我来说,传统的管理系统le ...

  4. search(16)- elastic4s-内嵌文件:nested and join

    从SQL领域来的用户,对于ES的文件关系维护方式会感到很不习惯.毕竟,ES是分布式数据库只能高效处理独个扁平类型文件,无法支持关系式数据库那样的文件拼接.但是,任何数据库应用都无法避免树型文件关系,因 ...

  5. search(6)- elastic4s-CRUD

    如果我们把ES作为某种数据库来使用的话,必须熟练掌握ES的CRUD操作.在这之前先更正一下上篇中关于检查索引是否存在的方法:elastic4s的具体调用如下: //删除索引 val rspExists ...

  6. search(7)- elastic4s-search-filter模式

    现在我们可以开始探讨ES的核心环节:搜索search了.search又分filter,query两种模式.filter模式即筛选模式:将符合筛选条件的记录作为结果找出来.query模式则分两个步骤:先 ...

  7. 【阅读笔记】Ranking Relevance in Yahoo Search (一)—— introduction & background

    ABSTRACT: 此文在相关性方面介绍三项关键技术:ranking functions, semantic matching features, query rewriting: 此文内容基于拥有百 ...

  8. search(11)- elastic4s-模糊查询

    很多时候搜索用户对查询语句具有模糊感觉,他们只能提供大约的描述.比如一个语句的部分,或者字句顺序颠倒等.通过模糊查询可以帮助用户更准确的找出他们希望搜索的结果. 模糊查询包括前后缀,语句(phrase ...

  9. search(12)- elastic4s-聚合=桶+度量

    这篇我们介绍一下ES的聚合功能(aggregation).聚合是把索引数据可视化处理成可读有用数据的主要工具.聚合由bucket桶和metrics度量两部分组成. 所谓bucket就是SQL的GROU ...

  10. search(1)- elasticsearch结构概念

    上篇提到选择了elasticsearch ES作为专业化搜索引擎的核心,这篇讨论一下ES的基本结构和应用概念.首先,从硬结构方面来讲:ES是在一个集群(cluster)环境里运行的,所以ES应该具备高 ...

随机推荐

  1. Trailing Zeroes (III) LightOJ - 1138 二分+找规律

    Time Limit: 2 second(s) Memory Limit: 32 MB You task is to find minimal natural number N, so that N! ...

  2. 签名旧版的pom文件

    <?xml version="1.0" encoding="UTF-8"?><project xmlns="http://maven ...

  3. 吴裕雄--python学习笔记:os模块的使用

    在自动化测试中,经常需要查找操作文件,比如说查找配置文件(从而读取配置文件的信息),查找测试报告(从而发送测试报告邮件),经常要对大量文件和大量路径进行操作,这就依赖于os模块. 1.当前路径及路径下 ...

  4. python标准库-calendar 模块

    原文地址:http://www.bugingcode.com/blog/python_calendar.html calendar 模块是python实现的unix 的 cal命令.它可以以标准的模式 ...

  5. 吴裕雄--天生自然 R语言开发学习:分类(续一)

    #-----------------------------------------------------------------------------# # R in Action (2nd e ...

  6. MAC使用nginx分发80至8080端口

    由于项目必须要启动80端口,但是mac系统中非root用户无法直接使用1024以下的端口 2.释放apache的80端口 由于Mac OS是自带Apache服务的,它本身占用了80端口,首先你需要将A ...

  7. 理解 Java 内存模型的因果性约束

    目录 理解 Java 内存模型的因果性约束 欢迎讨论 规范理解 例子练习 例子1 例子2 总结 理解 Java 内存模型的因果性约束 欢迎讨论 欢迎加入技术交流群186233599讨论交流,也欢迎关注 ...

  8. django之学习前的准备

    一.配置环境 Windows 10操作系统 Python安装配置教程参考:https://www.cnblogs.com/huangbiquan/p/7784533.html 安装Python虚拟环境 ...

  9. 输入URL到浏览器显示页面的过程,搜集各方面资料总结一下

    面试中经常会被问到这个问题吧,唉,我最开始被问到的时候也就能大概说一些流程.被问得多了,自己就想去找找这个问题的全面回答,于是乎搜了很多资料和网上的文章,根据那些文章写一个总结. 写得不好,或者有意见 ...

  10. PYTHON 第二天学习记录