graphql-platform icon indicating copy to clipboard operation
graphql-platform copied to clipboard

MongoDB integration not filtering child collections

Open jkears opened this issue 2 years ago • 13 comments

Is there an existing issue for this?

  • [X] I have searched the existing issues

Describe the bug

I have a simple document that has a collection (modelReferences), and within it that I would like to filter the modelReferences by their Id property, however filtering at these child collections does not appear to work.

Note: When the exact same model is configured with the EFCore integration it works as expected, but not using MongoDb integratation.

Here is a simple example, of an unfiltered query on modelReferences child collection:

{
   gatewayServicesGatewayDefinitions {
     items{
        name name
        modelReferences {
           id
        }
     }
   }
}

Resultant data :

{
  "data": {
    "gatewayServicesGatewayDefinitions": {
      "items": [
        {
          "name": "Test",
          "modelReferences": [
            {
              "id": "f00554bc-fcef-417c-baee-9234534cb6bd"
            }
          ]
        }
      ]
    }
  }
}

And with the filter in which the id does not exist:"

{
   gatewayServicesGatewayDefinitions {
     items{
        name name
        modelReferences (where: {id:{eq:"f00554bc-fcef-417c-baee-9234534cb6be"}}) {
           id
        }
     }
   }
}

Should return no data modelReferences collection items, as that ID does not exist, however it returns everything as if is unfiltered:

{
  "data": {
    "gatewayServicesGatewayDefinitions": {
      "items": [
        {
          "name": "Test",
          "modelReferences": [
            {
              "id": "f00554bc-fcef-417c-baee-9234534cb6bd"
            }
          ]
        }
      ]
    }
  }
}

Here is the GraphQL configuration:

builder.Services.AddGraphQLServer()
.AddAuthorization()
.AddType<ErrorResponse>()
.AddType<AggregateEvent>()
.AddType<GatewayServicesGatewayDefinitionSM.GatewayDefinition>()
.AddQueryType()
.AddTypeExtension<GatewayDefinitionGraphQLQuery>()
.AddMutationType()
.AddTypeExtension<GatewayDefinitionGraphQLMutation>()
.AddMongoDbFiltering()
.AddMongoDbSorting()
.AddMongoDbProjections()  
.AddMongoDbPagingProviders(); 

Here is the Query Definition:

    public partial class GatewayDefinitionGraphQLQuery
    {
         
        [UseFirstOrDefault]
        public IExecutable<GatewayServicesGatewayDefinitionSM.GatewayDefinition> GetGatewayServicesGatewayDefinition([Service] IMongoCollection<GatewayServicesGatewayDefinitionSM.GatewayDefinition> collection, Guid id)
        {
            return collection.Find(x => x.Id == id).AsExecutable();
        }
        [UseOffsetPaging(IncludeTotalCount = false, DefaultPageSize = 10)]
        [UseProjection]
        [UseSorting]
        [UseFiltering]
        public IExecutable<GatewayServicesGatewayDefinitionSM.GatewayDefinition> GetGatewayServicesGatewayDefinitions([Service] IMongoCollection<GatewayServicesGatewayDefinitionSM.GatewayDefinition> collection)
        {
            return collection.AsExecutable();
        }
    }

Here is the model definition:

 [HotChocolate.AspNetCore.Authorization.Authorize]
   [GraphQLName("gatewayServices_gatewayDefinition")]
   [Serializable]
   [BsonIgnoreExtraElements]
   public class GatewayDefinition
   {
       
       public string Name { get; set; } = string.Empty;
       
       [HotChocolate.Data.UseFiltering]
       [HotChocolate.Data.UseSorting]
       public List<GatewayServicesGatewayDefinitionSM.ModelReference> ModelReferences { get; set; } = new();
       
	   [BsonId(IdGenerator = typeof(GuidGenerator)), BsonRepresentation(BsonType.String)]
       [DataMember(Name = "id")]
       public System.Guid? Id { get; set; } = Guid.Empty;
   }

Here is the Node Resolver:

public class GatewayDefinitionNodeResolver
    {
        public Task<GatewayServicesGatewayDefinitionSM.GatewayDefinition> ResolveAsync([Service] IMongoCollection<GatewayServicesGatewayDefinitionSM.GatewayDefinition> collection, Guid id)
        {
            return collection.Find(x => x.Id == id).FirstOrDefaultAsync();
        }
    }

Here is the related child type, "ModelRerference" that I am trying to filter:

 [HotChocolate.AspNetCore.Authorization.Authorize]
   
    [GraphQLName("gatewayServices_modelReference")]
    [Serializable]
    [BsonIgnoreExtraElements]
    public class ModelReference
    {
      
        public System.Guid DomainModelId { get; set; } = Guid.Empty;
       
        public int Depth { get; set; } = 2;

        [BsonId(IdGenerator = typeof(GuidGenerator)), BsonRepresentation(BsonType.String)]
        public System.Guid? Id { get; set; } = Guid.Empty;
    }

Steps to reproduce

  1. Apply same code as above

Relevant log output

No response

Additional Context?

No response

Product

Hot Chocolate

Version

12.8.2

jkears avatar Jul 10 '22 17:07 jkears

I included the definition of the ModelReference (child collection).

jkears avatar Jul 12 '22 17:07 jkears

@PascalSenn , I was told you are the maintainer of the MongoDB integration library. Please see the above issue and let me know if you require further information.

Is filtering of children not possible in the MongoDB integration?

If yes and you can't provide me with an answer, can you at least provide me with hints as to where to look within the MongoDB integration source code so that I can try to figure this out. This is a major issue for us at the present time.

Thank you in advance, John

jkears avatar Jul 12 '22 18:07 jkears

@jkears at the moment this is not possible. How would the mongo query look like if you would issue one? Also, do you mind filtering in memory? this would be possbile.

PascalSenn avatar Jul 12 '22 18:07 PascalSenn

@PascalSenn , thanks for your prompt reply. I thought we were doing something wrong.

Yes, filtering in memory would be fine for most of our scenarios, how would I integrate this via a GraphQL query?

jkears avatar Jul 12 '22 18:07 jkears

@PascalSenn, this seems to be the way that you'd filter it.

jkears avatar Jul 12 '22 18:07 jkears

@jkears okok. so opting for the aggregation pipeline.. i mean, could work. Has other issues of course ;) like scalability of queries. But depending on the document you receive this might not even be necessary.

so try this

services.AddMongoDbFiltering(); //ordermatters
services.AddFiltering("queryable")

and then do

[HotChocolate.Data.UseFiltering("queryable")]
[HotChocolate.Data.UseSorting]
public List<GatewayServicesGatewayDefinitionSM.ModelReference> ModelReferences { get; set; } = new();

PascalSenn avatar Jul 12 '22 18:07 PascalSenn

@PascalSenn

I swear I was able to add a string previously, but now I get this error ..

image

error CS1503: Argument 1: cannot convert from 'string' to 'System.Type?'

Here are my package references...

image

jkears avatar Jul 12 '22 19:07 jkears

mybad [UseFiltering(Scope = "queryable")]

PascalSenn avatar Jul 12 '22 19:07 PascalSenn

@PascalSenn Yes I had also figure that I needed to define scope.

I also have to change this to

.AddMongoDbFiltering()
.AddFiltering("queryable") 

to this ..'

.AddMongoDbFiltering("queryable")
.AddFiltering()

as when it was complaining that it was missing AddFiltering()

However, now it is complaining ...

1. The name gatewayServices_modelReferenceFilterInput was already registered by another type. (HotChocolate.Data.Filters.FilterInputType<NextWare.CoreServices.SharedModels.GatewayServices.GatewayDefinitionAggregate.Models.ModelReference>)

jkears avatar Jul 12 '22 21:07 jkears

@PascalSenn I am still not getting this to work...

As mentioned, when I attempted to configure filtering this way....

.AddMongoDbFiltering()
.AddFiltering("queryable") 

I get this exception at startup ...

[nextwarecoreservicesgatewayservices_825fad40-c]: 1. No default filter convention found. Call `AddFiltering()` on the schema builder.
[nextwarecoreservicesgatewayservices_825fad40-c]: (HotChocolate.Types.ObjectType<NextWare.CoreServices.SharedModels.GatewayServices.GatewayDefinitionAggregate.Models.GatewayDefinition>)

and if I try it this way ...

.AddMongoDbFiltering("queryable")
.AddFiltering()

I get this exception at startup...

1. The name gatewayServices_modelReferenceFilterInput was already registered by another type. (HotChocolate.Data.Filters.FilterInputType<NextWare.CoreServices.SharedModels.GatewayServices.GatewayDefinitionAggregate.Models.ModelReference>)

We are desperately trying to get this working, can you please advise what it is I am doing incorrectly? Many thanks!

jkears avatar Jul 14 '22 17:07 jkears

@PascalSenn , can you please help me resolve this issue?

jkears avatar Jul 19 '22 18:07 jkears

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

stale[bot] avatar Nov 16 '22 18:11 stale[bot]

Exact same issue as @jkears . Is there a fix? I couldn't get it working even as much as https://github.com/ChilliCream/graphql-platform/issues/5622 describes.
Following the callout: https://chilly-relay-docs.netlify.app/docs/hotchocolate/v11/integrations/mongodb/#filtering

My ServiceCollection.cs has this:

            .AddMongoDbFiltering("queryable")
            .AddMongoDbSorting()
            .AddMongoDbProjections()
            .UseDefaultPipeline()
            .AddFiltering()
            .AddSorting()

My query has this:

        [UsePaging]
	[UseFiltering(Scope ="queryable")]
	[UseSorting]
	[Authorize(Policy = Policy.Name.LicensedUsersOnly)]
	[GraphQLDescription("Get all sites")]
	public async Task<IEnumerable<Site?>> GetSitesAsync()

Additionally I injected the IResolverContext to the query and attempted to filter like this: var result = sites.Filter(context) where context is the IResolverContext. It throws an exception here: Filtering was not found. Register filtering with [UseFiltering] or descriptor.UseFiltering() . If I remove the Scope from [UseFiltering], there is no error, but obviously the filter is not attempted.

parihatch avatar Feb 15 '23 16:02 parihatch