[英]Indexing/Searching “complex” JSON in elasticsearch
我有一些类似于以下内容的JSON:让我们将该字段称为元数据
{
"somekey1": "val1",
"someotherkey2": "val2",
"more_data": {
"contains_more": [
{
"foo": "val5",
"bar": "val6"
},
{
"foo": "val66",
"baz": "val44"
},
],
"even_more": {
"foz" : 1234,
}
}
}
这只是一个简单的例子。 真正的人可能变得更加复杂。 密钥可以出现多次。 值也可以是int或str。
现在的第一个问题是,我不确定如何在Elasticsearch中正确索引该索引,以便可以找到具有特定请求的内容。
我正在使用Django / Haystack,其中的索引如下所示:
class FooIndex(indexes.SearchIndex, indexes.Indexable):
text = indexes.CharField(document=True, use_template=True)
metadata = indexes.CharField(model_attr='get_metadata')
# and some more specific fields
和模板:
{
"foo": {{ object.foo }},
"metadata": {{ object.metadata}},
# and some more
}
然后,元数据将被上面的示例填充,结果将如下所示:
{
"foo": "someValue",
"metadata": {
"somekey1": "val1",
"someotherkey2": "val2",
"more_data": {
"contains_more": [
{
"foo": "val5",
"bar": "val6"
},
{
"foo": "val66",
"baz": "val44"
},
],
"even_more": {
"foz" : 1234,
}
}
},
}
它将进入elasticsearch的“文本”列。
因此,现在的目标是能够搜索以下内容:
第二个问题:例如,当我搜索foo:val5时,它将匹配仅具有键“ foo”的所有对象以及结构中其他位置具有val5的所有对象。
这就是我在Django中搜索的方式:
self.searchqueryset.auto_query(self.cleaned_data['q'])
有时结果是“好的”,有时是完全没有用的。
我可能需要一个正确方向的指针,并了解我在这里犯的错误。 谢谢!
编辑:我添加了我的最终解决方案作为下面的答案!
可以肯定的一件事是,您首先需要根据您的特定数据并根据您的查询需求来设计自定义映射,我的建议是contains_more
应该为nested
类型,以便您可以在字段上发出更精确的查询。
我不知道您的字段的确切名称,但是根据您显示的内容,一种可能的映射可能是这样的。
{
"your_type_name": {
"properties": {
"foo": {
"type": "string"
},
"metadata": {
"type": "object",
"properties": {
"some_key": {
"type": "string"
},
"someotherkey2": {
"type": "string"
},
"more_data": {
"type": "object",
"properties": {
"contains_more": {
"type": "nested",
"properties": {
"foo": {
"type": "string"
},
"bar": {
"type": "string"
},
"baz": {
"type": "string"
}
}
}
}
}
}
}
}
}
}
然后,正如mark在其评论中已经提到的那样, auto_query
不会删除它,主要是因为有多个嵌套级别。 据我所知,Django / Haystack不支持开箱即用的嵌套查询,但是您可以扩展Haystack来支持它。 这是一篇博客文章,解释了如何解决此问题: http : //www.stamkracht.com/extending-haystacks-elasticsearch-backend 。 不确定这是否有帮助,但是您应该尝试一下,如果需要更多帮助,请告诉我们。
索引:
首先,如果要相对于键名定义特定的映射,或者您的文档不具有相同的结构,则应使用动态模板 。
但是30键并不是那么高,您应该更喜欢定义自己的映射,而不是让Elasticsearch为您猜测(如果首先添加了不正确的数据,则将根据这些数据定义映射)
搜索:
您无法搜寻
foz: val5
因为“ foz”键不存在。
但是键“ metadata.more_data.even_more.foz”确实=>您所有的键都从文档的根开始展平
这样,您将必须搜索
foo: val5
metadata.more_data.even_more.foz: 12*
metadata.more_data.contains_more.bar: val*
metadata.somekey1: val1
以query_string为例
"query_string": {
"default_field": "metadata.more_data.even_more.foz",
"query": "12*"
}
或者,如果您想在多个字段中搜索
"query_string": {
"fields" : ["metadata.more_data.contains_more.bar", "metadata.somekey1"],
"query": "val*"
}
花了一段时间才找到适合我的正确解决方案
它既是@juliendangers和@Val提供的答案, 又是更多的自定义项。
向模型添加了自定义get_type_mapping
方法
@classmethod def get_type_mapping(cls): return { "properties": { "somekey": { "type": "<specific_type>", "format": "<specific_format>", }, "more_data": { "type": "nested", "include_in_parent": True, "properties": { "even_more": { "type": "nested", "include_in_parent": True, } /* and so on for each level you care about */ } } }
向模型添加了自定义get_document
方法
@classmethod def get_document(cls, obj): return { 'somekey': obj.somekey, 'more_data': obj.more_data, /* and so on */ }
添加自定义搜索表单
class Searchform(ElasticsearchForm): q = forms.Charfield(required=False) def get_index(self): return 'your_index' def get_type(self): return 'your_model' def prepare_query(self): if not self.cleaned_data['q']: q = "*" else: q = str(self.cleaned_data['q']) return { "query": { "query_string": { "query": q } } } def search(self): esp = ElasticsearchProcessor(self.es) esp.add_search(self.prepare_query, page=1, page_size=25, index=self.get_index(), doc_type=self.get_type()) responses = esp.search() return responses[0]
所以这对我有用 , 涵盖了我的用例 。 也许对某人会有帮助。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.