我想编写一个小应用程序,在那里我可以存储下面名为reqUrl
的传入请求URL,并使用compareUrls
函数检查它是否已存在。
如果两个网站位于同一个域中,则返回true,否则返回false,例如执行compareUrls(stackoverflow.com, http://www.stackoverflow.com)
时。这用于不添加重复的URL。
我试图在MongoDB查询中使用该函数,如下所示:
app.get("/:reqUrl", function(req, res)
{
var reqUrl = req.params.reqUrl;
MongoClient.connect(Url, function(err, db)
{
if (err) throw err;
db.collection("mydb").find({$where: function() {
if (compareUrls(reqUrl, this.url) //if true, simply return the url
{
return this.url;
} else { //if not existing insert it into the database
db.collection("mydb").insert({"url":reqUrl});
};
}}).toArray();
//Code continues below
现在的问题是,由于范围界定,reqUrl
变量无法识别,我不知道任何解决方法。即使使用compareUrls
的局部变量,我也会收回整个元素集合。我想通过简单地调用.find
并针对每个项目检查reqUrl
来检索将所有结果检索到数组,但这远远超过效率。
请注意,我是MongoDB的新手。
任何反馈都将不胜感激,谢谢。
答案 0 :(得分:2)
这里的底线是你不能在$where
子句的逻辑中执行其他数据库操作,也不应该因为它完全没有必要而且现有标准运算符和方法实际上支持你的动作。
你真正想要的是.findOneAndUpdate()
。对于您正在进行的匹配条件,您不需要$where
,只需检查一个值即可。这实际上是要查询“查询”部分的$regex
搜索条件。
至于“插入”部分,那就是"upserts"的用途。因此,当数据未“找到”时,“upsert”会在集合中创建/插入新文档,否则在发现时会“更新”。在这种情况下,您可以使用$setOnInsert
修饰符对其进行调整,以便实际修改“找到的”文档,并且仅在“插入”时触及数据:
db.collection("mydb").findOneAndUpdate(
{ "url": new RegExp(reqUrl) },
{ "$setOnInsert": { "url": reqUrl } },
{ "upsert": true, "returnOriginal": false },
function(err, doc) {
// deal with result here
}
)
当然$regex
用法只是一个基本的“这个字符串出现在属性字符串”条件中。有一些特定于“域匹配”的高级正则表达式,例如您可以在现有答案中找到:Regex to match simple domain
但是基本逻辑仍然是相同的,“正则表达式”处理匹配条件然后你只是“upsert”。
也就是说,没有什么能阻止你使用$where
子句来匹配条件。只是实际操作仍然是“upsert”,而不是试图在提供的函数内“调用”数据库方法,该函数可以调用server function或包含在内:
db.collection("mydb").findOneAndUpdate(
{ "$where": function() { return compareUrls(reqUrl, this.url); } },
{ "$setOnInsert": { "url": reqUrl } },
{ "upsert": true, "returnOriginal": false },
function(err, doc) {
// deal with result here
}
)
只需确保在$where
条件下服务器函数或任何结果实际返回布尔值true/false
,因为这就是$where
的操作方式。
另请注意此处使用"returnOriginal": false
,因为.findOneAndUpdate()
方法的默认行为是在修改前返回“原始”文档。在某些情况下,这是可取的,但最常见的用法是将文档恢复为已修改状态。
当然,如果您根本不需要该文档,那么.updateOne()
就足以作为一种方法,并减少了“通过线路”返回文档内容的开销。