def get_by_search_string(self, search_string, ensure_privacy=True, limit=options.pagination, skip=0): """based on a search string of: pyhon #code @mark build a query with the foundation of: MATCH (tag_resource:`Resource`)-[:`HasTag`]->(tag:`Tag`) WHERE tag.tag_normalized = 'code' WITH tag_resource MATCH (user_resource:`Resource`)<-[:`AddedResource`]-(user:`User`) WHERE id(user_resource) = id(tag_resource) WITH user_resource, user MATCH (resource:`Resource`)-[:`HasTag`]->(tag:`Tag`) id(resource) = id(user_resource) RETURN resource, user, collect(tag) """ from datcode.common.model.graph.node import User search = parse_search_string(search_string) user_mapper = self.get_mapper(User) user_mapper.ensure_privacy = ensure_privacy p = Pypher() p.node('resource', labels='Resource') p.rel_in(labels='AddedResource').node('user', labels='User') p2 = Pypher() p2.node('resource').rel_out(labels='HasTag').node('tag', labels='Tag') p2 = Pypher() p2.node('resource').rel_out(labels='HasTag').node('tag', labels='Tag') p3 = Pypher() p3.node('resource').rel_out(labels='HasTag').node('tags', labels='Tag') query = Pypher() query.MATCH(p, p2, p3) wheres = [] search_ors = [] user_ors = [] tag_ors = [] # filter the resource title and descripiton by search string powersets for contains in search['search']['contains']: term = "(?i).*{}.*".format(contains) d = Pypher() t = Pypher() d.resource.__description__.re(term) t.resource.__title__.re(term) search_ors.append(d) search_ors.append(t) if search_ors: ors = Pypher() ors.COR(*search_ors) wheres.append(ors) # filter by users for user in search['users']: u = Pypher() u.user.__username__ == user user_ors.append(u) if user_ors: ors = Pypher() ors.COR(*user_ors) wheres.append(ors) # filter by tags for tag in search['tags']: u = Pypher() u.tag.__tag_normalized__ == normalize(tag) tag_ors.append(u) if tag_ors: ors = Pypher() ors.COR(*tag_ors) wheres.append(ors) if wheres: query.WHERE.CAND(*wheres) # paginate and get a total count total = query.clone() total.RETURN('COUNT(DISTINCT(resource)) AS total') total_res = self.mapper.query(pypher=total) try: total_results = total_res.first()['result'] except: total_results = 0 query.RETURN('DISTINCT(resource)', 'user') query.ORDERBY('resource.date_created').DESC query.SKIP(skip).LIMIT(limit) results = self.mapper.query(pypher=query) result_data = [] all_tags = [] all_tag_ids = [] for res in results: tags = self(res['resource'])['Tags']() username = res['user']['username'] if ensure_privacy: username = '******' result_data.append({ 'resource': self.data(res['resource']), 'user': { 'username': username, }, 'tags': self.data(tags), }) for tag in tags: if tag.id not in all_tag_ids: all_tags.append(tag.data) all_tag_ids.append(tag.id) return { 'total': total_results, 'results': result_data, 'all_tags': all_tags, }
class RelatedEntityQuery(_BaseQuery): def __init__(self, direction='out', relationship_entity=None, relationship_type=None, relationship_properties=None, start_entity=None, end_entity=None, params=None, single_relationship=False, start_query_variable='start_node', relationship_query_variable='relt', end_query_variable='end_node'): super(RelatedEntityQuery, self).__init__() self.direction = direction self.relationship_entity = relationship_entity self.relationship_type = relationship_type self.start_query_variable = start_query_variable self.relationship_query_variable = relationship_query_variable self.end_query_variable = end_query_variable self._start_entity = None self.start_entity = start_entity self._end_entity = None self.end_entity = end_entity self.relationship_properties = relationship_properties or {} self.params = params self.skip = None self.limit = 1 if single_relationship else None def reset(self): super(RelatedEntityQuery, self).reset() self.skip = None self.limit = None if self.start_entity: self.start_entity.query_variable = self.start_query_variable VM.set_query_var(self.start_entity) if self.end_entity: self.end_entity.query_variable = self.end_query_variable VM.set_query_var(self.end_entity) def _get_relationship_entity(self): return self._relationship_entity def _set_relationship_entity(self, relationship): if relationship is not None and not isinstance(relationship, Relationship): raise AttributeError('Must be an <Relationship> and not' ' a <{t}>'.format(t=type(relationship))) self._relationship_entity = relationship return self relationship_entity = property(_get_relationship_entity, _set_relationship_entity) def _get_start_entity(self): return self._start_entity def _set_start_entity(self, entity): if entity is not None and not isinstance(entity, Node): raise AttributeError('entity must be a Node instance') if entity: entity.query_variable = self.start_query_variable VM.set_query_var(entity) self._start_entity = entity start_entity = property(_get_start_entity, _set_start_entity) def _get_end_entity(self): return self._end_entity def _set_end_entity(self, entity): if entity is not None and not isinstance(entity, Node): raise AttributeError('entity must be a Node instance') if entity: entity.query_variable = self.end_query_variable VM.set_query_var(entity) self._end_entity = entity end_entity = property(_get_end_entity, _set_end_entity) def _build_start(self): pypher = Pypher() if self.start_entity.id is not None: qv = self.start_entity.query_variable where = __.ID(qv) == self.start_entity.id pypher.NODE(qv) self.wheres.append(where) else: pypher.NODE(self.start_query_variable, labels=self.start_entity.labels) return pypher def _build_end(self): pypher = Pypher() qv = self.end_query_variable labels = None if self.end_entity: qv = self.end_entity.query_variable labels = self.end_entity.labels pypher.NODE(qv, labels=labels) self.returns.append(qv) return pypher def _build_relationship(self): pypher = Pypher() if self.relationship_entity: self.relationship_entity.query_variable =\ self.relationship_query_variable VM.set_query_var(self.relationship_entity) pypher.relationship(self.relationship_entity.query_variable, direction=self.direction, labels=self.relationship_entity.labels, **self.relationship_properties) else: pypher.relationship(direction=self.direction, labels=self.relationship_type) return pypher def query(self, return_relationship=False, returns=None): if not self.start_entity: raise RelatedQueryException(('Related objects must have a' ' start entity')) self.pypher = Pypher() pypher = self.pypher self.matches.insert(0, self._build_end()) self.matches.insert(0, self._build_relationship()) self.matches.insert(0, self._build_start()) self.pypher.MATCH for match in self.matches: self.pypher.append(match) if self.wheres: self.pypher.WHERE.CAND(*self.wheres) if return_relationship: ret = getattr(__, self.relationship_query_variable) self.returns = [ ret, ] returns = returns or self.returns self.pypher.RETURN(*returns) if self.orders: self.pypher.ORDER.BY(*self.orders) if self.skip is not None: self.pypher.SKIP(self.skip) if self.limit is not None: self.pypher.LIMIT(self.limit) self.reset() return str(pypher), pypher.bound_params def connect(self, entity, properties=None): if not self.start_entity: message = ('The relationship {} does not have a start' ' entity'.format(self.relationship_entity or self.relationship_type)) raise RelatedQueryException(('The relationship {} does not ')) if self.start_entity == entity: msg = ('the start entity {} cannot be the same as the' ' end entity'.format(entity)) raise RelatedQueryException(msg) start = self.start_entity end = entity if self.direction == 'in': start, end = end, start kwargs = { 'start': start, 'end': end, 'properties': properties, } if self.relationship_entity: return self.relationship_entity.__class__(**kwargs) kwargs['labels'] = self.relationship_type return Relationship(**kwargs) def delete(self, entity): if isinstance(entity, Relationship): if entity.id is not None: query = Query(entity) return query.delete() elif entity.end and hasattr(entity.end, 'id'): self.matches.insert(0, self._build_end()) self.matches.insert(0, self._build_relationship()) self.matches.insert(0, self._build_start()) self.pypher.MATCH for match in self.matches: self.pypher.append(match) self.pypher.DETACH.DELETE(self.relationship_query_variable) self.reset() return str(self.pypher), self.pypher.bound_params def delete_by_entity_id(self, *ids): if not ids: msg = 'There must be ids passed in to the delete method' raise AttributeError(msg) def _build_start(): pypher = Pypher() if self.start_entity.id is not None: qv = self.start_entity.query_variable where = __.ID(qv) == self.start_entity.id pypher.NODE(qv) self.wheres.append(where) else: pypher.NODE(self.start_query_variable) return pypher self.pypher = Pypher() self.matches.insert(0, self._build_end()) self.matches.insert(0, self._build_relationship()) self.matches.insert(0, _build_start()) self.pypher.MATCH for match in self.matches: self.pypher.append(match) id_params = [] for i in ids: key = 'end_id_{}'.format(i) id_params.append(Param(key, i)) self.wheres.append(__.ID(self.end_query_variable).IN(*id_params)) _id = __.ID(self.start_query_variable) if self.start_entity.id is not None: self.wheres.append(_id == self.start_entity.id) # else: # wheres.append(_id) self.pypher.WHERE.CAND(*self.wheres) self.pypher.DELETE(self.relationship_query_variable) self.reset() return str(self.pypher), self.pypher.bound_params