【问题标题】:How to select only matching fields of child object in ElasticSearch?如何在 ElasticSearch 中仅选择子对象的匹配字段?
【发布时间】:2019-09-05 12:21:18
【问题描述】:

我在使用 Elasticsearch 进行搜索时遇到问题。我正在使用 ES 7.3 来实现。

下面是我的班级结构:

    [ElasticsearchType]
    public class InquiryInformation
    {
        [Text(Name = "inquiryNumber")]
        public string InquiryNumber { get; set; }

        [Text(Name = "inquiryStatus")]
        public string InquiryStatus { get; set; }

        [Text(Name = "submitedBy")]
        public string SubmittedBy { get; set; }

        [Nested]
        [PropertyName("files")]
        public List<FileInformation> Files { get; set; }

    }

    [ElasticsearchType]
    public class FileInformation
    {
        [Text(Name = "fileName")]
        public string FileName { get; set; }

        [Text(Name = "fileContent")]
        public string FileContent { get; set; }
    }

并使用此代码创建映射:

elasticClient.Map<InquiryInformation>(m => m.AutoMap());

这已经用 ES 创建了下面的索引映射:

{
  "city" : {
    "mappings" : {
      "properties" : {
        "files" : {
          "type" : "nested",
          "properties" : {
            "fileContent" : {
              "type" : "text"
            },
            "fileName" : {
              "type" : "text"
            }
          }
        },
        "inquiryNumber" : {
          "type" : "text"
        },
        "inquiryStatus" : {
          "type" : "text"
        },
        "submitedBy" : {
          "type" : "text"
        }
      }
    }
  }
}

现在,我必须编写一个查询来搜索用户以及用户的文件。

假设如果我搜索带有inquiryNumber XYZ1212 的用户,那么它将返回所有匹配的记录。现在的问题是 - 我只需要返回匹配的文件。就像如果 FileContent 的 FileName 包含匹配的记录信息,那么只有匹配的文件应该与文档一起返回。

我用 NEST 尝试了很多方法。但未能获得唯一匹配的文件。它总是向我返回特定用户的所有文件。

这能满足我的需求吗?

UPDATE 1 添加 C# (NEST) 的示例查询

var searchResult = elasticClient.Search<InquiryInformation>(s => s
                    .Query(q => q
                        .MultiMatch(m => m
                            .Fields(f => f
                                .Field("files.fileContent")
                                .Field("files.fileName")
                            )
                            .Query("Hello Stackoverflow")
                        )
                    )
                );

更新 2:添加另一个查询:

POST city/_search
{
  "_source": {
    "includes": [ "*" ],
    "excludes": [ "files" ]
  },
  "query": {
    "nested": {
      "path": "files",
      "inner_hits": {
        "_source": [
          "inquiryNumber", "submitedBy"
        ]
      },
      "query": {
        "bool": {
          "must": [
            {
              "term": {
                "files.fileContent": "Samsung"
              }
            }
          ]
        }
      }
    }
  }
}

【问题讨论】:

  • 最好显示您迄今为止尝试过的一个或多个查询。
  • @Val 添加了查询!
  • @Val:我还在stackoverflow.com/a/31721025/903507 上检查了您的 cmets。但我没有运气。如果您能帮助我实现这一目标,那可能对我很有帮助。我是 ES 的初学者

标签: c# asp.net elasticsearch kibana nest


【解决方案1】:

由于files 字段是nested 字段,您还需要利用nested 查询。像这样的:

 var searchResult = elasticClient.Search<InquiryInformation>(s => s
                .Source(false)
                .Query(q => q
                   .Nested(c => c
                      .InnerHits()
                      .Path(p => p.Files)
                      .Query(nq => nq
                        .MultiMatch(m => m
                          .Fields(f => f
                            .Field("files.fileContent")
                            .Field("files.fileName")
                          )
                          .Query("Hello Stackoverflow")
                        )
                      )
                   )
                )
            );

【讨论】:

  • 感谢您帮助我生成 C# 查询。现在这里的问题是我正在获取特定用户的所有文件。有没有办法只选择匹配关键字的文件。关键字不匹配的文件将从搜索结果中被丢弃。
  • 你不是在每个点击中都有一个InnerHits 部分吗?这就是您要查找的数据所在的位置。或者,您也可以将.Source(false) 添加到您的查询中,以便只显示内部点击而不是整个文档。
【解决方案2】:

感谢 Val 提供的宝贵帮助。

对于那些试图实现相同目标的人 - 以下是我目前使用的 C# (NEST) 查询:

 var searchResult = elasticClient.Search<InquiryInformation>(s => s
               .Query(q => q
                  .Nested(c => c
                     .InnerHits()
                     .Path(p => p.Files)
                     .Query(nq => nq
                       .MultiMatch(m => m
                         .Fields(f => f
                           .Field("files.fileContent")
                           .Field("files.fileName")
                         )
                         .Query("Samsung Galaxy S10")
                       )
                     )
                  )
               )
           );

foreach (var hit in searchResult.Hits)
{
     var file = hit.InnerHits["files"].Documents<FileInformation>();
}

更新 1 - 更正了关于 @Val 评论的查询:

var searchResult = elasticClient.Search<InquiryInformation>(s => s
               .Query(q => q
                  .Nested(c => c
                     .InnerHits()
                     .Path(p => p.Files)
                     .Query(nq => nq
                       .MultiMatch(m => m
                         .Fields(f => f
                           .Field("files.fileContent")
                           .Field("files.fileName")
                         )
                         .Query("A11117")
                       )
                     )
                  )
               ).Query(q => q
                    .MultiMatch(m => m
                        .Fields(f => f
                            .Field("inquiryNumber"))
                        .Query("A11117")))
           );

            foreach (var hit in searchResult.Hits)
            {
                var file = hit.InnerHits["files"].Documents<FileInformation>();
            }

【讨论】:

  • 您不能在此处添加 inquiryNumber,因为该字段不在嵌套的 Files 中,而是在文档的根级别,您需要在查询中添加另一个约束以匹配该字段。
  • 感谢您的指正!我又添加了一个查询。我正在按预期获得记录。如果我在这里也做错了什么,请您纠正我。
  • 您需要将两个查询组合成一个bool/must query,否则第二个查询可能会覆盖第一个。
猜你喜欢
  • 1970-01-01
  • 2017-03-02
  • 2018-12-13
  • 1970-01-01
  • 1970-01-01
  • 2013-11-06
  • 2014-05-11
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多