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
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
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
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
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
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