Reputation: 40500
Is it possible to insert/upsert multiple documents in MongoDB 4.2 only if the the number of documents matching a particular query is of a particular size?
Example:
Let's say I have an items
collection with the following 2 documents:
{ item: "ZZZ137", type="type1"}
{ item: "ZZZ138", type="type1"}
Now I want to insert these two documents:
{ item: "ZZZ139", type="type1"}
{ item: "ZZZ140", type="type1"}
but only of there are currently 2 items of type type1
in the collection (i.e. count of type1
is equal to 2).
Is it possible to somehow do this in MongoDB with a single command?
Update
To further illustrate my question let's imagine that insertMany had support for conditions. Then I'd like to do something like this (pseudo code that doesn't work):
db.items.insertMany({ { $count: { type: "type" } } : { $eq : 2 } } , [{ item: "ZZZ139", type="type1"}, { item: "ZZZ140", type="type1"}])
Where { { $count: { type: "type" } } : { $eq : 2 } }
would be the query that must be fulfilled in order to insert item ZZZ139
and ZZZ140
.
Upvotes: 1
Views: 303
Reputation: 22276
This can be achieved using $out
or $merge
if you insist on doing this in 1 call, however it's very inefficient due to the logic and restriction of these 2 operators. I personally recommend splitting it into 2 calls:
let typeTwoCount = await db.collection.countDocuments({type: "2"})
if (typeTwoCount === 2) {
await db.collection.insertMany(newItems)
}
Now we can use $out
but due to the fact that it re-writes the collection we'll have to carry the entire collection through the pipeline and into the $out
stage, which is ridiculous:
db.collection.aggregate([
{
$facet: {
typeTwo: [
{
$match: {
type: "2"
}
},
{
$count: "doc_count"
},
{
$addFields: {
newDocs: {
$cond: [
{$eq: ["$doc_count", 2]},
items,
[]
]
}
}
},
{
$unwind: "$newDocs"
},
{
$replaceRoot: {
newRoot: "$newDocs"
}
},
],
all: [
{
$match: {}
}
]
}
},
{
$addFields: {
merged: { $concatArrays: ["$all", "$typeTwo"]}
}
},
{
$unwind: "$merged"
},
{
$replaceRoot: {
newRoot: "$merged"
}
},
{
$out: "collection"
}
])
Now the issue with $merge
is the following restriction:
The output collection cannot be the same collection as the collection being aggregated.
So we can employ similar tactic to the $out
pipeline (with using the typeTwo
pipeline for the $merge
), but we'll have to start the aggregation with a different none empty dummy collection:
db.any_other_none_empty_collection.aggregate([
{
$limit: 1
},
{
$lookup: {
from: "collection",
let: {},
pipeline: [
{
$match: {
type: "2"
}
}
],
as: "all"
}
},
{
$addFields: {
doc_count: {$size: "$all"}
}
},
{
$addFields: {
newDocs: {
$cond: [
{$eq: ["$doc_count", 2]},
items,
[]
]
}
}
},
{
$unwind: "$newDocs"
},
{
$replaceRoot: {
newRoot: "$newDocs"
}
},
{
$merge: {
into: "collection"
}
}
])
Upvotes: 2