Mongoose schema 的深度嵌套导致查询效率低的解决方法
在开发过程中,我们经常会使用 Mongoose 这个库来操作 MongoDB 数据库。而在 Mongoose 中,我们可以定义 schema 来规定数据的格式。然而,在一些情况下我们可能需要使用深度嵌套的 schema,比如一个订单中会包含多个商品,每个商品又有自己的规格、价格等信息。但是,这样的深度嵌套会带来一个问题,就是查询效率低下。本文将介绍如何解决这个问题。
问题描述
假设我们有一个订单 schema,它包含多个商品,每个商品包含商品信息、价格等。我们可以这样定义 schema:
-- -------------------- ---- ------- ----- ----------- - --- ----------------- --------- - - ----- ------- ------ ------- ------ - - ----- ------- ------ ------ - - - -- ------ ------ --
当我们想要查询所有价格大于等于 100 的订单时,可以这样写:
const orders = await Order.find({ 'products.price': { $gte: 100 } })
这个查询语句似乎很简单,但是当数据量大的时候,查询效率就会变得非常低下。
最终,我们会发现一个简单的查询可能需要几秒钟才能完成,这是一个非常严重的问题。
解决方法
解决这个问题的方法是使用 population 和 virtual。
Population
Population 可以通过引用另一个 Collection 来避免嵌套文档的查询效率低下问题。对于我们的订单例子,我们可以定义一个商品 schema:
-- -------------------- ---- ------- ----- ------------- - --- ----------------- ----- ------- ------ ------- ------ - - ----- ------- ------ ------ - - --
然后在订单 schema 中,我们使用 productIds 来记录每个订单包含哪些商品:
const OrderSchema = new mongoose.Schema({ productIds: [{ type: mongoose.Schema.Types.ObjectId, ref: 'Product' }], total: Number })
这样我们就可以通过以下查询来查找所有价格大于等于 100 的订单:
const orders = await Order.find() .populate('productIds', 'name price specs') .where('productIds.price') .gte(100)
Virtual
Virtual 可以通过定义虚拟字段来避免嵌套文档的查询效率低下问题。对于我们的订单例子,我们可以定义一个虚拟字段 products,它其实是根据 productIds 查询出来的商品信息:
OrderSchema.virtual('products', { ref: 'Product', localField: 'productIds', foreignField: '_id', justOne: false })
再加上一个方法来筛选价格大于等于 100 的商品:
OrderSchema.methods.filterByPrice = function (price) { return this.products.filter(p => p.price >= price) }
这样我们就可以通过以下查询来查找所有价格大于等于 100 的订单:
const orders = await Order.find() .populate('products') .then(os => os.flatMap(o => o.filterByPrice(100)))
总结
在 Mongoose 中,深度嵌套的 schema 会导致查询效率低下的问题,但是我们可以通过 population 和 virtual 来解决这个问题。使用 population 可以避免嵌套文档的查询效率低下问题,而使用 virtual 可以定义虚拟字段来避免嵌套文档的查询效率低下问题。选择哪种方法取决于具体的情况。需要注意的是,使用 population 和 virtual 也会带来一些额外的性能开销,需要根据实际情况选择使用。
来源:JavaScript中文网 ,转载请注明来源 https://www.javascriptcn.com/post/64fc0a1df6b2d6eab3202ca8