Как сделать расширенный поиск с помощью Spring Data REST?

Моя задача - сделать расширенный поиск с помощью Spring Data REST. Как его реализовать?

Мне удалось создать способ для простого поиска, например:

public interface ExampleRepository extends CrudRepository<Example, UUID>{

    @RestResource(path="searchByName", rel="searchByName")
    Example findByExampleName(@Param("example") String exampleName);

}

Этот пример отлично работает, если мне нужно просто перейти к URL-адресу:

.../api/examples/search/searchByName?example=myExample

Но что мне делать, если есть несколько полей для поиска?

Например, если мой класс класса имеет 5 полей, какую реализацию следует выполнить для расширенного поиска со всеми возможными файлами?

Рассмотрим это:

.../api/examples/search/searchByName?filed1=value1&field2=value2&field4=value4

и этот:

.../api/examples/search/searchByName?filed1=value1&field3=value3

Что мне нужно сделать, чтобы реализовать этот поиск соответствующим образом?

Спасибо.

Ответ 1

Реализация методов запросов широко документирована в справочной документации Spring и тоннах технических блогов, хотя многие из них устарели.

Поскольку ваш вопрос, вероятно, "Как я могу выполнить многопараметрический поиск по любой комбинации полей, не объявляя при этом огромное количество методов findBy *?", Ответ - Querydsl, который поддерживается Spring.

Ответ 2

Spring Data Rest также интегрировал QueryDSL с веб-поддержкой, которую вы можете использовать для расширенного поиска. Вам нужно изменить свой репозиторий для реализации QueryDslPredicateExecutor и все будет работать из коробки.

Вот пример из статьи в блоге об этой функции:

$ http :8080/api/stores?address.city=York    
{
    "_embedded": {
        "stores": [
            {
                "_links": {
                    …
                }, 
                "address": {
                    "city": "New York", 
                    "location": { "x": -73.938421, "y": 40.851 }, 
                    "street": "803 W 181st St", 
                    "zip": "10033-4516"
                }, 
                "name": "Washington Hgts/181st St"
            }, 
            {
                "_links": {
                    …
                }, 
                "address": {
                    "city": "New York", 
                    "location": { "x": -73.939822, "y": 40.84135 }, 
                    "street": "4001 Broadway", 
                    "zip": "10032-1508"
                }, 
                "name": "168th & Broadway"
            }, 
            …
        ]
    }, 
    "_links": {
        …
    }, 
    "page": {
        "number": 0, 
        "size": 20, 
        "totalElements": 209, 
        "totalPages": 11
    }
}

Ответ 3

Я нашел рабочее решение для этой задачи.

@RepositoryRestResource(excerptProjection=MyProjection.class)
public interface MyRepository extends Repository<Entity, UUID> {

    @Query("select e from Entity e "
          + "where (:field1='' or e.field1=:field1) "
          + "and (:field2='' or e.field2=:field2) "
          // ...
          + "and (:fieldN='' or e.fieldN=:fieldN)"
    Page<Entity> advancedSearch(@Param("field1") String field1,
                               @Param("field2") String field2,
                               @Param("fieldN") String fieldN,
                               Pageable page);

}

С этим решением, используя этот базовый URL:

http://localhost:8080/api/examples/search/advancedSearch

Мы можем сделать расширенный поиск со всеми полями, которые нам нужны.

Некоторые примеры:

http://localhost:8080/api/examples/search/advancedSearch?field1=example
    // filters only for the field1 valorized to "example"

http://localhost:8080/api/examples/search/advancedSearch?field1=name&field2=surname
    // filters for all records with field1 valorized to "name" and with field2 valorized to "surname"

Ответ 5

Мне удалось реализовать это с помощью Query by Example.

Допустим, у вас есть следующие модели:

@Entity
public class Company {

  @Id
  @GeneratedValue
  Long id;

  String name;
  String address;

  @ManyToOne
  Department department;

}


@Entity
public class Department {

  @Id
  @GeneratedValue
  Long id;

  String name;

}

И хранилище:

@RepositoryRestResource
public interface CompanyRepository extends JpaRepository<Company, Long> {
}

(Обратите внимание, что JpaRepository реализует QueryByExampleExecutor).

Теперь вы реализуете собственный контроллер:

@RepositoryRestController
@RequiredArgsConstructor
public class CompanyCustomController {

  private final CompanyRepository repository;

  @GetMapping("/companies/filter")
  public ResponseEntity<?> filter(
      Company company,
      Pageable page,
      PagedResourcesAssembler assembler,
      PersistentEntityResourceAssembler entityAssembler
  ){

    ExampleMatcher matcher = ExampleMatcher.matching()
        .withIgnoreCase()
        .withStringMatcher(ExampleMatcher.StringMatcher.CONTAINING);

    Example example = Example.of(company, matcher);

    Page<?> result = this.repository.findAll(example, page);

    return ResponseEntity.ok(assembler.toResource(result, entityAssembler));

  }
}

И тогда вы можете делать запросы, как:

localhost:8080/companies/filter?name=google&address=NY

Вы даже можете запросить вложенные объекты, такие как:

localhost:8080/companies/filter?name=google&department.name=finances

Для краткости я опустил некоторые детали, но создал рабочий пример на Github.