def to_son(self, include_empty=True, exclude_fields=None, only_fields=None):
        """
        Return as a SON object.

        The content is identical to the result of 'to_mongo', except that empty
        fields are included and fields use their model names, instead of
        database names (e.g. '_id' -> 'id').

        Optionally, if include_empty is True (as it is by default), empty fields
        are included.

        Optionally, either of 'exclude_fields' or 'only_fields' (but not both)
        may be specified, containing an iterable of field names to explicitly
        include or exclude from the result. The 'id' field is always included.
        """
        if (exclude_fields is not None) and (only_fields is not None):
            raise Exception()

        # TODO: convert reference fields as {'id':.., 'label':..}

        # SON is ordered, so we must build a new one to change keys
        reverse_db_field_map = dict(self._reverse_db_field_map)
        reverse_db_field_map['_id'] = '_id'
        reverse_db_field_map['_cls'] = '_type'
        son = SON( (reverse_db_field_map[mongo_field], value)
                   for (mongo_field, value)
                   in self.to_mongo().iteritems() )

        # TODO: remove compatibility field
        son['id'] = son['_id']

        if include_empty:
            # 'to_mongo' omits empty fields
            for field_name in self:
                son.setdefault(field_name, None)

        if exclude_fields is not None:
            for field in exclude_fields:
                if field not in ['_id', '_type', 'id']:   # TODO: remove compatibility field
                    # use 'pop', in case 'include_empty' is False and the field doesn't exist
                    son.pop(field, None)
        elif only_fields is not None:
            only_fields = set(only_fields)
            # always include '_id' and (optionally) '_type'
            only_fields.add('_id')
            only_fields.add('_type')  # always include 'id'
            only_fields.add('id')  # TODO: remove compatibility field
            for field in son.iterkeys():
                if field not in only_fields:
                    del son[field]

        return son
    def as_command(self, sock_info):
        """Return a find command document for this query."""
        # We use the command twice: on the wire and for command monitoring.
        # Generate it once, for speed and to avoid repeating side-effects
        # like incrementing the session's statement id.
        if self.__as_command is not None:
            return self.__as_command

        explain = '$explain' in self.spec
        cmd = _gen_find_command(self.coll, self.spec, self.fields,
                                self.ntoskip, self.limit, self.batch_size,
                                self.flags, self.read_concern, self.collation)
        if explain:
            self.name = 'explain'
            cmd = SON([('explain', cmd)])
        session = self.session
        if session:
            session._apply_to(cmd, False, self.read_preference)
            # Explain does not support readConcern.
            if (not explain and session.options.causal_consistency
                    and session.operation_time is not None
                    and not session._in_transaction):
                cmd.setdefault('readConcern',
                               {})['afterClusterTime'] = session.operation_time
        sock_info.send_cluster_time(cmd, session, self.client)
        self.__as_command = cmd, self.db
        return self.__as_command
    def as_command(self, sock_info):
        """Return a find command document for this query."""
        # We use the command twice: on the wire and for command monitoring.
        # Generate it once, for speed and to avoid repeating side-effects.
        if self._as_command is not None:
            return self._as_command

        explain = '$explain' in self.spec
        cmd = _gen_find_command(
            self.coll, self.spec, self.fields, self.ntoskip,
            self.limit, self.batch_size, self.flags, self.read_concern,
            self.collation)
        if explain:
            self.name = 'explain'
            cmd = SON([('explain', cmd)])
        session = self.session
        if session:
            session._apply_to(cmd, False, self.read_preference)
            # Explain does not support readConcern.
            if (not explain and session.options.causal_consistency
                    and session.operation_time is not None
                    and not session._in_transaction):
                cmd.setdefault(
                    'readConcern', {})[
                    'afterClusterTime'] = session.operation_time
        sock_info.send_cluster_time(cmd, session, self.client)
        self._as_command = cmd, self.db
        return self._as_command
Exemple #4
0
 def _touch_query(self):
     if self._query_additions:
         spec = SON({'$query': self.spec or {}})
         for k, v in self._query_additions:
             if k == 'sort':
                 ordering = spec.setdefault('$orderby', SON())
                 ordering.update(v)
         self.spec = spec
Exemple #5
0
 def _touch_query(self):
     if self._query_additions:
         spec = SON({'$query': self.spec or {}})
         for k, v in self._query_additions:
             if k == 'sort':
                 ordering = spec.setdefault('$orderby', SON())
                 ordering.update(v)
         self.spec = spec
Exemple #6
0
def _gen_find_command(coll, spec, projection, skip, limit, batch_size, options,
                      session, client, read_concern=DEFAULT_READ_CONCERN,
                      collation=None):
    """Generate a find command document."""
    cmd = SON([('find', coll)])
    if '$query' in spec:
        cmd.update([(_MODIFIERS[key], val) if key in _MODIFIERS else (key, val)
                    for key, val in spec.items()])
        if '$explain' in cmd:
            cmd.pop('$explain')
        if '$readPreference' in cmd:
            cmd.pop('$readPreference')
    else:
        cmd['filter'] = spec

    if projection:
        cmd['projection'] = projection
    if skip:
        cmd['skip'] = skip
    if limit:
        cmd['limit'] = abs(limit)
        if limit < 0:
            cmd['singleBatch'] = True
    if batch_size:
        cmd['batchSize'] = batch_size
    if read_concern.level:
        cmd['readConcern'] = read_concern.document
    if collation:
        cmd['collation'] = collation
    if options:
        cmd.update([(opt, True)
                    for opt, val in _OPTIONS.items()
                    if options & val])
    if session:
        cmd['lsid'] = session._use_lsid()
        if (session.options.causal_consistency
                and session.operation_time is not None):
            cmd.setdefault(
                'readConcern', {})['afterClusterTime'] = session.operation_time
    if client:
        client._send_cluster_time(cmd, session)
    return cmd
Exemple #7
0
def _gen_explain_command(
        coll, spec, projection, skip, limit, batch_size,
        options, read_concern, session, client):
    """Generate an explain command document."""
    cmd = _gen_find_command(coll, spec, projection, skip, limit, batch_size,
                            options, session=None, client=None)
    if read_concern.level:
        explain = SON([('explain', cmd), ('readConcern', read_concern.document)])
    else:
        explain = SON([('explain', cmd)])

    if session:
        explain['lsid'] = session._use_lsid()
        if (session.options.causal_consistency
                and session.operation_time is not None):
            explain.setdefault(
                'readConcern', {})['afterClusterTime'] = session.operation_time

    client._send_cluster_time(explain, session)
    return explain
Exemple #8
0
    def as_command(self, sock_info):
        """Return a find command document for this query.

        Should be called *after* get_message.
        """
        explain = '$explain' in self.spec
        cmd = _gen_find_command(self.coll, self.spec, self.fields,
                                self.ntoskip, self.limit, self.batch_size,
                                self.flags, self.read_concern, self.collation)
        if explain:
            self.name = 'explain'
            cmd = SON([('explain', cmd)])
        session = self.session
        if session:
            cmd['lsid'] = session._use_lsid()
            # Explain does not support readConcern.
            if (not explain and session.options.causal_consistency
                    and session.operation_time is not None):
                cmd.setdefault('readConcern',
                               {})['afterClusterTime'] = session.operation_time
        sock_info.send_cluster_time(cmd, session, self.client)
        return cmd, self.db
Exemple #9
0
    def as_command(self, sock_info):
        """Return a find command document for this query.

        Should be called *after* get_message.
        """
        explain = '$explain' in self.spec
        cmd = _gen_find_command(
            self.coll, self.spec, self.fields, self.ntoskip,
            self.limit, self.batch_size, self.flags, self.read_concern,
            self.collation)
        if explain:
            self.name = 'explain'
            cmd = SON([('explain', cmd)])
        session = self.session
        if session:
            cmd['lsid'] = session._use_lsid()
            # Explain does not support readConcern.
            if (not explain and session.options.causal_consistency
                    and session.operation_time is not None):
                cmd.setdefault(
                    'readConcern', {})[
                    'afterClusterTime'] = session.operation_time
        sock_info.send_cluster_time(cmd, session, self.client)
        return cmd, self.db