ChilliCream / graphql-platform

Welcome to the home of the Hot Chocolate GraphQL server for .NET, the Strawberry Shake GraphQL client for .NET and Banana Cake Pop the awesome Monaco based GraphQL IDE.

Home Page:https://chillicream.com

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

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 .

  1. 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#"].

  2. 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?

ezgif-4-07fc270323

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):

Screenshot 2024-04-05 at 14 42 02

@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