按照計劃,這篇開始嘗試用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會出現索引不存在錯誤。