弹性嵌套查询字段应该存在过滤器

Elastic nest query field should exist filter

我正在使用带有 Nest 客户端的 elasticsearch 5.2 进行查询。我有一个日期范围的工作查询,如下所示:

var boolQuery = new BoolQueryDescriptor<AttractionDocument>();

//https://github.com/elastic/elasticsearch-net/issues/2570 must is not additive, we cannot split out query as before it all has to be one big one

boolQuery.Must(
    mn => AddRegionQuery(permissions, mn),
    mn => AddOffersQuery(permissions, mn),
    mn => request.AddDateFilter ? mn.DateRange(d => d.Field(f => f.AvailableFrom).LessThanOrEquals(DateTime.Now)) : mn,
    mn => request.AddDateFilter ? mn.DateRange(d => d.Field(f => f.AvailableTo).GreaterThanOrEquals(DateTime.Now)) : mn,
    mn => AddGenresQuery(genres, mn)
);

我遇到的问题是 AvailableTo 并不总是被填充,因此对于一些不存在的文档来说它会被填充。

我尝试添加以下内容:

if (request.AddDateFilter)
{
    boolQuery.MustNot(mn => mn.Exists(f => f.Field(e => e.AvailableTo)));
}

现在的问题是查询变得过于严格。理想情况下,我想要的存在部分应该是什么?我想要实现的是,如果我们忽略该字段并且 return 结果没有该日期,则仅对 AvailableTo 应用日期范围。如果我取出 AvailableTo 部分,我确实会得到结果。

好吧,我想我已经猜到了。我取出了必须的日期范围并将其设为应该查询现在看起来像:

 var boolQuery = new BoolQueryDescriptor<AttractionDocument>();

        if (request.AddDateFilter)
        {
            boolQuery.Should(mn => mn.DateRange(d => d.Field(f => f.AvailableTo).GreaterThanOrEquals(DateTime.Now)));
        }


        //https://github.com/elastic/elasticsearch-net/issues/2570 must is not additive, we cannot split out query as before it all has to be one big one
        boolQuery.Must(
            mn => AddRegionQuery(permissions, mn),

            mn => AddOffersQuery(permissions, mn),

            mn => request.AddDateFilter ? mn.DateRange(d => d.Field(f => f.AvailableFrom).LessThanOrEquals(DateTime.Now)) : mn,

            mn => AddGenresQuery(genres, mn)
         );

我现在得到的结果都在我们没有可用截止日期集的日期和匹配项的范围内。

您应该能够将 exists 查询与 AvailableTo 上的 range 查询结合起来,以包含 AvailableTo 字段存在且必须满足范围的文档条件,并在 bool 查询 must_not 子句中创建一个带有 AvailableTo exists 查询的析取,即反转 exists。

类似于以下内容(我已注释掉未提供的查询)

var client = new ElasticClient(settings);

var request = new 
{
    AddDateFilter = true
};

var boolQuery = new BoolQueryDescriptor<AttractionDocument>();

boolQuery.Must(
    // mn => AddRegionQuery(permissions, mn),
    // mn => AddOffersQuery(permissions, mn),
    mn => request.AddDateFilter ? mn.DateRange(d => d.Field(f => f.AvailableFrom).LessThanOrEquals(DateTime.Now)) : mn,
    mn => request.AddDateFilter ? (mn.Exists(d => d.Field(f => f.AvailableTo)) &&
                                  mn.DateRange(d => d.Field(f => f.AvailableTo).GreaterThanOrEquals(DateTime.Now))) ||
                                  !mn.Exists(d => d.Field(f => f.AvailableTo)) : mn //,
    // mn => AddGenresQuery(genres, mn)
);

client.Search<AttractionDocument>(s => s
    .Query(q => q.Bool(b => boolQuery))
);

这会产生以下查询

{
  "query": {
    "bool": {
      "must": [
        {
          "range": {
            "availableFrom": {
              "lte": "2018-11-15T20:18:10.528482+10:00"
            }
          }
        },
        {
          "bool": {
            "should": [
              {
                "bool": {
                  "must": [
                    {
                      "exists": {
                        "field": "availableTo"
                      }
                    },
                    {
                      "range": {
                        "availableTo": {
                          "gte": "2018-11-15T20:18:10.5304815+10:00"
                        }
                      }
                    }
                  ]
                }
              },
              {
                "bool": {
                  "must_not": [
                    {
                      "exists": {
                        "field": "availableTo"
                      }
                    }
                  ]
                }
              }
            ]
          }
        }
      ]
    }
  }
}

由于范围和存在查询是谓词(文档要么匹配条件,要么不匹配),而不是应该计算相关性分数的查询,这些可以是 bool 查询 filter条款

boolQuery.Must(
    // Uncomment below queries, or add (QueryContainer[])null to run
    // mn => AddRegionQuery(permissions, mn),
    // mn => AddOffersQuery(permissions, mn),
    // mn => AddGenresQuery(genres, mn)
).Filter(
    mn => request.AddDateFilter ? mn.DateRange(d => d.Field(f => f.AvailableFrom).LessThanOrEquals(DateTime.Now)) : mn,
    mn => request.AddDateFilter ? (+mn.Exists(d => d.Field(f => f.AvailableTo)) &&
                                  +mn.DateRange(d => d.Field(f => f.AvailableTo).GreaterThanOrEquals(DateTime.Now))) ||
                                  !mn.Exists(d => d.Field(f => f.AvailableTo)) : mn    
);

client.Search<AttractionDocument>(s => s
    .Query(q => q.Bool(b => boolQuery))
);

创建查询

{
  "query": {
    "bool": {
      "filter": [
        {
          "range": {
            "availableFrom": {
              "lte": "2018-11-15T20:22:25.4556963+10:00"
            }
          }
        },
        {
          "bool": {
            "should": [
              {
                "bool": {
                  "filter": [
                    {
                      "exists": {
                        "field": "availableTo"
                      }
                    },
                    {
                      "range": {
                        "availableTo": {
                          "gte": "2018-11-15T20:22:25.4587138+10:00"
                        }
                      }
                    }
                  ]
                }
              },
              {
                "bool": {
                  "must_not": [
                    {
                      "exists": {
                        "field": "availableTo"
                      }
                    }
                  ]
                }
              }
            ]
          }
        }
      ]
    }
  }
}

Operator overloading on queries 在这里真的很有帮助,可以更简洁地编写复杂的 bool 查询