def _where_clause(self): """Build the where clause. Example: If the path is Environment->Host WHERE environment.uuid = $identity AND r_t1_state.from <= $t1 < r_t1_state.to AND r_t2_state.from <= $t2 < r_t2_state.to AND t1_state <> t2_state AND ALL (r IN RELATIONSHIPS(p_t1) WHERE r.from <= $t1 < r.to) AND ALL (r in RELATIONSHIPS(p_t2) WHERE r.from <= $t2 < r.to) :returns: Where clause :rtype: str """ wheres = [ '{}.{} = $identity'.format( self._var_from_label(self.path[0]), registry.identity_property(self.path[0])) ] wheres.append('r_t1_state.from <= $t1 < r_t1_state.to') wheres.append('r_t2_state.from <= $t2 < r_t2_state.to') wheres.append('t1_state <> t2_state') wheres.append( 'ALL (r IN RELATIONSHIPS(p_t1) WHERE r.from <= $t1 < r.to)') wheres.append( 'ALL (r in RELATIONSHIPS(p_t2) WHERE r.from <= $t2 < r.to)') cipher = "WHERE " + ' AND '.join(wheres) return cipher
def __init__(self, path, identity, times, pagesize=5000): """Init the diff query :param path: Path to build a diff query around :type path: list :param identity: Starting node's identity :type identity: str :param times: Two times for comparison :type times: tuple :param pagesize: How many results to fetch at once :type pagesize: int """ self.path = path self.end = self.path[-1] self.full_path = registry.path(self.end) self.t1 = times[0] self.t2 = times[1] self.identity = identity self.pagesize = pagesize self.selects = [ '{}.{}'.format(self._var_from_label(m), registry.identity_property(m)) for m in self.path ] self.params = {'t1': self.t1, 't2': self.t2, 'identity': self.identity}
def __str__(self): var = self.label.lower() identity_prop = registry.identity_property(self.label) cypher = "MATCH p = ({}:{})-[*]->(other)".format(var, self.label) cypher += "\nWHERE {}.{} = $identity".format(var, identity_prop) cypher += "\nWITH relationships(p) as rels" cypher += "\nUNWIND rels as r" cypher += "\nreturn DISTINCT r.from as t" cypher += "\nORDER BY t DESC" return cypher
def getnode(self, model, data): """Get a node of model type matching data. Create a new node if one does not exist. :param model: Name of the model :type model: str :param data: Dict representation of node :type data: dict :returns: Node with matching data :rtype: Node """ identity = data[registry.identity_property(model)] nodemap = self.nodes.setdefault(model, {}) node = nodemap.setdefault(identity, Node(identity, model)) return node
def node_at_time(self, t): """Get a node from the database at time t. :param t: A time in milliseconds :type t: int :returns: A dictionary of properties :rtype: dict """ var_models = [] rels = [] returns = ['n'] for model, reltype in self.full_path: var_models.append((model.lower(), model)) rels.append(reltype) var_models.append(('n', self.model)) if registry.state_properties(self.model): var_models.append(('ns', registry.models[self.model].state_label)) rels.append('HAS_STATE') returns.append('ns') cipher = 'MATCH p = ({}:{})'.format(var_models[0][0], var_models[0][1]) for var_model, rel in zip(var_models[1:], rels): var, model = var_model cipher += '-[:{}]->({}:{})'.format(rel, var, model) cipher += ( ' WHERE ALL (r IN RELATIONSHIPS(p) WHERE r.from <= $t < r.to) AND' ) cipher += ( ' n.{} = $identity'.format(registry.identity_property(self.model)) ) cipher += ' RETURN ' + ','.join(returns) + ' LIMIT 1' self.params['t'] = t record = self._fetch(cipher).single() if record is None: node = {} else: node = {k: v for k, v in record['n'].items()} if 'ns' in record: for k, v in record['ns'].items(): node[k] = v return node
def _orderby_clause(self): """Create order by clause. :returns: ORDER BY clause :rtype: str """ cypher = ' \nORDER BY ' # Default to ordering by identity property ob = [] if not self._orderby: self.orderby(registry.identity_property(self.label), 'ASC', label=self.label) for ob_varname, ob_prop, ob_dir in self._orderby: ob.append('{}.{} {}'.format(ob_varname, ob_prop, ob_dir)) cypher += ', '.join(ob) return cypher
def feedpath(self, path, time, side): """Feed all of a path from a side to the diff. :param path: List of models indicating path through data to a model :type path: list :param time: Integer milliseconds since epoch :type time: int :param side: Which side to feed the diff from. :type side: str """ # Build query q = Query(path[-1]) \ .filter( registry.identity_property(self.model), '=', self.identity, self.model ).time(time) # Start at page 1 and page through all results. page = 1 records = q.page(page, self.pagesize) while (records): for record in records: parent = None for label in path: # Update diff with results node = self.getnode(label, record[label]) node.update(record[label], side) # Make parent -> child relationship if parent is not None: parent.add_child(label, node, side) node.parents.add(parent) # Advance parent for next part of path. parent = node page += 1 records = q.page(page, self.pagesize)
def _match_clause(self): """Build the multistage match clause. Find all paths that match in t2. Then find all paths that match in t1 that are not in t2. Example: If your path is Environment->Host->AptPackage MATCH p_t2 = (environment:Environment)-[:HAS_HOST]-> (host:Host)-[:HAS_APT_PACKAGE]-> (aptpackage:AptPackage) WHERE environment.uuid = $identity AND ALL (r IN RELATIONSHIPS(p_t2) WHERE r.from <= $t2 < r.to) WITH COLLECT([environment,host,aptpackage]) as t2_nodes MATCH p_t1 = (environment:Environment)-[:HAS_HOST]-> (host:Host)-[:HAS_APT_PACKAGE]-> (aptpackage:AptPackage) WHERE environment.uuid = $identity AND ALL (r IN RELATIONSHIPS(p_t1) WHERE r.from <= $t1 < r.to) AND NOT [environment,host,aptpackage] IN t2_nodes :returns: Major portion of cipher query :rtype: str """ path_vars = [] for model, _ in self.full_path: path_vars.append(self._var_from_label(model)) path_vars.append(self._var_from_label(self.end)) cipher = 'MATCH p_t2 = ' for model, reltype in self.full_path: cipher += '({}:{})-[:{}]->'.format(self._var_from_label(model), model, reltype) cipher += '({}:{})'.format(self._var_from_label(self.end), self.end) cipher += '\nWHERE ' cipher += '{}.{} = $identity AND '.format( self._var_from_label(self.path[0]), registry.identity_property(self.path[0])) cipher += 'ALL (r IN RELATIONSHIPS(p_t2) WHERE r.from <= $t2 < r.to)' cipher += '\nWITH COLLECT([{}]) as t2_nodes'.format( ','.join(path_vars)) cipher += '\nMATCH p_t1 = ' for model, reltype in self.full_path: cipher += '({}:{})-[:{}]->'.format(self._var_from_label(model), model, reltype) cipher += '({}:{})'.format(self._var_from_label(self.end), self.end) cipher += '\nWHERE ' cipher += '{}.{} = $identity AND '.format( self._var_from_label(self.path[0]), registry.identity_property(self.path[0])) cipher += 'ALL (r IN RELATIONSHIPS(p_t1) WHERE r.from <= $t1 < r.to)' cipher += ' AND NOT [{}] IN t2_nodes'.format(','.join(path_vars)) return cipher