Lowdb - push data into an array inside another array - node.js

I'm basicaly trying to do a inventory for each user, to do this i used Lowdb to help me out with the data.
My current structure is like this:
{
"users": [
{
"id": "450453034724491266",
"tag": "Briggs#4992",
"inventory": [
{"itemID": "1320488779", "rarity": "rare"},
{"itemID": "1674364779", "rarity": "common"},
]
},
{
"id": "272659147974115328",
"tag": "Zytekaron#0572",
"inventory": [
{"itemID": "1320488779", "rarity": "rare"},
{"itemID": "1674364779", "rarity": "common"},
]
}
]
}
what I have in mind is something like
//get 'users', find the id and then write inside the inventory array (which i dont know how to)
db.get('users')
.find({ id: 'id' })

Tested this code snippet and it is working perfectly.
// Get your user
let inventory = db
.get('users')
.find({ id: '272659147974115328' })
.get('inventory')
.value();
// Do something to your user's inventory
inventory.push({ itemID: '9999999', rarity: 'common' });
// Update the user with the modified inventory
db.get('users')
.find({ id: '272659147974115328' })
.assign({ inventory })
.write();

two quick answers:
get the object by the id. modify->replace-save;
you can do an assign like: object.assign({inventory:[newInventory]}).write();
where object is your user you just got from the db.

It should be possible to use push directly:
db
.get('users')
.find({ id: '272659147974115328' })
.get('inventory')
.push({ itemID: '9999999', rarity: 'common' })
.write();

Related

MongoDB adding ObjectId to array of IDs

I have Poems schema which has a linked array of ObjectIds under field name 'communities':
{
_id: ObjectId("61659ef70e87b90018f7baa1"),
schemaName: 'Poem',
helps: [ ObjectId("5d15c609832d390c41ab6872") ],
communities:
[ ObjectId("5eafbabaf0be6f0017303eb3"),
ObjectId("5eba549a45bd9300170f6311") ],
}
I am trying to add a new ObjectId to the array using updateOne and $push:
db.poems.updateOne(
{title: "My stillness"},
{$push: {communities: {ObjectId: ('61f942b737bdc10018722539')}}}
)
While the new Id gets added, it is not in the correct format (see also attached image from MongoDB Compass for further clarity on the difference in format). How can I adjust my updateOne/$push method to add the ObjectId in the correct format? Thanks
{
_id: ObjectId("61659ef70e87b90018f7baa1"),
schemaName: 'Poem',
helps: [ ObjectId("5d15c609832d390c41ab6872") ],
communities:
[ ObjectId("5eafbabaf0be6f0017303eb3"),
ObjectId("5eba549a45bd9300170f6311"),
{ ObjectId: '61f942b737bdc10018722539' } ],
}
You are pushing key-value pair into the array.
ObjectId: ('61f942b737bdc10018722539')
Instead, it should be:
ObjectId('61f942b737bdc10018722539')
db.poems.updateOne(
{ title: "My stillness" },
{ $push: { communities: ObjectId('61f942b737bdc10018722539') } }
)
Sample Mongo Playground

How to return field based on other fields with mongoose

I have mongoose schema that looks something like this:
{
_id: someId,
name: 'mike',
keys: {
apiKey: 'fsddsfdsfdsffds',
secretKey: 'sddfsfdsfdsfdsds'
}
}
I don't want to send back to the front the keys of course, but I want some indication, like:
{
_id: someId,
name: 'mike',
hasKeys: true
}
There is built in way to create 'field' on the way based on other fields, or do I need every time fetch the whole document, check if keys is not empty and set object property based on that?
For Mongo version 4.2+ What you're looking for is called pipelined updates, it let's you use a (restricted) aggregate pipeline as your update allowing the usage of existing field values.
Here is a toy example with your data:
db.collection.updateOne(
{ _id: someId },
[
{
"$set": {
"hasKeys": {
$cond: [
{
$ifNull: [
"$keys",
false
]
},
true,
false
]
}
}
},
])
Mongo Playground
For older Mongo versions you have to do it in code.
If you don't want to update the actual document but just populate this field when you fetch it you can use the same aggregation to fetch the document
you can use $project in mongoose aggregation like this.
$project: { hasKeys: { $cond: [{ $eq: ['$keys', null] }, false, true]}}

Mongoose - How to populate only the first element in a nested array of every object

I am trying to create a Chat App in NodeJS, Mongoose and React Native and I want to show to the user the last message of every conversation.
I have an array of messages in my Conversation Schema as following:
const ConversationSchema = new mongoose.Schema({
{...}
messages: [
{
type: mongoose.Schema.Types.ObjectId,
ref: 'Message'
}
]
{...}
})
I wonder if anyone can help me to be able to only populate the first message of every Conversation so the response will be:
conversations: [
{
"_id": ObjectId(...)
"messages": [
"text": "Last message"
]
},
{
"_id": ObjectId(...)
"messages": [
"text": "Last message"
]
},
...
]
I am currently using the populate function of mongoose but the problem is that if only populates the first conversation:
Conversation.find().populate(query).exec((err, conversations => {
{...}
})
const query = {
{
path: "messages",
options: {
limit: 1,
sort: { _id: -1 }
}
}
}
Note: If I do not specify the limit: 1 and sort: { _id: -1 } it correctly populates all elements of the array but that's not what I am looking for.
Thanks to anyone who can help me!
You need to use perDocumentLimit than Limit
If you need the correct limit, you should use the perDocumentLimit option (new in Mongoose 5.9.0). Just keep in mind that populate() will execute a separate query for each story, which may cause populate() to be slowe

Search and lookup arrays nested in multiple objects

I find all websites that match the IDs I have in my array and in the below case it is two websites. I then want to look inside each of the conversations arrays of those websites and search a different collection called conversations for conversations that match those IDs. I then want to grab some/all of the fields from those conversations and add it to the JSON document I already have, grouping them by conversation within each website. I've been playing around with aggregate, unwind, and group but can't seem to get anything to work.
router.post('/staffLoadUpTwo', async (req, res) => {
var userId = '5e8f964a9c2d0780c0163825';
const company = await Company.findOne({ 'roles.admins': userId });
var unwrappedCompany = JSON.parse(JSON.stringify(company));
console.log(unwrappedCompany.websites);
const website = await Website.find({
_id: { $in: unwrappedCompany.websites },
});
// const unwindTest = await Website.aggregate([{$unwind : "$conversations"}]);
console.log(website);
});
console.log(website);
[ { conversations: [ '5e90d9ceb089812c9ba1a67b', '5e8f5a6a2582bf629998c3fd' ],
_id: 5e949cc02483c0c0056a1a98,
domain: 'http://x.com',
__v: 0 },
{ conversations: [ '5e8e23595ce6d611cec5033f', '5e8e3afee8e95e1ff94650d3' ],
_id: 5e949ce8f53450c0341b36cd,
domain: 'http://y.com',
__v: 0 } ]
ideal output
[{
_id: "5e949cc02483c0c0056a1a98",
domain: 'http://x.com'
conversations: [
{conversationId: "5e90d9ceb089812c9ba1a67b", messages: {messageArray: ['a'], timeSent: 2}},
{conversationId: "5e8f5a6a2582bf629998c3fd", messages: {messageArray: ['b'], timeSent: 6}}
]
}
_id: "5e949ce8f53450c0341b36cd",
domain: 'http://y.com'
conversations: [
{conversationId: "5e8e23595ce6d611cec5033f", messages: {messageArray: ['c'], timeSent: 1}},
{conversationId: "5e8e3afee8e95e1ff94650d3", messages: {messageArray: ['d'], timeSent: 8}}
]
}]
You need not stress yourself with MongoDB aggregation. Since you're using Mongoose, you can easily use mongoose populate to achieve the result you described in the question.
Provided you've defined the website scheme to be something like this:
const websiteSchema = {
// ...schema definition for other properties
/* Note the ref property used below, the value must be the name of the
conversation model, i.e the stringValue you passed into
mongoose.model(<stringValue>, conversationSchema); */
conversations: [ { type: mongoose.Types.ObjectId, ref: 'Conversations' } ]
}
A mongoose query like this:
const website = await Website.find({
_id: { $in: unwrappedCompany.websites },
}).populate('conversations');
will output an array of website documents whose conversations field are populated i.e, you get the actual conversation document and not just their _ids.
More about Mongoose populate here

MongoDB Query Returns Empty Nested Object

I've got a 'conversations' collection in MongoDB which I'm querying from NodeJS to use the returned data to render the conversation's page.
The data has been stored in the database correctly as far as I can see, when I query it everything comes back as I'd expect, apart from a couple of nested objects - the two users that the conversation belongs to.
Here's what I get when I console.log a conversation (note the 'participants' field:
[ { _id: 57f96549cc4b1211abadf28e,
__v: 1,
messages: [ 57f96549cc4b1211abadf28d ],
participants: { user2: [Object], user1: [Object] } } ]
In Mongo shell the participants has the correct info - the id and username for both participants.
Here's the Schema:
var ConversationSchema = new mongoose.Schema({
participants: {
user1:
{
id: String,
username: String
},
user2:
{
id: String,
username: String
},
},
started: Number,
messages: [
{
type: mongoose.Schema.Types.ObjectId,
ref: "Message"
}
]
});
Here's the creation of the conversation document:
var conv = {
participants : {
"user1" : {
"id" : req.body.senderId,
"username" : req.body.senderName
},
"user2" : {
"id" : req.body.recipientId,
"username" : req.body.recipientName
}
},
created : Date.now(),
messages : [] // The message _id is pushed in later.
}
Conversation.create(conv, function(err, newConvo){
if(err){
console.log(err);
} else {
newConvo.messages.push(newMessage);
newConvo.save();
}
})
And lastly, in case it's useful, here's the query to Mongo:
// view all conversations a user belongs to
app.get('/messages', function(req, res){
Conversation.find({
$or : [
{"participants.user1.id" : req.user._id},
{"participants.user2.id" : req.user._id}
]
}, function(err, convos){
if(err){
console.log('Error getting Convos ' + err)
} else {
res.render('messages', {convos: convos, currentUser: req.user});
}
});
});
Thanks a lot for any help that!
It seems that everything is alright, the console.log just doesn't print nested objects by default. Try using:
console.log(JSON.stringify(conversation))
When logging a conversation in order to see the participants objects.
Fixed it!
Andresk's answer above was a big shove in the right direction. As he said, everything was OK, but I wasn't accessing the returned object in the correct way. It's obvious now, but I wasn't providing the index number for the 'convos' object.
I simply needed to do this, even though I was only getting one 'conversation' document back from MongoDB:
console.log(convos[0].participants.user1.username);

Resources