10

当我使用 nodejs 中“$where”子句中传递的函数查询我的数据库时,它总是返回数据库中的所有文档。

例如,如果我这样做

var stream = timetables.find({$where: function() { return false; }}).stream();

它还给我所有的文件。相反,如果我这样做

var stream = timetables.find({$where: 'function() { return false; }'}).stream();

该函数真的被执行了,并且这段代码不返回任何文档。

问题是,如果我在字符串中转换我的函数,上下文的绑定将被删除,我需要它们来进行更复杂的查询。例如:

var n = 1;
var f = function() { return this.number == n; }
var stream = timetables.find({$where: f.toString()}).stream();
// error: n is not defined

这是正常行为吗?我该如何解决我的问题?请原谅我糟糕的英语!

4

7 回答 7

7

首先,请记住,由于此处$where解释的原因,几乎不应该使用操作符(感谢 @WiredPrairie)。

回到您的问题,您想要采用的方法即使在 mongodb shell 中也行不通(它明确允许使用$where操作员执行裸 js 函数)。提供给$where操作员的 javascript 代码在 mongo 服务器上执行,并且无法访问封闭环境(“上下文绑定”)。

> db.test.insert({a: 42})
> db.test.find({a: 42})
{ "_id" : ObjectId("5150433c73f604984a7dff91"), "a" : 42 }
> db.test.find({$where: function() { return this.a == 42 }}) // works
{ "_id" : ObjectId("5150433c73f604984a7dff91"), "a" : 42 }
> var local_var = 42
> db.test.find({$where: function() { return this.a == local_var }})
error: {
    "$err" : "error on invocation of $where function:\nJS Error: ReferenceError: local_var is not defined nofile_b:1",
    "code" : 10071
}

此外,node.js 原生 mongo 驱动程序的行为似乎与 shell 不同,因为它不会自动序列化您在查询对象中提供的 js 函数,而是可能完全删除该子句。这将为您留下等效的timetables.find({})返回集合中的所有文档。

于 2013-03-25T12:40:24.663 回答
4

这对我有用,只需尝试将查询作为字符串存储在一个变量中,然后将您的变量连接到查询字符串中,

var local_var = 42

var query = "{$where: function() { return this.a == "+local_var+"}}"

db.test.find(查询)

于 2017-08-30T11:13:45.950 回答
2

将您的查询存储到一个变量中,并在您的查找查询中使用该变量。它的工作原理......:D

于 2017-08-30T11:32:10.197 回答
1

您可以使用包装器来传递基本的 JSON 对象,即。(请原谅咖啡脚本):

# That's the main wrapper.
wrap = (f, args...) ->
  "function() { return (#{f}).apply(this, #{JSON.stringify(args)}) }"

# Example 1
where1 = (flag) ->
  @myattr == 'foo' or flag

# Example 2 with different arguments
where2 = (foo, options = {}) ->
  if foo == options.bar or @_id % 2 == 0
    true
  else
    false

db.collection('coll1').count $where: wrap(where1, true), (err, count) ->
  console.log err, count

db.collection('coll1').count $where: wrap(where2, true, bar: true), (err, count) ->
  console.log err, count

您的函数将被传递为:

function () {
    return (function (flag) {
        return this.myattr === 'foo' || flag;
    }).apply(this, [true])
}

...和示例 2:

function () {
    return (
        function (foo, options) {
            if (options == null) {
                options = {};
            }
            if (foo === options.bar || this._id % 2 === 0) {
                return true;
            } else {
                return false;
            }
        }
    ).apply(this, [ true, { "bar": true } ])
}
于 2014-04-04T13:19:30.963 回答
1

上下文将始终是 mongo 数据库的上下文,因为函数在那里执行。无法在两个实例之间共享上下文。您必须重新考虑查询方式并提出不同的策略。

于 2013-03-25T10:46:17.007 回答
0

这就是它应该的样子。驱动程序不会将客户端代码转换为 mongo 函数 javascript 代码。

于 2013-03-25T11:07:46.417 回答
0

我假设您正在使用Mongoose来查询您的数据库。

如果您查看实际的 Query 对象实现,您会发现只有字符串才是where 原型的有效参数。

使用 where 子句时,应与标准运算符(如 gt、lt)一起使用,这些运算符在 where 函数创建的路径中进行操作。

请记住,Mongoose 查询,就像在 Mongo 中一样,是一个示例,您可能希望以更具描述性的方式重新考虑您的查询规范。

于 2013-03-25T11:19:48.343 回答