Elasticsearch 必须与子查询相交

Elasticsearch must with subquery intersection

我想执行一个结合了两个子查询(AND 运算符)的 Elasticsearch 查询,每个子查询都在不同的字段中搜索(OR 运算符)。

例如,如果我传递“姓名”参数,它只会在姓名字段(名字 + 姓氏)中搜索,如果我传递“联系人”参数,它会在联系人字段(ContactEmail + ContactTelephone)中搜索。

下面的代码return:

searchQuery = searchQuery
    .MinScore(minScore)
    .Query(qu => qu
        .Bool(b => b
            .Must(m => m
                .MultiMatch(mm=> mm
                    .Fields(fs=> fs
                        .Field(f => f.Firstname)
                        .Field(f => f.Lastname)
                    )
                    .Query(name)
                    .Operator(Operator.Or)
                )
            )
            .Must(m => m
                .MultiMatch(mm => mm
                    .Fields(fs => fs
                        .Field(f => f.ContactEmail)
                        .Field(f => f.ContactTelephone)
                    )
                    .Query(contact)
                    .Operator(Operator.Or)
                )
            )
        )
    );

我正在使用 Must 因为我想要关联的分数。

我认为有 2 个问题:应用 AND 而不是 OR 以及如果条件为空则忽略子查询。有什么想法吗?

must 子句中的两个查询 必须 是同一个 .Must() 调用的一部分。

鉴于以下 POCO

public class Person
{
    public string Firstname { get; set; }
    public string Lastname { get; set; }
    public string ContactEmail {get;set;}
    public string ContactTelephone {get;set;}

}

查询应如下所示

var client = new ElasticClient(settings);

var minScore = 2;
string name = "name";
string contact = "contact";

var response = client.Search<Person>(s => s
    .MinScore(minScore)
    .Query(qu => qu
        .Bool(b => b
            .Must(m => m
                .MultiMatch(mm => mm
                    .Fields(fs => fs
                        .Field(f => f.Firstname)
                        .Field(f => f.Lastname)
                    )
                    .Query(name)
                    .Operator(Operator.Or)
                ), m => m
                .MultiMatch(mm => mm
                    .Fields(fs => fs
                        .Field(f => f.ContactEmail)
                        .Field(f => f.ContactTelephone)
                    )
                    .Query(contact)
                    .Operator(Operator.Or)
                )
            )
        )
    )
);

产生以下结果

{
  "min_score": 2.0,
  "query": {
    "bool": {
      "must": [
        {
          "multi_match": {
            "fields": [
              "contactEmail",
              "contactTelephone"
            ],
            "operator": "or",
            "query": "contact"
          }
        }
      ]
    }
  }
}

如果 namecontactnull,则该查询将被忽略。例如,将 name 设置为 null

string name = null;

var response = client.Search<Person>(s => s
    .MinScore(minScore)
    .Query(qu => qu
        .Bool(b => b
            .Must(m => m
                .MultiMatch(mm => mm
                    .Fields(fs => fs
                        .Field(f => f.Firstname)
                        .Field(f => f.Lastname)
                    )
                    .Query(name)
                    .Operator(Operator.Or)
                ), m => m
                .MultiMatch(mm => mm
                    .Fields(fs => fs
                        .Field(f => f.ContactEmail)
                        .Field(f => f.ContactTelephone)
                    )
                    .Query(contact)
                    .Operator(Operator.Or)
                )
            )
        )
    )
);

产量

{
  "min_score": 2.0,
  "query": {
    "bool": {
      "must": [
        {
          "multi_match": {
            "fields": [
              "contactEmail",
              "contactTelephone"
            ],
            "operator": "or",
            "query": "contact"
          }
        }
      ]
    }
  }
}

这利用了称为 无条件查询 的客户端功能 - 如果查询的某个组成部分为 空[=51,则特定查询被认为是无条件的=] 当它不应该时,为了形成一个完整的查询。例如,for a multi_match query, it is considered to be conditionless if the Query is null

无条件查询的目的是让编写更复杂的查询变得更容易,the behaviour can be bypassed by specifying Verbatim() on the query. Since they go against the principle of least surprise however, there is an intention to remove them in the future。在不依赖无条件行为的情况下执行相同的查询

var response = client.Search<Person>(s => s
    .MinScore(minScore)
    .Query(qu => qu
        .Bool(b => b
            .Must(m =>
                {
                    if (name == null)
                        return m;
                        
                    return m
                        .MultiMatch(mm => mm
                            .Fields(fs => fs
                                .Field(f => f.Firstname)
                                .Field(f => f.Lastname)
                            )
                            .Query(name)
                            .Operator(Operator.Or)
                        );
                }, m => 
                {
                    if (contact == null)
                        return m;

                    return m
                        .MultiMatch(mm => mm
                            .Fields(fs => fs
                                .Field(f => f.ContactEmail)
                                .Field(f => f.ContactTelephone)
                            )
                            .Query(contact)
                            .Operator(Operator.Or)
                        );
                }
            )
        )
    )
);