用猫鼬去规范化:如何同步更改 [英] Denormalization with Mongoose: How to synchronize changes

查看:97
本文介绍了用猫鼬去规范化:如何同步更改的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

当您使用非规范化的架构时,传播更新的最佳方法是什么?是否应该全部在同一个功能中完成?

What is the best way to propagate updates when you have a denormalized Schema? Should it be all done in the same function?

我有一个类似的架构:

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字段将始终仅使用句点的名称(这是唯一的,不能有两个具有相同名称的句点).然后说我将我的模式变成这样:

Now say I want to denormalize Authors, since the period field will always just use the name of the period (which is unique, there can't be two periods with the same name). Say then that I turn my schema into this:

var Authors = new Schema({
        ...
        name: {type: String, required:true},
        period: String, //no longer a ref
        active: Boolean,
    ...
})

现在,猫鼬不再知道period字段已连接到Period模式.因此,当期间名称更改时,由我决定更新该字段.我创建了一个提供如下接口的服务模块:

Now Mongoose doesn't know anymore that the period field is connected to the Period schema. So it's up to me to update the field when the name of a period changes. I created a service module that offers an interface like this:

exports.updatePeriod = function(id, changes) {...}

在此功能中,我将进行更改以更新需要更新的期间凭证.所以这是我的问题.那么,我应该在此方法中更新所有作者吗?因为那样的话,该方法将必须知道Author模式以及使用句点的任何其他模式,从而在这些实体之间创建了很多耦合.有更好的方法吗?

Within this function I go through the changes to update the period document that needs to be updated. So here's my question. Should I, then, update all authors within this method? Because then the method would have to know about the Author schema and any other schema that uses period, creating a lot of coupling between these entities. Is there a better way?

也许我可以发出一个事件,即已更新了句点,并且所有具有非规范化句点引用的模式都可以观察到它,这是一个更好的解决方案吗?我不太确定如何解决这个问题.

Perhaps I can emit an event that a period has been updated and all the schemas that have denormalized period references can observe it, is that a better solution? I'm not quite sure how to approach this issue.

推荐答案

好吧,虽然我等待一个比自己的答案更好的答案,但我将尝试发布到目前为止所做的事情.

Ok, while I wait for a better answer than my own, I will try to post what I have been doing so far.

前置/后置中间件

我尝试做的第一件事是使用中间件之前/之后来同步彼此引用的文档. (例如,如果您具有AuthorQuote,并且Author具有类型为quotes: [{type: Schema.Types.ObjectId, ref:'Quotes'}]的数组,则每当删除Quote时,都必须从该数组中删除其_id.或者,如果作者被删除,您可能希望删除其所有引号).

The first thing I tried was to use the pre/post middlewares to synchronize documents that referenced each other. (For instance, if you have Author and Quote, and an Author has an array of the type: quotes: [{type: Schema.Types.ObjectId, ref:'Quotes'}], then whenever a Quote is deleted, you'd have to remove its _id from the array. Or if the Author is removed, you may want all his quotes removed).

此方法有一个重要的优势:如果在其自己的文件中定义每个Schema,则可以在其中定义中间件并将其整齐地组织.每当您查看架构时,都可以在下面看到其功能,其更改如何影响其他实体,等等:

This approach has an important advantage: if you define each Schema in its own file, you can define the middleware there and have it all neatly organized. Whenever you look at the schema, right below you can see what it does, how its changes affect other entities, etc:

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.
    )
})

主要的缺点,但是,当您调用update或任何Model时,这些钩子未执行静态更新/删除功能.而是需要检索文档,然后对它们调用save()remove().

The main disadvantage however is that these hooks are not executed when you call update or any Model static updating/removing functions. Rather you need to retrieve the document and then call save() or remove() on them.

另一个较小的缺点是Quote现在需要知道引用它的任何人,以便每当更新或删除Quote时,Quote都可以对其进行更新.因此,假设Period具有引号列表,而Author也具有引号列表,Quote将需要了解这两个信息才能对其进行更新.

Another smaller disadvantage is that Quote now needs to be aware of anyone that references it, so that it can update them whenever a Quote is updated or removed. So let's say that a Period has a list of quotes, and Author has a list of quotes as well, Quote will need to know about these two to update them.

之所以这样做,是因为这些函数将原子查询直接发送到数据库.虽然这很好,但我讨厌使用save()Model.Update(...)之间的不一致.也许将来有人或您不小心使用了静态更新功能而您的中间件没有被触发,这使您头痛,难以摆脱.

The reason for this is that these functions send atomic queries to the database directly. While this is nice, I hate the inconsistency between using save() and Model.Update(...). Maybe somebody else or you in the future accidently use the static update functions and your middleware isn't triggered, giving you headaches that you struggle to get rid of.

NodeJS事件机制

我目前所做的并不是真正的最佳选择,但它为我提供了足够的好处,足以抵消不利因素的影响(或者,我相信,如果有人愿意给我一些反馈的话,那会很棒).我创建了一个围绕模型的服务,例如,扩展了events.EventEmitterAuthorService是一个构造函数,其外观大致如下:

What I am currently doing is not really optimal but it offers me enough benefits to actually outweight the cons (Or so I believe, if anyone cares to give me some feedback that'd be great). I created a service that wraps around a model, say AuthorService that extends events.EventEmitter and is a Constructor function that will look roughly like this:

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)
  • 行情不需要知道所有引用它的文档,服务仅触发QuoteUpdated事件,发生这种情况时需要执行操作的所有文档都将这样做.
  • Any interested function can register to the Service events and be notified. That way, for instance, when a Quote is updated, the AuthorService can listen to it and update the Authors accordingly. (Note 1)
  • Quote doesn't need to be aware of all the documents that reference it, the Service simply triggers the QuoteUpdated event and all the documents that need to perform operations when this happens will do so.

注意1:只要有人需要与猫鼬互动,只要使用此服务即可.

缺点:

  • 使用服务而不是猫鼬添加了样板代码.
  • 现在,当您调用什么函数时,现在还不是很清楚 触发事件.
  • 您以牺牲易读性为代价将生产者和消费者脱钩(因为 你只是emit('EventName', args),这不是立即显而易见的 哪些服务正在监听此事件)
  • Added boilerplate code, using a service instead of mongoose directly.
  • Now it isn't exactly obvious what functions get called when you trigger the event.
  • You decouple producer and consumer at the cost of legibility (since you just emit('EventName', args), it's not immediately obvious which Services are listening to this event)

另一个缺点是,有人可以从服务中检索模型并调用save(),其中不会触发事件 ,尽管我确信可以通过某种方式解决这两种解决方案之间的混合.

Another disadvantage is that someone can retrieve a Model from the Service and call save(), in which the events won't be triggered though I'm sure this could be addressed with some kind of hybrid between these two solutions.

我非常乐意接受该领域的建议(这就是为什么我首先发布此问题的原因).

I am very open to suggestions in this field (which is why I posted this question in the first place).

这篇关于用猫鼬去规范化:如何同步更改的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆