我有一些JSON,看起来像这样:让我们把这个字段命名为metadata
{
"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中的'text'列。
所以现在的目标是能够搜索如下内容:
- foo: val5
- foz: 12 *
- 栏:val *
- somekey1: val1
- 等等
第二个问题:例如,当我搜索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"键不存在。
但是键" metdata .more_data.even_more. "Foz " does =>所有的键都是从文档的根开始平放的
这样的话,你必须搜索
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*"
}
花了一段时间才找到适合我的正确解决方案
它是由@ julidangerers 和@Val提供的答案和一些更多的定制的混合。
- 我用更具体的django-simple-elasticsearch代替了Haystack
-
添加自定义
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]
这就是我的工作,涵盖了我的用例。也许对别人有帮助。