3. Soft limits, hard limits and no limits
你有一个项目的输入并且允许用户指定数字项目返回。你应该把问题的结果像这样返回:
db.items.find({ ... }).limit(N);
N值是由 用户供给的。我们当然希望小心的将用户限制在50之内,否则网络上的任何人只需简单地提供一个非常大的N值都可以下载我们的应用服务器和数据库。
function getItems (N) { if (N > 50) N = 50; return db.items.find({}).sort({ year: 1 }).limit(N); }
看起来是个有道理运行在你的node.js app上的代码。
陷阱:如果用户提供0作为一个项目的值,他希望MongoDB可以理解为把所有都给他。
这在文档里写的很清楚,但很多情况下并不是那么显然:在MongoDB中零表示无限制。我猜想MongoDB的代码可能将undefined, null, 0等等所有的false值当做无限制对待。
这没关系,我们可以对0进行单独处理:
function getItems (N) { if (N > 50 || !N) // check if N is falsy ("no limit") N = 50; return db.items.find({}).sort({ year: 1 }).limit(N); }
看上去不错?但是如果用户输入一个负值怎么办?这可能么?这又意味着什么?
事实上像db.items.find().limit(-1000000000000)这类的语句可能返回非常多的项。很难找到相关的文档,但几个月前我在node.js的驱动文档中看到一篇文章描述了这种行为,它将其表述为“硬”限制和“软”限制。我不知道这是什么意思。
那么我们服务器端方法的最终版就是这样了:
function getItems (N) { if (N < 0) N = -N; if (N > 50 || !N) // check if N is falsy ("no limit") N = 50; return db.items.find({}).sort({ year: 1 }).limit(N); }
总结: 限制可以是负数。它在广义上和正数是一样的但是负数限制是“软限制”。
4.数组的特殊待遇
很多人并不知道这个特性,但数组确实是经过特殊处理的。
> db.c.insert({ a: [{x: 2}, {x: 3}], _id: "aaa"}) > db.c.find({'a.x': { $gt: 1 }}) < { "_id" : "aaa", "a" : [ { "x" : 2 }, { "x" : 3 } ] } > db.c.find({'a.x': { $gt: 2 }}) < { "_id" : "aaa", "a" : [ { "x" : 2 }, { "x" : 3 } ] } > db.c.find({'a.x': { $gt: 3 }}) < Nothing found
因此每当有一个数组对象,选择器都会“分发”给每一个元素,这就像“如果其中一个元素匹配,那么整个文档(document)都会被匹配”。
值得注意的是,它并不适用于嵌套数组::
> db.x.insert({ _id: "bbb", b: [ [{x: 0}, {x: -1}], {x: 1} ] }) > db.x.find({ 'b.x': 1 }) < { "_id" : "bbb", "b" : [ [ { "x" : 0 }, { "x" : -1 } ], { "x" : 1 } ] } > db.x.find({ 'b.x': 0 }) < Nothing found > db.x.find({ 'b.x': -1 }) < Nothing found
同样也适用于预测数组中字段(field)的一些特性:
> db.z.insert({a:[[{b:1,c:2},{b:2,c:4}],{b:3,c:5},[{b:4, c:9}]]}) > db.z.find({}, {'a.b': 1}) < { "_id" : ObjectId("52ca24073e47d3d91146f2b7"), "a" : [ [ { "b" : 1 }, { "b" : 2 } ], { "b" : 3 }, [ { "b" : 4 } ] ] }
如果我们在选择器上将以上特性与使用数字键做更多的组合,那么这个特性将变得越来越难以预测:
> db.z.insert({a: [[{x: "00"}, {x: "01"}], [{x: "10"}, {x: "11"}]], _id: "zzz"}) > db.z.find({'a.x': '00'}) < Nothing found > db.z.find({'a.x': '01'}) < Nothing found > db.z.find({'a.x': '10'}) < Nothing found > db.z.find({'a.x': '11'}) < Nothing found > db.z.find({'a.0.0.x': '00'}) < { "_id" : "zzz", "a" : [ [ { "x" : "00" }, { "x" : "01" } ], [ { "x" : "10" }, { "x" : "11" } ] ] } > db.z.find({'a.0.0.x': '01'}) < Nothing found > db.z.find({'a.0.x': '00'}) < { "_id" : "zzz", "a" : [ [ { "x" : "00" }, { "x" : "01" } ], [ { "x" : "10" }, { "x" : "11" } ] ] } > db.z.find({'a.0.x': '01'}) < { "_id" : "zzz", "a" : [ [ { "x" : "00" }, { "x" : "01" } ], [ { "x" : "10" }, { "x" : "11" } ] ] } > db.z.find({'a.0.x': '10'}) < Nothing found > db.z.find({'a.0.x': '11'}) < Nothing found > db.z.find({'a.1.x': '00'}) < Nothing found > db.z.find({'a.1.x': '01'}) < Nothing found > db.z.find({'a.1.x': '10'}) < { "_id" : "zzz", "a" : [ [ { "x" : "00" }, { "x" : "01" } ], [ { "x" : "10" }, { "x" : "11" } ] ] } > db.z.find({'a.1.x': '11'}) < { "_id" : "zzz", "a" : [ [ { "x" : "00" }, { "x" : "01" } ], [ { "x" : "10" }, { "x" : "11" } ] ] }