【发布时间】:2021-07-28 17:36:15
【问题描述】:
使用elastic4s 7.12.1 和spray-json 1.3.6(和scala 2.13.5):
有没有办法将 Elasticsearch 文档的 _id 读入字段,例如 . id,case class 实例,
仅使用隐式 spray-json RootJsonFormat,即。 e.无需为elastic4s 编写自定义HitReader,如果是这样,怎么办?
编写文档也是如此:有没有办法插入case class 的实例而不序列化(使其成为ES 中_source 的一部分)id 字段,仅使用上述RootJsonFormat,即。 e.无需编写自定义Indexable?
根据elastic4s 文档,这应该可以使用jackson 来实现,我想避免这种情况,因为它总是会出现许多关键的安全问题。
考虑这个case类,它应该被ES索引:
case class Foo(id: String, name: String)
使用spray-json,我只需要定义一个RootJsonFormat:
implicit val fooJsonFormat: RootJsonFormat[Foo] = jsonFormat2(Foo)
并且可以使用elastic4s这种方式来索引和搜索Foos:
val someFoo = Foo("idWhichShouldBeOverwrittenByES", "someName")
client.execute {
indexInto("foos").doc(someFoo)
}
val result: Response[SearchResponse] = client.execute {
search("foos").query {
boolQuery().must {
matchQuery("name", "someName")
}
}
}.await
result match {
case RequestSuccess(_, _, _, result) => result.to[Foo].foreach(println)
case RequestFailure(_, _, _, error) => println(error.toString)
}
但是,这种方法存在重大问题:
- 我需要在创建
Foo时提供id,而实际上我希望ES 在索引文档时为我生成_id。这当然主要是由于使用case class - 加载
Foo文档时,它的id字段包含我在索引它时使用的(无意义的)虚拟值,而不是它存储在ES 节点中的实际_id
为了解决这些问题(第一个只是部分解决),我当然可以像这样写自己的Indexable 和HitReader:
implicit object FooHitReader extends HitReader[Foo] {
override def read(hit: Hit): Try[Foo] = Try({
val source = hit.sourceAsMap
Foo(
id = hit.id,
name = source("name").toString
)
})
}
implicit object FooIndexable extends Indexable[Foo] {
override def json(t: Foo): String =
JsObject(
"name" -> JsString(t.name),
).compactPrint
}
这在一个小例子中看起来并不算太糟糕,但我认为很明显这种方法可扩展性很差,不提供类型安全性并且是重构的噩梦,因为字段的名称(例如 "name")需要手动指定。
底线: 有没有更好的方法来获得类似spring-data-elasticsearch 的体验,或者elastic4s 和spray-json 不适合这项任务?
编辑: 另一种可能性是从Foo 中删除id 字段,引入包装器case class,例如FooResultWrapper,将Foo 的搜索结果存储在Map[String, Foo] 中,使用RootJsonFormat[Foo] 和HitReader[FooResultWrapper] 将_source 转换为Foo 并将其存储在hit.id 中。但这也不是很令人满意。
【问题讨论】:
标签: scala elasticsearch spray-json elastic4s