def node_to_mql(e, node, visited_nodes): mql_query = {} num_relations = 0 visited_nodes.add(node) for relation, dest in e.iter_edges(node): if not dest in visited_nodes: relation = format_mql_uri(relation) if isnode(dest): dest_mql = node_to_mql(e, dest, visited_nodes) if type(dest_mql) == dict: dest_mql = [dest_mql] mql_query[relation] = dest_mql else: mql_query[relation] = adapt_mql(dest) num_relations += 1 for relation, dest in iter_incoming_edges(e, node, visited_nodes): if not dest in visited_nodes: relation = '!' + format_mql_uri(relation) if isnode(dest): dest_mql = node_to_mql(e, dest, visited_nodes) if type(dest_mql) == dict: dest_mql = [dest_mql] mql_query[relation] = dest_mql else: mql_query[relation] = adapt_mql(dest) num_relations += 1 if num_relations == 0: return None return mql_query
def generate_mql(e): """ Generates a MQL query for the `Expression` `e`. """ start = choose_start_node(e) graph = to_bidirected_graph(e) generated = {} for node in post_order_depth_first(graph, start): d = {} for relation, other in graph[node]: if isnode(other): try: other = generated[other] except KeyError: continue # other is not in post_order_depth_first order d[relation] = other generated[node] = [d] mql_query = json.dumps(generated[start], sort_keys=True, indent=2, separators=(',', ': ')) mql_query = _tidy(mql_query) target = paths_from_root(graph, start)[e.get_head()] return target, mql_query
def to_bidirected_graph(e): """ Rewrite the graph such that there are reversed edges for every forward edge. If an edge goes into a data, it should not be reversed. """ graph = {node: [] for node in e.iter_nodes()} for node in e.iter_nodes(): for relation, other in e.iter_edges(node): relation = safely_to_unicode(relation) if isnode(other): graph[other].append((u"!" + relation, node)) else: other = safely_to_unicode(other) graph[node].append((relation, other)) assert all(isnode(x) for x in graph) and len(e) == len(graph) return graph
def adapt(x): if isnode(x): x = "?x{}".format(x) return x if isinstance(x, str): assert_valid_encoding(x) if x.startswith("\"") or ":" in x: return x return '"{}"'.format(x) return str(x)
def adapt(x): if isnode(x): x = u"?x{}".format(x) return x if isinstance(x, basestring): assert_valid_encoding(x) if x.startswith(u"\"") or ":" in x: return x return u'"{}"'.format(x) return unicode(x)
def adapt(x): if isnode(x): x = u"x{}".format(x) return x if isinstance(x, basestring): assert_valid_encoding(x) x = escape(x) if x.startswith(u'"'): return x return u'"{}"'.format(x) return unicode(x)
def test_acyclic(self): head = self.e.get_head() q = [head] seen = set() while q: current = q.pop() self.assertNotIn(current, seen) seen.add(current) for relation, child in self.e.iter_edges(current): if isnode(child): q.append(child)
def make_canonical_expression(e): i = 0 q = [e.get_head()] seen = set() while i != len(q): node = q[i] i += 1 assert node not in seen, "Nouuu, expression is cyclic!" for relation, child in e.iter_edges(node): if isnode(child): q.append(child) q.reverse() canon = {} for node in q: childs = [] for label, child in e.iter_edges(node): if isnode(child): child = canon[child] childs.append((label, child)) childs.sort() canon[node] = tuple(childs) return canon[e.get_head()]
def paths_from_root(graph, start): """ Generates paths from `start` to every other node in `graph` and puts it in the returned dictionary `paths`. ie.: `paths_from_node(graph, start)[node]` is a list of the edge names used to get to `node` form `start`. """ paths = {start: []} q = [start] seen = set() while q: node = q.pop() seen.add(node) for relation, child in graph[node]: if isnode(child) and child not in seen: q.append(child) paths[child] = paths[node] + [relation] return paths
def post_order_depth_first(graph, start): """ Iterate over the nodes of the graph (is a tree) in a way such that every node is preceded by it's childs. `graph` is a dict that represents the `Expression` graph. It's a tree too beacuse Expressions are trees. `start` is the node to use as the root of the tree. """ q = [start] seen = set() i = 0 while i != len(graph): node = q[i] seen.add(node) i += 1 for _, other in graph[node]: if isnode(other) and other not in seen: q.append(other) assert len(q) == len(graph) q.reverse() return q