spring-projects / spring-graphql

Spring Integration for GraphQL
https://spring.io/projects/spring-graphql
Apache License 2.0
1.54k stars 306 forks source link

Query by Example doesn't have a binding customization mechanism #1083

Open danvega opened 2 weeks ago

danvega commented 2 weeks ago

I have a GraphQLRepository that extends QueryByExampleExectutor

@GraphQlRepository
public interface BookRepository extends JpaRepository<Book, Long>, QueryByExampleExecutor<Book> {}

If I try to do a partial match on the book title no results come back. If I enter the full book title the results come back as expected.

// Find books by partial title match
query {
  books(book: { title: "Spring Boot" }) {
    id
    title
    author
    publishedYear
  }
}

I realize I can write a data fetcher for this but this:

@Controller
public class BookController {

    private final BookRepository repository;

    public BookController(BookRepository repository) {
        this.repository = repository;
    }

    @QueryMapping
    public List<Book> booksContaining(@Argument(name = "book") Book filterBook) {
        ExampleMatcher matcher = ExampleMatcher.matching()
                .withStringMatcher(ExampleMatcher.StringMatcher.CONTAINING)
                .withIgnoreCase()
                .withIgnoreNullValues();

        Example<Book> example = Example.of(filterBook, matcher);
        return repository.findAll(example);
    }
}

But I would like a way to customize this so I can use a StringMatcher.CONTAINING and match on part of the book title.

mp911de commented 2 weeks ago

Customizing ExampleMatcher is a reasonable request. Introducing a strategy interface that creates ExampleMatcher would be my initial approach. I'm not quite sure about the context that we should provide to the method. I think something along the lines for an initial cut:

interface ExampleMatcherProvider<T> {

  ExampleMatcher getExampleMatcher(T probe, DataFetchingEnvironment environment);
}

Such a signature would provide the most meaningful context.

For auto-registration, I think a programming model to let repositories implement the interface through a default method would be neat:

@GraphQlRepository
public interface BookRepository extends JpaRepository<Book, Long>, 
                                        QueryByExampleExecutor<Book>, ExampleMatcherProvider<Book> {

  default ExampleMatcher getExampleMatcher(Book probe, DataFetchingEnvironment environment) {
    return …;
  }
}

Alternatively, defining a @Bean ExampleMatcherProvider<Book> and using ResolvableType to correlate the strategy object could work too.