UseFiltering with array using 'all' operator
hengtanoms opened this issue · comments
Product
Hot Chocolate
Version
13.9.0
Link to minimal reproduction
https://drive.google.com/file/d/1nz0IdBP8f4nHX0XEwuN_99crs6b5Sbq-/view?usp=sharing
Steps to reproduce
Hi all, I have put together a minimal sample code on how to reproduce the filtering issue that i'm facing when using the 'all' operator on array property.
Here is the nuget package version i'm using in my .csproj:
<PackageReference Include="HotChocolate" Version="13.9.0" />
<PackageReference Include="HotChocolate.AspNetCore" Version="13.9.0" />
<PackageReference Include="HotChocolate.Data" Version="13.9.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="8.0.3" />
and here is my Program.cs with .Net 8.0
using Microsoft.EntityFrameworkCore;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddDbContext<DatabaseContext>();
builder.Services
.AddGraphQLServer()
.AddQueryType<Query>()
.AddFiltering();
var app = builder.Build();
using (var scope = app.Services.CreateScope())
{
var context = scope.ServiceProvider.GetService<DatabaseContext>();
context.Database.EnsureCreated();
}
app.MapGraphQL();
app.Run();
public class DatabaseContext : DbContext
{
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseInMemoryDatabase(databaseName: "AuthorDb");
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Author>()
.HasMany(e => e.Books)
.WithOne(e => e.Author)
.HasForeignKey(e => e.AuthorId)
.IsRequired();
modelBuilder.Entity<Author>().HasData(
new Author
{
Id = 1,
FirstName ="Joydip",
LastName ="Kanjilal"
},
new Author
{
Id = 2,
FirstName ="Yashavanth",
LastName ="Kanetkar"
},
new Author
{
Id = 3,
FirstName ="Victor",
LastName ="Little"
},
new Author
{
Id = 4,
FirstName ="NoBook",
LastName ="Author"
}
);
modelBuilder.Entity<Book>().HasData(
new Book { Id= 1, Title = "Mastering C# 8.0", AuthorId = 1 },
new Book { Id= 2, Title = "Entity Framework Tutorial", AuthorId = 1 },
new Book { Id= 3, Title = "ASP.NET 4.0 Programming", AuthorId = 1 },
new Book { Id= 4, Title = "Let us C", AuthorId = 2 },
new Book { Id= 5, Title = "Let us C++", AuthorId = 2 },
new Book { Id= 6, Title = "Let us C#", AuthorId = 2 },
new Book { Id= 7, Title = "Let us C", AuthorId = 3 },
new Book { Id= 8, Title = "Let us C++", AuthorId = 3 });
}
public DbSet<Author> Authors { get; set; }
public DbSet<Book> Books { get; set; }
}
public class Book
{
public int Id { get; set; }
public string Title { get; set; }
public Author Author { get; set; }
public int AuthorId { get; set; }
}
public class Author
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public List<Book> Books { get; set; }
}
public class Query
{
[UseFiltering]
public IQueryable<Author> GetAuthors([Service]DatabaseContext databaseContext)
{
return databaseContext.Authors.Include(x => x.Books);
}
}
What is expected?
I'm facing 2 issues when running the 'Authors' endpoint .
-
When using 'all' operator I would assume that it shouldn't return author that doesn't have any books. Since 'all' indicate that all item in the array it should match the specific condition. In this case the title should be "in": ["Let us C", "Let us C++", "Let us C#"].
-
I'm struggling to trying to find a way to use the filtering operator to only return author that have exactly all 3 books ["Let us C", "Let us C++", "Let us C#"], not just any one of the 3 books. It looks like the hotchocolate library doesn't support to do that.
query ($where: AuthorFilterInput) {
authors (where: $where) {
id,
firstName,
lastName,
books {
id,
title
}
}
}
input variable :
{
"where": {
"books" : {
"all": {
"title" : {
"in": ["Let us C", "Let us C++", "Let us C#"]
}
}
}
}
}
What is actually happening?
Relevant log output
No response
Additional context
No response
You can get your desired result like this:
query yy {
test(where: { and:
[
{ books: { some: { title: {eq: "Let us C"} } } },
{ books: { some: { title: {eq: "Let us C++"} } } },
{ books: { some: { title: {eq: "Let us C#"} } } }
] }) {
firstName
lastName
books {
title
}
}
}
It's normal that a all() query will return true on empty sets. See wikipedia: https://en.wikipedia.org/wiki/Vacuous_truth#In_computer_programming
You can get your desired result like this:
query yy { test(where: { and: [ { books: { some: { title: {eq: "Let us C"} } } }, { books: { some: { title: {eq: "Let us C++"} } } }, { books: { some: { title: {eq: "Let us C#"} } } } ] }) { firstName lastName books { title } } }
It's normal that a all() query will return true on empty sets. See wikipedia: https://en.wikipedia.org/wiki/Vacuous_truth#In_computer_programming
@jfheins thanks for your suggestion. I just did a test using the filtering you suggested, it indeed return the author with the exact matching book title. But if you remove the condition : { books: { some: { title: {eq: "Let us C#"} } } }
to just match 2 books "Let us C" and "Let us C++":
query yy {
test(where: { and:
[
{ books: { some: { title: {eq: "Let us C"} } } },
{ books: { some: { title: {eq: "Let us C++"} } } },
] }) {
firstName
lastName
books {
title
}
}
}
It return the author "Yashavanth, Kanetkar" that have 3 book titles "Let us C", "Let us C++" and "Let us C#" which is not the exact match that i'm looking for (the expected result should only return author "Victor, Little" because that is the exact match):
@jfheins I think I found the solution, to have the exact match I just need to add an addition "none" operator as follow:
{
"where": {
"and": [
{
"books": {
"some": {
"title": {
"eq": "Let us C"
}
}
}
},
{
"books": {
"some": {
"title": {
"eq": "Let us C++"
}
}
}
},
{
"books": {
"some": {
"title": {
"eq": "Let us C#"
}
}
}
},
{
"books": {
"none": {
"title": {
"nin": [
"Let us C",
"Let us C++",
"Let us C#"
]
}
}
}
}
]
}
}
many thanks for you help @jfheins