StringComparison.OrdinalIgnoreCase не работает в CreateDocumentQuery (). Where () для запроса CosmosDB - PullRequest
1 голос
/ 22 января 2020

Вот что у меня есть и что я хочу сделать

У меня есть Cosmos DB с контейнером, давайте назовем контейнер "Проекты". В этом контейнере у меня есть сотни проектных документов, которые имеют следующую структуру (пример в json):

{
 "name": "Death Star - Software update",
 "number": "133701",
 "customer" : ""
} 

Чтобы сделать данные доступными для любого типа клиента, я использую http-триггер azure функция (. Net Core 2.1). Функция azure имеет входную привязку к базе данных cosmos, несколько механизмов безопасности и ожидает один или несколько параметров get, называемых «Поиск», поэтому URL запроса может выглядеть следующим образом:

https://www.someUrlToMyAzureStuff.com/api/GetProjects?Search=Death&Search=update

Идея состоит в том, что функция позволяет выполнять поиск по ключевым словам, и то, что когда-либо проект содержит эти ключевые слова в своих свойствах, будет возвращено.

Вот краткий пример кода для функции azure:

[FunctionName("GetProjects")]
public static async Task<IActionResult> Run(
    [HttpTrigger(AuthorizationLevel.Anonymous, "get", "post")] HttpRequest request,
    [CosmosDB(databaseName: "ProjectsDatabase", collectionName: "ProjectsContainer",
            ConnectionStringSetting = "someConnectionString")] DocumentClient projectsContainer,
    ILogger log)
{
    try
    {
        if (CheckSecurityStuff...)
        {
            // The httpGetParameters contains a List<string> that contains the "Search" Get parameters from the Url
            var httpGetParameters = RESTUtility.RetrieveHttpGetParametersFromRequest(request);
            var projects = await CosmosDataProvider.GetProjectsByFiltersAsync(projectsContainer, httpGetParameters.UrlParams);                  

            return new OkObjectResult(projects);
        }
        else
        {
            return new BadRequestResult();
        }
    }
    catch (Exception)
    {
        return new BadRequestResult();
    }
}

И вот функция CosmosDataProvider.GetProjectsByFiltersAsyn c, где я сталкиваюсь со странным поведением:

        internal static async Task<List<Project>> GetProjectsByFiltersAsync(DocumentClient projectsContainer, List<string> filters)
        {
            return await Task.Run(() =>
            {
                var uri = UriFactory.CreateDocumentCollectionUri("ProjectsDatabase", "ProjectsContainer");
                var feedOptions = new FeedOptions { EnableCrossPartitionQuery = true };

                var projects = projectsContainer.CreateDocumentQuery<Project>(uri, feedOptions)
                    .Where(project => project.Name.Contains(filters[0]) ||
                                      project.Number.Contains(filters[0]) ||
                                      project.Customer.Contains(filters[0])).ToList();

                foreach (var filter in filters.Skip(1))
                {
                    projects = projects.Where(project =>
                                      project.Name.Contains(filter) ||
                                      project.Number.Contains(filter) ||
                                      project.Customer.Contains(filter)).ToList();
                }

                return projects;
            }
        }

Пока все хорошо: если я отправлю запрос с параметрами поиска «Смерть» и «обновление», функция вернет вышеупомянутый проект. Который работает нормально, но возвращается только тогда, когда ключевые слова соответствуют регистру. Так что я хочу сделать поиск, игнорируя случай. Поэтому я начал использовать:

Contains(string, StringComparison) 

с StringComparison.OrdinalIgnoreCase. Что выглядит следующим образом:

        internal static async Task<List<Project>> GetProjectsByFiltersAsync(DocumentClient projectsContainer, List<string> filters)
        {
            return await Task.Run(() =>
            {
                var uri = UriFactory.CreateDocumentCollectionUri("ProjectsDatabase", "ProjectsContainer");
                var feedOptions = new FeedOptions { EnableCrossPartitionQuery = true };

                var projects = projectsContainer.CreateDocumentQuery<Project>(uri, feedOptions)
                    .Where(project => project.Name.Contains(filters[0], StringComparison.OrdinalIgnoreCase) ||
                                      project.Number.Contains(filters[0], StringComparison.OrdinalIgnoreCase) ||
                                      project.Customer.Contains(filters[0], StringComparison.OrdinalIgnoreCase)).ToList();

                foreach (var filter in filters.Skip(1))
                {
                    projects = projects.Where(project =>
                                      project.Name.Contains(filter) ||
                                      project.Number.Contains(filter) ||
                                      project.Customer.Contains(filter)).ToList();
                }

                return projects;
            }
        }

Я ожидаю, что я ищу «смерть» и «uPdAte» и получаю проект. Но Cosmos DB не возвращает его.

Вопросы

-> Есть ли у вас какие-либо идеи, почему StringComparison не работает в условии CreateDocumentQuery where?

-> Так как Contains (строка, StringComparison) доступна только в. Net Core, это может быть конфликт с Azure SDK. Net, который может использовать. Net Framework ?

(Между прочим: я также попытался заменить функцию «Contains» на «IndexOf» или «RegEx.IsMatch», оба не сработали, за исключением того, что они не поддерживаются в операторе LINQ Where) Сведения об исключении:

One or more errors occurred. (Method 'IndexOf' is not supported., Windows/10.0.18363 documentdb-netcore-sdk/2.3.0)

-> Любая другая идея, как я могу заменить функцию содержит, если нет решения?

1 Ответ

1 голос
/ 24 января 2020

К сожалению, здесь есть несколько проблем.

Первая проблема - это то, что Cosmos DB не учитывает регистр. Чтобы сделать равенство строк, вам нужно было бы записать строки в Cosmos во втором свойстве как весь верхний регистр, а затем использовать .ToUpper () в строке для равенства. Если вы попытаетесь использовать ToUpper () в Cosmos там, где он не будет использовать индекс, вы получите низкую производительность. Вторая, более серьезная проблема - Contains () также не будет использовать индекс. Недавно мы обновили наш запрос по устранению неполадок, выполнив c здесь для этих двух системных функций. https://docs.microsoft.com/en-us/azure/cosmos-db/troubleshoot-query-performance#understand -which-system-functions-utilize-the-index

В настоящее время мы рекомендуем в этом сценарии использовать клиенты Azure Поиск в верхней части Космоса и выполнение их текстового поиска сюда. В будущем мы будем работать над обеспечением свободного текстового поиска и индексации без учета регистра, но пока не будем указывать ETA.

Надеюсь, это будет полезно.

...