Reputation: 4352
I have a collection with schema, with mutually exclusive fields:
{
item_id: Number, //if this field is present in document
a_item_id: Number, //then this both field doesn't exists and vice versa
h_item_id: Number, //then this both field doesn't exists and vice versa
quantity: Number,
other_field: String
}
I need to split documents via
aggregate
stage in such order, that every document which contains botha_item_id
&&h_item_id
(twin) fields will become two separate documents (they should inherit their base fields)
Also, the point is, that I can't just $group
by two fields at the same time, and then $unwind
because, if this fields are presented in a document they always have different values between each other, like:
a_item_id: 2
h_item_id: 3
So, if item_id
isn't present in document, then a_item_id
and h_item_id
are presented at the same time, always. AND they have different values between each other.
MongoPlayground example
Does anyone have any idea to split this twins and achieve such results? Like this:
{
"_id": ObjectId("5a934e000102030405000000"),
"item_id": 1,
"quantity": 1
},
{
"_id": ObjectId("5a934e000102030405000001"),
"a_item_id": 2,
"quantity": 1
},
{
"_id": ObjectId("5a934e000102030405000002"),
"h_item_id": 3,
"quantity": 1
}
P.S. I understand that using this schema is a total failure, but I haven't project it. So I cann't re-build the collection in my own image.
I am trying to solve my problem, by adding new array and push every value from every document to it, for later $unwind
{
$addFields: {
items_array: [
"$item_id",
"$h_item_id",
"$a_item_id"
]
}
}
Upvotes: 0
Views: 370
Reputation: 14317
This aggregation uses another approach, using $facet
stage:
db.collection.aggregate([
{
$match: { a_item_id: { $exists: true }, h_item_id: { $exists: true } }
},
{
$facet: {
doc_a_item: [
{ $addFields: { h_item_id: "$$REMOVE" } },
],
doc_h_item: [
{ $addFields: { a_item_id: "$$REMOVE" } }
]
}
},
{
$project: { doc: { $concatArrays: [ "$doc_a_item", "$doc_h_item" ] } }
},
{
$unwind: "$doc"
},
{
$replaceWith: "$doc"
}
] )
The output, as two documents:
{
"_id" : ObjectId("5f1ed8090fdddd9a43c261e5"),
"a_item_id" : 34,
"quantity" : 50
}
{
"_id" : ObjectId("5f1ed8090fdddd9a43c261e5"),
"h_item_id" : 56,
"quantity" : 50
}
The input document:
{
_id : ObjectId("5f1ed8090fdddd9a43c261e5"),
a_item_id: 34,
h_item_id: 56,
quantity: 50
}
Upvotes: 1
Reputation: 4352
«Fine, I'll do it myself»
MongoPlayground example for future uses
Model.aggregate([
{
$match: {
$or: [
{
a_item_id: 2,
},
{
h_item_id: 3,
},
{
item_id: 1,
}
]
}
},
{
$addFields: {
item_id: {
$filter: {
input: [
"$item_id",
"$h_item_id",
"$a_item_id"
],
as: "d",
cond: {
$ne: [
"$$d",
null
]
}
}
}
}
},
{
$unset: [
"a_item_id",
"h_item_id",
]
},
{
$unwind: {
path: "$item_id",
preserveNullAndEmptyArrays: true
}
},
{
$group: {
_id: "$item_id"
}
}
])
At last group I could add:
data: {
$push: "$$ROOT"
}
and then $unwind
data field and receive the original documents.
Upvotes: 0