rqlrewrite.py
branchstable
changeset 9189 9448215c73c4
parent 9188 0677e03077fb
child 9358 1e0235478403
--- a/rqlrewrite.py	Wed Jul 24 16:55:24 2013 +0200
+++ b/rqlrewrite.py	Thu Jul 25 09:07:25 2013 +0200
@@ -173,6 +173,29 @@
                 return True
     return False
 
+def _compatible_relation(relations, stmt, sniprel):
+    """Search among given rql relation nodes if there is one 'compatible' with the
+    snippet relation, and return it if any, else None.
+
+    A relation is compatible if it:
+    * belongs to the currently processed statement,
+    * isn't negged (i.e. direct parent is a NOT node)
+    * isn't optional (outer join) or similarly as the snippet relation
+    """
+    for rel in relations:
+        # don't share if relation's scope is not the current statement
+        if rel.scope is not stmt:
+            continue
+        # don't share neged relation
+        if rel.neged(strict=True):
+            continue
+        # don't share optional relation, unless the snippet relation is
+        # similarly optional
+        if rel.optional and rel.optional != sniprel.optional:
+            continue
+        return rel
+    return None
+
 
 def iter_relations(stinfo):
     # this is a function so that test may return relation in a predictable order
@@ -384,9 +407,14 @@
                                                       subselect.solutions, self.kwargs)
                     return
                 if varexistsmap is None:
-                    vi['rhs_rels'] = dict( (r.r_type, r) for r in sti['rhsrelations'])
-                    vi['lhs_rels'] = dict( (r.r_type, r) for r in sti['relations']
-                                           if not r in sti['rhsrelations'])
+                    # build an index for quick access to relations
+                    vi['rhs_rels'] = {}
+                    for rel in sti['rhsrelations']:
+                        vi['rhs_rels'].setdefault(rel.r_type, []).append(rel)
+                    vi['lhs_rels'] = {}
+                    for rel in sti['relations']:
+                        if not rel in sti['rhsrelations']:
+                            vi['lhs_rels'].setdefault(rel.r_type, []).append(rel)
                 else:
                     vi['rhs_rels'] = vi['lhs_rels'] = {}
         previous = None
@@ -657,36 +685,33 @@
         """if the snippet relation can be skipped to use a relation from the
         original query, return that relation node
         """
+        if sniprel.neged(strict=True):
+            return None # no way
         rschema = self.schema.rschema(sniprel.r_type)
         stmt = self.current_statement()
         for vi in self.varinfos:
             try:
                 if target == 'object':
-                    orel = vi['lhs_rels'][sniprel.r_type]
+                    orels = vi['lhs_rels'][sniprel.r_type]
                     cardindex = 0
                     ttypes_func = rschema.objects
                     rdef = rschema.rdef
                 else: # target == 'subject':
-                    orel = vi['rhs_rels'][sniprel.r_type]
+                    orels = vi['rhs_rels'][sniprel.r_type]
                     cardindex = 1
                     ttypes_func = rschema.subjects
                     rdef = lambda x, y: rschema.rdef(y, x)
             except KeyError:
                 # may be raised by vi['xhs_rels'][sniprel.r_type]
                 continue
-            # don't share if relation's scope is not the current statement
-            if orel.scope is not stmt:
-                continue
-            # can't share neged relation or relations with different outer join
-            if (orel.neged(strict=True) or sniprel.neged(strict=True)
-                or (orel.optional and orel.optional != sniprel.optional)):
-                continue
-            # if cardinality is in '?1', we can ignore the snippet relation and use
-            # variable from the original query
+            # if cardinality isn't in '?1', we can't ignore the snippet relation
+            # and use variable from the original query
             if _has_multiple_cardinality(vi['stinfo']['possibletypes'], rdef,
                                          ttypes_func, cardindex):
                 continue
-            return orel
+            orel = _compatible_relation(orels, stmt, sniprel)
+            if orel is not None:
+                return orel
         return None
 
     def _use_orig_term(self, snippet_varname, term):