def find_and_modify(self, query={}, update=None, upsert=False, **kwargs): def wrapper(result): no_obj_error = "No matching object found" if not result['ok']: if result["errmsg"] == no_obj_error: return None else: raise ValueError("Unexpected Error: %s" % (result, )) return result.get('value') if (not update and not kwargs.get('remove', None)): raise ValueError("Must either update or remove") if (update and kwargs.get('remove', None)): raise ValueError("Can't do both update and remove") cmd = SON([("findAndModify", self._collection_name)]) cmd.update(kwargs) # No need to include empty args if query: cmd['query'] = query if update: cmd['update'] = update if upsert: cmd['upsert'] = upsert no_obj_error = "No matching object found" d = self._database["$cmd"].find_one(cmd) d.addCallback(wrapper) return d
def find(self, spec=None, skip=0, limit=0, fields=None, filter=None, _proto=None): if spec is None: spec = SON() if not isinstance(spec, types.DictType): raise TypeError("spec must be an instance of dict") if not isinstance(fields, (types.ListType, types.NoneType)): raise TypeError("fields must be an istance of list") if not isinstance(skip, types.IntType): raise TypeError("skip must be an instance of int") if not isinstance(limit, types.IntType): raise TypeError("limit must be an instance of int") if fields is not None: if not fields: fields = ["_id"] fields = self._fields_list_to_dict(fields) if isinstance(filter, (qf.sort, qf.hint, qf.explain, qf.snapshot)): spec = SON(dict(query=spec)) for k, v in filter.items(): spec[k] = isinstance(v, types.TupleType) and SON(v) or v # send the command through a specific connection # this is required for the connection pool to work # when safe=True if _proto is None: proto = self._database._connection else: proto = _proto return proto.OP_QUERY(str(self), spec, skip, limit, fields)
def find_and_modify(self, query={}, update=None, upsert=False, **kwargs): def wrapper(result): no_obj_error = "No matching object found" if not result['ok']: if result["errmsg"] == no_obj_error: return None else: raise ValueError("Unexpected Error: %s" % (result,)) return result.get('value') if (not update and not kwargs.get('remove', None)): raise ValueError("Must either update or remove") if (update and kwargs.get('remove', None)): raise ValueError("Can't do both update and remove") cmd = SON([("findAndModify", self._collection_name)]) cmd.update(kwargs) # No need to include empty args if query: cmd['query'] = query if update: cmd['update'] = update if upsert: cmd['upsert'] = upsert no_obj_error = "No matching object found" d = self._database["$cmd"].find_one(cmd) d.addCallback(wrapper) return d
def findAndModify(self, query={}, update=None, upsert=False, **kwargs): '''@see: http://api.mongodb.org/python/1.10.1%2B/api/pymongo/collection.html#pymongo.collection.Collection.find_and_modify''' cmd = SON([("findAndModify", self._collection_name), ("query", query), ("update", update), ("upsert", upsert), ]) cmd.update(**kwargs) d = self._database["$cmd"].find_one(cmd) return d
def aggregate(self, pipeline): if not isinstance(pipeline, (dict, list, tuple)): raise TypeError("pipeline must be a dict, list or tuple") if isinstance(pipeline, dict): pipeline = [pipeline] command = SON([("aggregate", self._collection_name)]) command.update({"pipeline": pipeline}) return self._database["$cmd"].find_one(command)
def findAndModify(self, query={}, update=None, upsert=False, **kwargs): '''@see: http://api.mongodb.org/python/1.10.1%2B/api/pymongo/collection.html#pymongo.collection.Collection.find_and_modify''' cmd = SON([ ("findAndModify", self._collection_name), ("query", query), ("update", update), ("upsert", upsert), ]) cmd.update(**kwargs) d = self._database["$cmd"].find_one(cmd) return d
def command(self, command, value=1, **kwargs): ''' @see: http://www.mongodb.org/display/DOCS/Commands @see: http://api.mongodb.org/python/1.10.1%2B/api/pymongo/database.html#pymongo.database.Database.command ''' if isinstance(command, basestring): command = SON([(command, value)]) command.update(kwargs) d = self["$cmd"].find_one(command) return d
def map_reduce(self, map, reduce, full_response=False, **kwargs): def wrapper(result, full_response): if full_response: return result return result.get("result") cmd = SON([("mapreduce", self._collection_name), ("map", map), ("reduce", reduce)]) cmd.update(**kwargs) d = self._database["$cmd"].find_one(cmd) d.addCallback(wrapper, full_response) return d
def runCommand(self, command, value=1, **kwargs): ''' @see: http://www.mongodb.org/display/DOCS/Commands @deprecated: use database.command() instead, as in pymongo http://api.mongodb.org/python/1.10.1%2B/api/pymongo/database.html#pymongo.database.Database.command ''' warnings.warn("collection.runCommand: use database.command() instead", DeprecationWarning) cmd = SON([ (command, value) ]) cmd.update(**kwargs) d = self._database["$cmd"].find_one(cmd) return d
def runCommand(self, command, value=1, **kwargs): ''' @see: http://www.mongodb.org/display/DOCS/Commands @deprecated: use database.command() instead, as in pymongo http://api.mongodb.org/python/1.10.1%2B/api/pymongo/database.html#pymongo.database.Database.command ''' warnings.warn("collection.runCommand: use database.command() instead", DeprecationWarning) cmd = SON([(command, value)]) cmd.update(**kwargs) d = self._database["$cmd"].find_one(cmd) return d
def map_reduce(self, map, reduce, full_response=False, **kwargs): def wrapper(result, full_response): if full_response: return result return result.get("result") cmd = SON([("mapreduce", self._collection_name), ("map", map), ("reduce", reduce)]) cmd.update(**kwargs) d = self._database["$cmd"].find_one(cmd) d.addCallback(wrapper, full_response) return d
def count(self, spec=None, fields=None): def wrapper(result): return result["n"] if fields is not None: if not fields: fields = ["_id"] fields = self._fields_list_to_dict(fields) spec = SON([("count", self._collection_name), ("query", spec or SON()), ("fields", fields)]) d = self._database["$cmd"].find_one(spec) d.addCallback(wrapper) return d
def create_index(self, sort_fields, **kwargs): def wrapper(result, name): return name if not isinstance(sort_fields, qf.sort): raise TypeError("sort_fields must be an instance of filter.sort") if "name" not in kwargs: name = self._gen_index_name(sort_fields["orderby"]) else: name = kwargs.pop("name") key = SON() for k,v in sort_fields["orderby"]: key.update({k:v}) index = SON(dict( ns=str(self), name=name, key=key )) if "drop_dups" in kwargs: kwargs["dropDups"] = kwargs.pop("drop_dups") if "bucket_size" in kwargs: kwargs["bucketSize"] = kwargs.pop("bucket_size") index.update(kwargs) d = self._database.system.indexes.insert(index, safe=True) d.addCallback(wrapper, name) return d
def filemd5(self, spec): if not isinstance(spec, ObjectId): raise ValueError( "filemd5 expected an objectid for its on-keyword argument") spec = SON([("filemd5", spec), ("root", self._collection_name)]) doc = yield self._database['$cmd'].find_one(spec) defer.returnValue((doc or {}).get('md5'))
def as_doc(self): """Get the SON document representation of this DBRef. Generally not needed by application developers """ doc = SON([("$ref", self.collection), ("$id", self.id)]) if self.database is not None: doc["$db"] = self.database return doc
def create_index(self, sort_fields, unique=False, dropDups=False): def wrapper(result, name): return name if not isinstance(sort_fields, qf.sort): raise TypeError("sort_fields must be an instance of filter.sort") name = self._gen_index_name(sort_fields["orderby"]) index = SON( dict(ns=str(self), name=name, key=SON(dict(sort_fields["orderby"])), unique=unique, dropDups=dropDups)) d = self._database.system.indexes.insert(index, safe=True) d.addCallback(wrapper, name) return d
def remove(self, spec, safe=False): if isinstance(spec, ObjectId): spec = SON(dict(_id=spec)) if not isinstance(spec, types.DictType): raise TypeError("spec must be an instance of dict, not %s" % type(spec)) proto = self._database._connection proto.OP_DELETE(str(self), spec) return self.__safe_operation(proto, safe)
def drop_index(self, index_identifier): if isinstance(index_identifier, types.StringTypes): name = index_identifier elif isinstance(index_identifier, qf.sort): name = self._gen_index_name(index_identifier["orderby"]) else: raise TypeError("index_identifier must be a name or instance of filter.sort") cmd = SON([("deleteIndexes", self._collection_name), ("index", name)]) return self._database["$cmd"].find_one(cmd)
def test_index_haystack(self): db = self.db coll = self.coll yield coll.drop_indexes() _id = yield coll.insert({ "pos": { "long": 34.2, "lat": 33.3 }, "type": "restaurant" }) yield coll.insert({ "pos": { "long": 34.2, "lat": 37.3 }, "type": "restaurant" }) yield coll.insert({ "pos": { "long": 59.1, "lat": 87.2 }, "type": "office" }) yield coll.create_index( filter.sort(filter.GEOHAYSTACK("pos") + filter.ASCENDING("type")), **{'bucket_size': 1}) # TODO: A db.command method has not been implemented yet. # Sending command directly command = SON([ ("geoSearch", "mycol"), ("near", [33, 33]), ("maxDistance", 6), ("search", { "type": "restaurant" }), ("limit", 30), ]) results = yield db["$cmd"].find_one(command) self.assertEqual(2, len(results['results'])) self.assertEqual( { "_id": _id, "pos": { "long": 34.2, "lat": 33.3 }, "type": "restaurant" }, results["results"][0])
def create_index(self, sort_fields, **kwargs): def wrapper(result, name): return name if not isinstance(sort_fields, qf.sort): raise TypeError("sort_fields must be an instance of filter.sort") if "name" not in kwargs: name = self._gen_index_name(sort_fields["orderby"]) else: name = kwargs.pop("name") key = SON() for k,v in sort_fields["orderby"]: key.update({k:v}) index = SON(dict( ns=str(self), name=name, key=key )) if "drop_dups" in kwargs: kwargs["dropDups"] = kwargs.pop("drop_dups") if "bucket_size" in kwargs: kwargs["bucketSize"] = kwargs.pop("bucket_size") index.update(kwargs) d = self._database.system.indexes.insert(index, safe=True) d.addCallback(wrapper, name) return d
def filemd5(self, spec): def wrapper(result): return result.get('md5') if not isinstance(spec, ObjectId): raise ValueError( "filemd5 expected an objectid for its on-keyword argument") spec = SON([("filemd5", spec), ("root", self._collection_name)]) d = self._database['$cmd'].find_one(spec) d.addCallback(wrapper) return d
def create_collection(self, name, options={}): def wrapper(result, deferred, collection): if result.get("ok", 0.0): deferred.callback(collection) else: deferred.errback(RuntimeError(result.get("errmsg", "unknown error"))) deferred = defer.Deferred() collection = Collection(self, name) if options: if "size" in options: options["size"] = float(options["size"]) command = SON({"create": name}) command.update(options) d = self["$cmd"].find_one(command) d.addCallback(wrapper, deferred, collection) else: deferred.callback(collection) return deferred
def authenticate_with_nonce(self, result, name, password, d): nonce = result['nonce'] key = helpers._auth_key(nonce, name, password) # hacky because order matters auth_command = SON(authenticate=1) auth_command['user'] = unicode(name) auth_command['nonce'] = nonce auth_command['key'] = key # Now actually authenticate self["$cmd"].find_one(auth_command).addCallback( self.authenticated, d).addErrback(d.errback)
def create_collection(self, name, options={}): def wrapper(result, deferred, collection): if result.get("ok", 0.0): deferred.callback(collection) else: deferred.errback(RuntimeError(result.get("errmsg", "unknown error"))) deferred = defer.Deferred() collection = Collection(self, name) if options: if "size" in options: options["size"] = float(options["size"]) command = SON({"create": name}) command.update(options) d = self["$cmd"].find_one(command) d.addCallback(wrapper, deferred, collection) else: deferred.callback(collection) return deferred
def distinct(self, key, spec=None): def wrapper(result): if result: return result.get("values") return {} cmd = SON([("distinct", self._collection_name), ("key", key)]) if spec: cmd["query"] = spec d = self._database["$cmd"].find_one(cmd) d.addCallback(wrapper) return d
def _authenticate_with_nonce(self, result, name, password, d): nonce = result['nonce'] key = helpers._auth_key(nonce, name, password) # hacky because order matters auth_command = SON(authenticate=1) auth_command['user'] = unicode(name) auth_command['nonce'] = nonce auth_command['key'] = key # Now actually authenticate Collection(self.database, "$cmd").find_one(auth_command, _proto=self).addCallback( self._authenticated, d).addErrback(self._auth_error, d)
def find_one(self, spec=None, fields=None, _proto=None): def wrapper(docs): doc = docs and docs[0] or {} if doc.get("err") is not None: raise errors.OperationFailure(doc) else: return doc if isinstance(spec, ObjectId): spec = SON(dict(_id=spec)) d = self.find(spec, limit=-1, fields=fields, _proto=_proto) d.addCallback(wrapper) return d
def checkMaster(self, deferred): try: c = self.connection() if isinstance( c, _offline ): # don't bother trying to check for master if not connected return cursor = yield c.OP_QUERY("admin.$cmd", SON([('isMaster', 1)]), 0, -1) results = yield cursor.as_list() info = results and results[0] or {} self.isMaster = info.get('ismaster', False) if 'hosts' in info: self.manager.updateHosts(info['hosts']) deferred.callback(self.isMaster) except: reactor.callLater(1, self.checkMaster, deferred)
def append(self, conn): if not self._checkedMaster: self._checkedMaster = True cursor = yield conn.OP_QUERY("admin.$cmd", SON([('isMaster', 1)]), 0, -1) info = yield cursor.as_list() info = info and info[0] or {} self.isMaster = info.get('ismaster', False) if 'hosts' in info: self.manager.updateHosts(info['hosts']) self.ecount -= 1 self.size += 1 self.pool.append(conn) if self.deferred and self.size == self.pool_size: self.deferred.callback(self) self.deferred = None if self.disconnecting: conn.transport.loseConnection()
def find_one(self, spec=None, fields=None, filter=None, slave_okay=False, _proto=None): if isinstance(spec, ObjectId): spec = SON(dict(_id=spec)) cursor = yield self.find(spec, limit=-1, fields=fields, filter=filter, slave_okay=slave_okay, _proto=_proto) docs = yield cursor.as_list() if not docs: defer.returnValue(None) if isinstance(docs[0], dict) and docs[0].get("err") is not None: raise errors.OperationFailure(docs[0]) defer.returnValue(docs[0])
def _element_to_bson(key, value, check_keys): if not isinstance(key, (str, unicode)): raise InvalidDocument("documents must have only string keys, key was %r" % key) if check_keys: if key.startswith("$"): raise InvalidName("key %r must not start with '$'" % key) if "." in key: raise InvalidName("key %r must not contain '.'" % key) name = _make_c_string(key, True) if isinstance(value, float): return "\x01" + name + struct.pack("<d", value) # Use Binary w/ subtype 3 for UUID instances try: import uuid if isinstance(value, uuid.UUID): value = Binary(value.bytes, subtype=3) except ImportError: pass if isinstance(value, Binary): subtype = value.subtype if subtype == 2: value = struct.pack("<i", len(value)) + value return "\x05%s%s%s%s" % (name, struct.pack("<i", len(value)), chr(subtype), value) if isinstance(value, Code): cstring = _make_c_string(value) scope = _dict_to_bson(value.scope, False) full_length = struct.pack("<i", 8 + len(cstring) + len(scope)) length = struct.pack("<i", len(cstring)) return "\x0F" + name + full_length + length + cstring + scope if isinstance(value, str): cstring = _make_c_string(value) length = struct.pack("<i", len(cstring)) return "\x02" + name + length + cstring if isinstance(value, unicode): cstring = _make_c_string(value) length = struct.pack("<i", len(cstring)) return "\x02" + name + length + cstring if isinstance(value, dict): return "\x03" + name + _dict_to_bson(value, check_keys) if isinstance(value, (list, tuple)): as_dict = SON(zip([str(i) for i in range(len(value))], value)) return "\x04" + name + _dict_to_bson(as_dict, check_keys) if isinstance(value, ObjectId): return "\x07" + name + value.binary if value is True: return "\x08" + name + "\x01" if value is False: return "\x08" + name + "\x00" if isinstance(value, (int, long)): # TODO this is a really ugly way to check for this... if value > 2 ** 64 / 2 - 1 or value < -2 ** 64 / 2: raise OverflowError("MongoDB can only handle up to 8-byte ints") if value > 2 ** 32 / 2 - 1 or value < -2 ** 32 / 2: return "\x12" + name + struct.pack("<q", value) return "\x10" + name + struct.pack("<i", value) if isinstance(value, datetime.datetime): millis = int(calendar.timegm(value.timetuple()) * 1000 + value.microsecond / 1000) return "\x09" + name + struct.pack("<q", millis) if value is None: return "\x0A" + name if isinstance(value, _RE_TYPE): pattern = value.pattern flags = "" if value.flags & re.IGNORECASE: flags += "i" if value.flags & re.LOCALE: flags += "l" if value.flags & re.MULTILINE: flags += "m" if value.flags & re.DOTALL: flags += "s" if value.flags & re.UNICODE: flags += "u" if value.flags & re.VERBOSE: flags += "x" return "\x0B" + name + _make_c_string(pattern, True) + _make_c_string(flags) if isinstance(value, DBRef): return _element_to_bson(key, value.as_doc(), False) if isinstance(value, Timestamp): inc = struct.pack("<i", value.inc) timestamp = struct.pack("<i", value.time) return "\x11" + name + inc + timestamp raise InvalidDocument("cannot convert value of type %s to bson" % type(value))
def rename(self, new_name): cmd = SON([("renameCollection", str(self)), ("to", "%s.%s" % \ (str(self._database), new_name))]) return self._database("admin")["$cmd"].find_one(cmd)
def runCommand(self, command, value, **kwargs): '''@see: http://www.mongodb.org/display/DOCS/Commands''' cmd = SON([ (command, value) ]) cmd.update(**kwargs) d = self._database["$cmd"].find_one(cmd) return d