Move elements from $pull to another array - mongodb

I have many documents in the same collection that look like this:
{
"_id": ObjectId("4d525ab2924f0000000022ad"),
"array": [
{ id: 1, other: 23 },
{ id: 1, other: 21 },
{ id: 0, other: 235 },
{ id: 1, other: 765 }
],
"zeroes": []
}
and I want them to look like this:
{
"_id": ObjectId("4d525ab2924f0000000022ad"),
"array": [
{ id: 1, other: 23 },
{ id: 1, other: 21 },
{ id: 1, other: 765 }
],
"zeroes": [
{ id: 0, other: 235 }
]
}
Basically, I want to be able to pull some elements in an array, and push it to another array. I know I can use $pull to conditionally remove elements from an array, but is it possible to relocate these pulled elements?

Not really. You can use cursor for operation like this.
db.foo.find({}).forEach(function(doc) {
doc.zeros = doc.array.filter(function(x) { return x.id == 0 });
doc.array = doc.array.filter(function(x) { return x.id != 0 });
db.foo.save(doc)};
)

Related

How to make and requests in mongodb queries

I've worked on this for about an hour now and I can't figure anything out that works so sorry if this is obvious.
I want my query to only return results where every result matches, but right now it returns a result if at least one match is found.
My document looks like this...
{
country: 'Great Britain',
data: [
{
school: 'King Alberts',
staff: [
{
name: 'John',
age: 37,
position: 'Head Teacher'
},
{
name: 'Grace',
age: 63,
position: 'Retired'
},
{
name: 'Bob',
age: 25,
position: 'Receptionist'
}
]
},
{
school: 'St Johns',
staff: [
{
name: 'Alex',
age: 22,
position: 'Head of Drama'
},
{
name: 'Grace',
age: 51,
position: 'English Teacher'
},
{
name: 'Jack',
age: 33,
position: 'Receptionist'
}
]
},
// ... more schools
]
}
The query I'm currently making looks like...
{ 'data.staff.name': { $in: names } }
and the 'names' array that is being provided looks like ['Bob', 'Grace', 'John', 'Will', 'Tom']. Currently both schools are being returned when I make this query, I think it's because the 'names' array contains 'Grace' which is a name present at both schools and so the document it matching. Does anyone know if there's a query I could make so mongodb only returns the school object if every name in the 'names' array is a member of staff at the school?
You need to use the aggregation pipeline for this, after matching the document we'll just filter out the none matching arrays, like so:
db.collection.aggregate([
{
$match: {
"data.staff.name": {
$in: names
}
}
},
{
$addFields: {
data: {
$filter: {
input: "$data",
cond: {
$eq: [
{
$size: {
"$setIntersection": [
"$$this.staff.name",
names
]
}
},
{
$size: "$$this.staff"
}
]
}
}
}
}
}
])
Mongo Playground

MongoDB, How can I update multiple values with condition

I using Express in Mongoose,
Result would be like this,,
Consider initial count is 0
var arr = [0,0,0,1,1,1,2,3,4,5,6,7,8,9]
{
_id: '1234'
inventory: [
{
itemIndex: 0,
count: 3 // increate 3 times, since it included upper array
},
{
itemIndex: 1,
count: 3 // Increase 3 times this, since it included upper array
},
{
itemIndex: 5,
count: 1 // Increase 1 , since it included upper array
},
{
itemIndex: 10,
count: 0
},
{
itemIndex: 11,
count: 0
},
]
}
** Result would be
enter code here
I tried this code, but I don't know what should I do.
var filter = { _id: id }
await User.updateOne(filter, {
$inc: {
clubInventories: {
itemIndex: ...... ??
}
}
},
Should I use updateMany instead of updateOne?
Please Help.
You can use arrayFitlers,
elem is a user defined identifier, you can put condition on array field and use that identifier in update part using $[<identifier>] positional operator.
var filter = { _id: id };
var arr = [0,1,2,3,4,5,6,7,8,9];
await User.updateOne(filter,
{
$inc: { "inventory.$[elem].count": 1 }
},
{
arrayFilters: [
{ "elem.itemIndex": { $in: arr } }
]
}
)
To update different count value in different index then use,
var filter = { _id: id };
var arr = [1,2,3,4,5,6,7,8,9];
var arr1 = [0];
await User.updateOne(filter,
{
$inc: {
"inventory.$[elem].count": 1,
"inventory.$[elem1].count": 5
}
},
{
arrayFilters: [
{ "elem.itemIndex": { $in: arr } },
{ "elem1.itemIndex": { $in: arr1 } }
]
}
)

MongoDB query - unwind and match preserving null OR different value/ add a new field based on a condition

If a have a following structure :
{
_id: 1,
name: 'a',
info: []
},
{
_id: 2,
name: 'b',
info: [
{
infoID: 100,
infoData: 'my info'
}
]
},
{
_id: 3,
name: 'c',
info: [
{
infoID: 200,
infoData: 'some info 200'
},
{
infoID: 300,
infoData: 'some info 300'
}
]
}
I need to query in such a way to obtain the documents where infoID is 100 showing the infoData, or nothing if info is empty, or contains subdocuments with infoID different from 100.
That is, I would want the following output:
{
_id: 1,
name: 'a',
infoData100: null
},
{
_id: 2,
name: 'b',
infoData100: 'my info'
},
{
_id: 3,
name: 'c',
infoData100: null
}
If I $unwind by info and $match by infoID: 100, I lose records 1 and 3.
Thanks for your responses.
Try below query :
Query :
db.collection.aggregate([
/** Adding a new field or you can use $project instead of addFields */
{
$addFields: {
infoData100: {
$cond: [
{
$in: [100, "$info.infoID"] // Check if any of objects 'info.infoID' has value 100
},
{
// If any of those has get that object & get infoData & assign it to 'infoData100' field
$let: {
vars: {
data: {
$arrayElemAt: [
{
$filter: {
input: "$info",
cond: { $eq: ["$$this.infoID", 100] }
}
},
0
]
}
},
in: "$$data.infoData"
}
},
null // If none has return NULL
]
}
}
}
]);
Test : MongoDB-Playground

How to update multiple mongodb elements with different values

I have a simplified order model that looks likes:
order = {
_id: 1,
productGroups:[
{ productId: 1, qty: 3 },
{ productId: 2, qty: 5 }
],
cancels:[]
}
Now I have an api that cancels part of the order.
The request could be something like cancel {productId:1, qty:2}, {productId:2, qty:2} from order where orderId:1. The result should be
order = {
_id: 1,
productGroups:[
{ productId: 1, qty: 2 },
{ productId: 2, qty: 2 }
],
cancels:[
{
productGroups:[
{ productId: 1, qty: 1 },
{ productId: 2, qty: 3 }
]
}
]
}
let order = await Order.findOneAndUpdate(
{
_id: id
},
{
$inc: {
'productGroups.$.qty': cancelQty //this is the part that needs fixing. how do I get different cancelQty according to productId
},
$push: {
cancels: {
products: cancelProductGropus
}
}
},
{ new: true }
);
Now I know I can just findOne, update the model with javascript, and then .save() the model. But if possible I would like to do this update in one go. Or if it is not possible, can I fix the schema so that I can do such update in a single request?

How to pull array of documents from array of documents?

I have a document like this
{
users: [
{
name: 'John',
id: 1
},
{
name: 'Mark',
id: 2
},
{
name: 'Mike',
id: 3
},
{
name: 'Anna',
id: 4
}
]
}
and I want to remove users from the array with ids 2 and 4. To do that I execute the following code:
const documents = [
{
id: 2
},
{
id: 4
},
]
Model.updateOne({ document_id: 1 }, { $pull: { users: { $in: documents } } });
But it doesn't remove any user.
Could you say me what I'm doing wrong and how to achieve the needed result?
This works if you can redefine the structure of your documents array:
const documents = [2, 4]
Model.updateOne({ document_id: 1 }, { $pull: { users: { id: { $in: documents } } } })