微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

elastic4s:如何自动将文档 ID 读入案例类实例?

如何解决elastic4s:如何自动将文档 ID 读入案例类实例?

elastic4s 7.12.1spray-json 1.3.6(和 scala 2.13.5)一起使用:
有没有办法将 Elasticsearch 文档的 _id 读入一个字段,例如 . idcase class 实例的,
仅使用隐式 spray-json RootJsonFormat,i。 e.无需为 HitReader 编写自定义 elastic4s,如果是,如何编写?
编写文档也是如此:有没有办法只使用前面提到的 case class 字段插入 _source 的实例而不序列化(使其成为 ES 中 id 的一部分)RootJsonFormat 字段{1}},我。 e.不编写自定义 Indexable
根据 elastic4s 文档,使用 jackson 应该可以做到这一点,我想避免这种情况,因为它有很多关键的安全问题,一直出现。

考虑这个case类,它应该被索引到ES中:

case class Foo(id: String,name: String)

使用 spray-json,我只需要定义一个 RootJsonFormat

implicit val foojsonFormat: RootJsonFormat[Foo] = jsonFormat2(Foo)

并且可以使用 elastic4s 这种方式来索引和搜索 Foo

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)
      }

但是,这种方法存在主要问题:

  • 我需要在创建 id 时提供 Foo,而我实际上希望 ES 在索引文档时为我生成 _id。这当然主要是由于使用了 case class
  • 当加载一个 Foo 文档时,它的 id 字段包含我在索引它时使用的(无意义的)虚拟值,而不是它存储在 ES 节点内的实际 _id

为了解决这些问题(第一个只是部分),我当然可以像这样编写自己的 IndexableHitReader

  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 的体验,或者 elastic4sspray-json 不适合这项任务?


编辑:另一种可能性是从 id删除 Foo 字段,引入包装器 case class,例如FooResultWrapperFoo_id 搜索结果存储在 Map[String,Foo] 中,使用 RootJsonFormat[Foo]HitReader[FooResultWrapper] 转换 _sourceFoo 并由 hit.id 存储。但这也不是很令人满意。

解决方法

看我想出的绝妙解决方案(基本上是我在编辑问题时提出的建议):
删除了我的域 id(例如 case class)的 Foo 字段并引入了通用 case class 来包装结果并强制使用 objects 来实现 {{1 }} 来自 read 的特定 elastic4s

case class

以及一个通用的 case class ESResultWrapper[T](id: String,result: T) ,它包含在 trait 实例中包装 T 类型的结果的实现:

ESResultWrapper

现在真正的“域”类剩下的就是使用特定的案例类(也存在 trait ESResultWrapperHitReader[T] extends HitReader[ESResultWrapper[T]] { def readInternal(hit: Hit)(implicit reader: HitReader[T]): Try[ESResultWrapper[T]] = Try({ ESResultWrapper( id = hit.id,result = hit.to[T] ) }) } )扩展 ESResultWrapperHitReader[T] trait 并委托 {{1} } 到 RootJsonFormat,从而通过 hit:

隐式提供一个 hitInternal
HitReader[T]

用法非常简单(坚持问题中的示例):

RootJsonFormat[T]

导致 e。 G。: implicit object FooResultWrapperHitReader extends ESResultWrapperHitReader[Foo] { override def read(hit: Hit): Try[ESResultWrapper[Foo]] = readInternal(hit) }
最好的部分是:改变包装实现不会影响域类。

我为自己在使用 Scala 的第三天提出这个想法而鼓掌。干得好。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其他元素将获得点击?
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。)
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbcDriver发生异常。为什么?
这是用Java进行XML解析的最佳库。
Java的PriorityQueue的内置迭代器不会以任何特定顺序遍历数据结构。为什么?
如何在Java中聆听按键时移动图像。
Java“Program to an interface”。这是什么意思?