1

I'm wondering what the cleanest way to integrate a Graphene DjangoFilterConnectionField custom input argument into the default queryset is?

I'm iterating on the following Graphene Query class whose custom input attribute attempts to integrate the PgVector CosineDistance function:

class Query(graphene.ObjectType):

    all_the_small_things = DjangoFilterConnectionField(
        VectorNode,
        embedding=graphene.List(graphene.Float),
    )

    def resolve_all_the_small_things(
        root,
        info,
        **kwargs,
    ):
        from pgvector.django import CosineDistance

        annotations = {}

        if kwargs.get("embedding"):
            annotations["embedding"] = CosineDistance(
                "embedding", kwargs.pop("embedding")
            )

        if annotations:
            # How best to integrate this into the existing behavior?
            # VectorNode.objects.annotate(**annotations)
            # now use filter functionality provided by
            # the default resolver.
  

I would like to augment the default query behavior (i.e. filtering) if the custom input is provided and use the default behavior as-is if it isn't. Do I need to implement my own queryset and/or filterset? Is there a way to reflect/modify the default behavior using the function arguments?

Note: I think the long term answer to this is to add support for PG Vector columns to Graphene and/or creating a django-filters filter subclass but I really need to understand how this all comes together before seriously thinking about how to approach any of that upstream.

pdoherty926
  • 9,895
  • 4
  • 37
  • 68

1 Answers1

-1

To integrate a custom input argument into the default queryset of a Graphene DjangoFilterConnectionField:

  1. Create a Custom FilterSet

  2. Override the get_queryset method:

    from django_filters import FilterSet, filters
    from graphene_django.filter import DjangoFilterConnectionField
    
    class VectorFilterSet(FilterSet):
        cosine_distance = filters.Float()  # Define your custom input argument here
    
        class Meta:
            model = VectorNode
            fields = []  # Specify other fields for filtering if needed
    
         class Query(graphene.ObjectType):
    
        all_the_small_things = DjangoFilterConnectionField(
            VectorNode,
            filterset_class=VectorFilterSet,  # Use your custom FilterSet
            cosine_distance=graphene.Float(),  # Add the custom input argument to the Query field
        )
    
        def resolve_all_the_small_things(
            root,
            info,
            **kwargs,
        ):
            from pgvector.django import CosineDistance
    
            queryset = VectorNode.objects.all()
    
            cosine_distance = kwargs.pop("cosine_distance", None)
            if cosine_distance is not None:
                queryset = queryset.annotate(
                    cosine_distance=CosineDistance("embedding", cosine_distance)
                )
    
            return queryset
    

If your requirements become more complex or you need more fine-grained control over the filtering behavior, you may consider implementing a custom resolver or creating a custom connection Field that extends DjangoFilterConnectionField.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Hasan Raza
  • 424
  • 3
  • 9