Sheesh, solved with subqueries

This commit is contained in:
shamoon 2024-12-12 23:15:28 -08:00
parent 590c638ba0
commit a8eb460c3a

View File

@ -9,12 +9,12 @@ from django.contrib.contenttypes.models import ContentType
from django.db.models import Case from django.db.models import Case
from django.db.models import CharField from django.db.models import CharField
from django.db.models import Count from django.db.models import Count
from django.db.models import DateTimeField
from django.db.models import Exists from django.db.models import Exists
from django.db.models import FloatField
from django.db.models import IntegerField from django.db.models import IntegerField
from django.db.models import OuterRef from django.db.models import OuterRef
from django.db.models import Q from django.db.models import Q
from django.db.models import Subquery
from django.db.models import Sum
from django.db.models import Value from django.db.models import Value
from django.db.models import When from django.db.models import When
from django.db.models.functions import Cast from django.db.models.functions import Cast
@ -785,133 +785,101 @@ class DocumentsOrderingFilter(OrderingFilter):
annotation = None annotation = None
match field.data_type: match field.data_type:
case CustomField.FieldDataType.STRING: case CustomField.FieldDataType.STRING:
annotation = Case( annotation = Subquery(
When( CustomFieldInstance.objects.filter(
custom_fields__field_id=custom_field_id, document_id=OuterRef("id"),
then=Cast( field_id=custom_field_id,
"custom_fields__value_text", ).values("value_text")[:1],
output_field=CharField(),
),
),
default=Value(""),
output_field=CharField(),
) )
case CustomField.FieldDataType.INT: case CustomField.FieldDataType.INT:
annotation = Case( annotation = Subquery(
When( CustomFieldInstance.objects.filter(
custom_fields__field_id=custom_field_id, document_id=OuterRef("id"),
then=Cast( field_id=custom_field_id,
"custom_fields__value_int", ).values("value_int")[:1],
output_field=IntegerField(),
),
),
default=Value(0),
output_field=IntegerField(),
) )
case CustomField.FieldDataType.FLOAT: case CustomField.FieldDataType.FLOAT:
annotation = Case( annotation = Subquery(
When( CustomFieldInstance.objects.filter(
custom_fields__field_id=custom_field_id, document_id=OuterRef("id"),
then=Cast( field_id=custom_field_id,
"custom_fields__value_float", ).values("value_float")[:1],
output_field=FloatField(),
),
),
default=Value(0),
output_field=FloatField(),
) )
case CustomField.FieldDataType.DATE: case CustomField.FieldDataType.DATE:
annotation = Case( annotation = Subquery(
When( CustomFieldInstance.objects.filter(
custom_fields__field_id=custom_field_id, document_id=OuterRef("id"),
then=Cast( field_id=custom_field_id,
"custom_fields__value_date", ).values("value_date")[:1],
output_field=DateTimeField(),
),
),
default=Value("1900-01-01T00:00:00Z"),
output_field=DateTimeField(),
) )
case CustomField.FieldDataType.MONETARY: case CustomField.FieldDataType.MONETARY:
annotation = Case( annotation = Subquery(
When( CustomFieldInstance.objects.filter(
custom_fields__field_id=custom_field_id, document_id=OuterRef("id"),
then=Cast( field_id=custom_field_id,
"custom_fields__value_monetary_amount", ).values("value_monetary_amount")[:1],
output_field=FloatField(),
),
),
default=Value(0),
output_field=FloatField(),
) )
case CustomField.FieldDataType.SELECT: case CustomField.FieldDataType.SELECT:
select_options = field.extra_data.get("select_options", []) # Select options are a little more complicated since the value is the id of the option, not
whens = [] # the label. Additionally, to support sqlite we can't use StringAgg, so we need to create a
# Create a case for each select option # case statement for each option, setting the value to the index of the option in a list
for option in select_options: # sorted by label, and then summing the results to give a single value for the annotation
whens.append(
When( select_options = sorted(
custom_fields__field_id=custom_field_id, field.extra_data.get("select_options", []),
custom_fields__value_select=option.get("id"), key=lambda x: x.get("label"),
then=Value(option["label"], output_field=CharField()), )
), whens = [
When(
custom_fields__field_id=custom_field_id,
custom_fields__value_select=option.get("id"),
then=Value(idx, output_field=IntegerField()),
) )
annotation = Case( for idx, option in enumerate(select_options)
*whens, ]
default=Value(""), whens.append(
output_field=CharField(), When(
custom_fields__field_id=custom_field_id,
custom_fields__value_select__isnull=True,
then=Value(
len(select_options),
output_field=IntegerField(),
),
),
)
annotation = Sum(
Case(
*whens,
default=Value(0),
output_field=IntegerField(),
),
) )
case CustomField.FieldDataType.DOCUMENTLINK: case CustomField.FieldDataType.DOCUMENTLINK:
annotation = Case( annotation = Subquery(
When( CustomFieldInstance.objects.filter(
custom_fields__field_id=custom_field_id, document_id=OuterRef("id"),
then=Cast( field_id=custom_field_id,
"custom_fields__value_document_ids", ).values("value_document_ids")[:1],
output_field=CharField(),
),
),
default=Value(""),
output_field=CharField(),
) )
case CustomField.FieldDataType.URL: case CustomField.FieldDataType.URL:
annotation = Case( annotation = Subquery(
When( CustomFieldInstance.objects.filter(
custom_fields__field_id=custom_field_id, document_id=OuterRef("id"),
then=Cast( field_id=custom_field_id,
"custom_fields__value_url", ).values("value_url")[:1],
output_field=CharField(),
),
),
default=Value(""),
output_field=CharField(),
) )
case CustomField.FieldDataType.BOOL: case CustomField.FieldDataType.BOOL:
annotation = Case( annotation = Subquery(
When( CustomFieldInstance.objects.filter(
custom_fields__field_id=custom_field_id, document_id=OuterRef("id"),
custom_fields__value_bool=True, field_id=custom_field_id,
then=Value( ).values("value_bool")[:1],
1,
output_field=IntegerField(),
),
),
When(
custom_fields__field_id=custom_field_id,
custom_fields__value_bool=False,
then=Value(
0,
output_field=IntegerField(),
),
),
default=Value(0),
output_field=IntegerField(),
) )
if not annotation: if not annotation:
raise ValueError("Invalid custom field data type") raise ValueError("Invalid custom field data type")
ids_sorted_by_cf = ( queryset = (
queryset.annotate( queryset.annotate(
# We need to annotate the queryset with the custom field value # We need to annotate the queryset with the custom field value
custom_field_value=annotation, custom_field_value=annotation,
@ -930,16 +898,7 @@ class DocumentsOrderingFilter(OrderingFilter):
"custom_field_value", "custom_field_value",
), ),
) )
.values_list("id", flat=True) .distinct()
) )
# We need to preserve the order of the ids sorted by custom field, see https://docs.djangoproject.com/en/dev/ref/models/querysets/#distinct
preserved = Case(
*[
When(id=id, then=position)
for position, id in enumerate(ids_sorted_by_cf)
],
)
queryset = queryset.filter(id__in=ids_sorted_by_cf).order_by(preserved)
return super().filter_queryset(request, queryset, view) return super().filter_queryset(request, queryset, view)