mirror of
https://github.com/paperless-ngx/paperless-ngx.git
synced 2025-05-01 11:19:32 -05:00
Sheesh, solved with subqueries
This commit is contained in:
parent
590c638ba0
commit
a8eb460c3a
@ -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(
|
|
||||||
|
select_options = sorted(
|
||||||
|
field.extra_data.get("select_options", []),
|
||||||
|
key=lambda x: x.get("label"),
|
||||||
|
)
|
||||||
|
whens = [
|
||||||
When(
|
When(
|
||||||
custom_fields__field_id=custom_field_id,
|
custom_fields__field_id=custom_field_id,
|
||||||
custom_fields__value_select=option.get("id"),
|
custom_fields__value_select=option.get("id"),
|
||||||
then=Value(option["label"], output_field=CharField()),
|
then=Value(idx, output_field=IntegerField()),
|
||||||
|
)
|
||||||
|
for idx, option in enumerate(select_options)
|
||||||
|
]
|
||||||
|
whens.append(
|
||||||
|
When(
|
||||||
|
custom_fields__field_id=custom_field_id,
|
||||||
|
custom_fields__value_select__isnull=True,
|
||||||
|
then=Value(
|
||||||
|
len(select_options),
|
||||||
|
output_field=IntegerField(),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
annotation = Case(
|
annotation = Sum(
|
||||||
|
Case(
|
||||||
*whens,
|
*whens,
|
||||||
default=Value(""),
|
|
||||||
output_field=CharField(),
|
|
||||||
)
|
|
||||||
case CustomField.FieldDataType.DOCUMENTLINK:
|
|
||||||
annotation = Case(
|
|
||||||
When(
|
|
||||||
custom_fields__field_id=custom_field_id,
|
|
||||||
then=Cast(
|
|
||||||
"custom_fields__value_document_ids",
|
|
||||||
output_field=CharField(),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
default=Value(""),
|
|
||||||
output_field=CharField(),
|
|
||||||
)
|
|
||||||
case CustomField.FieldDataType.URL:
|
|
||||||
annotation = Case(
|
|
||||||
When(
|
|
||||||
custom_fields__field_id=custom_field_id,
|
|
||||||
then=Cast(
|
|
||||||
"custom_fields__value_url",
|
|
||||||
output_field=CharField(),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
default=Value(""),
|
|
||||||
output_field=CharField(),
|
|
||||||
)
|
|
||||||
|
|
||||||
case CustomField.FieldDataType.BOOL:
|
|
||||||
annotation = Case(
|
|
||||||
When(
|
|
||||||
custom_fields__field_id=custom_field_id,
|
|
||||||
custom_fields__value_bool=True,
|
|
||||||
then=Value(
|
|
||||||
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),
|
default=Value(0),
|
||||||
output_field=IntegerField(),
|
output_field=IntegerField(),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
case CustomField.FieldDataType.DOCUMENTLINK:
|
||||||
|
annotation = Subquery(
|
||||||
|
CustomFieldInstance.objects.filter(
|
||||||
|
document_id=OuterRef("id"),
|
||||||
|
field_id=custom_field_id,
|
||||||
|
).values("value_document_ids")[:1],
|
||||||
|
)
|
||||||
|
case CustomField.FieldDataType.URL:
|
||||||
|
annotation = Subquery(
|
||||||
|
CustomFieldInstance.objects.filter(
|
||||||
|
document_id=OuterRef("id"),
|
||||||
|
field_id=custom_field_id,
|
||||||
|
).values("value_url")[:1],
|
||||||
|
)
|
||||||
|
case CustomField.FieldDataType.BOOL:
|
||||||
|
annotation = Subquery(
|
||||||
|
CustomFieldInstance.objects.filter(
|
||||||
|
document_id=OuterRef("id"),
|
||||||
|
field_id=custom_field_id,
|
||||||
|
).values("value_bool")[:1],
|
||||||
)
|
)
|
||||||
|
|
||||||
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)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user