[entity] Make it possible to specify sorting terms in cw_related_rqlst method
authorDenis Laxalde <denis.laxalde@logilab.fr>
Fri, 15 Apr 2016 10:20:34 +0200
changeset 11290 12d226a5bab9
parent 11286 0b38e373b985
child 11291 7c565548fb09
[entity] Make it possible to specify sorting terms in cw_related_rqlst method Closes #12306543.
cubicweb/entity.py
cubicweb/test/unittest_entity.py
--- a/cubicweb/entity.py	Thu Jun 02 17:52:43 2016 +0200
+++ b/cubicweb/entity.py	Fri Apr 15 10:20:34 2016 +0200
@@ -1012,7 +1012,19 @@
         return self.cw_related_rqlst(
             rtype, role=role, targettypes=targettypes, limit=limit).as_string()
 
-    def cw_related_rqlst(self, rtype, role='subject', targettypes=None, limit=None):
+    def cw_related_rqlst(self, rtype, role='subject', targettypes=None,
+                         limit=None, sort_terms=None):
+        """Return the select node of the RQL query of entities related through
+        `rtype` with this entity as `role`, possibly filtered by
+        `targettypes`.
+
+        The RQL query can be given a `limit` and sort terms with `sort_terms`
+        arguments being a sequence of ``(<relation type>, <sort ascending>)``
+        (e.g. ``[('name', True), ('modification_date', False)]`` would lead to
+        a sorting by ``name``, ascending and then by ``modification_date``,
+        descending. If `sort_terms` is not specified the default sorting is by
+        ``modification_date``, descending.
+        """
         vreg = self._cw.vreg
         rschema = vreg.schema[rtype]
         select = Select()
@@ -1053,22 +1065,31 @@
         if gcard == '1':
             select.remove_sort_terms()
         elif not select.orderby:
-            # if modification_date is already retrieved, we use it instead
-            # of adding another variable for sorting. This should not be
-            # problematic, but it is with sqlserver, see ticket #694445
-            for rel in select.where.get_nodes(RqlRelation):
-                if (rel.r_type == 'modification_date'
-                    and rel.children[0].variable == mainvar
-                    and rel.children[1].operator == '='):
-                    var = rel.children[1].children[0].variable
-                    select.add_sort_var(var, asc=False)
-                    break
-            else:
-                mdvar = select.make_variable()
-                rel = make_relation(mainvar, 'modification_date',
-                                    (mdvar,), VariableRef)
-                select.add_restriction(rel)
-                select.add_sort_var(mdvar, asc=False)
+            # Build a mapping (rtype, node) for relations usable for sorting.
+            sorting_relations = {}
+            for r in select.where.get_nodes(RqlRelation):
+                lhs, rhs = r.children
+                if lhs.variable != mainvar:
+                    continue
+                if r.operator() != '=':
+                    continue
+                rhs_term = rhs.children[0]
+                if not isinstance(rhs_term, VariableRef):
+                    continue
+                sorting_relations[r.r_type] = r
+            sort_terms = sort_terms or [('modification_date', False)]
+            for term, order in sort_terms:
+                # Add a relation for sorting only if it is not only retrieved
+                # (e.g. modification_date) instead of adding another variable
+                # for sorting. This should not be problematic, but it is with
+                # sqlserver, see ticket #694445.
+                rel = sorting_relations.get(term)
+                if rel is None:
+                    mdvar = select.make_variable()
+                    rel = make_relation(mainvar, term, (mdvar,), VariableRef)
+                    select.add_restriction(rel)
+                var = rel.children[1].children[0].variable
+                select.add_sort_var(var, asc=order)
         return select
 
     # generic vocabulary methods ##############################################
--- a/cubicweb/test/unittest_entity.py	Thu Jun 02 17:52:43 2016 +0200
+++ b/cubicweb/test/unittest_entity.py	Fri Apr 15 10:20:34 2016 +0200
@@ -319,6 +319,17 @@
                              'WHERE E eid %(x)s, E tags X, X is Personne, X modification_date AA, '
                              'X nom AB')
 
+    def test_related_rql_sort_terms(self):
+        with self.admin_access.web_request() as req:
+            tag = self.vreg['etypes'].etype_class('Tag')(req)
+            select = tag.cw_related_rqlst('tags', 'subject',
+                                          sort_terms=(('nom', True),
+                                                      ('modification_date', False)))
+            expected = (
+                'Any X,AA ORDERBY AB,AA DESC '
+                'WHERE E eid %(x)s, E tags X, X modification_date AA, X nom AB')
+            self.assertEqual(select.as_string(), expected)
+
     def test_related_rql_ambiguous_cant_use_fetch_order(self):
         with self.admin_access.web_request() as req:
             tag = self.vreg['etypes'].etype_class('Tag')(req)