Mongoose - REST API - 带有查询到不同模型的模式

Posted

技术标签:

【中文标题】Mongoose - REST API - 带有查询到不同模型的模式【英文标题】:Mongoose - REST API - Schema With Query to different model 【发布时间】:2015-02-13 00:29:42 【问题描述】:

我正在尝试避免数据库回调查询。

假设您有两个看起来像这样的模式:

1st) 用户架构

 username : type: String, unique: true,
 age : type: Number

2nd) 活动架构

 owner: [type: Schema.Types.ObjectId, ref: 'User'],
 city: type: String,
 date: type: Date

到目前为止一切顺利。

现在假设您有一条到/user/:id 的路线,您希望得到usernameage,但是如果我还想在该路线上返回最新活动怎么办

编辑:请注意latest activity 不是数据库中的值。它是像activity.find(owner: ObjectId(id)).sort(date: -1).limit(1)一样自动计算的

现在做了什么:

User.findOne(username:req.params.username).lean().exec(function(err,userDoc)


   if(err) return errHandler(err);

   Activity.findOne(owner:userDoc.username).sort(date:-1).exec(function(err,EventDoc)

     if(err) return errHandler(err);

     userDoc.latest_activity = EventDoc._id;

     res.json(userDoc);
     res.end();

   )

)

上面sn-p的问题是很难维护, 如果我们想为这个 API 功能添加更多内容怎么办?除非我们实现 Q,否则我们将在查询地狱般的回调中结束。

我们尝试查看虚拟,但问题是您不能 真正在 mongoose Virtual 中查询,因为它返回一个 竞争条件,您很可能无法按时获得该文件。

我们也尝试查看 populate,但由于有关 populate 的文档非常糟糕,我们无法做到。

问题: 有没有办法让它更加模块化?

有什么办法可以避免地狱的数据库查询回调?

例如这种事情可能吗?

    User.findOne(username:req.params.username).lean().populate(
path:'Event',sort:Date:-1, limit(1)
).exec(function(req,res))...

谢谢!

【问题讨论】:

我又更新了一遍,应该更清楚了。我认为没有人解决这个问题是荒谬的。 您正在执行手动联接。该空间中似乎有许多模块:npmjs.com/search?q=mongo+join(或者如果您确实有关系数据,请使用关系数据库) 所以你在这里说的是,如果我试图构建一个 REST API 路由来返回文档旁边的额外数据(对不同集合的查询),则无法避免回调地狱除非我使用 RDBMS,否则查询,对吗?必须有另一种方法.. mongoose 中的功能架构设计 populate 仅适用于包含 ref 的模型(在本例中为 Activity)。因此,您可以获取用户的最新活动并填充其owner 数组,但反之则不行。 您可以通过多种方式避免回调地狱,例如异步模块、承诺等。 . .但是,如果您花时间手动加入集合,那就错了。另一种选择是布置您的数据,以便您不需要加入它,例如当活动发生时使用 last_activity 更新用户。 【参考方案1】:

受@BrianShambien 的回答的启发,您可以使用帖子保存,但不是仅将_id 存储在用户上,而是仅存储最后一个活动的子文档。然后,当您抓住该用户时,它就在那里有最后一个活动。

用户模型

username :     type: String, unique: true,
age :          type: Number,
last_activity: ActivitySchema

然后你在你的ActivitySchema上做一个post save hook

ActivitySchema.post('save', function(doc) 
    UserSchema.findOne(username: doc.owner).exec(function(err, user)
        if (err) errHandler(err);

        user.last_activity = doc;

        user.save(function(err) 
            if (err) errHandler(err);
        );
    );
);

**********更新************

如果用户不是所有者,而是活动的参与者,这将包括对用户的更新。

ActivitySchema.post('save', function(doc) 
    findAndUpdateUser(doc.owner, doc);

    if (doc.participants) 
        for (var i in doc.participants) 
            findAndUpdateUser(doc.participants[i], doc);
        
    
);

var findAndUpdateUser = function (username, doc) 
    UserSchema.findOne(username: username).exec(function (err, user) 
        if (err) errHandler(err);

        user.last_activity = doc;

        user.save(function (err) 
            if (err) errHandler(err);
        );
    );
);

【讨论】:

与 Brian 的回答太相似了,但是看看我写的评论,如果你能解决这个问题,那就太棒了。 类似是的,但是他仍然会要求您进行第二次查询以获取最新活动。我的只需要你拉用户,因为最新的活动将在用户身上 这更有意义,我更新了我的答案。你是这个意思吗?【参考方案2】:

在这种情况下,最好的处理方法是向您的Activity 架构添加一个保存后挂钩,以将最新的_id 存储在您的latest_activity 架构的latest_activity 路径中。这样您就可以随时访问 id 而无需进行额外的查询。

ActivitySchema.post('save', function(doc) 
    UserSchema.findOne(username: doc.owner).exec(function(err, user)
        if (err)
            console.log(err); //do something with the error
        else if (user) 
            user.latest_activity = doc._id;

            user.save(function(err) 
                if (err)
                    console.log(err); //do something with the error
            );
        
    );
);

【讨论】:

您是否还需要进行额外的查询,无论是通过_id 还是user._id 搜索活动? 在您的原始示例中,您只是将 _id 值分配给 latest_activity,从而获得了相同的结果。使用我的方法,如果需要,您仍然需要加载活动详细信息,就像您在示例中所做的那样,但它消除了每次查找最新 _id 的昂贵排序查询的需要。 总结你的答案:你建议布局数据,在用户架构中添加最新的活动,这将根据“保存”钩子更新最新的活动?这很好,但是,如果最新的活动有点复杂怎么办?例如 latest_activity = 所有者或参加了活动? (问题中没有提到这个,我只是想更好地理解)。填补空白,您将成为英雄 @Linial 我在这里有点困惑,Brian Shambien 的示例确实可以处理任何复杂的情况。它将为您提供用户参与的活动的参考。他的示例缩短了查询时间,就像他说它从查找查询中删除了排序一样。你能解释一下“有点复杂”是什么意思吗? 例如:最新活动 = db.activity.find($or:[owner:username,participated:$in:[username]]).sort(date:-1).limit(1) 这意味着最新活动是拥有或参与的活动的组合,并且从最新到最新排序。

以上是关于Mongoose - REST API - 带有查询到不同模型的模式的主要内容,如果未能解决你的问题,请参考以下文章

不使用 API REST MEAN + Mongoose 中的方法“UPDATE”

用于 rest api 的 typegoose/mongoose 日期格式

单元/集成测试 Express REST API, mongoose, mocha, sinon, chai, supertest

如何对使用 Express + Mongoose 构建的 NodeJs REST API 服务器进行单元测试?

使用 JWT (Node.js + mongoose) 在 REST API 中管理用户权限的最佳方法

功能/集成测试 NodeJS ReST API 实现