Пример #1
0
    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,
        }
Пример #2
0
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