How to access the properties of a query result in Mongo - mongodb

I can find a document on my database. A call to:
subject = await Subject.find({ name: 'Math' });
res.send(subject);
returns the document correctly:
{
"topics": [],
"_id": "5ab71fe102863b28e8fd1a3a",
"name": "Math",
"__v": 0
}
The problem is when I try to access the properties of subject. Any of the following calls returns nothing:
res.send(subject._id);
res.send(subject.name);
I've tried subject.toObject() and subject.toArray() but I receive an error:
(node:2068) UnhandledPromiseRejectionWarning: TypeError: subject.toObject is not a function
Any help will be appreciated. Thanks!
NB:
before res.send(subject), I called console.log(subject) and the output is:
[ { topics: [],
_id: 5ab71fe102863b28e8fd1a3a,
name: 'cocei5',
__v: 0 } ]

That is because find method in MongoDB always returns an array.
subject = await Subject.find({ name: 'Math' });
So in above line the Subject.find({name: 'Math'}) is returning an array. Which you are storing in subject variable. if you are getting only single object from DB then you might access the object properties by using subject[0].propertyName.
like if you want to send just an id you can do it by
res.send(subject[0]._id);

You can always use the es6 destructuring feature to get the first element returned in the array, as long as you are sure the result will always be on the 0th index.
const [subject] = await Subject.find({ name: 'Math' });
res.send(subject._id);
res.send(subject.name);
ref: Destructuring arrays and objects
Details for find api
OR you can either use
const subject = await Subject.findOne({ name: 'Math' });
res.send(subject._id);
res.send(subject.name);
As findOne return object whereas find returns an array of objects.
ref: Details for findOne api

Related

Mongoose update entire nested elements

I am using mongoose and MongoDB. Is there any way to update the entire schedule array in the schema? or do I have to use a for loop and insert one by one?
My req.body is an array to replace the entire schedules array object.
[{"_id":"1","description":"hi","time":"Jul 29, 2020 8:55 PM","url":"aaa.com"},{"_id":"2","description":"hi","time":"Jul 29, 2020 8:58 PM","url":"bbb.com"},{"_id":"3","description":"hi"}]
here is my schema.
const schedule = new mongoose.Schema({
user_id: Number,
schedules: [{
description: String,
time: String,
url: String
}]
});
Thank you.
If you're using mongoose, we can avoid using $set. #jitendra's answer is a pure mongodb query which you could run in the mongo shell.
You can refer to this link https://mongoosejs.com/docs/api.html#model_Model.findOneAndUpdate .
As the link says,
var query = { name: 'borne' };
Model.findOneAndUpdate(query, { name: 'jason bourne' }, options, callback)
// is sent as
Model.findOneAndUpdate(query, { $set: { name: 'jason bourne' }}, options, callback)
This helps prevent accidentally overwriting your document with { name: 'jason bourne' }.
So in your case we just need to write :
Schedule.findOneAndUpdate({_id: "id_of_object"}, {schedules: req.body});
That should do it for you. But internally, as the doc says, it is being sent as:
Schedule.findOneAndUpdate({_id: "id_of_object"}, {$set: {schedules: req.body}})
Ofcourse this assumes that your req.body consists of the array of schedules only. Most likely you're sending it as an object from the front end so maybe it's req.body.object_name . Up to you.
You can update the entire schedule array by using $set , try as follow:
db.collection.update({query},{ $set: { schedules: req.body } },{option});
Here your req.body should be the array with same keys as per defined in your schema.

Getting a value from MongoDB using ExpressJS

I'm trying to single fetch a value from my database.
I have a collection named: randoms
Model's Name: Random
Inside the collection, I have one object and with a name field. I want to retrieve that value of 'name' and display it in my console.
The result in the console should be: 'TestName' only.
This has been my approach:
Random.find({}, (err, randoms) => {
if(err) throw err
// console.log(randoms)
res.render('entries', console.log(randoms.name))
})
The console displays the result: undefined.
Here's the database.
If I console.log(randoms) instead of console.log(randoms.name) I get the result:
[{
_id: 5e256c819f972c268493488c,
name: 'TestName',
defination: 'Home page paragraph text',
count: 2,
__v: 0
}]
so that means the connection is alright.
db.collection.find returns a Cursor which is A pointer to the result set of a query. Clients can iterate through a cursor to retrieve results.
yourCollection.find().forEach(function(item){})

Mongoose $in [ObjectIds] returns 0 records

In our Mongoose model, we have a product referring to an article.
this is a piece of the schema:
const product = new Schema({
article_id: Schema.Types.ObjectId,
title: String,
description: String,
...
In the API we are looking for products that are referring to a list of specific articles, and I wanted to use the $in operator:
const articles = ["5dcd2a95d7e2999332441825",
"5dcd2a95d7e2999332441827",
"5dcd2a96d7e2999332441829"]
filter.article_id = {
$in: articles.map(
article => new mongoose.Types.ObjectId(article)
),
};
return Product.find({ ...filter })
This returns 0 records, whereas I know for sure it should have returned at least 3. Looking at the console log, all that has happened is that the double quotes have been removed from the array during the ObjectId conversion.
Then I tried a different approach by returning {$oid: "id goes here"} for each mapped array item:
const articles = ["5dcd2a95d7e2999332441825",
"5dcd2a95d7e2999332441827",
"5dcd2a96d7e2999332441829"]
filter.article_id = {
$in: articles.map(
article => ({$oid: article})
),
};
return Product.find({ ...filter })
This gives a different array:
console.log(filter);
// {article_id: {$in: [{$oid: "5dcd2a95d7e2999332441825"}, {$oid: "5dcd2a95d7e2999332441827"}, {$oid: "5dcd2a96d7e2999332441829"}]}}
But in this case I get following error:
CastError: Cast to ObjectId failed for value "\"{$oid: \"5dcd2a95d7e2999332441825\"}\"".
Though - if I take that particular console logged filter and pass it in Studio 3T as a filter, I do indeed get the desired results.
Any idea what I doing wrong in this case?
Frick me! I am just a big idiot.. Apparently there was a .skip(10) added after the find() method -.-'.... Now I understand why 0 records where returned... Been spending hours on this..
For future references, Mongoose casts strings to ObjectIds automatically if defined in Schema. Therefor following is working exactly as it should given you don't skip the first 10 records:
const articles = ["5dcd2a95d7e2999332441825",
"5dcd2a95d7e2999332441827",
"5dcd2a96d7e2999332441829"]
filter.article_id = {
$in: articles
};
return Product.find({ ...filter }) // Note that I DON'T put .skip() here..

How to find the data inside of array using mongoose

I have a data like below in MongoDB.
const A = { uid: '1234',
works: [
{ name: 'car',
item:['tire','wheel']
},
{ name: 'ship',
item:['tire','wheel']
}
]
My goal is to find whether the name duplicated exists or not.
In conclusion, I want to get only the names.
So 'res.send(result)' gives me like '[car, ship]'.
But the code below doesn't work. How can I make that function? Thank you so much.
const workName = await User.findOne({
uid: userID, works:{$in:['name']}
});
The easy way is to find the aggregate of name and check if the count is greater than one. Since it is inside the the array we need to unwind it.
The below query works, to detect if a sub document contain duplicates for each document.
db.getCollection('collection_name').aggregate([{"$unwind":"$works"} ,{"$group":{"_id":{"name":"$works.name","uid":"$uid"},"count":{"$sum":1}}},{"$match":{"count":{"$gte":2}}}])
if you want to find in all the documents remove uid from _id of group.

How do I use new Meteor.Collection.ObjectID() in my mongo queries with meteor?

I have a Collection that has documents with an array of nested objects.
Here is fixture code to populate the database:
if (Parents.find().count() == 0) {
var parentId = Parents.insert({
name: "Parent One"
});
Children.insert({
parent: parentId,
fields: [
{
_id: new Meteor.Collection.ObjectID(),
position: 3,
name: "three"
},
{
_id: new Meteor.Collection.ObjectID(),
position: 1,
name: "one"
},
{
_id: new Meteor.Collection.ObjectID(),
position: 2,
name: "two"
},
]
});
}
You might be asking yourself, why do I even need an ObjectID when I can just update based off of the names. This is a simplified example to a much more complex schema that I'm currently working on and the the nested object are going to be created dynamically, the ObjectID's are definitely going to be necessary to make this work.
Basically, I need a way to save those nested objects with a unique ID and be able to update the fields by their _id.
Here is my Method, and the call I'm making from the browser console:
Meteor.methods({
upChild: function( options ) {
console.log(new Meteor.Collection.ObjectID());
Children.update({_id: options._id, "fields._id": options.fieldId }, {$set: {"fields.$.position": options.position}}, function(error){
if(error) {
console.log(error);
} else {
console.log("success");
}
});
}
});
My call from the console:
Meteor.call('upChild', {
_id: "5NuiSNQdNcZwau92M",
fieldId: "9b93aa1ef3868d762b84d2f2",
position: 1
});
And here is a screenshot of the html where I'm rendering all of the data for the Parents and Children collections:
Just an observation, as I was looking how generate unique IDs client side for a similar reason. I found calling new Meteor.Collection.ObjectID() was returning a object in the form 'ObjectID("abc...")'. By assigning Meteor.Collection.ObjectID()._str to _id, I got string as 'abc...' instead, which is what I wanted.
I hope this helps, and I'd be curious to know if anyone has a better way of handling this?
Jason
Avoid using the _str because it can change in the future. Use this:
new Meteor.Collection.ObjectID().toHexString() or
new Meteor.Collection.ObjectID().valueOf()
You can also use the official random package:
Random.id()