Как я могу отфильтровать результаты запроса, используяasticsearch - PullRequest
0 голосов
/ 24 января 2019

Я пытаюсь создать компонент, который предлагает подсказки автозаполнения для пользователей и групп. Я используюasticsearch 6.5.3. Я создал индекс, который содержит поля, которые я хочу найти, и 3 дополнительных поля для фильтрации (isGroup, isUser, organizationId). В некоторых случаях я хочу использовать этот компонент для поиска всех пользователей и групп, иногда просто пользователей или просто групп или просто пользователей, принадлежащих к определенной организации. Я планировал предоставить фильтр вместе с условиями поиска в зависимости от конкретного варианта использования. Я использую гнездо для поиска, но не могу понять, как это сделать. Возможно ли это сделать, и если да, то как? Я иду по неверному пути для этого? Я в основном следовал этому руководству для создания анализатора и прочего. Я могу опубликовать свой индекс, если это поможет, но он довольно длинный.

Вот поиск с двумя из возвращенных предметов.

 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; }

}

И пользовательский класс

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

Основываясь на ответе Russ Cam ниже, я изменил утверждение Must, как показано ниже Это дает мне фильтрацию, которую я хотел, но не функциональность typeahead. Мне все еще нужно набрать слово целиком, прежде чем я начну получать совпадения.

.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 голосов
/ 25 января 2019

То, что вы ищете, - это способ объединения нескольких запросов:

  1. запрос по условиям поиска
  2. запрос по isGroup
  3. запрос поisUser
  4. запрос на organizationId

и выполнение поиска с некоторой их комбинацией.Это где сложный запрос, как bool запрос приходит. Учитывая следующее 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"
          }
        }
      ]
    }
  }
}

Если

  • isUser равен false, фильтр запроса term в поле isUser не будет включен в поисковый запрос
  • isGroup равно false, фильтр запроса term в поле isGroup не будет включен в поисковый запрос
  • organizationId равен null или пустой строке,фильтр запросов term для organizationId не будет включен в поисковый запрос.

Теперь мы можем пойти дальше и сделать логические значения isGroup и isUser обнуляемыми (bool?),Затем, когда значение любого из них равно null, соответствующий фильтр запроса term не будет включен в поисковый запрос, отправленный Elasticsearch.Для этого используется функция, известная как безусловных запросов в Nest, которая предназначена для облегчения написания более сложных запросов.Кроме того, мы можем использовать перегрузку операторов для запросов , чтобы упростить написание 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)
    )
);
...