【问题标题】:Elasticsearch query for user data overrides用户数据覆盖的 Elasticsearch 查询
【发布时间】:2019-12-30 00:57:19
【问题描述】:

我需要设计一个可以支持用户特定文档编辑的查询。下面的文档描述了一种存储此数据的方法。下面的文档包括一个根文档 Description 属性。根文档 Description 属性应该被所有用户搜索,除了 Eric 和 Alex。对于 Eric 和 Alex,Description 属性已经过定制,由这些用户中的任何一个执行的搜索查询都应该在嵌套的 UserData 数组中搜索他们的自定义 Description 字段数据。由 Eric 或 Alex 执行的搜索查询不应搜索根文档描述字段。

对于我的用例,用户可以自定义 0 个或多个根文档属性。对于用户自定义的任何根文档属性,应该只为该用户搜索该属性的自定义值。

解决此问题的蛮力方法是为每个自定义文档的单独副本编制索引。我试图避免这种情况,担心为用户自定义的每个文档创建多个副本会通过复制不合法复制的文档内容来不公平地加权索引。

{ 
  "Name": "doc1",
  "Description": "Base description1",
  "Spec": "Base document spec",
  "UserData":[
  {
    "EnteredBy": "Eric",
    "Description": "Desc entered by Eric, abc"
  },
  {
    "EnteredBy": "Alex",
    "Description": "Desc entered by Alex, def",
    "Spec": "Spec entered by Alex"
  }]
}

编辑 1

下面列出了我考虑过的选项。

选项 1:我可以为每个用户创建一个单独的索引。在该索引中,我将添加用户未自定义的所有基本文档,并添加用户自定义的每个文档。这将产生 1000 多个索引。

选项 2:我可以使用 script_score 功能并使用上述覆盖逻辑手动计算每个文档的分数。据我所知,评分逻辑必须是原始的,最终可能会否定 Elasticsearch 的强大功能。

编辑 2

该解决方案最多需要支持 40 个字段,并且在任何一个字段已由多达 200 个用户自定义的情况下。该索引将包含 750,000 个文档。

【问题讨论】:

    标签: elasticsearch


    【解决方案1】:

    如何创建一点不同的文档结构、嵌套字段并将用户添加到嵌套参数?举例

    POST /st_t2/_doc
    { 
      "Name":"doc1",
      "Description": [
        {"base": "wtf"},
        {"Alex": "Desc entered by Alex, aaa"}
    
        ],
      "Spec": [
        {"base": "Base document spec"}
       ]
    }
    

    然后你可以像这样创建布尔查询:

    GET st_t2/_search
    {
      "query": {
        "bool": {
          "should": [
            {
              "bool": {
                "must": [
                  {
                    "exists": {
                      "field": "Description.Eric"
                    }
                  },
                  {
                    "match": {
                      "Description.Eric": "wtf"
                    }
                  }
                ]
              }
            },
            {
              "bool": {
                "must_not": [
                  {
                    "exists": {
                      "field": "Description.Eric"
                    }
                  }
                ],
                "must": [
                  {
                    "match": {
                      "Description.base": "wtf"
                    }
                  }
                ]
              }
            }
          ]
        }
      }
    }
    

    更新:

    在实施此解决方案期间,@Eric Bowden 决定使用nested 映射,并在嵌套字段中使用提供的existmatchworking example

    【讨论】:

    • 您是否希望这种方法可以扩展到多达 20 个字段以及多达 200 个用户可以自定义文档上的一个或多个字段的情况?我的索引将包含 735,000 个文档。
    • 735k 文件不是那么多,但是我理解您的担忧。在基础搜索中,查询将非常快速和简单 - 没有脚本,简单的逻辑。但你的任务并不常见——最好的答案是尝试。我认为这个可以工作,但最好的方法是检查数据集。
    • 您引导我正确答案,但您的答案对我不起作用,如上所述。在我的查询中,我必须将查询指定为嵌套的。您确定您的答案有效吗?我想我需要你的索引定义来测试它,我自己。如果您同意查询需要嵌套,您是否会更新您的答案,我会将其标记为正确并奖励赏金。非常感谢您在这方面的帮助!
    • 在我的回答中,主要是我试图解决搜索查询的问题(这个技巧存在且不能)。我也查找了嵌套变体,但并没有停下来简化我的答案 - 并为我们的想法指明方向。我很高兴听到我的解决方案对您有所帮助!将更新答案 - 赏金将是美好的一年开始:)
    • @EricBowden 我用您的解决方案链接更新了答案。我认为没有理由在我的回答中重复它:) 你怎么看?
    【解决方案2】:

    使用来自@Oleksii Baidan 的输入,以下查询对我有用。对于下面的示例查询,由于用户 Eric 为字段 Description 提供了自定义值,因此返回了一个文档。如果我要修改下面的查询,搜索“abc”而不是“jkl”,那么查询将不会像预期的那样返回结果,因为用户 Eric 已经覆盖了描述字段,隐藏了描述的基值.

    GET index1/_search
    {
      "query": {
        "bool": {
          "should": [
            {
              "bool": {
                "must": [
                  {
                    "nested": {
                    "path":"Description",
                    "query":{
                    "exists": {
                      "field": "Description.Eric"
                    }}}},
                  {
                    "nested": {
                    "path":"Description",
                    "query":{
                    "match": {
                      "Description.Eric": "jkl"
                    }}}}
                ]
            }},
            {
              "bool": {
                "must_not": [{
                  "nested": {
                    "path":"Description",
                    "query": {
                    "exists": {
                      "field": "Description.Eric"
                  }}}}
                ],
                "must": [{
                  "nested": {
                  "path":"Description",
                  "query":{
                  "match": {
                    "Description.base": "jkl"
                  }}}}
                ]}}
          ]}}
    }
    
    

    索引定义。

    PUT index1
    {
      "settings": {
        "number_of_shards": 2,
        "number_of_replicas": 0
      },
      "mappings": {
        "properties" : {
          "Name" : {
            "type":"nested"
          },
          "Description" : {
            "type":"nested"
          },
          "Spec" : {
            "type":"nested"
          }}}
    }
    

    示例文档

    POST index1/_doc
    { 
      "Name": [
          {"base":"NameBase2"}
        ],
      "Description": [
        {"base": "DescriptionBase2 abc"},
        {"Alex": "DescriptionAlex2 def"},
        {"Eric": "DescriptionEric2 jkl"}
        ],
      "Spec": [
        {"base": "SpecBase2"}
       ]
    }
    

    更新 进一步处理这个问题,我意识到没有必要将用户属性配置为嵌套。作为示例,我将保留此 SF 帖子,但据我了解,没有必要将用户字段配置为嵌套,并且不会提供额外的价值。

    【讨论】:

      【解决方案3】:

      在尝试了我发布的prior answer 之后,我发现这导致我的索引中的一些字段超过了 Elasticsearch 推荐的最大数量,即 1000。

      作为解决方案,我尝试使用父/子文档解决此问题。这似乎有效。唯一的缺点是查询结果中没有检索到子文档数据。

      索引

      PUT index1
      {
        "settings": {
          "number_of_shards": 2,
          "number_of_replicas": 0
        },
        "mappings": {
          "properties": {
            "ProductCustomizationField": { 
              "type": "join",
              "relations": {
                "Product": "ProductCustomization" 
              }
            }
          }
        }
      }
      

      示例父文档

      POST index1/_doc/TestDoc1
      { 
        "Name": "TestDoc1 abc",
        "ProductCustomizationField":"Product"
      }
      

      子文档示例

      POST index1/_doc/TestDoc1-Eric?routing=TestDoc
      { 
        "Name": "TestDoc1-Eric def",
        "Owner":"Eric",
        "ProductCustomizationField": {
          "name":"ProductCustomization",
          "parent":"TestDoc1"
        }
      }
      
      

      示例查询

      这是用户 Eric 执行的示例查询,搜索“def”。将找到结果,因为 Eric 自定义了产品名称以包含“def”,请参见示例子文档(上)。如果另一个用户,例如亚历克斯,在哪里搜索“def”,则不会返回结果,因为“def”只存在于Eric的自定义产品名称中。

      GET index1/_search
      {
        "query": {
          "bool": {
            "should": [
              {
                "bool": {
                  "must": [
                    {
                      "has_child" : {
                      "type" : "ProductCustomization",
                      "query": {
                        "match": {
                          "Owner": "Eric"
                        }}}},
                    {
                      "has_child" : {
                      "type" : "ProductCustomization",
                      "query": { 
                        "match": {
                        "ModelNumber": "def"
                      }}}}
                  ]
              }},
              {
                "bool": {
                  "must_not": [
                    {
                      "has_child" : {
                      "type" : "ProductCustomization",
                      "query": { 
                      "match": {
                        "Owner": "Eric"
                      }}}},
                      {
                        "exists" : {
                          "field":"Owner"
                        }}],
                  "must": [
                  {
                     "match": {
                        "ModelNumber": "def"
                  }}]}}
            ]}}
      }
      

      【讨论】:

        猜你喜欢
        • 2014-07-03
        • 2021-08-16
        • 2013-10-04
        • 1970-01-01
        • 2014-11-04
        • 1970-01-01
        • 1970-01-01
        • 2022-08-22
        • 2018-10-04
        相关资源
        最近更新 更多