Sorry about the bad title, but here is the problem I'm trying to solve:
I have a couple of collections, including ticket, fields, and fieldOptions that look something like this:
Ticket:
{
_id: 1,
subject: "Lorem ipsum dolor sit amet, consectetur adipiscing elit",
fields: [
{
key: "part-number",
value: "abc123",
},
{
key: "price",
value: "10",
},
{
key: "officer",
value: "2",
},
]
}
Fields:
[
{
_id: 1,
created: ISODate("2020-09-10T20:23:46.382Z"),
options: [],
updated: ISODate("2020-09-10T20:23:46.382Z"),
active: true,
displayOrder: 0,
key: "part-number",
label: "Part Number",
required: false,
type: "text",
},
{
_id: 2,
created: ISODate("2020-09-10T20:23:46.382Z"),
options: [],
updated: ISODate("2020-09-10T20:23:46.382Z"),
active: true,
displayOrder: 1,
key: "price",
label: "Price",
required: false,
type: "text",
},
{
_id: 3,
created: ISODate("2020-09-10T20:23:46.382Z"),
options: [1, 2, 3, 4],
updated: ISODate("2020-09-10T20:23:46.382Z"),
active: true,
displayOrder: 2,
key: "officer",
label: "Officer",
required: false,
type: "select",
},
// THIS WAS ADDED AFTER THE 'TICKET' DOC WAS CREATED, SO IT'S NOT LISTED UNDER 'FIELDS' IN THERE
{
_id: 4,
created: ISODate("2020-09-10T20:23:46.382Z"),
options: [],
updated: ISODate("2020-09-10T20:23:46.382Z"),
active: true,
displayOrder: 1,
key: "notes",
label: "Notes",
required: false,
type: "text",
},
]
Field Options:
[
{
_id: 1,
created: ISODate("2020-09-10T20:23:46.382Z"),
updated: ISODate("2020-09-10T20:23:46.382Z"),
active: true,
key: "none",
label: "None",
displayOrder: 0,
legacy: false,
},
{
_id: 2,
created: ISODate("2020-09-10T20:23:46.382Z"),
updated: ISODate("2020-09-10T20:23:46.382Z"),
active: true,
key: "picard",
label: "Picard",
displayOrder: 1,
legacy: false,
},
{
_id: 3,
created: ISODate("2020-09-10T20:23:46.382Z"),
updated: ISODate("2020-09-10T20:23:46.382Z"),
active: true,
key: "riker",
label: "Riker",
displayOrder: 2,
legacy: false,
},
{
_id: 4,
created: ISODate("2020-09-10T20:23:46.382Z"),
updated: ISODate("2020-09-10T20:23:46.382Z"),
active: true,
key: "data",
label: "Data",
displayOrder: 3,
legacy: false,
},
{
_id: 5,
created: ISODate("2020-09-10T20:23:46.382Z"),
updated: ISODate("2020-09-10T20:23:46.382Z"),
active: true,
key: "red-shirt",
label: "Red Shirt",
displayOrder: 4,
legacy: true,
},
]
In my aggregation pipeline, I have the following:
[
{ $match: {_id: 2} },
{ $unwind: { path: "$fields", "preserveNullAndEmptyArrays": true } },
{
"$lookup": {
"from": "fields",
"let": { "key": "$fields.key" },
"as": "fields.def",
"pipeline": [
{ "$match": { "$expr": { "$eq": ["$key", "$$key"] } } },
{
"$lookup": {
"from": "fieldoptions",
"let": { "options": "$options" },
"pipeline": [
{ "$match": { "$expr": { "$in": ["$_id", "$$options"] } } },
],
"as": "options"
}
}
]
}
},
{ $unwind: { path: "$fields.def", "preserveNullAndEmptyArrays": true } },
{
$group: {
_id: "$_id",
fields: { $push: "$$ROOT.fields" },
root: { $mergeObjects: "$$ROOT" },
}
},
{
$replaceRoot: {
newRoot: {
$mergeObjects: ["$root", "$$ROOT"]
}
}
},
{
$unset: "root"
},
{ $project: { fields: '$fields'}} // this is to remove other junk for testing
]
Which brings back the following (almost correct) result:
{
"_id" : 2,
"fields" : [
{
"key" : "part-number",
"value" : "abc123",
"def" : {
"_id" : 1,
"created" : ISODate("2020-09-10T20:23:46.382Z"),
"options" : [],
"updated" : ISODate("2020-09-10T20:23:46.382Z"),
"active" : true,
"displayOrder" : 0,
"key" : "part-number",
"label" : "Part Number",
"required" : false,
"type" : "text",
"__v" : 0
}
},
{
"key" : "price",
"value" : "10",
"def" : {
"_id" : 2,
"created" : ISODate("2020-09-10T20:23:46.382Z"),
"options" : [],
"updated" : ISODate("2020-09-10T20:23:46.382Z"),
"active" : true,
"displayOrder" : 1,
"key" : "price",
"label" : "Price",
"required" : false,
"type" : "text",
"__v" : 0
}
},
{
"key" : "officer",
"value" : "2",
"def" : {
"_id" : 3,
"created" : ISODate("2020-09-10T20:23:46.382Z"),
"options" : [
{
"_id" : 1,
"created" : ISODate("2020-09-10T20:23:46.382Z"),
"updated" : ISODate("2020-09-10T20:23:46.382Z"),
"active" : true,
"key" : "none",
"label" : "None",
"displayOrder" : 0,
"legacy" : false,
"__v" : 0
},
{
"_id" : 2,
"created" : ISODate("2020-09-10T20:23:46.382Z"),
"updated" : ISODate("2020-09-10T20:23:46.382Z"),
"active" : true,
"key" : "picard",
"label" : "Picard",
"displayOrder" : 1,
"legacy" : false,
"__v" : 0
},
{
"_id" : 3,
"created" : ISODate("2020-09-10T20:23:46.382Z"),
"updated" : ISODate("2020-09-10T20:23:46.382Z"),
"active" : true,
"key" : "riker",
"label" : "Riker",
"displayOrder" : 2,
"legacy" : false,
"__v" : 0
},
{
"_id" : 4,
"created" : ISODate("2020-09-10T20:23:46.382Z"),
"updated" : ISODate("2020-09-10T20:23:46.382Z"),
"active" : true,
"key" : "data",
"label" : "Data",
"displayOrder" : 3,
"legacy" : false,
"__v" : 0
}
],
"updated" : ISODate("2020-09-10T20:23:46.382Z"),
"active" : true,
"displayOrder" : 2,
"key" : "officer",
"label" : "Officer",
"required" : false,
"type" : "select",
"__v" : 0
}
}
]
}
My issue is that if I add something to the 'fields' collection (let's call it property 'foo'), the existing ticket objects do not have the 'foo' ref, so it does not show up in the ticket object, I would like to somehow (in the aggregation framework) get the list of current fields, and for each look up the corresponding value if it exists, otherwise return with an empty value like so:
new ticket result:
{
_id: 2,
fields: [
... //existing fields
{
key: "foo", // <-- new fields / fields not listed on the ticket
value: null,
def: {
_id: 1,
created: ISODate("2020-09-10T20:23:46.382Z"),
options: [],
updated: ISODate("2020-09-10T20:23:46.382Z"),
active: true,
displayOrder: 0,
key: "foo",
label: "Foo",
required: false,
type: "text",
__v: 0,
},
},
],
}
How can this be accomplished? Also, is there a better way to do this than what I have? I'm very new to aggregation and still experimenting.