mongoengine教程(3)——数据查询

与Django一样,Document类都有一个 objects 属性。它用于将类与数据库关联起来。objects属性是一个QuerySetManager类型的对象,它的操作会返回一个QuerySet类型的对象。可以通过对QuerySet对象的迭代获取数据库中的数据。

class User(Document):
    name = StringField()
    country = StringField()

class Paper(Document):
    content = StringField()
    author = ReferenceField(User)

查询过滤

可以在查询是指定过滤条件以获取想要的结果。例如想要查询英国的用户:

uk_users = User.objects(country='uk')

与Django类似,要查询引用的对象只需要使用双下划线即可。例如想要查询英国用户的论文:

uk_papers = Paper.objects(author__country='uk')

查询操作

与Django类似,MongoEngine同样也提供了一些条件语句。

  • ne - 不相等
  • lt - 小于
  • lte - 小于等于
  • gt - 大于
  • gte - 大于等于
  • not - 取反
  • in - 值在列表中
  • nin - 值不在列表中
  • mod - 取模
  • all - 与列表的值相同
  • size - 数组的大小
  • exists - 字段的值存在

例如查询年龄小于等于18岁的用户:

young_users = Users.objects(age__lte=18)

对于不同类型的数据提供了不同的条件语句。

查询结果个数限制

跟传统的ORM一样,MongoEngine也可以限制查询结果的个数。一种方法是在QuerySet对象上调用limit和skip方法;另一种方法是使用数组的分片的语法。例如:

users = User.objects[10:15]
users = User.objects.skip(10).limit(5)

聚合操作

MongoEngine提供了一些数据库的聚合操作。

统计结果个数即可以使用QuerySet的count方法,也可以使用Python风格的方法:

num_users = len(User.objects)
num_users = User.objects.count()

其他的一些聚合操作。
求和:

yearly_expense = Employee.objects.sum('salary')

求平均数:

mean_age = User.objects.average('age')

高级查询

有时需要将多个条件进行组合,前面提到的方法就不能满足需求了。这时可以使用MongoEngine的Q类。它可以将多个查询条件进行 &(与) 和 |(或) 操作。

例如下面的语句是查询所有年龄大于等于18岁的英国用户,或者所有年龄大于等于20岁的用户。

User.objects((Q(country='uk') & Q(age__gte=18)) | Q(age__gte=20))

在服务器端执行javascript代码

通过MongoEngine QuerySet对象的 exec_js 方法可以将javascript代码作为字符串发送给服务器端执行,然后返回执行的结果。

例如查询该数据库都有那些集合:

User.objects.exec_js("db.getCollectionNames()")

mongoengine教程(2)——文档模式

在MongoDB中一个文档(document)与关系型数据库中的一行(row)相似;文档保存在集合(collection)中,行保存在表(table)中。


定义文档的模式


与django类似,要定义一个文档模式只需要创建一个类继承自 Document,并添加一些 Field 对象。


from mongoengine import *
import datetime

class Page(Document):
title = StringField(max_length=200, required=True)
date_modified = DateTimeField(default=datetime.datetime.now)

如上定义了一个文档模式具有 title和date_modified 两个字段。


同时MongoDB本身就是无模式的,因此我们还可以创建动态的文档模式。它可以在添加数据时为不同的数据设置不同的字段。


class Page(DynamicDocument):
title = StringField(max_length=200, required=True)

添加数据:


page = Page(title=’Using MongoEngine’)
page.tags = [‘mongodb’, ‘mongoengine’]
page.save()

文档字段


文档字段(Field)不是必需的,但是使用它来进行数据验证、设置默认值等操作会比较方便。


MongoEngine提供了如下这些类型的Field:



  • BinaryField

  • BooleanField

  • ComplexDateTimeField

  • DateTimeField

  • DecimalField

  • DictField

  • DynamicField

  • EmailField

  • EmbeddedDocumentField

  • FileField

  • FloatField

  • GenericEmbeddedDocumentField

  • GenericReferenceField

  • GeoPointField

  • ImageField

  • IntField

  • ListField

  • MapField

  • ObjectIdField

  • ReferenceField

  • SequenceField

  • SortedListField

  • StringField

  • URLField

  • UUIDField


文档之间引用关系


在关系型数据库中多个表可以使用外键进行关联。然而MongoDB是无模式的,因此想要达到这样的效果就这能在应用程序中自己手动的进行关联了。


不过还好,使用MongoEngine的ReferenceField可以很方便的实现。


class User(Document):
name = StringField()

class Page(Document):
content = StringField()
author = ReferenceField(User)

john = User(name=”John Smith”)
john.save()

post = Page(content=”Test Page”)
post.author = john
post.save()

一对多的关系


对于一对多的关系可以使用ListField来保存一个ReferenceField列表。在进行查询操作是需要传入一个实例对象。


class User(Document):
name = StringField()

class Page(Document):
content = StringField()
authors = ListField(ReferenceField(User))

bob = User(name=”Bob Jones”).save()
john = User(name=”John Smith”).save()

Page(content=”Test Page”, authors=[bob, john]).save()
Page(content=”Another Page”, authors=[john]).save()

# Find all pages Bob authored
Page.objects(authors__in=[bob])

引用对象的删除操作


MongoDB默认不会检查数据的完整性,因此在删除一个对象是就需要自己手动的处理引用了该对象的其他对象。


同样的MongoEngine也提供了一样的功能。ReferenceField有一个 reverse_delete_rule 参数可以进行设置。它的取值如下:



  • mongoengine.DO_NOTHING:默认就是这个值,它不会进行任何操作。

  • mongoengine.DENY:如果该对象还被其他对象引用,则拒绝删除。

  • mongoengine.NULLIFY:将其他对象对该对象的引用字段设为null。

  • mongoengine.CASCADE:将引用了该对象的其他对象也删除掉。

  • mongoengine.PULL:移除对该对象的引用。


索引


与django的Model相似,MongoEngine的Document也可以在meta属性中设置索引。


class Page(Document):
title = StringField()
rating = StringField()
meta = {
‘indexes’: [‘title’, (‘title’, ‘-rating’)]
}

meta中的indexes可以是一个列表,也可以是一个字典。

mongoengine教程(1)——概述

MongoEngine是MongoDB的一个ODM(Object-Document Mapper)框架,它提供了类似Django的语法来操作MongoDB数据库。


安装


安装 MongoEngine 需要先安装 PyMongo。


使用pip安装


$ [sudo] pip install mongoengine

通过源代码安装


先从 PyPi 或者 Github 下载源代码。然后再进行安装。


$ [sudo] python setup.py install

使用


首先启动 mongodb 服务器:


$ mongod

连接服务器


使用 connect 方法进行数据库链接,与pymongo的用法相似,其参数可以是多种型式的。


from mongoengine import connect
connect(‘project1’)
connect(‘project1’, host=’mongodb://localhost:27017/test_database’)

从 MongoEngine 0.6 开始增加了多数据库的支持, connect 的第二个参数可以为每个链接设置一个别名。


定义数据模型


mongoengine的 Document 与django的 Model 相似。


class User(mongoengine.Document):
name = mongoengine.StringField()

meta = {“db_alias”: “default”}

数据操作


数据的添加过程也与django相似:


User.objects.create(name=”test1”)
User.objects.create(name=”test2”)
User(name=”test3”).save()

查询数据:


User.objects.filter(name=”test2”)

删除数据:


User.objects.filter(name=”test2”).delete()

MongoEngine虽然提供了ODM,但是我们同样还是可以直接对数据库进行操作。

获取 pymongo 的 collection 对象:


User.objects._collection

然后就可以使用原生的pymongo操作了。

pymongo教程(3)——自定义数据类型

pymongo提供一些常用的数据类型,如:数据、字符串、日期等。如果感觉还不能满足需求,那么还可以自定义数据类型。

首先定义一个类:

class Custom(object):
    def __init__(self, x):
        self.__x = x

    def x(self):
        return self.__x

要将自定义类型的数据存入数据库中需要先进行编码;将数据从数据库读取出来后又需要再解码。

手动编码/解码

我们可以定义两个方法,在插入和查询数据时进行手动的编码、解码。

def encode_custom(custom):
    return {"_type": "custom", "x": custom.x()}

def decode_custom(document):
    assert document["_type"] == "custom"
    return Custom(document["x"])

print(db.test.insert({"custom": encode_custom(Custom(5))}))
print(db.test.find_one()['custom'])

自动编码/解码

手动地进行编码虽然可行,但是还是不太方便。我们还可以使用 SONManipulator 进行自动编码。

from pymongo.son_manipulator import SONManipulator
class Transform(SONManipulator):
    def transform_incoming(self, son, collection):
        for (key, value) in son.items():
            if isinstance(value, Custom):
                son[key] = encode_custom(value)
            elif isinstance(value, dict): # Make sure we recurse into sub-docs
                son[key] = self.transform_incoming(value, collection)
        return son

    def transform_outgoing(self, son, collection):
        for (key, value) in son.items():
            if isinstance(value, dict):
                if "_type" in value and value["_type"] == "custom":
                    son[key] = decode_custom(value)
                else: # Again, make sure to recurse into sub-docs
                    son[key] = self.transform_outgoing(value, collection)
        return son

db.add_son_manipulator(Transform())
print(db.test.insert({"custom": Custom(5)}))
print(db.test.find_one())

二进制编码

我们也可以将其编码成二进制进行存储。

from bson.binary import Binary
def to_binary(custom):
    return Binary(str(custom.x()), 128)

def from_binary(binary):
    return Custom(int(binary))

class TransformToBinary(SONManipulator):
    def transform_incoming(self, son, collection):
        for (key, value) in son.items():
            if isinstance(value, Custom):
                son[key] = to_binary(value)
            elif isinstance(value, dict):
                son[key] = self.transform_incoming(value, collection)
        return son

    def transform_outgoing(self, son, collection):
        for (key, value) in son.items():
            if isinstance(value, Binary) and value.subtype == 128:
                son[key] = from_binary(value)
            elif isinstance(value, dict):
                son[key] = self.transform_outgoing(value, collection)
        return son

db.add_son_manipulator(TransformToBinary())
print(db.test.insert({"custom": Custom(5)}))
print(db.test.find_one())

pymongo教程(2)——聚合操作

在MongoDB中常用的聚合操作有 aggregation、map/reduce和group 。


首先先添加一些测试数据:


db.things.insert({“x”: 1, “tags”: [“dog”, “cat”]})
db.things.insert({“x”: 2, “tags”: [“cat”]})
db.things.insert({“x”: 2, “tags”: [“mouse”, “cat”, “dog”]})
db.things.insert({“x”: 3, “tags”: []})

aggregation


以下例子是统计 tags 字段内的各个值的出现的次数。


from bson.son import SON
db.things.aggregate([
{“$unwind”: “$tags”},
{“$group”: {“_id”: “$tags”, “count”: {“$sum”: 1}}},
{“$sort”: SON([(“count”, -1), (“_id”, -1)])}
])

{‘ok’: 1.0, ‘result’: [{‘count’: 3, ‘_id’: ‘cat’}, {‘count’: 2, ‘_id’: ‘dog’}, {‘count’: 1, ‘_id’: ‘mouse’}]}

注意:aggregate操作要求服务器程序为 2.1.0 以上的版本。PyMongo 驱动程序为 2.3 以上的版本。


Map/Reduce


上面的操作同样也可以使用 Map/Reduce 完成。


from bson.code import Code
mapper = Code(“””
function () {
this.tags.forEach(function(z) {
emit(z, 1);
});
}
“””)

reducer = Code(“””
function (key, values) {
var total = 0;
for (var i = 0; i < values.length; i++) {
total += values[i];
}
return total;
}
“””)

result = db.things.map_reduce(mapper, reducer, “myresults”)
for doc in result.find():
print(doc)

{u’_id’: u’cat’, u’value’: 3.0}
{u’_id’: u’dog’, u’value’: 2.0}
{u’_id’: u’mouse’, u’value’: 1.0}

map和reduce都是一个javascript的函数; map_reduce 方法会将统计结果保存到一个临时的数据集合中。


Group


group 操作与SQL的 GROUP BY 相似,同时比 Map/Reduce 要简单。


reducer = Code(“””
function(obj, prev){
prev.count++;
}
“””)

results = db.things.group(key={“x”:1}, condition={}, initial={“count”: 0}, reduce=reducer)
for doc in results:
print(doc)

{‘x’: 1.0, ‘count’: 1.0}
{‘x’: 2.0, ‘count’: 2.0}
{‘x’: 3.0, ‘count’: 1.0}

注意:在MongoDB的集群环境中不支持 group 操作,可以使用 aggregation 或者 map/reduce 代替。


完整的MongoDB聚合文档: http://docs.mongodb.org/manual/aggregation/

pymongo教程(1)——概述

MongoDB是使用C++开发的一款文档型数据库,PyMongo是MongoDB的Python驱动。

安装

使用pip安装

$ [sudo] pip install pymongo

如果要安装特定的版本则:

$ [sudo] pip install pymongo==2.6.3

通过源代码安装

$ git clone git://github.com/mongodb/mongo-python-driver.git pymongo
$ cd pymongo/
$ [sudo] python setup.py install

注意:使用C的扩展会对性能提升会有帮助。但是在uwsgi中会出现警告,则可以选择只安装python驱动,而不安装C扩展。

$ [sudo] python setup.py --no_ext install

注意: 如果你使用的是Python3的话,PyMongo只支持 Python 3.1以上的版本。

使用

首先启动 mongodb 服务器:

$ mongod

连接服务器

然后执行python程序连接服务器:

from pymongo import MongoClient
client = MongoClient()

以上会连接到默认的主机和端口(localhost:27017),也可以指定主机名和端口:

client = MongoClient('localhost', 27017)

或者:

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

访问数据库

db = client.test_database

如果数据库的名称不能直接使用属性名的风格访问,那么就需要使用字典的风格:

db = client['test-database']

访问数据集合

与访问数据库相似:

collection = db.test_collection
collection = db['test-collection']

插入数据

在MongoDB中数据是以类似JSON格式进行保存的,在PyMongo中则是使用字典风格。然后可以数据集合对象的 insert() 方法进行插入数据。

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

查询数据

可以数据集合对象的 find() 方法进行查询数据。

db.posts.find({"author": "Mike"})
db.posts.find_one({"author": "Mike"})

自动生成.gitignore文件

.gitignore文件是用于对git进行设置,让其忽略对某些文件的跟踪。

最近发现每创建一个新的仓库都要把.gitignore文件重新写一遍,甚是麻烦。于是就想能否自动生成.gitignore文件,这样的话就比较方便。最后找到了 gitignore.io 这个网站,它可以根据需求生成相应的.gitignore文件。比较你是用vim编辑器编写python代码,则输入vim python就会生成对应vim和python的gitignore文件了。

为了方便使用我编写了一个shell脚本。从 https://gist.github.com/wusuopu/9408486 下载代码,保存为mkgitignore,并加上执行权限。然后执行如下命令生成.gitignore文件。

$ mkgitignore vim,python
$ mkgitignore vim,python .gitignore

第一条命令是直接将结果输出到终端,第二条命令是将结果输出到.gitignore文件中。

最后补充一个git的小知识:
如果想要在所有的项目中都忽略掉某些文件的话,那么可以设置一个全局的gitignore。执行如下命令:

$ git config --global core.excludesfile ~/.gitignore

Linux下VNC使用

安装

VNC (Virtual Network Computer)是虚拟网络计算机的缩写。VNC 是一款优秀的远程控制工具软件。

VNC程序有多个软件包可选择,例如执行以下命令进行安装:

$ sudo apt-get install vnc4server
$ sudo pacman -S tigervnc

启动服务器程序

修改$HOME/.vnc/xstartup配置文件,我是用的xfce,配置如下:

#!/bin/sh

# Uncomment the following two lines for normal desktop:
# unset SESSION_MANAGER
# exec /etc/X11/xinit/xinitrc

[ -x /etc/vnc/xstartup ] && exec /etc/vnc/xstartup
[ -r $HOME/.Xresources ] && xrdb $HOME/.Xresources
xsetroot -solid grey
vncconfig -iconic &
startxfce4 &

然后运行服务程序,首次运行需要先设置密码:

$ vncserver

启动客户端程序

执行如下命令链接vnc服务:

$ vncviewer <host>:<DISPLAY>

例如要链接到10.0.0.101的1号DISPLAY,则是:

$ vncviewer 10.0.0.101:1

然后根据提示输入密码即可。

退出程序

如果要退出客户端程序,直接将程序窗口关闭即可。
如果要退出服务端程序,则在服务端执行命令:

$ vncserver -kill :1

修改Mongo命令提示符

默认的启动mongo终端后,命令行的提示符就只有一个大于符号(>)。虽然这不会有什么问题,但是如果同时开启了多个终端连接时,如一个本地连接一个远程连接。终端来回切换,有时就会忘了哪个是哪个,一旦误操作后果是很严重的。因此如果能够显示当前是哪个连接的话会很方便的。

修改mongo的配置文件,将提示符改为“当前连接的主机:当前操作的数据库 (当前执行的命令数)>” 这样的形式。

编辑 ~/.mongorc.js 文件,没有则创建:

var cmdCountNum = 0;
var prompt = function () {
  var host = db.getMongo().host;
  var database = db.getName();
  cmdCountNum++;
  return host + ':' + database + ' (' + cmdCountNum + ')> ';
}

现在再执行mongo命令试试。

$ mongo
127.0.0.1:test (1)> db
test
127.0.0.1:test (2)>

使用nvm进行node多版本管理

nvm与Python的virtualenv和Ruby的rvm类似。

NVM (Node Version Manager,Node多版本管理器)是一个通用的叫法,它目前有许多不同的实现。通常我们说的 nvm 是指 https://github.com/creationix/nvm 或者 https://github.com/visionmedia/n 。这两个工具都是使用shell编写的。

n

个人感觉 n 比较简洁些,它就只有一个脚本文件。

安装

如果已经安装了npm的话,可以直接执行命令进行安装n :

$ npm install -g n

或者是直接获取源代码安装:

$ git clone https://github.com/visionmedia/n.git
$ cd n
$ [sudo] make install

使用

直接运行 n 命令查看所有已安装的版本。

$ n

运行命令 n 进行安装指定版本的node。 version 可以是 stable(最新稳定版)、latest(最新版)或者具体的版本数字。

$ n stable
install : v0.10.24
mkdir : /usr/local/n/versions/0.10.24
fetch : http://nodejs.org/dist/v0.10.24/node-v0.10.24-linux-x64.tar.gz

指定的版本将会安装在 /usr/local/n/versions 目录下。

运行命令 n rm 删除已安装指定版本的node。

运行命令 n use 选择使用指定版本的node。

nvm

安装

$ git clone https://github.com/creationix/nvm.git ~/.nvm
$ source ~/.nvm/nvm.sh

使用

查看已安装的版本:

$ nvm ls

查看可以安装的版本:

$ nvm ls-remote

安装指定的版本:

$ nvm install <version>

指定的版本将会直接安装在 nvm 程序所在的目录下。

删除指定的版本:

$ nvm uninstall <version>

使用选定的版本:

$ nvm use <version>