#35586: Queryset count and length do not match when annotating using
JSONB_PATH_QUERY
----------------------------+-----------------------------------------
     Reporter:  devin13cox  |                     Type:  Bug
       Status:  new         |                Component:  Uncategorized
      Version:  5.0         |                 Severity:  Normal
     Keywords:              |             Triage Stage:  Unreviewed
    Has patch:  0           |      Needs documentation:  0
  Needs tests:  0           |  Patch needs improvement:  0
Easy pickings:  0           |                    UI/UX:  0
----------------------------+-----------------------------------------
 There is a bug where the queryset count is inaccurate when doing an
 annotation with JSONB_PATH_QUERY. The count is fixed after the queryset is
 evaluated.

 I discovered this bug via `PageNumberPagination` from
 `rest_framework.pagination`, specifically in the function
 `paginate_queryset`. This Paginator takes in an object_list (in this case
 a Queryset) and determines the length by first checking if a .count()
 method is available, otherwise by doing len(object_list). In this case, it
 is determining length through Queryset.count().

 When the Queryset is annotated, and the length of the results increases,
 the count remains stale--only returning the original count.

 Currently I have a workaround by adding a DISTINCT, INTERSECTION, UNION,
 or DIFFERENCE to the queryset. Unfortunately, I now also need to have
 ordering which makes this tricky to continue working around.

 I believe this was a regression back in 4.2. This was previously asked
 about here: https://code.djangoproject.com/ticket/28477#comment:22, and
 this thread was referred: https://forum.djangoproject.com/t/django-4-2
 -behavior-change-when-using-arrayagg-on-unnested-arrayfield-postgresql-
 specific/21547. However, the `Unnest` approach does not work for our use
 case.

 Here are steps to reproduce:

 **models.py:
 **

 {{{
 class TestModel(models.Model):
     test_json = models.JSONField(default=dict, blank=True)
     id = models.IntegerField(primary_key=True)
 }}}

 **tests.py**

 {{{
 from .models import TestModel
 from django.contrib.postgres.fields import JSONField
 from django.db.models import Func, Value

     def test_bug(self):
         test_model = TestModel.objects.create(
             test_json={
                 "test_key" : [
                     {
                         "id" : 1,
                         "name" : "test1"
                     },
                     {
                         "id" : 2,
                         "name" : "test2"
                     }
                 ]
             },
             id=1
         )
         test_model.save()
         qs = TestModel.objects.annotate(
             table_element=Func(
                 "test_json",
                 Value("$.test_key[*]"),
                 function="jsonb_path_query",
                 output_field=JSONField(),
                 subquery=True
             )
         ).filter(pk=1)

         # qs.count() and len(qs) should be equal, but currently they are
 not. Running qs.count() after len(qs) is currently equal because the
 queryset was evaluated.

         self.assertEqual(qs.count(), len(qs))

 }}}

 Thank you for any guidance and/or support!
-- 
Ticket URL: <https://code.djangoproject.com/ticket/35586>
Django <https://code.djangoproject.com/>
The Web framework for perfectionists with deadlines.

-- 
You received this message because you are subscribed to the Google Groups 
"Django updates" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To view this discussion on the web visit 
https://groups.google.com/d/msgid/django-updates/0107019098f10db0-ca0a1ea1-2e79-46d1-a3f5-6cc3089068ba-000000%40eu-central-1.amazonses.com.

Reply via email to