Collections

Meteor用集合保存数据。集合里保存的Javascript对象叫做文档。使用new Mongo.Collection声明一个集合。

new Mongo.Collection(name, [options])

Anywhere

Constructor for a Collection

Arguments

name String

The name of the collection. If null, creates an unmanaged (unsynchronized) local collection.

调用Mongo.Collection构造函数创建一个集合对象,它的行为就像MongoDB 的集合。如果在创建集合时传入了一个name参数,那么声明的就是一个持久性集合 — 保存在服务端并可以发布到客户端。

要想使客户端代码和服务端代码都可以通过相同的API访问相同的集合,最好在一个客户端和服务端都能加载到的javascript文件中把集合声明为全局变量。

示例:声明两个命名的,持久性的集合作为全局变量:

// In a JS file that's loaded on the client and the server
Posts = new Mongo.Collection("posts");
Comments = new Mongo.Collection("comments");

如果传入null作为name参数,那么创建出来的就是一个本地集合。本地集合不会在客户端和服务端之间进行同步;它只是javascript对象的临时集合,支持Mongo-style find, insert, update, 和 remove操作。

默认情况下,Meteor会自动发布所有集合里的文档到每一个连接上的客户端。要禁用此行为,必须移除autopublish包:

$ meteor remove autopublish

然后,使用Meteor.publishMeteor.subscribe来指定集合的哪一部分发送到哪些客户端。

使用findOnefind从集合里检索文档。

collection.findOne([selector], [options])

Anywhere

Finds the first document that matches the selector, as ordered by sort and skip options.

Arguments

selector Mongo Selector, Object ID, or String

A query describing the documents to find

Options

sort Mongo Sort Specifier

Sort order (default: natural order)

skip Number

Number of results to skip at the beginning

fields Mongo Field Specifier

Dictionary of fields to return or exclude.

findOne方法可以从集合里查找特定的文档。调用findOne时,通常都会传入一个特定文档的_id:

var post = Posts.findOne(postId);

然而,也可以给findOne传入一个Mongo 选择器,Mongo选择器是一个对象,指明了目标文档要满足的一系列属性。 例如,下面的选择器

var post = Posts.findOne({
  createdBy: "12345",
  title: {$regex: /first/}
});

会匹配到下面的文档

{
  createdBy: "12345",
  title: "My first post!",
  content: "Today was a good day." }

关于MongoDB query operators 例如$regex, $lt (小于), $text (文本搜索)的更多信息参见MongoDB documentation

一个非常有用但是不那么明显的功能就是Mongo 选择器可以匹配数组里的元素。例如:下面的选择器

Post.findOne({
  tags: "meteor" });

会匹配到下面的文档

{
  title: "I love Meteor",
  createdBy: "242135223",
  tags: ["meteor", "javascript", "fun"] }

findOne方法和Session.get一样,也是响应式的,也就是说,如果你在template helper或是 Tracker.autorun回调函数中使用了findOne,如果findOne返回的文档发生了变化,那么模板就会自动重新渲染,计算会重新执行。

注意,如果findOne没有找到任何文档,则返回null,通常发生在文档还没加载或是已经从集合中移除,所以要有处理null值的准备。

collection.find([selector], [options])

Anywhere

Find the documents in a collection that match the selector.

Arguments

selector Mongo Selector, Object ID, or String

A query describing the documents to find

Options

sort Mongo Sort Specifier

Sort order (default: natural order)

skip Number

Number of results to skip at the beginning

limit Number

Maximum number of results to return

fields Mongo Field Specifier

Dictionary of fields to return or exclude.

find方法和findOne类似,不同的是,它不返回单一文档,而是返回一个MongoDB 游标。游标是一个特殊的对象,代表一个查询里会被返回的文档列表。可以在模板Helper里返回游标,或是其它可以返回数组的地方:

Template.blog.helpers({
  posts: function () {
    // this helper returns a cursor of
    // all of the posts in the collection
    return Posts.find();
  }
});
<!-- a template that renders multiple posts -->
<template name="blog">
  {{#each posts}}
    <h1>{{title}}</h1>
    <p>{{content}}</p>
  {{/each}}
</template>

要想从一个游标里检索当前的文档列表时,调用游标的.fetch()方法:

// get an array of posts
var postsArray = Posts.find().fetch();

记住,虽然调用fetch的计算会在数据变化时重新运行,但是the resulting array will not be reactive if it is passed somewhere else.

通过调用insert,update, 或 remove来修改保存在Mongo.Collection里的数据。

collection.insert(doc, [callback])

Anywhere

Insert a document in the collection. Returns its unique _id.

Arguments

doc Object

The document to insert. May not yet have an _id attribute, in which case Meteor will generate one for you.

callback Function

Optional. If present, called with an error object as the first argument and, if no error, the _id as the second.

下面的例子展示了如何插入文档到集合里:

Posts.insert({
  createdBy: Meteor.userId(),
  createdAt: new Date(),
  title: "My first post!",
  content: "Today was a good day." });

每个Mongo.Collection里的每个文档都有一个_id字段。它必须是唯一的,如果你没有提供,会自动生成。collection.findOne使用_id可以用来检索特定的文档。

collection.update(selector, modifier, [options], [callback])

Anywhere

Modify one or more documents in the collection. Returns the number of affected documents.

Arguments

selector Mongo Selector, Object ID, or String

Specifies which documents to modify

modifier Mongo Modifier

Specifies how to modify the documents

callback Function

Optional. If present, called with an error object as the first argument and, if no error, the number of affected documents as the second.

Options

multi Boolean

True to modify all matching documents; false to only modify one of the matching documents (the default).

upsert Boolean

True to insert a document if no matching documents are found.

这里的选择器和你传给find方法的是一致的,它可以匹配多个文档。修改器是一个对象,它指明了对匹配到的文档要做的修改。注意:除非你使用了$set操作符,否则update方法会直接用修改器替换整个匹配到的文档。

下面的例子展示了,设置所有标题包含"first"的文章的内容字段

Posts.update({
  title: {$regex: /first/}
}, {
  $set: {content: "Tomorrow will be a great day."}
});

关于支持的所有operators参见MongoDB documentation

有一点需要注意:当在客户端调用update的时候,只能通过_id来查找文档。要使用所有可用的选择器,必须在服务端代码或是 method中调用update

collection.remove(selector, [callback])

Anywhere

Remove documents from the collection

Arguments

selector Mongo Selector, Object ID, or String

Specifies which documents to remove

callback Function

Optional. If present, called with an error object as its argument.

remove方法使用和findupdate一样的选择器,并且从数据库中移除所有匹配到的文档。请小心使用remove — 删掉的数据没办法恢复。

update一样,客户端代码只能通过_id移除文档,而服务端代码和methods可以使用任何选择器移除文档。

collection.allow(options)

Server

Allow users to write directly to this collection from client code, subject to limitations you define.

Options

insert, update, remove Function

Functions that look at a proposed modification to the database and return true if it should be allowed.

在新创建的APP中,Meteor允许任何客户端和服务端代码调用insert, update, 和 remove 。这是因为用meteor create创建的APP默认包含了insecure包,目的是简化开发。很显然,如果任何用户都可以修改数据库,这是很不安全的,所以移除insecure包,并声明一些权限规则是很重要的:

$ meteor remove insecure

一旦你移除了insecure包,就可以用allowdeny来控制谁可以执行哪些数据库操作。默认情况下,客户端所有的操作都被禁止,所以,需要添加一些allow规则。记住:服务端代码和methods 不受allowdeny影响 — 这些规则只应用于当不可信的客户端代码调用insert, update, 和 remove时。

例如,假设只有当createBy字段为当前用户ID时,才允许用户插入新文章,这样用户就不能冒充其他人

// In a file loaded on the server (ignored on the client)
Posts.allow({
  insert: function (userId, post) {
    // can only create posts where you are the author
    return post.createdBy === userId;
  },
  remove: function (userId, post) {
    // can only delete your own posts
    return post.createdBy === userId;
  }
  // since there is no update field, all updates
  // are automatically denied
});

allow方法接受三个回调函数:insert,remove,update。三个回调函数的第一个参数是登录用户的_id,其余的参数如下:

  1. insert(userId, document)

    document 是将要插入到数据库的文档。如果允许插入,则返回true,否则返回false

  2. update(userId, document, fieldNames, modifier)

    document 是将要被修改的文档。fieldNames是一个数组,包含了受这次修改影响的一级字段。 modifier是传给collection.update的第二个参数Mongo Modifier。 如果使用这个回调无法实现正确的校验,那么推荐使用methods。 如果允许修改,则返回true,否则返回false

  3. remove(userId, document)

    document是将要从数据库中移除的文档。如果允许移除则返回true,否则返回false

collection.deny(options)

Server

Override allow rules.

Options

insert, update, remove Function

Functions that look at a proposed modification to the database and return true if it should be denied, even if an allow rule says otherwise.

deny方法允许你选择性的重写allow规则。只要有一个allow回调函数返回true,就允许修改,但必须所有deny规则都返回false,才允许修改。

例如,我们要重写上面定义的allow规则:排除特定标题的文章:

// In a file loaded on the server (ignored on the client)
Posts.deny({
  insert: function (userId, post) {
    // Don't allow posts with a certain title
    return post.title === "First!";
  }
});