Реализация поиска в ASP.NET Core Web API

Что представляет собой поиск на сайте?

В этой статье мы собираемся затронуть тему поиска в ASP.NET Core Web API. Поиск - одна из тех функций, которые могут сделать или сломать ваш API, и уровень сложности при его реализации может сильно варьироваться в зависимости от ваших требований.

Если вам нужно реализовать базовую функцию поиска, когда вы просто пытаетесь искать в одном поле в базе данных, вы можете легко реализовать ее. С другой стороны, если это многостолбцовый многопунктовый поиск, вам, вероятно, лучше всего подойдут некоторые из замечательных библиотек поиска, таких как Lucene.NET, которые уже оптимизированы и проверены.

Мы не сомневаемся, что вы видели поле поиска почти на каждом веб-сайте в Интернете.

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

В нашем простом проекте одним из вариантов использования поиска будет поиск владельца по его/ее имени.

Посмотрим, как этого добиться.

Как реализовать поиск в веб-API ASP.NET Core

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

Мы хотим добиться примерно следующего: https://localhost:5001/api/owners?name=Anna Bosh

Это должно вернуть только один результат: Анна Бош. Конечно, поиск должен работать вместе с фильтрацией и разбиением по страницам, так что это одна из вещей, о которых нам тоже нужно помнить.

Как и в случае с фильтрацией, мы собираемся сначала расширить наш класс OwnerParameters, поскольку мы собираемся отправлять наш поисковый запрос в качестве параметра запроса:

public class OwnerParameters : QueryStringParameters
{
	public uint MinYearOfBirth { get; set; }
	public uint MaxYearOfBirth { get; set; } = (uint)DateTime.Now.Year;

	public bool ValidYearRange => MaxYearOfBirth > MinYearOfBirth;

	public string Name { get; set; }
}

Мы добавили только одно новое свойство - Name.

Все просто.

Теперь мы можем писать запросы с name="term" в них.

Следующее, что нам нужно сделать, это реализовать функцию поиска в нашем классе OwnerRepository:

public PagedList<Owner> GetOwners(OwnerParameters ownerParameters)
{
	var owners = FindByCondition(o => o.DateOfBirth.Year >= ownerParameters.MinYearOfBirth &&
								o.DateOfBirth.Year <= ownerParameters.MaxYearOfBirth);

	SearchByName(ref owners, ownerParameters.Name);

	return PagedList<Owner>.ToPagedList(owners.OrderBy(on => on.Name),
		ownerParameters.PageNumber,
		ownerParameters.PageSize);
}

private void SearchByName(ref IQueryable<Owner> owners, string ownerName)
{
	if (!owners.Any() || string.IsNullOrWhiteSpace(ownerName))
		return;

	owners = owners.Where(o => o.Name.ToLower().Contains(ownerName.Trim().ToLower()));
}

Во-первых, нам нужно проверить, действительно ли параметр name отправлен, выполнив простую IsNullOrWhiteSpace проверку свойства Name. В противном случае нет смысла что-либо искать.

После этого мы используем предложение Where с обрезанной строкой Name (на всякий случай). Мы делаем это после фильтрации, чтобы искать по меньшему количеству результатов.

Все остальное остается прежним.

На этом наша реализация закончена. Как видите, это действительно не так сложно, поскольку это самый простой поиск, и у нас уже есть набор инфраструктуры.

Тестирование нашей реализации

Осталось только протестировать наше решение.

Во-первых, давайте вспомним, как выглядит наша таблица базы данных.

А теперь попробуем найти Анну: https://localhost:5001/api/owners?name=Anna Bosh

Конечно, результат получился.

Реализация поиска в ASP.NET Core

Если бы мы ввели ее полное имя, Анна Бош, результат был бы таким же. Это потому, что по умолчанию мы ищем весь термин с .Contains().

В качестве дополнительного примера давайте попробуем найти всех владельцев, которые содержат букву «о»:

https://localhost:5001/api/owner?name=o

Теперь мы должны получить три результата вместо одного:

[
  {
    "id": "261e1685-cf26-494c-b17c-3546e65f5620",
    "name": "Anna Bosh",
    "dateOfBirth": "1974-11-14T00:00:00",
    "address": "27 Colored Row"
  },
  {
    "id": "24fd81f8-d58a-4bcc-9f35-dc6cd5641906",
    "name": "John Keen",
    "dateOfBirth": "1980-12-05T00:00:00",
    "address": "61 Wellfield Road"
  },
  {
    "id": "66774006-2371-4d5b-8518-2177bcf3f73e",
    "name": "Nick Somion",
    "dateOfBirth": "1998-12-15T00:00:00",
    "address": "North sunny address 102"
  }
]

Теперь давайте объединим это с фильтрацией и разбиением по страницам:

https://localhost:5001/api/owner?name=o&minYearOfBirth=1974&maxYearOfBith=1985&pageSize=1&pageNumber=2

Вот и все, мы успешно внедрили и протестировали нашу функцию поиска.

Подведем итоги.

Заключение

В этой статье мы рассмотрели:

  • Что такое поиск и чем он отличается от фильтрации
  • Способ реализации поиска в веб-API ASP.NET Core
  • Как протестировать поиск в нашем API.
  • Сочетание поиска с нашими существующими решениями для разбиения по страницам и фильтрации