def __init__(self, node, direction, relationship_type, related_classes): assert isinstance(direction, int) and not isinstance(direction, bool) self.node = node if isinstance(related_classes, list): self.related_classes = related_classes else: # to keep backward compatibility self.related_classes = [related_classes] self.__related_objects = None if direction > 0: self.__match_args = (self.node, relationship_type, None) self.__start_node = False self.__end_node = True self.__relationship_pattern = "(a)-[_:%s]->(b)" % cypher_escape( relationship_type) elif direction < 0: self.__match_args = (None, relationship_type, self.node) self.__start_node = True self.__end_node = False self.__relationship_pattern = "(a)<-[_:%s]-(b)" % cypher_escape( relationship_type) else: self.__match_args = (self.node, relationship_type, None, True) self.__start_node = True self.__end_node = True self.__relationship_pattern = "(a)-[_:%s]-(b)" % cypher_escape( relationship_type)
def top(self, query, fields, collect=None, sumfields=None): """Returns an iterator of: {fields: <fields>, count: <number of occurrence or sum of sumfields>, collected: <collected fields>}. WARNING/FIXME: this mutates the query """ collect = collect or [] sumfields = sumfields or [] for flist in fields, collect, sumfields: for i in range(len(flist)): if flist[i].startswith("link."): flist[i] = flist[i].replace("flow.", "link.") if "." not in flist[i]: flist[i] = "link.%s" % flist[i] flist[i] = '.'.join( cypher_escape(elt) for elt in flist[i].split(".")) cy_fields = "[%s]" % ', '.join(fields) cy_collect = "[%s]" % ', '.join(collect) cy_sumfields = "SUM(%s)" % ' + '.join(sumfields) query.add_clause( "WITH src.elt as src, link.elt as link, dst.elt as dst\n" "WITH %s as fields, %s as count, %s as collected" % (cy_fields, "COUNT(*)" if not sumfields else cy_sumfields, "NULL" if not collect else "COLLECT(DISTINCT %s)" % cy_collect)) query.ret = "RETURN fields, count, collected" query.orderby = "ORDER BY count DESC" top = self._cursor2top(self.run(query)) return top
def top(self, query, fields, collect=None, sumfields=None): """Returns an iterator of: {fields: <fields>, count: <number of occurrence or sum of sumfields>, collected: <collected fields>}. WARNING/FIXME: this mutates the query """ collect = collect or [] sumfields = sumfields or [] for flist in fields, collect, sumfields: for i in range(len(flist)): if flist[i].startswith("link."): flist[i] = flist[i].replace("flow.", "link.") if "." not in flist[i]: flist[i] = "link.%s" % flist[i] flist[i] = '.'.join(cypher_escape(elt) for elt in flist[i].split(".")) cy_fields = "[%s]" % ', '.join(fields) cy_collect = "[%s]" % ', '.join(collect) cy_sumfields = "SUM(%s)" % ' + '.join(sumfields) query.add_clause( "WITH src.elt as src, link.elt as link, dst.elt as dst\n" "WITH %s as fields, %s as count, %s as collected" % (cy_fields, "COUNT(*)" if not sumfields else cy_sumfields, "NULL" if not collect else "COLLECT(DISTINCT %s)" % cy_collect) ) query.ret = "RETURN fields, count, collected" query.orderby = "ORDER BY count DESC" top = self._cursor2top(self.run(query)) return top
def __init__(self, node, direction, relationship_type, related_class): assert isinstance(direction, int) and not isinstance(direction, bool) self.node = node self.related_class = related_class self.__related_objects = None if direction > 0: self.__match_args = (self.node, relationship_type, None) self.__start_node = False self.__end_node = True self.__relationship_pattern = "(a)-[_:%s]->(b)" % cypher_escape(relationship_type) elif direction < 0: self.__match_args = (None, relationship_type, self.node) self.__start_node = True self.__end_node = False self.__relationship_pattern = "(a)<-[_:%s]-(b)" % cypher_escape(relationship_type) else: self.__match_args = (self.node, relationship_type, None, True) self.__start_node = True self.__end_node = True self.__relationship_pattern = "(a)-[_:%s]-(b)" % cypher_escape(relationship_type)
def _add_clause_from_filter(self, flt, mode="node"): """Returns a WHERE clause (tuple (query, parameters)) from a single filter (no OR). Devs: `flt` **can** be set from an untrusted source. """ if not flt: return None if flt[0] in "-!~": neg = True flt = flt[1:] else: neg = False array_mode = None len_mode = None if flt.startswith("ANY "): array_mode = "ANY" flt = flt[4:] elif flt.startswith("ALL "): array_mode = "ALL" flt = flt[4:] elif flt.startswith("ONE "): array_mode = "SINGLE" flt = flt[4:] elif flt.startswith("NONE "): array_mode = "NONE" flt = flt[5:] elif flt.startswith("LEN "): len_mode = "LENGTH" flt = flt[4:] try: operator = self.operators_re.search(flt).group() except AttributeError: operator = None attr = flt else: attr, value = [elt.strip() for elt in flt.split(operator, 1)] value = utils.str2pyval(value) if attr[0] in "@#": qtype = attr[0] attr = attr[1:] else: qtype = "@" try: # Sorry for the horrendous code -- jalet elements, attr = attr.rsplit('.', 1) if elements == "meta": if mode == "edge": elements = ["linkmeta"] self.meta_link = True elif mode == "node": elements = ["srcmeta", "dstmeta"] self.meta_src = True self.meta_dst = True elif elements == "src.meta": elements = ["srcmeta"] self.meta_src = True elif elements == "dst.meta": elements = ["dstmeta"] self.meta_dst = True else: elements = [elements] except ValueError: if mode == "node": elements = ["src", "dst"] elif mode == "edge": elements = ["link"] else: raise ValueError() else: assert all(self.identifier.search(elt) for elt in elements) assert self.identifier.search(attr) if operator is None: if qtype == "@": return ( "%s(%s)" % ( "NOT " if neg else "", " OR ".join("EXISTS(`%s`.`%s`)" % (elt, attr) for elt in elements), ), {}, ) if qtype == "#": identifier = self.nextid() return ( "%s(%s)" % ( "NOT " if neg else "", " OR ".join("{%s} IN labels(`%s`)" % (identifier, elt) for elt in elements), ), { identifier: attr }, ) if qtype == "@": identifier = self.nextid() operator = self.operators[operator] clauses = [] for elt in elements: attr_expr = "%s.%s" % tuple( cypher_escape(s) for s in (elt, attr)) if array_mode is not None: lval = "x" elif len_mode is not None: lval = "%s(%s)" % (len_mode, attr_expr) else: lval = attr_expr clause_part = "%s %s {%s}" % (lval, operator, identifier) if array_mode is not None: if array_mode in ["ALL", "ANY", "SINGLE"]: prereq = "LENGTH(%s) <> 0 AND" % attr_expr elif array_mode in ["NONE"]: prereq = "LENGTH(%s) = 0 OR" clause_part = "%s %s(x IN %s WHERE %s)" % ( prereq, array_mode, attr_expr, clause_part, ) clauses.append(clause_part) clause = " OR ".join(clauses) if neg: clause = "%s OR NOT (%s)" % ( " OR ".join("NOT EXISTS(`%s`.`%s`)" % (elt, attr) for elt in elements), clause, ) value = Neo4jDB.to_dbprop(attr, value) return ( "%s" % clause, { identifier: value }, ) raise ValueError()
def _add_clause_from_filter(self, flt, mode="node"): """Returns a WHERE clause (tuple (query, parameters)) from a single filter (no OR). Devs: `flt` **can** be set from an untrusted source. """ if not flt: return None if flt[0] in "-!~": neg = True flt = flt[1:] else: neg = False array_mode = None len_mode = None if flt.startswith("ANY "): array_mode = "ANY" flt = flt[4:] elif flt.startswith("ALL "): array_mode = "ALL" flt = flt[4:] elif flt.startswith("ONE "): array_mode = "SINGLE" flt = flt[4:] elif flt.startswith("NONE "): array_mode = "NONE" flt = flt[5:] elif flt.startswith("LEN "): len_mode = "LENGTH" flt = flt[4:] try: operator = self.operators_re.search(flt).group() except AttributeError: operator = None attr = flt else: attr, value = [elt.strip() for elt in flt.split(operator, 1)] value = utils.str2pyval(value) if attr[0] in "@#": qtype = attr[0] attr = attr[1:] else: qtype = "@" try: # Sorry for the horrendous code -- jalet elements, attr = attr.rsplit('.', 1) if elements == "meta": if mode == "edge": elements = ["linkmeta"] self.meta_link = True elif mode == "node": elements = ["srcmeta", "dstmeta"] self.meta_src = True self.meta_dst = True elif elements == "src.meta": elements = ["srcmeta"] self.meta_src = True elif elements == "dst.meta": elements = ["dstmeta"] self.meta_dst = True else: elements = [elements] except ValueError: if mode == "node": elements = ["src", "dst"] elif mode == "edge": elements = ["link"] else: raise ValueError() else: assert all(self.identifier.search(elt) for elt in elements) assert self.identifier.search(attr) if operator is None: if qtype == "@": return ( "%s(%s)" % ( "NOT " if neg else "", " OR ".join( "EXISTS(`%s`.`%s`)" % (elt, attr) for elt in elements ), ), {}, ) if qtype == "#": identifier = self.nextid() return ( "%s(%s)" % ( "NOT " if neg else "", " OR ".join( "{%s} IN labels(`%s`)" % (identifier, elt) for elt in elements ), ), {identifier: attr}, ) if qtype == "@": identifier = self.nextid() operator = self.operators[operator] clauses = [] for elt in elements: attr_expr = "%s.%s" % tuple(cypher_escape(s) for s in (elt, attr)) if array_mode is not None: lval = "x" elif len_mode is not None: lval = "%s(%s)" % (len_mode, attr_expr) else: lval = attr_expr clause_part = "%s %s {%s}" % (lval, operator, identifier) if array_mode is not None: if array_mode in ["ALL", "ANY", "SINGLE"]: prereq = "LENGTH(%s) <> 0 AND" % attr_expr elif array_mode in ["NONE"]: prereq = "LENGTH(%s) = 0 OR" clause_part = "%s %s(x IN %s WHERE %s)" % ( prereq, array_mode, attr_expr, clause_part, ) clauses.append(clause_part) clause = " OR ".join(clauses) if neg: clause = "%s OR NOT (%s)" % ( " OR ".join("NOT EXISTS(`%s`.`%s`)" % (elt, attr) for elt in elements), clause, ) value = Neo4jDB.to_dbprop(attr, value) return ( "%s" % clause, {identifier: value}, ) raise ValueError()