Python-MongoDB翻译

文章首发:点点寒彬的博客

前言

都说MongoDB这个NoSql数据库特别友好,使用起来也非常方便,今天稍微接触了一下,发现真的是非常非常简单,但是网上对这块内容涉及的很少,而且很多内容都是很老的版本使用的方法,我看了一下官方API文档中描述的内容也不多,顺手翻译一下吧,造福一下后来人。

注:我并不是逐字逐句的翻译,在某些地方我无法精确翻译的时候,我概括了大概的意思,有能力的可以直接看官方文档

前提条件

首先,我们需要安装PyMongo模块,在PythonShell中下面的代码能正常运行且无报错。

>>> import pymongo

当然,你的MongoDB需要在默认地址和端口开启,你可以按照这个方法确认你是否安装了MongoDB:

$ mongod

建立一个MongoClient

第一步我们需要建立一个MongoClient来运行MongoDB,这个做起来非常简单:

>>> from pymongo import MongoClient
>>> client = MongoClient()

上面的代码会连接默认的地址和端口,我们也可以自己指定地址和端口,像下面这样:

>>> client = MongoClient('localhost', 27017)

或者使用MongoDB的URI连接:

>>> client = MongoClient('mongodb://localhost:27017/')

获取数据库

一个MongoDB可以提供多个数据库接口。当使用PyMongo的时候,你可以使用获取属性的方式建立MongoClient:

>>> db = client.test_database

如果你使用上面的方法无法获取数据库的时候,你可以使用字典的方式来获取数据库。

获取连接

获取连接的方式和获取数据库的方式一样,也可以使用获取属性的方式:

>>> collection = db.test_collection

或者使用字典的方式:

>>> collection = db['test-collection']

注意:使用MongoDB建立连接非常容易,但是上面的所有操作都不是实实在在的操作了MongoDB,当第一次获取数据库和建立连接的时候,数据库和连接会被创建。

文档

MongoDB的数据格式存储使用的是JSON风格,在PyMongo中,我们使用的字典类型就相当于数据。比如下面的数据就类似博客中的post信息:

>>> import datetime
>>> post = {"author": "Mike",
...         "text": "My first blog post!",
...         "tags": ["mongodb", "python", "pymongo"],
...         "date": datetime.datetime.utcnow()}

数据格式可以容纳python的类型(就像datetime.datetime)

插入数据

往数据库插入一条数据我们使用insert_one()方法:

>>> posts = db.posts
>>> post_id = posts.insert_one(post).inserted_id
>>> post_id
ObjectId('...')

当我们插入数据成功的时候,MongoDB会生成一个特殊的主键“_id”。这个主键“_id”的值是唯一的。insert_one()返回的值就是这个主键的值。

插入第一条数据后,数据库、数据表和连接都建立了,我们可以查看列表中的所有信息:

>>> db.collection_names(include_system_collections=False)
[u'posts']

使用find_one()方法获取一条记录

在MongoDB中最基本的方法就是find_one()。这个方法返回一条符合条件的记录(或者None)。当你知道某条信息只有一个的时候,这个方法非常有用,或者返回第一条数据,这里我们使用这个方法获取第一条数据:

>>> posts.find_one()
{u'date': datetime.datetime(...), u'text': u'My first blog post!', u'_id': ObjectId('...'), u'author': u'Mike'

结果是我们之前插入的一条字典型的数据。

注意:返回结果中的“_id”是插入数据时自动添加的。

find_one()同时也提供了精确查找的功能,下面的示例是我们定位author为Mike的代码:

>>> posts.find_one({"author": "Mike"})
{u'date': datetime.datetime(...), u'text': u'My first blog post!', u'_id': ObjectId('...'), u'author': u'Mike'

如果我们把author换成另外一个,则不会返回结果:

>>> posts.find_one({"author": "Eliot"})
>>>

使用ObjectId查询

我们也可以用“_id”来查询,下面是我们的示例:

>>> post_id
ObjectId(...)
>>> posts.find_one({"_id": post_id})
{u'date': datetime.datetime(...), u'text': u'My first blog post!', u'_id': ObjectId('...'), u'author': u'Mike'

注意:ObjectId和转换为字符串的ObjectId是不一样的:

>>> post_id_as_str = str(post_id)
>>> posts.find_one({"_id": post_id_as_str}) # No result
>>>

在web应用中一个很常见的现象就是从URI中获取ObjectId来查询后台记录。在使用这个之前需要把str类型转换成ObjectId类型:

from bson.objectid import ObjectId

# The web framework gets post_id from the URL and passes it as a string
def get(post_id):
    # Convert from string to ObjectId:
    document = client.db.collection.find_one({'_id': ObjectId(post_id)})

Unicode Strings注意事项

你可能会注意到我们的数据和存进去的数据有一些不一样,比如( u’Mike’ 而不是‘Mike’)。

MongoDB是使用BSON来存储数据。BSON是使用utf-8来编码的。所以PyMongo存储的str类型必须是utf-8的。(‘str’类型)也是要被支持的,因此Unicode类型(<‘unicode’类型>)需要先编码成utf-8,我们的示例中PythonShell显示的是u’Mike’而不是‘Mike’就是因为PyMongo解码了BSON的string变成Python的unicode string。

大量的插入数据

为了让查询更有意思,我们需要插入多一些数据。能够新增一条记录,我们也可以做到新增多条记录。使用insert_many()方法插入由第一条数据组成的列表,只需要往服务端发送一条命令:

>>> new_posts = [{"author": "Mike",
...               "text": "Another post!",
...               "tags": ["bulk", "insert"],
...               "date": datetime.datetime(2009, 11, 12, 11, 14)},
...              {"author": "Eliot",
...               "title": "MongoDB is fun",
...               "text": "and pretty easy too!",
...               "date": datetime.datetime(2009, 11, 10, 10, 45)}]
>>> result = posts.insert_many(new_posts)
>>> result.inserted_ids
[ObjectId('...'), ObjectId('...')]

这个例子有几个有意思的地方:

  • insert_many()返回了两个相互对应ObjectId
  • new_post[1]有一个不同的地方,多了一个“tags”字段。而现在我们又多了一个字段,这就是我们说MongoDB非常schema-free(开放?)

查询一条以上的记录

我们使用find()方法来获取一条以上的记录。find()返回一组符合条件的记录,并且允许我们通过迭代的方式获取它们。例如,在post中我们可以迭代的获取所有查询的数据:

>>> for post in posts.find():
...   post
...
{u'date': datetime.datetime(...), u'text': u'My first blog post!', u'_id': ObjectId('...'), u'author': u'Mike', u'tags': [u'mongodb', u'python', u'pymongo']}
{u'date': datetime.datetime(2009, 11, 12, 11, 14), u'text': u'Another post!', u'_id': ObjectId('...'), u'author': u'Mike', u'tags': [u'bulk', u'insert']}
{u'date': datetime.datetime(2009, 11, 10, 10, 45), u'text': u'and pretty easy too!', u'_id': ObjectId('...'), u'author': u'Eliot', u'title': u'MongoDB is fun'}

就像我们使用find_one()一样,我们也可以在这里加上查询条件来获取符合条件的数据:

>>> for post in posts.find({"author": "Mike"}):
...   post
...
{u'date': datetime.datetime(...), u'text': u'My first blog post!', u'_id': ObjectId('...'), u'author': u'Mike', u'tags': [u'mongodb', u'python', u'pymongo']}
{u'date': datetime.datetime(2009, 11, 12, 11, 14), u'text': u'Another post!', u'_id': ObjectId('...'), u'author': u'Mike', u'tags': [u'bulk', u'insert']}

计数

如果我们只想获取数量,我们可以使用count()方法来代替全记录查询。我们可以获得查询的数量:

>>> posts.count()
3

或者获得符合条件的数量:

>>> posts.find({"author": "Mike"}).count()
2

不同种类的查询

MongoDB还提供了许多种类的查询方式。例如我们查询数据的日期小于某一个日期,且按照author字段来排序:

>>> d = datetime.datetime(2009, 11, 12, 12)
>>> for post in posts.find({"date": {"$lt": d}}).sort("author"):
...   print post
...
{u'date': datetime.datetime(2009, 11, 10, 10, 45), u'text': u'and pretty easy too!', u'_id': ObjectId('...'), u'author': u'Eliot', u'title': u'MongoDB is fun'}
{u'date': datetime.datetime(2009, 11, 12, 11, 14), u'text': u'Another post!', u'_id': ObjectId('...'), u'author': u'Mike', u'tags': [u'bulk', u'insert']}

这里我们使用“$lt”来做一个限制的查询,使用sort()来对author字段排序。

主键

增加一个主键能够帮助我们快速而准确的查询记录,也可以使我们存储的数据更加有用。在这个示例中,我们将会展示在一个已存在主键的表中新增一个主键:

首先,我们要新增一个主键:

>>> result = db.profiles.create_index([('user_id', pymongo.ASCENDING)],
...                                   unique=True)
>>> list(db.profiles.index_information())
[u'user_id_1', u'_id_']

注意,我们现在有两个主键了,一个是MongoDB自带的”_id“,另一个是我们新增的”user_id“。

现在,我们来新增一些用户信息:

>>> user_profiles = [
...     {'user_id': 211, 'name': 'Luke'},
...     {'user_id': 212, 'name': 'Ziltoid'}]
>>> result = db.profiles.insert_many(user_profiles)

我们新增的主键阻止我们新增一条已存在这个主键的记录。

>>> new_profile = {'user_id': 213, 'name': 'Drew'}
>>> duplicate_profile = {'user_id': 212, 'name': 'Tommy'}
>>> result = db.profiles.insert_one(new_profile)  # This is fine.
>>> result = db.profiles.insert_one(duplicate_profile)
Traceback (most recent call last):
pymongo.errors.DuplicateKeyError: E11000 duplicate key error index: test_database.profiles.$user_id_1 dup key: { : 212 }

写在最后

官方文档上并没有给出删除,修改记录的方法,或许是我目前还没看到吧,等看到了我会再补充上来的。文档比较短,翻译的过程中由于本人英语水平有限,并不能做到逐字逐句的翻译,也无法保证完全准确,并且会有一些地方的内容念起来并不是那么的顺口,但是官方文档想表达的大概意思应该是不会错的。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,406评论 6 503
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,732评论 3 393
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,711评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,380评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,432评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,301评论 1 301
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,145评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,008评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,443评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,649评论 3 334
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,795评论 1 347
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,501评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,119评论 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,731评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,865评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,899评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,724评论 2 354

推荐阅读更多精彩内容