Node.js 将$lookup结果合并到现有文档数组中
-房间收藏Node.js 将$lookup结果合并到现有文档数组中,node.js,mongodb,mongoose,aggregation-framework,Node.js,Mongodb,Mongoose,Aggregation Framework,-房间收藏 _id: ObjectId("xxx") bedspaces: Array 0:ObjectId("xx") 1:ObjectId("xx") *** *** _id: ObjectId("xxxx"); number: 1 decks: Array { _id: ObjectId("xxx"); number: 1 status: "Vacant" tenant: ObjectId("5c964ae7f5097e3020d1926c") dueRent:
_id: ObjectId("xxx")
bedspaces: Array
0:ObjectId("xx")
1:ObjectId("xx")
***
***
_id: ObjectId("xxxx");
number: 1
decks: Array
{
_id: ObjectId("xxx");
number: 1
status: "Vacant"
tenant: ObjectId("5c964ae7f5097e3020d1926c")
dueRent: 11
away: null
},
{
_id: ObjectId("xxx");
number: 2
status: "Vacant"
tenant: null
dueRent: 11
away: null
}
_id: ObjectId("5c964ae7f5097e3020d1926c");
name: 'John Doe'
-床位收集
_id: ObjectId("xxx")
bedspaces: Array
0:ObjectId("xx")
1:ObjectId("xx")
***
***
_id: ObjectId("xxxx");
number: 1
decks: Array
{
_id: ObjectId("xxx");
number: 1
status: "Vacant"
tenant: ObjectId("5c964ae7f5097e3020d1926c")
dueRent: 11
away: null
},
{
_id: ObjectId("xxx");
number: 2
status: "Vacant"
tenant: null
dueRent: 11
away: null
}
_id: ObjectId("5c964ae7f5097e3020d1926c");
name: 'John Doe'
在decks数组下,是我的租户字段,它有objectId,我将在租户集合中查找这个对象id
-租户收款
_id: ObjectId("xxx")
bedspaces: Array
0:ObjectId("xx")
1:ObjectId("xx")
***
***
_id: ObjectId("xxxx");
number: 1
decks: Array
{
_id: ObjectId("xxx");
number: 1
status: "Vacant"
tenant: ObjectId("5c964ae7f5097e3020d1926c")
dueRent: 11
away: null
},
{
_id: ObjectId("xxx");
number: 2
status: "Vacant"
tenant: null
dueRent: 11
away: null
}
_id: ObjectId("5c964ae7f5097e3020d1926c");
name: 'John Doe'
-预期产量
/*room collection*/
_id: ObjectId("xxx")
bedspaces: [
{
_id: ObjectId("xxx")
number: 1
decks: [
{
_id: ObjectId("xxx")
number: 1
status: "Vacant"
tenant: {
name: 'John Doe'
}
dueRent: 11
away: null
},
{
_id: ObjectId("xxx");
number: 1
status: "Vacant"
tenant: null
dueRent: 11
away: null
}
]
}
]
还有一个实例,即deck数组等于null
在下面的聚合中,它将只显示具有对象id的租户的组,我想要的是显示这两个组
{
from: 'beds',
let: {bedspace: '$bedspaces'},
pipeline:[
{
$match: {
$expr: {
$in: ["$_id", "$$bedspace"]
}
}
},
{
$unwind: "$decks"
},
{
$lookup: {
from: 'tenants',
let: {tenant: "$decks.tenant"},
pipeline: [
{
$match: {
$expr: {
$eq: ["$_id", "$$tenant"]
}
}
}
],
as: "decks.tenant",
}
},
{
$unwind: "$decks.tenant"
},
{ $group: {
_id: "$_id",
decks: { $push: "$decks" },
number: {$first: "$number"}
}}
],
as: "bedspaces"
}
“我如何在第二次查找时添加条件,仅在租户不为null时执行”,这样我就可以检索两个组,或者检索任何相关工作,以便获得所需的结果我现在真的没有时间进行所有解释(抱歉)
解释
这里的基本问题是使用是您的问题,您不需要它。在生成的数组中使用与“decks”
数组合并的内容。然后您可以使用空值
这里要做的是将“租户”
集合中的值转换到“床位/床位”
集合中的现有数组中,以用于其自身的现有“租户”
值,这些值是外部集合的ObjectId
引用
stage不能简单地将中的字段路径命名为“
输出,而该路径已经位于另一个数组中,并且实际上的输出始终是从外部集合获得的结果数组。您希望每个实际匹配的值都是单数,当然,您希望在不匹配的地方有一个null
,当然要保持“deck”
的原始文档数组完好无损,但只包括找到它们的外部细节
您的代码尝试似乎部分意识到了这一点,因为您正在将“租户”
集合上的结果使用到一个“临时数组”(但您将其放入现有路径并覆盖内容),然后尝试“重新分组”“作为通过和的数组。但问题当然是,$lookup
结果不适用于“decks”
中的每个数组成员,因此最终得到的结果比您想要的少
真正的解决方案不是“有条件的”,而是将“临时数组”内容从结果转移到现有的“deck”
条目中。您可以使用来处理数组成员,并使用,以便通过匹配的\u id
值将匹配的元素从“临时数组”返回到的“租户”
注意,我们在中使用的是为了保留“decks”
数组的现有内容,并且仅为每个数组成员替换(或“合并”)覆盖的“tenant”
表示形式。您已经在使用expressive,这是MongoDB 3.6的一个特性
出于兴趣,同样的事情也可以通过指定数组中的每个字段来完成。i、 e:
"decks": {
"$map": {
"input": "$decks",
"in": {
"_id": "$$this._id",
"number": "$$this.number",
"tenant": {
// same expression
},
"__v": "$$this.__v" // just because it's mongoose
}
}
}
在中使用的$$REMOVE
也是MongoDB 3.6的另一个特性。您也可以只使用或省略不需要的字段:
{ "$project": {
"number": "$number",
"decks": {
"$map": { /* same expression */ }
},
"__v": "$__v"
// note we don't use the "tenant" temporary array
}}
但这基本上就是它的工作原理。通过获取结果,然后将这些结果转换回文档中的原始数组
示例列表
同时从前面的问题中提取数据,这比你在这里发布的问题要好一点。用于演示的可运行列表:
const { Schema, Types: { ObjectId } } = mongoose = require('mongoose');
const uri = 'mongodb://localhost:27017/hotel';
const opts = { useNewUrlParser: true };
mongoose.set('useFindAndModify', false);
mongoose.set('useCreateIndexes', true);
mongoose.set('debug', true);
const tenantSchema = new Schema({
name: String,
age: Number
});
const deckSchema = new Schema({
number: Number,
tenant: { type: Schema.Types.ObjectId, ref: 'Tenant' }
});
const bedSchema = new Schema({
number: Number,
decks: [deckSchema]
});
const roomSchema = new Schema({
bedspaces: [{ type: Schema.Types.ObjectId, ref: 'Bed' }]
});
const Tenant = mongoose.model('Tenant', tenantSchema);
const Bed = mongoose.model('Bed', bedSchema);
const Room = mongoose.model('Room', roomSchema);
const log = data => console.log(JSON.stringify(data, undefined, 2));
(async function() {
try {
const conn = await mongoose.connect(uri, opts);
// Clean data
await Promise.all(
Object.entries(conn.models).map(([k, m]) => m.deleteMany())
);
// Insert data
let [john, jane, bilbo ] = await Tenant.insertMany([
{
_id: ObjectId("5c964ae7f5097e3020d1926c"),
name: "john doe",
age: 11
},
{
_id: ObjectId("5c964b2531bc162fdce64f15"),
name: "jane doe",
age: 12
},
{
_id: ObjectId("5caa5454494558d863513b24"),
name: "bilbo",
age: 111
}
]);
let bedspaces = await Bed.insertMany([
{
_id: ObjectId("5c98d89c6bd5fc26a4c2851b"),
number: 1,
decks: [
{
number: 1,
tenant: john
},
{
number: 1,
tenant: jane
}
]
},
{
_id: ObjectId("5c98d89f6bd5fc26a4c28522"),
number: 2,
decks: [
{
number: 2,
tenant: bilbo
},
{
number: 3
}
]
}
]);
await Room.create({ bedspaces });
// Aggregate
let results = await Room.aggregate([
{ "$lookup": {
"from": Bed.collection.name,
"let": { "bedspaces": "$bedspaces" },
"pipeline": [
{ "$match": {
"$expr": { "$in": [ "$_id", "$$bedspaces" ] }
}},
{ "$lookup": {
"from": Tenant.collection.name,
"let": { "tenant": "$decks.tenant" },
"pipeline": [
{ "$match": {
"$expr": { "$in": [ "$_id", "$$tenant" ] }
}}
],
"as": "tenant"
}},
{ "$addFields": {
"decks": {
"$map": {
"input": "$decks",
"in": {
"$mergeObjects": [
"$$this",
{
"tenant": {
"$cond": {
"if": {
"$eq": [
{ "$indexOfArray": ["$tenant._id", "$$this.tenant"] },
-1
]
},
"then": null,
"else": {
"$arrayElemAt": [
"$tenant",
{ "$indexOfArray": ["$tenant._id", "$$this.tenant"]}
]
}
}
}
}
]
}
}
},
"tenant": "$$REMOVE"
}}
],
"as": "bedspaces"
}}
]);
log(results);
} catch (e) {
console.error(e)
} finally {
mongoose.disconnect();
}
})()
返回:
Mongoose: tenants.deleteMany({}, {})
Mongoose: beds.deleteMany({}, {})
Mongoose: rooms.deleteMany({}, {})
Mongoose: tenants.insertMany([ { _id: 5c964ae7f5097e3020d1926c, name: 'john doe', age: 11, __v: 0 }, { _id: 5c964b2531bc162fdce64f15, name: 'jane doe', age: 12, __v: 0 }, { _id: 5caa5454494558d863513b24, name: 'bilbo', age: 111, __v: 0 } ], {})
Mongoose: beds.insertMany([ { _id: 5c98d89c6bd5fc26a4c2851b, number: 1, decks: [ { _id: 5caa5af6ed3dce1c3ed72cef, number: 1, tenant: 5c964ae7f5097e3020d1926c }, { _id: 5caa5af6ed3dce1c3ed72cee, number: 1, tenant: 5c964b2531bc162fdce64f15 } ], __v: 0 }, { _id: 5c98d89f6bd5fc26a4c28522, number: 2, decks: [ { _id: 5caa5af6ed3dce1c3ed72cf2, number: 2, tenant: 5caa5454494558d863513b24 }, { _id: 5caa5af6ed3dce1c3ed72cf1, number: 3 } ], __v: 0 } ], {})
Mongoose: rooms.insertOne({ bedspaces: [ ObjectId("5c98d89c6bd5fc26a4c2851b"), ObjectId("5c98d89f6bd5fc26a4c28522") ], _id: ObjectId("5caa5af6ed3dce1c3ed72cf3"), __v: 0 })
Mongoose: rooms.aggregate([ { '$lookup': { from: 'beds', let: { bedspaces: '$bedspaces' }, pipeline: [ { '$match': { '$expr': { '$in': [ '$_id', '$$bedspaces' ] } } }, { '$lookup': { from: 'tenants', let: { tenant: '$decks.tenant' }, pipeline: [ { '$match': { '$expr': { '$in': [ '$_id', '$$tenant' ] } } } ], as: 'tenant' } }, { '$addFields': { decks: { '$map': { input: '$decks', in: { '$mergeObjects': [ '$$this', { tenant: [Object] } ] } } }, tenant: '$$REMOVE' } } ], as: 'bedspaces' } } ], {})
[
{
"_id": "5caa5af6ed3dce1c3ed72cf3",
"bedspaces": [
{
"_id": "5c98d89c6bd5fc26a4c2851b",
"number": 1,
"decks": [
{
"_id": "5caa5af6ed3dce1c3ed72cef",
"number": 1,
"tenant": {
"_id": "5c964ae7f5097e3020d1926c",
"name": "john doe",
"age": 11,
"__v": 0
}
},
{
"_id": "5caa5af6ed3dce1c3ed72cee",
"number": 1,
"tenant": {
"_id": "5c964b2531bc162fdce64f15",
"name": "jane doe",
"age": 12,
"__v": 0
}
}
],
"__v": 0
},
{
"_id": "5c98d89f6bd5fc26a4c28522",
"number": 2,
"decks": [
{
"_id": "5caa5af6ed3dce1c3ed72cf2",
"number": 2,
"tenant": {
"_id": "5caa5454494558d863513b24",
"name": "bilbo",
"age": 111,
"__v": 0
}
},
{
"_id": "5caa5af6ed3dce1c3ed72cf1",
"number": 3,
"tenant": null
}
],
"__v": 0
}
],
"__v": 0
}
]
在床位
数组中的第二个条目的第二个条目上按预期显示null
。不,没有任何此类情况,但不清楚您期望的是什么,除非您可以显示一些能够产生预期结果的数据,并显示可从该数据中获得的预期结果。这里最可能的问题是$unwind
,因为$lookup
在没有匹配的情况下返回空数组。但这只是一个有根据的猜测,没有看到一些数据来重现问题或预期结果。@NeilLunn你能建议一个解决方法,这样我就能达到我想要的结果吗?如果你真的提供一些数据,我可以。屏幕截图不是人们可以使用的数据。文档可以以文本形式查看,因此请在问题中包含“文本”内容,而不是图片。我们实际上可以复制和粘贴文本。查看文档文本表示的最佳方法是使用随MongoDB安装一起安装的mongo
shell。另外,请阅读我所说的内容,并注意到问题中的代码基本上来自其他人的答案,我们真的希望能够付出一些努力。@NeilLunn经过几个小时的尝试,还没有得到预期的结果,我希望你能帮我解决这个问题。我已经更新了上面的查询。you sirr,是一个向导。我只是想知道你是从哪里学到这些东西的,我很难找到教聚合的好资源,你能分享一些链接吗??