Scala elastic4s:如何将文档id自动读入案例类实例?
将elastic4s 7.12.1与Scala elastic4s:如何将文档id自动读入案例类实例?,scala,elasticsearch,spray-json,elastic4s,Scala,elasticsearch,Spray Json,Elastic4s,将elastic4s 7.12.1与spray json 1.3.6(和scala2.13.5)一起使用: 是否有办法将Elasticsearch文档的\u id读入字段,例如id,属于case类实例,仅使用隐式spray jsonRootJsonFormat,i。E无需为elastic4s编写自定义的点击阅读器,如果是,怎么做? 编写文档也是如此:是否有一种方法可以插入一个案例类的实例,而不必使用前面提到的RootJsonFormat将其序列化(使其成为ES中\u source的一部分)到id
spray json 1.3.6
(和scala2.13.5
)一起使用:是否有办法将Elasticsearch文档的
\u id
读入字段,例如id
,属于case类
实例,仅使用隐式
spray json
RootJsonFormat
,i。E无需为elastic4s编写自定义的点击阅读器,如果是,怎么做?编写文档也是如此:是否有一种方法可以插入一个
案例类的实例,而不必使用前面提到的RootJsonFormat
将其序列化(使其成为ES中\u source
的一部分)到id
字段。E没有编写自定义的可索引的
根据elastic4s
文档,使用jackson
应该可以做到这一点,我希望避免这种情况,因为它总是会出现许多关键的安全问题
考虑这个case类,它应该被索引到ES中:
case类Foo(id:String,name:String)
使用spray-json
,我只需要定义RootJsonFormat
:
implicit val fooJsonFormat:RootJsonFormat[Foo]=jsonFormat2(Foo)
并且可以使用elastic4s
这种方式来索引和搜索Foo
s:
val someFoo=Foo(“应该覆盖的ID”、“someName”)
client.execute{
indexInto(“foos”).doc(someFoo)
}
val结果:响应[SearchResponse]=client.execute{
搜索(“foos”).query{
boolQuery()。必须{
匹配查询(“名称”、“someName”)
}
}
}.等待
结果匹配{
case RequestSuccess(u,u,u,result)=>result.to[Foo].foreach(println)
case RequestFailure(u,u,u,error)=>println(error.toString)
}
然而,这种方法存在一些主要问题:
- 我需要在创建
Foo
时提供id
,而实际上我希望ES在索引文档时为我生成\u id
。当然,这主要是由于使用了案例类
- 加载
Foo
文档时,其id
字段包含我为其编制索引时使用的(无意义的)伪值,而不是存储在ES节点中的实际\u id
为了解决这些问题(第一个只是部分),我当然可以编写自己的Indexable
和HitReader
,如下所示:
隐式对象FooHitReader扩展HitReader[Foo]{
覆盖def读取(命中:命中):Try[Foo]=Try({
val source=hit.sourceAsMap
福(
id=hit.id,
名称=源(“名称”).toString
)
})
}
隐式对象FooIndexable扩展了可索引[Foo]{
重写def-json(t:Foo):字符串=
JsObject(
“name”->JsString(t.name),
).compactPrint
}
在一个小例子中,这看起来并不可怕,但我认为很明显,这种方法伸缩性很差,不提供类型安全,而且是重构的噩梦,因为字段的名称(例如,“name”
)需要手动指定
底线:是否有更好的方法来实现类似于spring data elasticsearch
的体验,或者elastic4s
与spray json
一起使用不适合此任务
编辑:另一种可能是从Foo
中删除id
字段,引入包装器case类
,例如fooResultRapper
,它通过\u id
在映射[String,Foo]
中存储Foo
搜索结果,使用RootJsonFormat[Foo]
和HitReader[fooResultRapper]
将\u源代码
转换为Foo
,并通过hit.id
存储。但这也不是很令人满意。看看我提出的精彩解决方案(基本上就是我在编辑问题时提出的):
删除了我的域案例类的id
字段(例如Foo
),并引入了一个通用的案例类
来包装结果,并强制使用对象
来实现从elastic4s
读取特定案例类
:
case类ESResultWrapper[T](id:String,result:T)
以及一个通用的trait
,它包含在ESResultWrapper
实例中包装T
类型结果的实现:
trait ESResultWrapperHitReader[T]扩展了HitReader[ESResultWrapper[T]]{
def readInternal(hit:hit)(隐式读取器:HitReader[T]):Try[ESResultWrapper[T]]=Try({
ESResultWrapper(
id=hit.id,
结果=命中到[T]
)
})
}
现在,实际的“域”类剩下的就是使用特定的case类(也存在RootJsonFormat
)扩展ESResultWrapperHitReader[T]
trait
,并将hit
委托给hitInternal
,从而隐式地提供HitReader[T]
通过RootJsonFormat[T]
:
隐式对象FooResultWrapperHitReader扩展ESResultWrapperHitReader[Foo]{
覆盖def read(hit:hit):尝试[ESResultWrapper[Foo]]=readInternal(hit)
}
用法非常简单(按照问题中的示例):
结果匹配{
case RequestSuccess(u,u,u,result)=>result.to[ESResultWrapper[Foo]].foreach(println)
case RequestFailure(u,u,u,error)=>println(error.toString)
}
导致e。g、 :
ESResultWrapper(-XMSQXkB-5ze1JvrVWup,Foo(“someFoo”))
最好的部分是:包装实现不会影响域类
我申请