995 :param safe: |
995 :param safe: |
996 if True, an empty rset/list of entities will be returned in case of |
996 if True, an empty rset/list of entities will be returned in case of |
997 :exc:`Unauthorized`, else (the default), the exception is propagated |
997 :exc:`Unauthorized`, else (the default), the exception is propagated |
998 """ |
998 """ |
999 rtype = str(rtype) |
999 rtype = str(rtype) |
1000 try: |
1000 if limit is None: |
1001 return self._cw_relation_cache(rtype, role, entities, limit) |
1001 # we cannot do much wrt cache on limited queries |
1002 except KeyError: |
1002 cache_key = '%s_%s' % (rtype, role) |
1003 pass |
1003 if cache_key in self._cw_related_cache: |
|
1004 return self._cw_related_cache[cache_key][entities] |
1004 if not self.has_eid(): |
1005 if not self.has_eid(): |
1005 if entities: |
1006 if entities: |
1006 return [] |
1007 return [] |
1007 return self._cw.empty_rset() |
1008 return self._cw.empty_rset() |
1008 rql = self.cw_related_rql(rtype, role) |
1009 rql = self.cw_related_rql(rtype, role, limit=limit) |
1009 try: |
1010 try: |
1010 rset = self._cw.execute(rql, {'x': self.eid}) |
1011 rset = self._cw.execute(rql, {'x': self.eid}) |
1011 except Unauthorized: |
1012 except Unauthorized: |
1012 if not safe: |
1013 if not safe: |
1013 raise |
1014 raise |
1014 rset = self._cw.empty_rset() |
1015 rset = self._cw.empty_rset() |
1015 self.cw_set_relation_cache(rtype, role, rset) |
1016 if entities: |
1016 return self.related(rtype, role, limit, entities) |
1017 if limit is None: |
1017 |
1018 self.cw_set_relation_cache(rtype, role, rset) |
1018 def cw_related_rql(self, rtype, role='subject', targettypes=None): |
1019 return self.related(rtype, role, limit, entities) |
|
1020 return list(rset.entities()) |
|
1021 else: |
|
1022 return rset |
|
1023 |
|
1024 def cw_related_rql(self, rtype, role='subject', targettypes=None, limit=None): |
1019 vreg = self._cw.vreg |
1025 vreg = self._cw.vreg |
1020 rschema = vreg.schema[rtype] |
1026 rschema = vreg.schema[rtype] |
1021 select = Select() |
1027 select = Select() |
1022 mainvar, evar = select.get_variable('X'), select.get_variable('E') |
1028 mainvar, evar = select.get_variable('X'), select.get_variable('E') |
1023 select.add_selected(mainvar) |
1029 select.add_selected(mainvar) |
|
1030 if limit is not None: |
|
1031 select.set_limit(limit) |
1024 select.add_eid_restriction(evar, 'x', 'Substitute') |
1032 select.add_eid_restriction(evar, 'x', 'Substitute') |
1025 if role == 'subject': |
1033 if role == 'subject': |
1026 rel = make_relation(evar, rtype, (mainvar,), VariableRef) |
1034 rel = make_relation(evar, rtype, (mainvar,), VariableRef) |
1027 select.add_restriction(rel) |
1035 select.add_restriction(rel) |
1028 if targettypes is None: |
1036 if targettypes is None: |
1073 return select.as_string() |
1081 return select.as_string() |
1074 |
1082 |
1075 # generic vocabulary methods ############################################## |
1083 # generic vocabulary methods ############################################## |
1076 |
1084 |
1077 def cw_unrelated_rql(self, rtype, targettype, role, ordermethod=None, |
1085 def cw_unrelated_rql(self, rtype, targettype, role, ordermethod=None, |
1078 vocabconstraints=True, lt_infos={}): |
1086 vocabconstraints=True, lt_infos={}, limit=None): |
1079 """build a rql to fetch `targettype` entities unrelated to this entity |
1087 """build a rql to fetch `targettype` entities unrelated to this entity |
1080 using (rtype, role) relation. |
1088 using (rtype, role) relation. |
1081 |
1089 |
1082 Consider relation permissions so that returned entities may be actually |
1090 Consider relation permissions so that returned entities may be actually |
1083 linked by `rtype`. |
1091 linked by `rtype`. |
1102 searchedvar = objvar = select.get_variable('O') |
1110 searchedvar = objvar = select.get_variable('O') |
1103 else: |
1111 else: |
1104 searchedvar = subjvar = select.get_variable('S') |
1112 searchedvar = subjvar = select.get_variable('S') |
1105 evar = objvar = select.get_variable('O') |
1113 evar = objvar = select.get_variable('O') |
1106 select.add_selected(searchedvar) |
1114 select.add_selected(searchedvar) |
|
1115 if limit is not None: |
|
1116 select.set_limit(limit) |
1107 # initialize some variables according to `self` existence |
1117 # initialize some variables according to `self` existence |
1108 if rdef.role_cardinality(neg_role(role)) in '?1': |
1118 if rdef.role_cardinality(neg_role(role)) in '?1': |
1109 # if cardinality in '1?', we want a target entity which isn't |
1119 # if cardinality in '1?', we want a target entity which isn't |
1110 # already linked using this relation |
1120 # already linked using this relation |
1111 variable = select.make_variable() |
1121 variable = select.make_variable() |
1195 ordermethod=None, lt_infos={}): # XXX .cw_unrelated |
1205 ordermethod=None, lt_infos={}): # XXX .cw_unrelated |
1196 """return a result set of target type objects that may be related |
1206 """return a result set of target type objects that may be related |
1197 by a given relation, with self as subject or object |
1207 by a given relation, with self as subject or object |
1198 """ |
1208 """ |
1199 try: |
1209 try: |
1200 rql, args = self.cw_unrelated_rql(rtype, targettype, role, |
1210 rql, args = self.cw_unrelated_rql(rtype, targettype, role, limit=limit, |
1201 ordermethod, lt_infos=lt_infos) |
1211 ordermethod=ordermethod, lt_infos=lt_infos) |
1202 except Unauthorized: |
1212 except Unauthorized: |
1203 return self._cw.empty_rset() |
1213 return self._cw.empty_rset() |
1204 # XXX should be set in unrelated rql when manipulating the AST |
|
1205 if limit is not None: |
|
1206 before, after = rql.split(' WHERE ', 1) |
|
1207 rql = '%s LIMIT %s WHERE %s' % (before, limit, after) |
|
1208 return self._cw.execute(rql, args) |
1214 return self._cw.execute(rql, args) |
1209 |
1215 |
1210 # relations cache handling ################################################# |
1216 # relations cache handling ################################################# |
1211 |
1217 |
1212 def cw_relation_cached(self, rtype, role): |
1218 def cw_relation_cached(self, rtype, role): |
1213 """return None if the given relation isn't already cached on the |
1219 """return None if the given relation isn't already cached on the |
1214 instance, else the content of the cache (a 2-uple (rset, entities)). |
1220 instance, else the content of the cache (a 2-uple (rset, entities)). |
1215 """ |
1221 """ |
1216 return self._cw_related_cache.get('%s_%s' % (rtype, role)) |
1222 return self._cw_related_cache.get('%s_%s' % (rtype, role)) |
1217 |
|
1218 def _cw_relation_cache(self, rtype, role, entities=True, limit=None): |
|
1219 """return values for the given relation if it's cached on the instance, |
|
1220 else raise `KeyError` |
|
1221 """ |
|
1222 res = self._cw_related_cache['%s_%s' % (rtype, role)][entities] |
|
1223 if limit is not None and limit < len(res): |
|
1224 if entities: |
|
1225 res = res[:limit] |
|
1226 else: |
|
1227 res = res.limit(limit) |
|
1228 return res |
|
1229 |
1223 |
1230 def cw_set_relation_cache(self, rtype, role, rset): |
1224 def cw_set_relation_cache(self, rtype, role, rset): |
1231 """set cached values for the given relation""" |
1225 """set cached values for the given relation""" |
1232 if rset: |
1226 if rset: |
1233 related = list(rset.entities(0)) |
1227 related = list(rset.entities(0)) |