search(5)- elastic4s-構建索引
按照計劃,這篇開始嘗試用elastic4s來做一系列索引管理和搜尋操作示範。前面提過,elastic4s的主要功能之一是通過組合Dsl語句形成json請求。那麼我們先試試組合一些Dsl語句,再想辦法產生出json請求文字,然後在kibana控制檯中驗證它的正確性。
首先看看elastic4s提供的一個show函式:
def show(implicit handler: Handler[T, _]): String = Show[ElasticRequest].show(handler.build(t))
又見到了這個Handler[T, _],作為show的一個隱式引數。前面說過這個Handler[T, _]是個代表構建T型別json請求的typeclass。具體構建函式就是上面的這個build(t)函式。我們先看看CreateIndexRequest型別的show函式示範:
val jsCreate = createIndex("company") .shards(1).replicas(1).show println(jsCreate)
產生了json如下:
PUT:/company? StringEntity({"settings":{"index":{"number_of_shards":1,"number_of_replicas":1}}},Some(application/json))
在kibana裡是如下表達的:
PUT /company { "settings":{ "index":{ "number_of_shards":1, "number_of_replicas":1 } } }
可能是歷史原因吧,elastic4s與ES7.6還有很多不相容的地方,或者說是elastic4s還有許多沒來得及更新的地方。具體有問題的語句或引數都可以通過把json body放在kibana裡進行驗證,如果elastic4s還有地方沒有完成覆蓋ES7.6功能的話,我們可以把一個正確的ES7.6 json指令碼直接通過source傳人到操作型別中去:
val js = """ |{ | "settings":{ | "index":{ | "number_of_shards":1, | "number_of_replicas":1 | } | } |} |""".stripMargin val createFromJs = (createIndex("company").source(js)).show println(createFromJs)
下面的例子是一套完整的索引建立過程:先刪除同名稱索引、建立索引、構建mapping:
import com.sksamuel.elastic4s.ElasticClient import com.sksamuel.elastic4s.akka._ import akka.actor._ import com.sksamuel.elastic4s.requests.mappings.{KeywordField, MappingDefinition, NestedField, SearchAsYouTypeField} import scala.concurrent.ExecutionContext.Implicits.global object Lesson03 extends App { import com.sksamuel.elastic4s.ElasticDsl._ //akka客戶端 private implicit lazy val system: ActorSystem = ActorSystem() val akkaClient = AkkaHttpClient(AkkaHttpClientSettings(List("localhost:9200"))) val client = ElasticClient(akkaClient) //刪除索引 val idxDelete = client.execute(deleteIndex("company")).await //構建索引 val idxCreate = client.execute(createIndex("company") .shards(1).replicas(1)).await //建立表結構 if(idxCreate.isSuccess) { val compMapping = client.execute( putMapping("company").fields( KeywordField("code"), SearchAsYouTypeField("name") .fielddata(true) .fields(KeywordField("keyword")), textField("biztype"), NestedField("addr").fields( textField("district"), textField("address"), KeywordField("zipcode") ), dateField("regdate") .ignoreMalformed(true) .format("strict_date_optional_time||epoch_millis"), textField("contact") )).await if(compMapping.isSuccess) println(s"mapping successfully created.") else println(s"mapping creation error: ${compMapping.error.reason}") } else { println(s"index creation error: ${idxCreate.error.reason}") } system.terminate() client.close() }
以上程式碼有幾個地方值得提一下:
1、這上面使用了一個基於akka-stream的客戶端。優點是響應式標準相容,用佇列queue來緩衝密集請求
2、在刪除索引前為甚麼不先檢查一下同名索引是否存在?elastic4s ExistApi還是ES7以前版本,不能用
3、client.execute(...)返回Future, 為什麼不用for-yield?試過了,一是deleteIndex,createIndex返回結果與實際刪除、構建操作可能有些延遲,createIndex會返回索引已經存在錯誤, mapping會出現索引不存在錯誤。
&n