ChilliCream/graphql-platform

Nested Filtering using basic Middleware

kwstnr opened this issue · 2 comments

Product

Hot Chocolate

Version

14.0.0.

Link to minimal reproduction

https://github.com/kwstnr/HotChocolateFilteringPrototype

Steps to reproduce

  1. Clone Repository
  2. Run docker-compose up
  3. start api
  4. run following queries in Nitro/BananaCakePop
query BooksIQueryable {
  booksIQueryable(where: { author: { name: { eq: "Author 1"}}}) {
    id
    title
    author {
      id
      name
    }
  }
}

query BooksInMemoryQueryable {
  booksInMemoryQueryable(where: { author: { name: { eq: "Author 1"}}}) {
    id
    title
    author {
      id
      name
    }
  }
}

query BooksInMemoryEnumerable {
  booksInMemoryEnumerable(where: { author: { name: { eq: "Author 1"}}}) {
    id
    title
    author {
      id
      name
    }
  }
}

What is expected?

As you can see, only the BooksIQueryable query returns a list of books for which the filtering applies. The other queries don't return any books at all, however, if you set breakpoints in the queries, you can see, that the query function returns a list of books but the filtering middleware filters them out.

What is actually happening?

According to this documentation I would expect the filtering middleware to be able to handle either IQueryable<T> and IEnumerable<T> types and it does when using basic filtering, but as soon as nested filtering is involved, only the query which returns a IQueryable from EFCore works. Even when you decide to go with in memory filtering, using asQueryable() or directly returning a IEnumerable<T> I would expect the filtering middleware to work.

Relevant log output

No response

Additional context

Maybe I don't understand the filtering middleware good enough but according to the documentation I would expect it to work this way.

The issue you have is because you just load books into memory with:

context.Books.ToListAsync()

If you want to use in-memory filtering also the nested object must be fully materialized into memory.

context.Books.Include(t => t.Author).ToListAsync()

Image

@michaelstaib so this means that filtering is always applied before the resolvers of the requested node?
It makes perfect sense to not resolve unnecessary properties of objects which are filtered out!

However, how would you suggest filtering for properties which are resolved after the filtering would be applied? If I follow your courses on dometrain, you suggest to use DataLoaders to efficiently resolve dependent, nested objects, like an author of a book in this case. This makes total sense in terms of querying your database efficiently, but what happens if you want to filter a list of books by their author, as it is depicted in my reproduction repository?

If you look at the latest commit, you can see that I added a DataLoader to resolve authors of books. Now if you want to apply the aforementioned filtering, you have to include the authors in the books query and then resolve them again for each book using the DataLoaders.

Is it expected that the authors are always materialized in the books query and are manually added to the DataLoader cache so that they are not requested twice from the db, or is there a way to apply filtering after all the requested properties are resolved?