Использование elastic4s 7.12.1
с spray-json 1.3.6
(и scala 2.13.5
):
Есть ли способ прочитать _id
документа Elasticsearch в поле, например . id
экземпляра case class
,
использующего только неявный spray-json
RootJsonFormat
, т.е. е. без создания пользовательского HitReader
для elastic4s
, и если да, то как?
То же самое касается написания документов: есть ли способ вставить экземпляр case class
без сериализации (сделав его частью _source
в ES) поля id
с помощью только вышеупомянутый RootJsonFormat
, т.е. е. без написания пользовательского Indexable
?
Согласно документации elastic4s
, это должно быть возможно с использованием jackson
, чего я хочу избежать из-за множества критических проблем с безопасностью, которые возникают постоянно.
Рассмотрим этот класс case, который должен быть проиндексирован в ES:
case class Foo(id: String, name: String)
Используя spray-json
, мне нужно было бы только определить RootJsonFormat
:
implicit val fooJsonFormat: RootJsonFormat[Foo] = jsonFormat2(Foo)
И может использовать elastic4s
таким образом для индексации и поиска Foo
s:
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
содержит (бессмысленное) фиктивное значение, которое я использовал при его индексации, а не фактическое_id
, под которым он хранится внутри узла ES.
Чтобы решить эти проблемы (первая только частично), я мог бы, конечно, написать свои собственные 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
просто не подходит для этой задачи?
Изменить: Другой возможностью было бы удалить поле id
из Foo
, ввести оболочку case class
, например. FooResultWrapper
, который сохраняет Foo
результаты поиска по _id
в Map[String, Foo]
, используйте RootJsonFormat[Foo]
и HitReader[FooResultWrapper]
, которые преобразуют _source
в Foo
и сохраняют их в hit.id
. Но это тоже не очень радует.