MongoDb- get length of array in updateMany (Node.js) - arrays

I am trying to do an update many.
The problem that I am encountering is getting the length of an array and seeing if it is less than or equal to another number being passed in from the params.
This is what I have done so far. But I feel this is a bad way of doing it. if anyone has a better way of doing it, please give your input. trying to improve :)
const updateStatus = (materialId, attempts) => {
return db.collection.updateMany(
{
_id,
[`data.${attempts}`]: { $exists: true },
status: { $in: ['outstanding', 'overdue'] },
},
{ status: 'restricted' }
)
}
this is the data I am trying to update many on
[{
_id: 1,
status: 'outstanding',
data:[],
},
{
_id: 2,
status: 'overdue',
data:[{passed: true},{passed: false}],
}
{
_id: 3,
status: 'outstanding',
data:[{passed: true},{passed: false}, {passed: false}],
}
]

Related

Get chats list MongoDB

In my chat app (+20m users for example) i need to get last updated chats but there's no optimal solution.
Chats
{
id: 1,
title: 'My Group',
updated_at: 137974654
},
{
id: 2,
title: 'Gamers',
updated_at: 137973654
}
Members
{
chat_id: 1,
user_id: 'A'
},
{
chat_id: 2,
user_id: 'B'
}
I don't want to embed members because a user can join thousands of chats so document size is beyond 16MB.
The maximum BSON document size is 16 megabytes.
A potential problem with the embedded document pattern is that it can lead to large documents, especially if the embedded field is unbounded. In this case, you can use the subset pattern to only access data which is required by the application, instead of the entire set of embedded data.
https://docs.mongodb.com/manual/tutorial/model-embedded-one-to-many-relationships-between-documents/#subset-pattern
My solution but not efficient
I first tried to get a list of chat_ids then query chats:
const members = Members.find({ user_id: 'A' }).project({ chat_id: 1 });
const chat_ids = members.map(item => item.chat_id);
const chats = Chats.find({
id: {
$in: chat_ids
}
}).sort({ updated_at: -1 }).limit(20);
But as i said, What if a user has joined +100000 chats? so the first query is too large + the last query is too slow.
What should i do? Do i need a relational database?
Making some assumptions that you are only interested in user_id: 'A', and keeping in mind that I am a MongoDB noob, and I only tested this with the example data you provided, does this do what you want?
db.members.aggregate([
{
$match: {
user_id: "A"
}
},
{
$lookup: {
from: "chats",
localField: "chat_id",
foreignField: "id",
as: "thechats"
}
},
{
"$unwind": "$thechats"
},
{
"$replaceRoot": {
"newRoot": "$thechats"
}
},
{
"$sort": {
updated_at: -1
}
},
{
"$limit": 20
}
])
Try it at mongoplayground.net.

How to get object in deeply nested array in mongoose using nodes

In my collection of users I have the following
{
_id: ObjectId('whatever user id'),
movies: [
{
_id: ObjectId('whatever id of this movie'),
name: 'name of this movie',
actors: [
{
_id: ObjectId('whatever id of this actor'),
name: 'name of this actor'
}
]
}
]
}
So in my users collection I want to be able to query for a actor by the user.id, pet.id, and the actor.id
I want to return the actor somewhat like this...
actor: {
fields...
}
I tried the following...
const actor = await User.findById(req.user.id, {
movies: {
$elemMatch: {
_id: req.params.movie_id,
actors: {
$elemMatch: {
_id: req.params.actor_id,
},
},
},
},
});
I have tried other things but can't seem to get it to work. I saw that you can maybe use aggregate but I am not sure how to query that while using the ids I have at my disposal.
I was able to figure it out by using aggregate. I was using this before but it seems that I needed to cast my ids with mongoose.Types.ObjectId so a simple req.user.id would not work.
In order to get my answer I did...
const user = await User.aggregate([
{ $match: { _id: mongoose.Types.ObjectId(req.user.id) } },
{ $unwind: '$movies' },
{ $match: { 'movies._id': mongoose.Types.ObjectId(req.params.movie_id) } },
{ $unwind: '$movies.actors' },
{
$match: {
'movies.actors._id': mongoose.Types.ObjectId(req.params.actor_id),
},
},
]);
This did not return data in the following format...
actor: {
fields...
}
but returns it instead like this...
user: {
movies: {
actor: {
fields...
}
},
otherFields...
}
then sending the response back...
res.status(200).json({
status: 'success',
data: {
actor
}
})
gives that format I wanted. However, I would still want to know how to just get the data actor without getting the full document

mongoDB sum of array lengths and missing fields [duplicate]

This question already has answers here:
MongoDB - The argument to $size must be an Array, but was of type: EOO / missing
(3 answers)
Closed 3 years ago.
I have a simple document that stores arrays of objects for a user, and I am looking to get a sum of these arrays and a total of all; the challenge is that some documents are missing fields that other documents have, and this causes my aggregate query to fail.
Document looks like this.
{
name: '',
type: '',
cars: [],
boats: [],
planes: []
}
Some people do not have boats or planes...and those documents might look like
{
name: '',
type: '',
cars: []
}
So when I run my aggregate
[
{
'$match': {
'type': 'big_spender'
}
}, {
'$project': {
'name': '$name',
'cars': {
'$size': '$cars'
},
'boats': {
'$size': '$boats'
},
'planes': {
'$size': '$planes'
}
}
}, {
'$addFields': {
'total_vehicles': {
'$add': [
'$cars', '$boats', '$planes'
]
}
}
}
]
I get the error: "The argument to $size must be an array, but was of type: missing"
I am pretty sure I can use $exists to avoid this problem, and return a 0, but I have no idea what that syntax might look like.
I need to return a 0 for arrays that don't exist so when i add them for totals, I get a correct total and no errors.
Any help appreciated.
Use $ifNull aggregation operator. It will replace the field with blank array if it does not exists.
[
{ '$match': { 'type': 'big_spender' }},
{ '$project': {
'name': '$name',
'cars': { '$size': '$cars' },
'boats': { '$size': { '$ifNull': ['$boats', []] }},
'planes': { '$size': { '$ifNull': ['$planes', []] }}
}},
{ '$addFields': {
'total_vehicles': {
'$add': [
'$cars', '$boats', '$planes'
]
}
}}
]

Count an array inside an array and then sum them MongoDB NodeJS

I have an object that has an array of page objects and each page object has an array of questions.
Ex object:
{
Id: 1,
UserId: 14,
Deleted: false,
Collaborators: [],
Title: "Awesome",
Pages: [{
Id: 1,
Title: 'Jank',
Questions: [
{ Id: 1, Content: 'Ask me about it' },
{ Id: 2, Content: 'Ask me about it again' }
]
}, {
Id: 2,
Title: 'Janker',
Questions: [
{ Id: 1, Content: 'Tell me about it' },
{ Id: 2, Content: 'Tell me about it again' }
]
}]
}
What I am trying to do is to get a count of all the questions for the entire bas object. I am not sure how to do that. I have tried to use aggregate and $sum the total questions and then do another function to $sum those all together to get a total for the entire object. Unfortunately my $sum is not working like I thought it would.
Ex code (nodejs):
var getQuestionCount = function(id) {
var cursor = mongo.collection('surveys').aggregate([{
$match: {
$or: [{
"UserId": id
}, {
"Collaborators": {
$in: [id]
}
}]
}
}, {
$match: {
"Deleted": false
}
}, {
$unwind: "$Pages"
},
{ $group: { _id: null, number: { $sum: "$Pages.Questions" } } }
], function(err, result) {
//This log just gives me [object Object], [object Object]
console.log('q count ' + result);
});
}
Any idea how to do this? My end result from the example object above would ideally return 4 as the question count for the whole object.
I'd try following shell query.
db.collection.aggregate([
// filter out unwanted documents.
{$match:{Id: 1}},
// Unwind Pages collection to access Questions array
{$unwind:"$Pages"},
// Count items in Questions array
{$project:{count: {$size:"$Pages.Questions"}}},
// Finally sum items previously counted.
{$group:{_id:"$_id", total: {$sum: "$count"}}}
])
Based on your sample document, it should return correct count of Questions.
{
"_id" : ObjectId("57723bb8c10c41c41ff4897c"),
"total" : NumberInt(4)
}

Update embedded mongoose document in array

Lets say that I have the following document in the books collection:
{
_id:0 ,
item: "TBD",
stock: 0,
info: { publisher: "1111", pages: 430 },
tags: [ "technology", "computer" ],
ratings: [ { _id:id1, by: "ijk", rating: 4 }, {_id:id2 by: "lmn", rating: 5 } ],
reorder: false
}
I would like to update the value of ratings[k].rating and all I know is the id of the collection and the _id of the objects existing in the array ratings.
The tutorial of mongoDB has the following example that uses the position of the object inside the array but I suppose that if the update can only be done by knowing the position, this means that I firstly have to find the position and then proceed with the update? Can I do the update with only one call and if so how I can do that?
db.books.update(
{ _id: 1 },
{
$inc: { stock: 5 },
$set: {
item: "ABC123",
"info.publisher": "2222",
tags: [ "software" ],
"ratings.1": { by: "xyz", rating: 3 }
}
}
)
Sorry for late answer; I think this is what you want to do with mongoose.
Books.findOneAndUpdate({
_id: 1,
'ratings._id': id1
},
{
$set: {
'ratings.$.rating' : 3
}
}, function(err, book){
// Response
});
Positional operator may help you:
db.books.update(
// find book by `book_id` with `rating_id` specified
{ "_id": book_id, "ratings._id": rating_id },
// set new `value` for that rating
{ $set: { 'ratings.$.rating': value }}
);
$ will save position of matched document.

Resources