search(3)- elastic4s-QueryDSL

来源:https://www.cnblogs.com/tiger-xc/archive/2020/03/22/12547909.html
-Advertisement-
Play Games

elastic4s是elasticsearch一個第三方開發的scala語言終端工具庫(Elastic4s is a concise, idiomatic, reactive, type safe Scala client for Elasticsearch.)。scala用戶可以用elastic4 ...


  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 201 | 200                   => Right(ResponseHandler.fromResponse[IndexResponse](response))
        case 400 | 401 | 403 | 409 | 500 => 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

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • JSP+MySQL+Java開發ssh網上預約預約掛號系統的設計與實現 需求使用SSH框架(spring+struts2+hibernate)實現一個網上預約預約掛號系統, 用戶登錄註冊登錄系統, 能按科室查看醫生, 並能夠進行預約掛號和線上留言, 後臺管理系統更能夠進行科室管理,醫生管理,預約管理 ...
  • 一、java.io.DataOutputStream;數據位元組輸出流 1.可以將記憶體中的“int i = 2;"寫入到硬碟文件裡面,寫進去的不是字元串,寫進去的是二進位數據,可以帶有類型。 package com.bjpowernode.java_learning; import java.io.* ...
  • 以前看到過NSQ這個東西,也一直沒去看。今天剛好有時間就搭建了下,簡單嘗試了下這個Go語言下的消息隊列NSQ,我這裡簡要記錄下。 其實,NSQ國內用的是比較少的,我這裡也是算瞭解這麼個東西吧 ,稍微看下源碼,學到東西而已。 NSQ簡介 NSQ是一個基於Go語言的分散式實時消息平臺, 它具有分散式、去 ...
  • 現在springboot的火熱程度已經超過了spring了,因為springboot簡單快速方便,springboot的初衷就是為了簡化spring的配置,是的開發中集成新功能時更快,簡化或者減少相關的配置。springboot的基礎是“約定大於配置”。整合了所有的框架,可以把springboot當 ...
  • ...
  • [TOC] 環境 idea 2019.1 Meavn 3.6.0 SpringBoot 2.2.5 jdk 1.8 構建eureka server 新建工程 啟動類添加註解 @EnableEurekaServer 其他配置 構建eureka client 新建工程 pom文件添加依賴,解決啟動失敗 ...
  • 距離Java 8發佈已經過去了7、8年的時間,Java 14也剛剛發佈。Java 8中關於函數式編程和新增的Stream流API至今飽受“爭議”。 如果你不曾使用Stream流,那麼當你見到Stream操作時一定對它發出過鄙夷的聲音,併在心裡說出“這都寫的什麼玩意兒”。 如果你熱衷於使用Stream ...
  • 恢復內容開始 1.背景:現在很多app或者網站都想要接入微信登錄,可以使用戶不需要註冊就能快速使用APP或網站。 2.微信登錄需要一些前置操作 2.1 搜索:微信開放平臺 鏈接:https://open.weixin.qq.com/ 2.2 註冊成功,獲取到開發所需要的appID和appsecret ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...