【问题标题】:Indexing/Searching "complex" JSON in elasticsearch在 elasticsearch 中索引/搜索“复杂”JSON
【发布时间】:2015-08-02 01:29:17
【问题描述】:

我有一些如下所示的 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
  • foz:12*
  • bar: val*
  • somekey1: val1
  • 等等

第二个问题: 当我搜索时对于 foo: val5 它匹配所有只有键“foo”的对象和所有在它的结构中其他地方有 val5 的对象。

这就是我在 Django 中搜索的方式:

self.searchqueryset.auto_query(self.cleaned_data['q'])

有时结果“还可以”,有时完全没用。

我可能需要一个指向正确方向的指针,并了解我在这里犯下的错误。谢谢!

编辑:我在下面添加了我的最终解决方案作为答案!

【问题讨论】:

  • 前言:我不是 django 用户,只是 ES。我的猜测:content 字段填充了所有数据,因此无法进行特定于字段的匹配。如果你想这样,你需要在你的过滤器/查询中表达出来(但我的猜测是:不使用auto_query)。
  • 您的元数据字段是否始终具有相同的结构?
  • @juliendangers 有时它有更多的字段或在数组中包含多个元素 有时没有数组,它可以很平坦。然而,密钥之前是已知的,并且可以有例如多达 30 多种不同的

标签: python json django elasticsearch django-haystack


【解决方案1】:

可以确定的一点是,您首先需要根据您的特定数据并根据您的查询需求制作自定义映射,我的建议是 contains_more 应该是 nested type 以便您可以发出更多对您的字段进行精确查询。

我不知道您的字段的确切名称,但根据您显示的内容,一种可能的映射可能是这样的。

{
  "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"
                  }
                }
              }
            }
          }
        }
      }
    }
  }
}

然后,正如马克在他的评论中已经提到的那样,auto_query 不会削减它,主要是因为多个嵌套级别。据我所知,Django/Haystack 不支持开箱即用的嵌套查询,但您可以扩展 Haystack 以支持它。这是一篇博客文章,解释了如何解决这个问题:http://www.stamkracht.com/extending-haystacks-elasticsearch-backend。不确定这是否有帮助,但您应该尝试一下,如果您需要更多帮助,请告诉我们。

【讨论】:

  • 这是否意味着我必须为所有可能的“键”及其结构定义映射?正如我在另一条评论中所写,可能有 30 多种不同的。
  • 嗯,你越多地指导你的映射,你的查询就越精确和强大。我想说,30 个字段不是杀手。我有数百个字段的文档,它们都被正确准确地映射为我需要它们做的事情。最好是尝试一下,看看它在您的特定情况下对您有什么帮助。
  • 谢谢。我会尝试并报告!
【解决方案2】:

索引:

首先你应该使用动态templates,如果你想定义相对于键名的特定映射,或者你的文档没有相同的结构。

但是 30 key 并不高,你应该更喜欢定义自己的映射而不是让 Elasticsearch 为你猜测它(如果先添加了不正确的数据,将根据这些数据定义映射)

搜索:

你不能搜索

foz: val5

因为“foz”键不存在。

但是 key "metadata.more_data.even_more.foz" 确实 => 你所有的 key 都是从你的文档的根目录开始的

这样你就必须搜索

foo: val5
metadata.more_data.even_more.foz: 12*
metadata.more_data.contains_more.bar: val*
metadata.somekey1: val1

以查询字符串为例

"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*"
}

【讨论】:

  • 那么数组也会被展平吗? (例如,不必使用 metadata.more_data.contains_more.0.key)
  • 是的,Elasticsearch会检测数组,“contains_more.foo”和“contains_more.bar”会变成多值字段
【解决方案3】:

花了一段时间才找到适合我的正确解决方案

它结合了 @juliendangers@Val 提供的答案以及更多自定义选项。

  1. 我用更具体的django-simple-elasticsearch 替换了 Haystack
  2. 为模型添加了自定义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 */
           }
         }
      }
    
  3. 为模型添加了自定义get_document方法

    @classmethod
    def get_document(cls, obj):
      return {
        'somekey': obj.somekey,
        'more_data': obj.more_data,
        /* and so on */
      }
    
  4. 添加自定义搜索表单

    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]
    

所以这对我有用并且涵盖了我的用例。也许它可以对某人有所帮助。

【讨论】:

    猜你喜欢
    • 2022-06-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-11-26
    相关资源
    最近更新 更多