Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

QueryBuilder 'order_by' has no effect if the same type is used twice #4327

Closed
greschd opened this issue Aug 27, 2020 · 2 comments · Fixed by #5093
Closed

QueryBuilder 'order_by' has no effect if the same type is used twice #4327

greschd opened this issue Aug 27, 2020 · 2 comments · Fixed by #5093

Comments

@greschd
Copy link
Member

greschd commented Aug 27, 2020

Describe the bug

When the same type is appended to the QueryBuilder twice (with difference filter clauses etc.), the order_by statement appears to have no effect.

Minimal example

Running the following code, the result does not respect the ctime ordering:

from aiida import orm
from aiida.engine import calcfunction


@calcfunction
def add(x, y):
    return x + y


x = orm.Int(1).store() # explicitly storing so that I know the order of creation
y = orm.Int(3).store()
z = orm.Int(5).store()

c = add(add(x, y), z)

qb = orm.QueryBuilder()
qb.append(orm.Int, filters={'uuid': c.uuid}, tag='c')
qb.append(orm.Int, with_descendants='c')
qb.order_by({orm.Int: {'ctime': 'desc'}})
print(qb.distinct().all())

The printed output is (formatted for readability)

[
  [<Int: uuid: 7c1a439c-9c04-419e-81f9-d4b6cb2de073 (pk: 59793) value: 1>], 
  [<Int: uuid: 3fb15a95-81a9-48e3-9ca7-359738de82d8 (pk: 59794) value: 3>], 
  [<Int: uuid: 8459de2a-eeb2-45aa-9d8a-6cd86d27a5ac (pk: 59795) value: 5>], 
  [<Int: uuid: 41f522fa-a473-46e6-be95-b5774e46b074 (pk: 59797) value: 4>]
]

which is in ascending ctime order. In fact, switching from desc to asc has no effect.

Counterexample: using a different type

Using a different type for the two append statements, the ordering works as expected:

from aiida import orm
from aiida.engine import calcfunction


@calcfunction
def add(x, y):
    return orm.Float((x + y).value)


x = orm.Int(1).store()
y = orm.Int(3).store()
z = orm.Int(5).store()

c = add(add(x, y), z)

qb = orm.QueryBuilder()
qb.append(orm.Float, filters={'uuid': c.uuid}, tag='c')
qb.append(orm.Int, with_descendants='c')
qb.order_by({orm.Int: {'ctime': 'desc'}})
print(qb.distinct().all())

Result:

[
  [<Int: uuid: 59350c15-1421-4ca1-ba8a-7da60815030f (pk: 59802) value: 5>], 
  [<Int: uuid: d782fbec-85cf-4c7a-8b0d-a529d17625f1 (pk: 59801) value: 3>], 
  [<Int: uuid: f04c16eb-91f5-4f76-89b7-a268ef747de1 (pk: 59800) value: 1>]
]

Environment

  • Operating system: Ubuntu 18.04, via WSL2
  • Python version: 3.7.3
  • aiida-core version: Latest develop (477fe30), Django backend

Workaround

A workaround is using the tag and referring to the entity that should be ordered with that, instead of passing the type. If the above is expected behavior, I think the order_by documentation should strongly encourage the use of tags.

@chrisjsewell
Copy link
Member

@greschd in #5093, this will now fail, rather than silently converting the object to the first tag (the current behaviour)

    ...: print(qb.distinct().all())
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-18-a52ad4c23029> in <module>
     17 qb.append(orm.Int, filters={'uuid': c.uuid}, tag='c')
     18 qb.append(orm.Int, with_descendants='c')
---> 19 qb.order_by({orm.Int: {'ctime': 'desc'}})
     20 print(qb.distinct().all())

~/Documents/GitHub/aiida_core_develop/aiida/orm/querybuilder.py in order_by(self, order_by)
    599                 if not isinstance(items_to_order_by, (tuple, list)):
    600                     items_to_order_by = [items_to_order_by]
--> 601                 tag = self._tags.get(tagspec)
    602                 _order_spec[tag] = []
    603                 for item_to_order_by in items_to_order_by:

~/Documents/GitHub/aiida_core_develop/aiida/orm/querybuilder.py in get(self, tag_or_cls)
   1403         if self._cls_to_tag_map.get(tag_or_cls, None):
   1404             if len(self._cls_to_tag_map[tag_or_cls]) != 1:
-> 1405                 raise ValueError(
   1406                     f'The object used as a tag ({tag_or_cls}) has multiple values associated with it: '
   1407                     f'{self._cls_to_tag_map[tag_or_cls]}'

ValueError: The object used as a tag (<class 'aiida.orm.nodes.data.int.Int'>) has multiple values associated with it: {'Int_1', 'c'}

Would you consider this to close the issue?

@chrisjsewell chrisjsewell linked a pull request Aug 24, 2021 that will close this issue
@greschd
Copy link
Member Author

greschd commented Aug 24, 2021

Yeah, I guess that's clear enough.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants