继续上一篇《Pylons 入门实例教程 - Hello》,现在开始讲在 Pylons 里如何提交表单和上传文件。
继续延用上篇里面的 hello 工程,在 HiController 里添加 form 方法:
def form(self):
return render('/form.mako')
加完以后可以访问:http://127.0.0.1:5000/hi/form,会报错。
Server Error,根据报错内容大致就知道模板文件不存在了。如果有其他错误,也可以通过这个页面查看,当然还有很强大的 Debug 个功能哦。当然正式环境一般都是关闭这个功能的。这个,你懂得。。。
好吧,写一个表单的模板,只包含一个简单的文本框和提交按钮示例。
<form action="/hi/submit" method="post">
name: <input type="text" name="name" />
<br />
<input type="submit" value="submit" />
</form>
再添加一个 submit 方法来处理表单提交,
def submit(self):
return "hello, name: %s" % request.params['name']
request.params 包含了表单或者URL提交的参数,建议 POST 数据参照下面的上传部分。想获取更详细的列表,可以查看文档或者自己手动 dir()查阅。
下面尝试一下文件上传,首先在 development.ini 添加一个变量,用来存放文件上传后的文件夹。
[app:main]
upload_dir = %(here)s/upload
%(here) 启动后 server 会替换到当前目录地址,上面的地址就是当前路径下的upload文件夹。
修改一下刚才的表单,加一个 file 上传,注意 multipart/form-data 这句,上传必须。
<form action="/hi/submit" method="post" enctype= "multipart/form-data">
name: <input type="text" name="name" />
<br />
file: <input type="file" name="file" />
<br />
<input type="submit" value="submit" />
</form>
修改 submit 方法,添加文件内容:
def submit(self):
name = request.POST['name']
myfile = request.POST['file']
import os
import shutil
from pylons import config
local_name = os.path.join(config['app_conf']['upload_dir'], myfile.filename)
local_file = open(local_name, "wb")
shutil.copyfileobj(myfile.file, local_file)
myfile.file.close()
local_file.close()
return "hello, name: %s, upload: %s" % (name, myfile.filename)
里面 import 部分这里仅仅为了示例,正式的代码请放入程序开头部分,POST 内容可以从 request.POST 获取。
config['app_conf']['upload_dir'] 就是刚才配置里 development.ini 定义的地址。这个目录需要自己手动创建哦。
smallfish@debian:~/workspace/python/hello$ mkdir upload
OK,到这里程序部分都已经修改完成。重新访问一下:http://127.0.0.1:5000/hi/form
尝试一下上传,上传后可以在 upload 文件夹下看到文件了吧。。
当然这里只是示例,还需要处理一下上传的名字,防止有特殊符号哦。
原文:http://golang.org/doc/code.html
简述
这篇文档描述了如何去写一个新的 package 和怎么去测试。本文假设你已经按照http://golang.org/doc/install.html装好Go。
在修改一个存在的 package 或者新建立一个package,确保先发一封邮件到 http://groups.google.com/group/golang-nuts,告诉大家你想做什么。这样有助于不要重复造轮子,在写代码之前最好讨论下。
社区资源
如果想获取实时帮助,可以加入 http://freenode.net/ 上 IRC 频道 #go-nuts。
Go 语言官方邮件列表是 http://groups.google.com/group/golang-nuts.
Bugs 可以参考http://code.google.com/p/go/issues/list.
对于那些想尝试开发代码的用户,这里有另外一个邮件列表 http://groups.google.com/group/golang-checkins,邮件里包含了那些刚提交到 Go 代码库的消息。
建立Go包
下面的源码假设 package 的导入路径是 x/y,约定下保存的路径是:$GOROOT/src/pkg/x/y
Makefile
这将是很好的利用 Go-specific 工具里安排源码结构,如何按照顺序和构建代码。Go 使用 GUN make。所以首先在一个新的package 文件夹里建立一个 Makefile。最简单的做法就是从 http://golang.org/src/pkg/container/vector/Makefile 源码包里拷贝一份。
include ../../../Make.$(GOARCH)
TARG=container/vector
GOFILES=\
intvector.go\
stringvector.go\
vector.go\
include ../../../Make.pkg
当然在上面的源码包之外写一个新的 package ,通常的 Makefile 如下:
include $(GOROOT)/src/Make.$(GOARCH)
TARG=mypackage
GOFILES=\
my1.go\
my2.go\
include $(GOROOT)/src/Make.pkg
第一行 include 标准的定义和规则,package 的路径一般相对路径来代替 $(GOROOT)/src ,这样做到目的就是防止 make 时候 $(GOROOT) 含有空格,这样做很方便开发中使用Go。
TRAG 是包安装路径,这行就是其他程序导入到名字。在 Go 源码中,这个名字必须和文件夹中的 Makefile 中的一致,不需要 $GOROOT/src/pkg/ 前缀。在源码之外,你可以任意起名而不要和标准 package 的重名即可。常见的做法是独立一个 package 名:myname/tree, myname/filter,确保你的代码在 myname 里,运行 make install,将会编译后把二进制文件放入 $GOROOT/pkg,可以很方便的找到。
GOFILES 放入文件内需要编译的源码列表。多行用 \ 字符隔开。
如果想在 Go 源码树中新建包,需要添加列表到 $GOROOT/src/pkg/Makefile 中作为标准库编译,编译运行:
cd $GOROOT/src/pkg
./deps.b
更新依赖文件 Make.deps (这是每次 make all 或者 make build 都需要)
Go 源文件
在每个源码里第一行的名字应该是 Makefile 里面定义的 package 名,这里的 name 是作为默认的名字导入。(包里的每个 package 名都应该是同样的名字)Go的惯例是用路径的最后一个元素作为名字,比如 "crypto/rot13" 应该是 rot13。到目前为止,Go 是依靠 package 名字来确定一个二进制的文件,当然可能会很快的取消。
Go 是在一个包内编译源码文件,在一个文件定义常量,变量,类型或者函数,在其他文件内适用不需要再定义和声明。
想写一个干净而优美的 Go 代码不在本文范围。请参考http://golang.org/doc/effective_go.html吧。
测试
Go 有一个轻量级的测试代码框架:gotest。编写一个测试代码,只需要新建一个跟你源码名字一样,加上:_test.go 即可,测试函数一般是 func TestXXX (t *testing.T)。每次运行测试其中的函数。如果想返回错误的话一般是返回: t.Error 或 t.Fail,测试时候就会抛错。详细 gotest 测试方法请参考:http://golang.org/cmd/gotest/
*_test.go 不需要在 Makefile 中声明。
运行测试,编译时候 make test 即可,和 gotest 效果一样。如果想单独测试某一个源码,比如:one_test.go,则运行:gotest one_test.go。
如果你的修改影响性能,可以添加一个性能测试函数(参考:http://golang.org/cmd/gotest/),然后运行:gotest -benchmarks=.
当你新的代码已经通过测试,也能运行,你就可以 review 和 commit. http://golang.org/doc/contribute.html。
一个测试的例子
这是一个名字叫 numbers 测试 package ,定义了一个叫 Double 的函数,返回一个整型,结果是输入的参数乘以2。一共有三个文件。
第一个是 numbers.go
package numbers
func Double(i int) int {
return i * 2
}
第二个是测试文件 numbers_test.go
package numbers
import (
"testing"
)
type doubleTest struct {
in, out int
}
var doubleTests = []doubleTest{
doubleTest{1, 2},
doubleTest{2, 4},
doubleTest{-5, -10},
}
func TestDouble(t *testing.T) {
for _, dt := range doubleTests {
v := Double(dt.in)
if v != dt.out {
t.Errorf("Double(%d) = %d, want %d.", dt.in, v, dt.out)
}
}
}
最后是 Makefile 文件
include $(GOROOT)/src/Make.$(GOARCH)
TARG=numbers
GOFILES=\
numbers.go\
include $(GOROOT)/src/Make.
运行 make install 将会构建和安装包到 GOROOT/pkg/ 文件夹内(他在系统的任何地方导入调用)。
运行 make test (或者 gotest)将会重新构建包,并会运行 numbers_test.go 中的 TestDouble 函数。如果输出 "PASS" 则表示测试成功通过。如果不是2到3的倍数,将会看到错误报告。
更详细的请查看http://golang.org/cmd/gotest/文档。
http://pylonshq.com/,当然,这些组件只是默认,你还可以根据自己喜好来选择其他组件,比如你可以采用 Jinja2 或 Genshi 模板,ORM也可以采用 SQLObject。完全是自由组合。
废话少说,现在开始安装吧。
smallfish@debian:~$ sudo aptitude install python-pylons
Debian/Ubuntu 系列系统可以直接 aptitude 安装,当然你也可以使用 easy_install 或者源码安装。
smallfish@debian:~$ sudo easy_install Pylons
更多安装文档请参考官网安装部分,http://pylonshq.com/docs/en/1.0/gettingstarted/#installing
好了,安装结束,来一个经典的Hello程序吧。
smallfish@debian:~/workspace/python$ paster create -t pylons hello
Selected and implied templates:
Pylons#pylons Pylons application template
Variables:
egg: hello
package: hello
project: hello
Enter template_engine (mako/genshi/jinja2/etc: Template language) ['mako']:
Enter sqlalchemy (True/False: Include SQLAlchemy 0.5 configuration) [False]:
Creating template pylons
Creating directory ./hello
下面输出略过,大致解说一下。Pylons 程序可以用 Paste 自动生成一些代码,包括controller。还可以运行 HTTP 服务来测试。
-t 表示自动创建的模板,可以如下来查看有哪些选项,更多就参考 help 吧。
smallfish@debian:~/workspace/python/hello$ paster create --list-templates
Available templates:
basic_package: A basic setuptools-enabled package
paste_deploy: A web application deployed through paste.deploy
pylons: Pylons application template
pylons_minimal: Pylons minimal application template
看一下hello的目录结构:
smallfish@debian:~/workspace/python/hello$ ls
development.ini ez_setup.py hello.egg-info README.txt setup.py
docs hello MANIFEST.in setup.cfg test.ini
这里具体各个文件意思就不讲解了,程序主体部分都在hello/hello目录下,development.ini 和 test.ini 分别是服务启动的配置文件,用于测试和开发环境。开始先运行一下,看效果吧。。
smallfish@debian:~/workspace/python/hello$ paster serve --reload development.ini
Starting subprocess with file monitor
Starting server in PID 1519.
serving on http://127.0.0.1:5000
在浏览器中打开:http://127.0.0.1:5000 看到页面了吧?恭喜。
继续,写一个简单的显示 hi的 controller 程序吧。
smallfish@debian:~/workspace/python/hello$ paster controller hi
Creating /home/smallfish/workspace/python/hello/hello/controllers/hi.py
Creating /home/smallfish/workspace/python/hello/hello/tests/functional/test_hi.py
自动生成程序和 test 文件。paster 启动服务不需要重启会自动加载,可以浏览器访问:http://127.0.0.1:5000/hi/index
很简单吧,打开 hi.py,基本如下:
class HiController(BaseController):
def index(self):
# Return a rendered template
#return render('/hi.mako')
# or, return a response
return 'Hello World'
上面注释部分可以 return 一个模板文件,模板放入 templates 目录下即可。
去除上面的 return 'Hello' 返回 return render('/hi.mako')
smallfish@debian:~/workspace/python/hello$ vi hello/templates/hi.mako
% for key, value in request.environ.items():
${key} = ${value}
% endfor
刷新 http://127.0.0.1:5000/hi/index ,可以看到一些环境变量的输出了吧。
今天就简单的说到这里吧,下回来一个完整的例子,包括URL、模板、数据库和Session的实例。
COPY 命令可以快速的导入数据到 PostgreSQL 中,文件格式类似CVS之类。适合批量导入数据,比 \i 和恢复数据表快。
导出表数据到文件或 STDOUT :
COPY tablename [(column [, ...])]
TO {'filename' | STDOUT}
[[WITH]
[BINARY]
[OIDS]
[DELIMITER [AS] 'delimiter']
[NULL [AS] 'null string']
[CSV [HEADER]
[QUOTE [AS] 'quote']
[ESCAPE [AS] 'escape']
[FORCE NOT NULL column [, ...]]
导入文件或者 STDIN 到表中:
COPY tablename [(column [, ...])]
FROM {'filename' | STDIN}
[[WITH]
[BINARY]
[OIDS]
[DELIMITER [AS] 'delimiter']
[NULL [AS] 'null string']
[CSV [HEADER]
[QUOTE [AS] 'quote']
[ESCAPE [AS] 'escape']
[FORCE QUOTE column [, ...]]
导出表 employee 到默认输出 STDOUT:
psql> COPY employee TO STDOUT;
1 JG100011 Jason Gilmore jason@example.com
2 RT435234 Robert Treat rob@example.com
3 GS998909 Greg Sabino Mullane greg@example.com
4 MW777983 Matt Wade matt@example.com
导出表 employee 到 sql 文件:
psql> COPY employee TO '/home/smallfish/employee.sql';
从文件导入数据:
psql> COPY employeenew FROM '/home/smallfish/employee.sql';
psql> SELECT * FROM employeenew;
employeeid | employeecode | name | email
------------+--------------+---------------------+---------------
1 | JG100011 | Jason Gilmore | jason@example.com
2 | RT435234 | Robert Treat | rob@example.com
3 | GS998909 | Greg Sabino Mullane | greg@example.com
4 | MW777983 | Matt Wade | matt@example.com
(4 rows)
输出对象ID(OIDS):
psql> COPY employee TO STDOUT OIDS;
24627 1 GM100011 Jason Gilmore jason@example.com
24628 2 RT435234 Robert Treat rob@example.com
24629 3 GS998909 Greg Sabino Mullane greg@example.com
24630 4 MW777983 Matt Wade matt@example.com
指定导出间隔符,默认是 \t ,这里为 | :
psql>COPY employee TO STDOUT DELIMITER '|';
1|GM100011|Jason Gilmore|jason@example.com
2|RT435234|Robert Treat|rob@example.com
3|GS998909|Greg Sabino Mullane|greg@example.com
4|MW777983|Matt Wade|matt@example.com
导入文件数据,指定间隔符为 | :
psql> COPY employeenew FROM '/home/smallfish/employee.sql' DELIMITER |;
导出指定字段的数据:
psql> COPY employee (name,email) TO STDOUT;
Jason Gilmore jason@example.com
Robert Treat rob@example.com
Greg Sabino Mullane greg@example.com
Matt Wade matt@example.com
为 NULL 字段设置默认值:
psql> COPY employee TO STDOUT NULL 'no email';
Jason Gilmore no email
Robert Treat rob@example.com
Greg Sabino Mullane greg@example.com
Matt Wade no email
导出为CVS格式:
psql> COPY employee (name, email) TO '/home/smallfish/employee.csv' CSV HEADER;
参考资料:http://apress.com/book/view/9781590595473
Virtual Box 是个不错的虚拟机,小巧,功能也齐全。好像有点推销鸟。说正题,上次有个朋友就提到怎么能主机里访问虚拟机里的服务,昨晚实验了下,颇为顺利。记录下。这里利用的是默认的NAT上网,也就是共享主机上网,而不是设置独立的IP。
主机:Win XP
虚拟:Ubuntu 9.10
目的:Win里ssh进Ubuntu,能访问里面提供的服务。
主要是通过 VBoxManage setextradata 设置一些属性。
先上几个步骤图把。注意一下修改,先得关闭虚拟机,修改完事以后再启动。
查看虚拟机中的名称:ubuntu9
进入本机Vbox目录,运行VBoxManage,查看下。
添加三个项目
下面的pcnet是vbox里的网络设置,0是表示第一个网卡,后面一次类推。22是ssh端口,映射到主机的22端口。
VBoxManage setextradata "ubuntu9" "VBoxInternal/Devices/pcnet/0/LUN#0/Config/guestssh/Protocol" TCP
VBoxManage setextradata "ubuntu9" "VBoxInternal/Devices/pcnet/0/LUN#0/Config/guestssh/GuestPort" 22
VBoxManage setextradata "ubuntu9" "VBoxInternal/Devices/pcnet/0/LUN#0/Config/guestssh/HostPort" 22
启动虚拟机。
设置putty登陆之。
到这里已经顺利ssh 到127.0.0.1,如果想访问虚拟机里的web服务器呢?同样很简单。
只要如下设置,web端口为81,跟上面也雷同:
VBoxManage setextradata "ubuntu9" "VBoxInternal/Devices/pcnet/0/LUN#0/Config/web/Protocol" TCP
VBoxManage setextradata "ubuntu9" "VBoxInternal/Devices/pcnet/0/LUN#0/Config/web/GuestPort" 81
VBoxManage setextradata "ubuntu9" "VBoxInternal/Devices/pcnet/0/LUN#0/Config/web/HostPort" 81
另外如果想清空上面设置的选项,只要不设置后面的值即可:
VBoxManage setextradata "ubuntu9" "VBoxInternal/Devices/pcnet/0/LUN#0/Config/web/Protocol"
原文参见:http://www.mongodb.org/display/DOCS/Tutorial
启动数据库
下载 http://www.mongodb.org/display/DOCS/Downloads, 解压后并启动:
MongoDB 默认存储数据目录为 /data/db/ (或者 c:\data\db), 当然你也可以修改成不同目录, 只需要指定 --dbpath 参数:
$ bin/mongod --dbpath /path/to/my/data/dir
获取数据库连接
现在我们就可以使用自带的shell工具来操作数据库了. (我们也可以使用各种编程语言的驱动来使用MongoDB, 自带的shell工具可以方便我们管理数据库)
启动 MongoDB JavaScript 工具:
默认 shell 连接的是本机localhost 上面的 test库, 会看到:
MongoDB shell version: 0.9.8
url: test
connecting to: test
type "help" for help
>
"connecting to:" 这个会显示你正在使用的数据库的名称. 想换数据库的话可以:
可以输入 help 来查看所有的命令.
插入数据到集合
下面我们来建立一个test的集合并写入一些数据. 建立两个对象, j 和 t , 并保存到集合中去.
在例子里 '>' 来表示是 shell 输入提示符
> j = { name : "mongo" };
{"name" : "mongo"}
> t = { x : 3 };
{ "x" : 3 }
> db.things.save(j);
> db.things.save(t);
> db.things.find();
{"name" : "mongo" , "_id" : ObjectId("497cf60751712cf7758fbdbb")}
{"x" : 3 , "_id" : ObjectId("497cf61651712cf7758fbdbc")}
>
有几点需要注意下 :
- 不需要预先建立一个集合. 在第一次插入数据时候会自动建立.
- 在例子其实可以存储任何结构的数据, 当然在实际应用我们存储的还是相同元素的集合. 这个特性其实可以在应用里很灵活, 你不需要类似 alter table 来修改你的数据结构
- 每次插入数据时候对象都会有一个ID, 名字叫 _id.
当你运行不同的例子, 你的对象ID值都是不同的.
下面再加点数据:
> for( var i = 1; i < 10; i++ ) db.things.save( { x:4, j:i } ); > db.things.find();
{"name" : "mongo" , "_id" : ObjectId("497cf60751712cf7758fbdbb")}
{"x" : 3 , "_id" : ObjectId("497cf61651712cf7758fbdbc")}
{"x" : 4 , "j" : 1 , "_id" : ObjectId("497cf87151712cf7758fbdbd")}
{"x" : 4 , "j" : 2 , "_id" : ObjectId("497cf87151712cf7758fbdbe")}
{"x" : 4 , "j" : 3 , "_id" : ObjectId("497cf87151712cf7758fbdbf")}
{"x" : 4 , "j" : 4 , "_id" : ObjectId("497cf87151712cf7758fbdc0")}
{"x" : 4 , "j" : 5 , "_id" : ObjectId("497cf87151712cf7758fbdc1")}
{"x" : 4 , "j" : 6 , "_id" : ObjectId("497cf87151712cf7758fbdc2")}
{"x" : 4 , "j" : 7 , "_id" : ObjectId("497cf87151712cf7758fbdc3")}
{"x" : 4 , "j" : 8 , "_id" : ObjectId("497cf87151712cf7758fbdc4")}
请注意下, 这里循环次数是10, 但是只显示到8, 还有2条数据没有显示.
如果想继续查询下面的数据只需要使用 it 命令, 就会继续下面的数据:
{"x" : 4 , "j" : 7 , "_id" : ObjectId("497cf87151712cf7758fbdc3")}
{"x" : 4 , "j" : 8 , "_id" : ObjectId("497cf87151712cf7758fbdc4")}
继续
> it
{"x" : 4 , "j" : 9 , "_id" : ObjectId("497cf87151712cf7758fbdc5")}
{"x" : 4 , "j" : 10 , "_id" : ObjectId("497cf87151712cf7758fbdc6")}
从技术上讲 find() 返回一个游标对象. 但在上面的例子里, 并没有拿到一个游标的变量. 所以 shell 自动遍历游标, 返回一个初始化的set, 并允许我们继续用 it 迭代输出.
当然我们也可以直接用游标来输出, 不过这个是下一部分的内容了.
查询数据
在没有深入查询之前, 我们先看看怎么从一个查询中返回一个游标对象. 可以简单的通过 find() 来查询, 他返回一个任意结构的集合. 如果实现特定的查询稍后讲解.
实现上面同样的查询, 然后通过 while 来输出:
> var cursor = db.things.find();
> while (cursor.hasNext()) { print(tojson(cursor.next())); }
{"name" : "mongo" , "_id" : ObjectId("497cf60751712cf7758fbdbb")}
{"x" : 3 , "_id" : ObjectId("497cf61651712cf7758fbdbc")}
{"x" : 4 , "j" : 1 , "_id" : ObjectId("497cf87151712cf7758fbdbd")}
{"x" : 4 , "j" : 2 , "_id" : ObjectId("497cf87151712cf7758fbdbe")}
{"x" : 4 , "j" : 3 , "_id" : ObjectId("497cf87151712cf7758fbdbf")}
{"x" : 4 , "j" : 4 , "_id" : ObjectId("497cf87151712cf7758fbdc0")}
{"x" : 4 , "j" : 5 , "_id" : ObjectId("497cf87151712cf7758fbdc1")}
{"x" : 4 , "j" : 6 , "_id" : ObjectId("497cf87151712cf7758fbdc2")}
{"x" : 4 , "j" : 7 , "_id" : ObjectId("497cf87151712cf7758fbdc3")}
{"x" : 4 , "j" : 8 , "_id" : ObjectId("497cf87151712cf7758fbdc4")}
{"x" : 4 , "j" : 9 , "_id" : ObjectId("497cf87151712cf7758fbdc5")}
>
上面的例子显示了游标风格的迭代输出. hasNext() 函数告诉我们是否还有数据, 如果有则可以调用 next() 函数. 这里我们也用了自带的 tojson() 方法返回一个标准的 JSON 格式数据.
当我们使用的是 JavaScript shell, 可以用到JS的特性, forEach 就可以输出游标了. 下面的例子就是使用 forEach() 来循环输出:
> db.things.find().forEach( function(x) { print(tojson(x));});
{"name" : "mongo" , "_id" : ObjectId("497cf60751712cf7758fbdbb")}
{"x" : 3 , "_id" : ObjectId("497cf61651712cf7758fbdbc")}
{"x" : 4 , "j" : 1 , "_id" : ObjectId("497cf87151712cf7758fbdbd")}
{"x" : 4 , "j" : 2 , "_id" : ObjectId("497cf87151712cf7758fbdbe")}
{"x" : 4 , "j" : 3 , "_id" : ObjectId("497cf87151712cf7758fbdbf")}
{"x" : 4 , "j" : 4 , "_id" : ObjectId("497cf87151712cf7758fbdc0")}
{"x" : 4 , "j" : 5 , "_id" : ObjectId("497cf87151712cf7758fbdc1")}
{"x" : 4 , "j" : 6 , "_id" : ObjectId("497cf87151712cf7758fbdc2")}
{"x" : 4 , "j" : 7 , "_id" : ObjectId("497cf87151712cf7758fbdc3")}
{"x" : 4 , "j" : 8 , "_id" : ObjectId("497cf87151712cf7758fbdc4")}
{"x" : 4 , "j" : 9 , "_id" : ObjectId("497cf87151712cf7758fbdc5")}
>
forEach() 必须定义一个函数供每个游标元素调用.
在 mongo shell 里, 我们也可以把游标当作数组来用 :
> var cursor = db.things.find();
> print (tojson(cursor[4]));
{"x" : 4 , "j" : 3 , "_id" : ObjectId("497cf87151712cf7758fbdbf")}
使用游标时候请注意占用内存的问题, 特别是很大的游标对象, 有可能会内存溢出. 所以应该用迭代的方式来输出.
下面的示例则是把游标转换成真实的数组类型:
> var arr = db.things.find().toArray();
> arr[5];
{"x" : 4 , "j" : 4 , "_id" : ObjectId("497cf87151712cf7758fbdc0")}
请注意这些特性只是在 mongo shell 里使用, 而不是所有的其他应用程序驱动都支持.
MongoDB 游标对象不是没有快照 - 如果有其他用户在集合里第一次或者最后一次调用 next(), 你可以得不到游标里的数据. 所以要明确的锁定你要查询的游标.
指定条件的查询
到这里我们已经知道怎么从游标里实现一个查询并返回数据对象, 下面就来看看怎么根据指定的条件来查询.
下面的示例就是说明如何执行一个类似SQL的查询, 并演示了怎么在 MongoDB 里实现. 这是在 MongoDB shell 里查询, 当然你也可以用其他的应用驱动或者语言来实现:
SELECT * FROM things WHERE name="mongo"
> db.things.find({name:"mongo"}).forEach(function(x) { print(tojson(x));});
{"name" : "mongo" , "_id" : ObjectId("497cf60751712cf7758fbdbb")}
>
SELECT * FROM things WHERE x=4
> db.things.find({x:4}).forEach(function(x) { print(tojson(x));});
{"x" : 4 , "j" : 1 , "_id" : ObjectId("497cf87151712cf7758fbdbd")}
{"x" : 4 , "j" : 2 , "_id" : ObjectId("497cf87151712cf7758fbdbe")}
{"x" : 4 , "j" : 3 , "_id" : ObjectId("497cf87151712cf7758fbdbf")}
{"x" : 4 , "j" : 4 , "_id" : ObjectId("497cf87151712cf7758fbdc0")}
{"x" : 4 , "j" : 5 , "_id" : ObjectId("497cf87151712cf7758fbdc1")}
{"x" : 4 , "j" : 6 , "_id" : ObjectId("497cf87151712cf7758fbdc2")}
{"x" : 4 , "j" : 7 , "_id" : ObjectId("497cf87151712cf7758fbdc3")}
{"x" : 4 , "j" : 8 , "_id" : ObjectId("497cf87151712cf7758fbdc4")}
{"x" : 4 , "j" : 9 , "_id" : ObjectId("497cf87151712cf7758fbdc5")}
>
查询条件是 { a:A, b:B, ... } 类似 "where a==A and b==B and ...", 更多的查询方式可以参考 Mongo 开发教程部分.
上面显示的是所有的元素, 当然我们也可以返回特定的元素, 类似于返回表里某字段的值, 只需要在 find({x:4}) 里指定元素的名字, 比如 j:
SELECT j FROM things WHERE x=4
> db.things.find({x:4}, {j:true}).forEach(function(x) { print(tojson(x));});
{"j" : 1 , "_id" : ObjectId("497cf87151712cf7758fbdbd")}
{"j" : 2 , "_id" : ObjectId("497cf87151712cf7758fbdbe")}
{"j" : 3 , "_id" : ObjectId("497cf87151712cf7758fbdbf")}
{"j" : 4 , "_id" : ObjectId("497cf87151712cf7758fbdc0")}
{"j" : 5 , "_id" : ObjectId("497cf87151712cf7758fbdc1")}
{"j" : 6 , "_id" : ObjectId("497cf87151712cf7758fbdc2")}
{"j" : 7 , "_id" : ObjectId("497cf87151712cf7758fbdc3")}
{"j" : 8 , "_id" : ObjectId("497cf87151712cf7758fbdc4")}
{"j" : 9 , "_id" : ObjectId("497cf87151712cf7758fbdc5")}
>
请注意 "_id" 元素会一直被返回.
findOne() - 语法糖
为了方便, mongo shell (其他驱动) 避免游标的可能带来的开销, 提供一个findOne() 函数. 这个函数和 find() 参数一样, 不过他返回游标里第一条数据, 或者返回 null 空数据库.
作为一个例子, name=='mongo' 可以用很多方法来实现, 可以用 next() 来循环游标(需要校验是否为null), 或者当做数组返回第一个元素.
但是用 findOne() 方法则更简单和高效:
> var mongo = db.things.findOne({name:"mongo"});
> print(tojson(mongo));
{"name" : "mongo" , "_id" : ObjectId("497cf60751712cf7758fbdbb")}
>
findOne 方法更跟 find({name:"mongo"}).limit(1) 一样.
limit() 查询
你可以需要限制结果集的长度, 可以调用 limit 方法.
这是强烈推荐高性能的原因, 通过限制条数来减少网络传输, 例如:
> db.things.find().limit(3);
in cursor for : DBQuery: example.things ->
{"name" : "mongo" , "_id" : ObjectId("497cf60751712cf7758fbdbb")}
{"x" : 3 , "_id" : ObjectId("497cf61651712cf7758fbdbc")}
{"x" : 4 , "j" : 1 , "_id" : ObjectId("497cf87151712cf7758fbdbd")}
>
更多帮助
除非了一般的 help 之外, 你还可以查询 help 数据库和db.whatever 来查询具体的说明.
如果你对一个函数要做什么, 你可以不输入 这些结束的括号则可以输出实现的源码, 例如:
> db.foo.insert
function (obj, _allow_dot) {
if (!obj) {
throw "no object passed to insert!";
}
if (!_allow_dot) {
this._validateForStorage(obj);
}
return this._mongo.insert(this._fullName, obj);
}
mongo 是一个完整的 JavaScript shell程序, 所以在 shell 里完全可以私用JS的方法、类、语法. 此外, MongoDB 定义很多自己的类和全局变量 (比如 db). 这里可以查看完整的API说明 http://api.mongodb.org/js/.
接下来
看完这篇教程后下一步则看MongoDB更详细的文档 http://www.mongodb.org/display/DOCS/Manual