技术开发 频道

详谈MongoDB数据库语法上的五大缺陷

  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" } ] ] }
0
相关文章