Как сортировать / фильтровать список так же, как вы сортируете результат в базе данных - PullRequest
1 голос
/ 04 мая 2020

В настоящее время я кодирую с. net предварительный просмотр ядра 5 3, и у меня возникла проблема с фильтрацией списка наиболее подходящих клиентов. разные результаты?

Как я могу исправить второй образец, чтобы он возвращал те же результаты, что и первый образец?

Первый образец (это работает)

//This properly gives the top 10 best matches from the database
using (var context = new CustomerContext(_contextOptions))
{
    customers = await context.vCustomer.Where(c => c.Account_Number__c.Contains(searchTerm))
                    .Select(c => new
                    {
                        vCustomer = c,
                        MatchEvaluator = searchTerm.Contains(c.Account_Number__c)
                    })
                    .OrderByDescending(c => c.MatchEvaluator)
                    .Select(c => new CustomerModel
                    {
                        CustomerId = c.vCustomer.Account_Number__c,
                        CustomerName = c.vCustomer.Name
                    })
                    .Take(10)
                    .ToListAsync();
}

Идентификатор клиента Результаты для первого образца (это лучшие результаты)

  • 247
  • 2470
  • 247105
  • 247109
  • 247110
  • 247111
  • 247112
  • 247113
  • 247116
  • 247117

Второй образец (это не работает так же, хотя это тот же код)

//this take all customers from database and puts them in a list so they can be cached and sorted on later.
List<CustomerModel> customers = new List<CustomerModel>();
using (var context = new CustomerContext(_contextOptions))
{
    customers = await context.vCustomer
                    .Select(c => new CustomerModel
                    {
                        CustomerId = c.Account_Number__c,
                        CustomerName = c.Name
                    })
                    .ToListAsync();
}

//This does not properly gives the top 10 best matches from the list that was generated from the database
List<CustomerModel> bestMatchedCustomers = await Task.FromResult(
    customers.Where(c => c.CustomerId.Contains(searchTerm))
                    .Select(c => new
                    {
                        Customer = c,
                        MatchEvaluator = searchTerm.Contains(c.CustomerId)
                    })
                    .OrderByDescending(c => c.MatchEvaluator)
                    .Select(c => new CustomerModel
                    {
                        CustomerId = c.Customer.CustomerId,
                        CustomerName = c.Customer.CustomerName
                    })
                    .Take(10)
                    .ToList()
);

Идентификатор клиента Результаты из второго образца

  • 247
  • 1065247
  • 247610
  • 32470
  • 324795
  • 624749
  • 762471
  • 271247
  • 247840
  • 724 732

Ответы [ 2 ]

2 голосов
/ 05 мая 2020

Вы спросили: «Почему они разные?», И для этого вам нужно понять, что в базах данных есть оптимизатор, который смотрит на выполняемый запрос и изменяет свою стратегию доступа к данным в зависимости от различных вещей, например, сколько записей выбирается, индексы применить, какая сортировка запрошена et c

Один из ваших запросов выбирает всю таблицу базы данных в список на стороне клиента, а затем использует этот список для фильтрации и сортировки, другой использует базу данных для выполнения фильтр и сортировка. Для базы данных это будут совершенно разные вещи; ударив по таблице, вы, вероятно, получите строки в том порядке, в котором они хранятся на диске, что может быть случайным. Используя фильтр, вы можете увидеть, что база данных использует некоторую стратегию индексирования, при которой она включает / уменьшает большое количество строк на основе индекса, или может даже использовать индекс для извлечения запрошенных данных. То, как он затем сортирует связи, если это так, может полностью отличаться от того, как список на стороне клиента сортирует связи (на самом деле ничего не делает с ними). В любом случае важным моментом является то, что база данных по-разному планирует и выполняет два разных запроса. Он видит разные запросы, потому что ваша вторая версия выполняет запрос без where или порядка

. Когда вы объединяете это с вашей операцией сортировки, выполняемой в столбце с невероятной мощностью (насколько уникальны значения в столбце) то есть ваш результат лида, тот, где запись равна поисковому запросу, равен 1, а ВСЕ остальное - 0. Это означает, что одна запись всплывает вверх, а остальные записи могут быть отсортированы, однако система, выполняющая сортировку, любит , а затем вы берете подмножество из них

... поэтому один выглядит как X, а другой как Y

Если вы не взяли подмножество, два набора данных были бы в разном порядке но все в наборе 1 будет где-то в наборе 2 ... просто один набор похож на 1 3 5 7 2 4 6, другой на 1 7 6 5 4 3 2, вы берете первые три результата и спрашивая "почему 1 3 5 отличается от 1 7 6"


Что касается вашего кода, я думаю, что я бы просто сделал что-то простое, что также сортирует стабильно (строки в том же порядке, потому что нет двусмысленности / связей), например:

await context.vCustomer
  .Where(c => c.Account_Number__c.Contains(searchTerm))
  .OrderBy(c => c.Account_Number__c.Length)
  .ThenBy(c => c.Account_Number__c) //stable, if unique
  .Take(10)
  .Select(c => new CustomerModel
    {
      CustomerId = c.vCustomer.Account_Number__c,
      CustomerName = c.vCustomer.Name
    }
  )
  .ToListAsync();

Если вы отсортируете результаты по их длине в символах, тогда 247 лучше соответствует, чем 2470, что лучше, чем 24711 или 12471 и т. Д. c

«Содержит» может сильно ухудшить производительность; возможно, рассмотрим StartsWith; теоретически, по крайней мере, для этого можно было бы использовать индекс

ps: вызов вашего var MatchEvaluator действительно сбивает с толку людей, которые хорошо знают регулярные выражения, btw

1 голос
/ 04 мая 2020

Вы заказываете по значению MatchEvaluator, которое равно 1 или 0.

Если я правильно понял, вы хотите сначала заказать MatchEvaluator, а затем CustomerId :

List<CustomerModel> bestMatchedCustomers = 
    await Task.FromResult(
        customers.Where(c => c.CustomerId.Contains(searchTerm))
                 .OrderBy(c => c.CustomerId.IndexOf(searchTerm))
                 .ThenBy(c => c.CustomerId)
                 .Select(c => new CustomerModel
                 {
                     CustomerId = c.Customer.CustomerId,
                     CustomerName = c.Customer.CustomerId
                 })
                 .Take(10)
                 .ToList()
);
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...