如何使用Elasticsearch过滤查询结果

时间:2019-01-24 14:34:24

标签: elasticsearch nest

我正在尝试创建一个为用户和组提供自动完成建议的组件。我正在使用elasticsearch 6.5.3。我创建了一个索引,其中包含我要搜索的字段以及3个其他字段,以供根据(isGroup,isUser,organizationId)进行过滤。在某些情况下,我想使用此组件来搜索所有用户和组,有时仅搜索用户或组,或者仅搜索属于特定组织的用户。我打算根据具体用例提供一个过滤器以及搜索词。我正在使用nest进行搜索,但是我不知道该怎么做。是否可以这样做,如果可以,怎么做?我为此走错了路吗?我主要遵循此guide来创建分析器和东西。如果可以,我可以发布索引,但是有点长。

这是一个搜索,其中返回了两个项目。

 return client.Search<UserGroupDocument>(s => s
            .Query(q=>q
                .QueryString(qs=>qs.Query("adm"))                    
            )
        );

    {
    "_index": "users_and_groups_autocomplete_index",
    "_type": "usergroupdocument",
    "_id": "c54956ab-c50e-481c-b093-f9855cc74480",
    "_score": 2.2962174,
    "_source": {
        "id": "c54956ab-c50e-481c-b093-f9855cc74480",
        "isUser": true,
        "isGroup": false,
        "name": "admin",
        "email": "admin@snapshotdesign.com",
        "organizationId": 2
    }
},
    {
        "_index": "users_and_groups_autocomplete_index",
        "_type": "usergroupdocument",
        "_id": "80f98d24-39e3-475d-9cb6-8f16ca472525",
        "_score": 0.8630463,
        "_source": {
        "id": "80f98d24-39e3-475d-9cb6-8f16ca472525",
        "isUser": false,
        "isGroup": true,
        "name": "new Group",
        "users": [
            {
                "name": "Test User 1",
                "email": "test@example.com"
            },
            {
                "name": "admin",
                "email": "admin@snapshotdesign.com"
            }
        ],
        "organizationId": 0
    }
},

因此,根据我在哪里使用此组件,我可能希望所有这些返回,只是用户,组或组织2中的用户。

这是我的UserGroupDocument类

public class UserGroupDocument
{
    public string Id { get; set; }
    public bool IsUser { get; set; }
    public bool IsGroup { get; set; }
    public string Name { get; set; }
    public string Email { get; set; }
    public List<User> Users { get; set; }
    public long OrganizationId { get; set; }

}

还有User类

public class User
{
    public string Name { get; set; }
    public string Email { get; set; }
}

基于以下Russ Cam的回答,我如下所示更改了Must语句。这给了我想要的过滤功能,但没有提前输入功能。开始匹配之前,我仍然必须输入整个单词。

.Must(mu => mu
    .QueryString(mmp => mmp
        .Query(searchTerms)
        .Fields(f => f
            .Field(ff => ff.Name)
            .Field(ff => ff.Users.Suffix("name"))
        )
    )
)

这是我正在使用的索引。

{
"users_and_groups_autocomplete_index": {
    "aliases": {},
    "mappings": {
        "usergroupdocument": {
            "properties": {
                "email": {
                    "type": "text",
                    "fields": {
                        "autocomplete": {
                            "type": "text",
                            "analyzer": "autocomplete"
                        }
                    }
                },
                "id": {
                    "type": "text",
                    "fields": {
                        "keyword": {
                            "type": "keyword",
                            "ignore_above": 256
                        }
                    }
                },
                "isGroup": {
                    "type": "boolean"   
                },
                "isUser": {
                    "type": "boolean"
                },
                "name": {
                    "type": "text",
                    "fields": {
                        "autocomplete": {
                            "type": "text",
                            "analyzer": "autocomplete"
                        }
                    }
                },
                "organizationId": {
                    "type": "long"
                },
                "users": {
                    "properties": {
                        "email": {
                            "type": "text",
                            "fields": {
                                "autocomplete": {
                                    "type": "text",
                                    "analyzer": "autocomplete"
                                }
                            }
                        },
                        "name": {
                            "type": "text",
                            "fields": {
                                "autocomplete": {
                                    "type": "text",
                                    "analyzer": "autocomplete"
                                }
                            }
                        }
                    }
                }
            }
        }
    },
    "settings": {
        "index": {
            "number_of_shards": "5",
            "provided_name": "users_and_groups_autocomplete_index",
            "creation_date": "1548363729311",
            "analysis": {
                "analyzer": {
                    "autocomplete": {
                        "filter": [
                            "lowercase"
                        ],
                        "type": "custom",
                        "tokenizer": "autocomplete"
                    }
                },
                "tokenizer": {
                    "autocomplete": {
                        "token_chars": [
                            "digit",
                            "letter"
                        ],
                        "min_gram": "1",
                        "type": "edge_ngram",
                        "max_gram": "20"
                    }
                }
            },
            "number_of_replicas": "1",
            "uuid": "Vxv-y58qQTG8Uh76Doi_dA",
            "version": {
                "created": "6050399"
            }
        }
    }
}
}

1 个答案:

答案 0 :(得分:2)

您正在寻找的是一种将多个查询组合在一起的方法:

  1. 查询搜索条件
  2. isGroup上查询
  3. isUser上查询
  4. organizationId上查询

,并结合使用这些搜索来执行搜索。这是诸如bool query之类的复合查询的来源。给出以下POCO

public class UserGroupDocument 
{
    public string Name { get; set; }
    public bool IsGroup { get; set; }
    public bool IsUser { get; set; }
    public string OrganizationId { get; set; }
}

我们可以从

开始
private static void Main()
{
    var defaultIndex = "default-index";
    var pool = new SingleNodeConnectionPool(new Uri("http://localhost:9200")); 
    var settings = new ConnectionSettings(pool)
        .DefaultIndex(defaultIndex);

    var client = new ElasticClient(settings);

    var isUser = true;
    var isGroup = true;
    var organizationId = "organizationId";

    var searchResponse = client.Search<UserGroupDocument>(x => x
        .Index(defaultIndex)
        .Query(q => q
            .Bool(b => b
                .Must(mu => mu
                    .QueryString(mmp => mmp
                        .Query("some admin")
                        .Fields(f => f
                            .Field(ff => ff.Name)
                        )
                    )
                )
                .Filter(fi => 
                    {
                        if (isUser)
                        {
                            return fi
                                .Term(f => f.IsUser, true);
                        }

                        return null;
                    }, fi =>
                    {
                        if (isGroup)
                        {
                            return fi
                                .Term(f => f.IsGroup, true);
                        }

                        return null;
                    }, fi => fi
                    .Term(f => f.OrganizationId, organizationId)
                )
            )
        )
    );
}

这将产生以下查询

POST http://localhost:9200/default-index/usergroupdocument/_search
{
  "query": {
    "bool": {
      "filter": [
        {
          "term": {
            "isUser": {
              "value": true
            }
          }
        },
        {
          "term": {
            "isGroup": {
              "value": true
            }
          }
        },
        {
          "term": {
            "organizationId": {
              "value": "organizationId"
            }
          }
        }
      ],
      "must": [
        {
          "query_string": {
            "fields": [
              "name"
            ],
            "query": "some admin"
          }
        }
      ]
    }
  }
}

如果

  • isUserfalseterm字段上的isUser查询过滤器将不包含在搜索查询中
  • isGroupfalseterm字段上的isGroup查询过滤器将不包含在搜索查询中
  • organizationIdnull或为空字符串,term上的organizationId查询过滤器将不包含在搜索查询中。

现在,我们可以更进一步,使isGroupisUser为可空布尔值(bool?)。然后,当其中一个值为null时,相应的term查询过滤器将不包含在发送给Elasticsearch的搜索查询中。这利用了Nest中称为 conditionless 查询的功能,该功能旨在简化编写更复杂的查询的过程。另外,我们可以使用operator overloading on queries来简化编写bool查询的过程。这一切都意味着我们可以将查询细化为

bool? isUser = true;
bool? isGroup = true;
var organizationId = "organizationId";

var searchResponse = client.Search<UserGroupDocument>(x => x
    .Index(defaultIndex)
    .Query(q => q
        .QueryString(mmp => mmp
            .Query("some admin")
            .Fields(f => f
                .Field(ff => ff.Name)
            )
        ) && +q
        .Term(f => f.IsUser, isUser) && +q
        .Term(f => f.IsGroup, isGroup) && +q
        .Term(f => f.OrganizationId, organizationId)
    )
);