当您拥有非规范化架构时,传播更新的最佳方式是什么?是否应该全部在同一个函数中完成?
我有这样的架构:
var Authors = new Schema({
...
name: {type: String, required:true},
period: {type: Schema.Types.ObjectId, ref:'Periods'},
quotes: [{type: Schema.Types.ObjectId, ref: 'Quotes'}]
active: Boolean,
...
})
然后:
var Periods = new Schema({
...
name: {type: String, required:true},
authors: [{type: Schema.Types.ObjectId, ref:'Authors'}],
active: Boolean,
...
})
现在说我想对作者进行非规范化,因为 period
字段将始终只使用句点的名称(这是唯一的,不能有两个同名的句点)。然后说我把我的模式变成了这样:
var Authors = new Schema({
...
name: {type: String, required:true},
period: String, //no longer a ref
active: Boolean,
...
})
现在 Mongoose 不再知道 period 字段已连接到 Period 架构。因此,当一个时期的名称发生变化时,由我来更新该字段。我创建了一个提供如下接口(interface)的服务模块:
exports.updatePeriod = function(id, changes) {...}
在此功能中,我通过更改来更新需要更新的期间文档。所以这是我的问题。那么,我应该更新此方法中的所有作者吗?因为那时该方法必须了解 Author 模式和任何其他使用句点的模式,从而在这些实体之间创建大量耦合。有没有更好的办法?
也许我可以发出一个周期已更新的事件,并且所有具有非规范化周期引用的模式都可以观察到它,这是一个更好的解决方案吗?我不太确定如何解决这个问题。
最佳答案
好的,虽然我在等待比我自己更好的答案,但我会尝试发布我到目前为止所做的事情。
前置/后置中间件
我尝试的第一件事是使用 pre/post middlewares同步相互引用的文档。 (例如,如果您有 Author
和 Quote
,并且 Author 有一个类型为的数组:quotes: [{type: Schema.Types.ObjectId, ref:'Quotes'}]
,那么每当一个 Quote 被删除时,你必须从数组中删除它的 _id
。或者如果 Author 被删除,你可能想要他的所有引号已删除)。
这种方法有一个重要的优势:如果您在其自己的文件中定义每个 Schema,您可以在其中定义中间件并将其整齐地组织。每当您查看架构时,您都可以在下方看到它的作用、其更改如何影响其他实体等:
var Quote = new Schema({
//fields in schema
})
//its quite clear what happens when you remove an entity
Quote.pre('remove', function(next) {
Author.update(
//remove quote from Author quotes array.
)
})
The main disadvantage however is that these hooks are not executed when you call update or any Model static updating/removing functions .相反,您需要检索文档,然后对它们调用 save()
或 remove()
。
另一个较小的缺点是 Quote 现在需要知道引用它的任何人,以便在更新或删除 Quote 时更新它们。因此,假设 Period
有一个引号列表,而 Author
也有一个引号列表, Quote 需要知道这两个来更新它们。
原因是这些函数直接向数据库发送原子查询。虽然这很好,但我讨厌使用 save()
和 Model.Update(...)
之间的不一致。也许将来别人或你不小心使用了静态更新功能,而你的中间件没有被触发,给你带来了难以摆脱的头痛。
NodeJS 事件机制
我目前所做的并不是最理想的,但它为我提供了足够的好处,实际上超过了缺点(或者我相信,如果有人愿意给我一些反馈,那就太好了)。我创建了一个围绕模型的服务,例如 AuthorService
扩展 events.EventEmitter
并且是一个构造函数,大致如下所示:
function AuthorService() {
var self = this
this.create = function() {...}
this.update = function() {
...
self.emit('AuthorUpdated, before, after)
...
}
}
util.inherits(AuthorService, events.EventEmitter)
module.exports = new AuthorService()
优点:
- 任何感兴趣的功能都可以注册到服务中
事件并得到通知。这样,例如,当
Quote
是 更新,AuthorService 可以监听它并更新Authors
因此。 (注1) - Quote 不需要知道引用它的所有文档,Service 只需触发
QuoteUpdated
事件,发生这种情况时需要执行操作的所有文档都会这样做。
注意 1:只要有人需要与 Mongoose 交互时使用此服务。
缺点:
- 添加了样板代码,直接使用服务而不是 Mongoose 。
- 现在,当您调用什么函数时,我们并不十分清楚 触发事件。
- 您以易读性为代价将生产者和消费者分离(因为
你只是
emit('EventName', args)
,这不是很明显 哪些服务正在监听此事件)
另一个缺点是有人可以从服务中检索模型并调用 save()
,其中事件不会被触发,但我确信这一点可以通过这两种解决方案之间的某种混合来解决。
我对这个领域的建议非常开放(这就是我首先发布这个问题的原因)。
关于node.js - 使用 Mongoose : How to synchronize changes 进行非规范化,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22201940/