Passing Route Parameter to Mongoose Query Express - node.js

Basically I'm trying to find a way in which to find a way to plug req.params.name into a find() query.
I have tried:
Trying to pass through my req.params variable in my find() object parameter Card.find({cardName: req.params.name}, callback) and any other possible variance of that.
I've tried a static method for findByName in which I just did Card.findByName(req.params.name, callback);
When I do console.lo(req.params.name) it returns the name of the card; however, when I got to show.ejs I do console.log(cardstatsok.cardName) it comes back undefined.
I've searched here on Stack Overflow, I've checked my Udemy message board, and I've tried finding any tutorial on passing route parameters to a find query, and alas, nothing.
Here's my code:
My schema and model:
var cardSchema = new mongoose.Schema({
cardName: String,
id: Number,
cardSet: String,
image: String,
ability: String
});
var Card = mongoose.model("Card", cardSchema);
My route for my single card page:
app.get("/cards/:name", function(req, res) {
Card.find({"cardName":req.params.name}, function(err, cardInfo){
if(err){
res.redirect("/cards");
console.log(err);
} else {
res.render("show", {cardstatsok: cardInfo});
}
});
});
When I do console.log(cardInfo) it returns many objects since I used "Forest" and the copy of the Magic: The Gathering card Forest has been printed many times. Here's one, though:
{ _id: 5a85bb554c8fff0c04f15b8e,
cardName: 'Forest',
id: 243461,
cardSet: 'Duel Decks: Knights vs. Dragons',
image: 'http://gatherer.wizards.com/Handlers/Image.ashx?multiverseid=243461&type=card',
__v: 0 }

find() returns an array which means cardstatsok is an array.
So console.log(cardstatsok.cardName) won't work. Use console.log(cardstatsok[0].cardName) instead for the first card or console.log(cardstatsok) for everything. If you want to print all the card names you have to loop over the array.
To find only one card you can use findOne() instead.

Related

Is collection.findOne({query}) not supposed to return the document itself?

I see a lot of tutorials/guides using collection.findOne({query}).field to get a value of a field in the document that is returned, but for me that doesn't seem to work and I wonder why. I did figure out another way to do it though. See below:
var rank = function(id) {
// My way of doing it
collection.findOne({ _id: id }, function(err, doc) {
console.log(doc.score); // Prints the actual score
});
// How some tutorials do it
var score = collection.findOne({ _id: id }).score;
console.log(score); // Gives a TypeError (Cannot read property 'score' of undefined)
};
// How some tutorials do it
Those tutorials are likely using mongodb's shell, not node.js api. The shell's api looks similar (all the same words, findOne, etc.), but it doesn't use callbacks. Shell's findOne does return the document inline.

Mongoose query returns no values

I'm currently creating a MEAN application to allow a user to create a football team from a list of ownedPlayers. Currently however when I attempt to make a http.get call to the ownedPlayers collection in MongoDB it always returns an empty array.
My mongoose model for OwnedPlayers is as follows.
var OwnedPlayers = mongoose.model('OwnedPlayers', {
userid: String,
playerid: String,
position: String
});
The endpoint and query is as follows.
app.get('/api/ownedPlayers', function (req, res) {
console.log('userid used in request', req.query.userid)
console.log('position used in request', req.query.position)
OwnedPlayers.find({
userid: req.query.userid, position: req.query.position
}, function (err, owned)
{
if (err)
{
res.send(err);
}
console.log('owned',owned);
res.json(owned);
});
});
I can confirm that the values in req.query.userid and req.query.position do match a single record in my ownedPlayers collection. This can be seen by my nodejs output below.
App listening on port 8080
GET /api/teams/57f50267418cb72fd020196a 304 31.936 ms - -
userid used in request 57f50267418cb72fd020196a
position used in request goal
owned []
GET /api/ownedPlayers?userid=57f50267418cb72fd020196a&position=goal 304 32.903 ms - -
I can also confirm that the key names are correct as userid and position.
The document I would expect to be returned when userid = 57f50267418cb72fd020196a and position = position= goal is as follows.
_id:57f7717ab1f0f22f3c3950b9
userid:57f50267418cb72fd020196a
playerid:57f7797e00ea2a0dc8609701
position:goal
Any help would be much appreciated as I'm very new to mongodb and mean applications.
Thanks in advance.
The problem was that since I was using an collection with documents which weren't created via mongoose it wasn't finding the collection correctly.
By passing in the collection name as a third parameter when creating my models this explicitly sets the collection name and points mongoose in the right direction.

searching an array of values against mongoose

I am not sure how I need to do what I'm wanting to do. My schemas are like this:
var userObj = mongoose.Schema({
'timestamp':{type: Date, default: Date.now()},
'password':{type:String, required:true},
"userName":{type:String, required:true, unique:true}
});
var groupSchema = mongoose.Schema({
'creator':String,
'members':Array, //contains the _id of users added to the group
'admins':Array,
'name':String,
'timestamp':{type: Date, default: Date.now()},
'description':String
});
Where the members schema has an array that contains IDs of users. I need to take the array from the group document and get back user names.
I started out with a loop using the mongoose .find() method and pushing the result into an array - but as I expected the array is empty outside of the scope of the callback function.
var dat = [];
for(var i = 0; i<passed.payload.length;i++){
user.find({'_id':passed.payload[i]},'userName',function(err,result){
if(err){
console.log(err);
}else{
dat.push(result);
}
})
}
//res.send(dat)
console.log(dat);
I am not sure how to do this - I considered using .find to pull all user IDs then running the array to return back only matches. That seems like a waste of resources to pull the full users table then test it.
Is there a more complex query I can use with mongoose to pull data like this using an array of _ids to match?
Have you considered using the $in operator?
db.users.find( { userName: { $in: ["Paul", "Jenna" ] } } )
This should be more efficient and removes the loop as well.
here a link to the MongoDB docs
https://docs.mongodb.com/manual/reference/operator/query/in/#op._S_in
The reason you are getting an empty array outside for loop is mongoose queries are asynchronous (in your case User.find()) and therefore for loop will be completed before mongoose queries are completed resulting into empty array you are getting. You can handle this either by using promises or some npm module like async.
However, as #karlkurzer suggested you don't need to loop through an array here, you can instead use $in operator. Above code can be replaced with,
user.find({'_id': {$in: passed.payload}},'userName',function(err,results){
if(err){
console.log(err);
}else{
console.log(results); //You will get array of user names here
}
});
and you should get an array of userNames associated with ids you passed in callback.
Hope this helps!

Mongoose - get length of array in model

I have this Mongoose schema:
var postSchema = mongoose.Schema({
postId: {
type: Number,
unique: true
},
upvotes: [
{
type: Number,
unique: true
}
]
});
what the best query to use to get the length of the upvotes array? I don't believe I need to use aggregation because I only want to query for one model, just need the length of the upvotes array for a given model.
Really struggling to find this info online - everything I search for mentions the aggregation methodology which I don't believe I need.
Also, as a side note, the unique schema property of the upvotes array doesn't work, perhaps I am doing that wrong.
find results can only include content from the docs themselves1, while aggregate can project new values that are derived from the doc's content (like an array's length). That's why you need to use aggregate for this, even though you're getting just a single doc.
Post.aggregate([{$match: {postId: 5}}, {$project: {upvotes: {$size: '$upvotes'}}}])
1Single exception is the $meta projection operator to project a $text query result's score.
I'm not normally a fan of caching values, but it might be an option (and after finding this stackoverflow answer is what I'm going to do for my use case) to calculate the length of the field when the record is updated in the pre('validate') hook. For example:
var schema = new mongoose.Schema({
name: String,
upvoteCount: Number,
upvotes: [{}]
});
schema.pre('validate', function (next) {
this.upvoteCount = this.upvotes.length
next();
});
Just note that you need to do your updates the mongoose way by loading the object using find and then saving changes using object.save() - don't use findOneAndUpdate
postSchema.virtual('upvoteCount').get(function () {
return this.upvotes.length
});
let doc = await Post.findById('foobar123')
doc.upvoteCount // length of upvotes
My suggestion would be to pull the entire upvotes fields data and use .length property of returned array in node.js code
//logic only, not a functional code
post.find( filterexpression, {upvote: 1}, function(err, res){
console.log(res.upvotes.length);
});
EDIT:
Other way of doing would be stored Javascript. You can query the
upvote and count the same in mongodb side stored Javascript using
.length

Mongoose - add method to object that is returned in the callback

Is there a way to add functions to the object that is returned in the callback?
User.find({'age':'20'}, function(err, users){
users.function();
});
It seems statics only work on the model. Schematically :
User.static();
and methods only work on instances
(new User()).method();
None of them seem to work on users, which i think is just a normal js Object variable. Am I missing something ?
The Schema.method description, from the docs:
Adds an instance method to documents constructed from Models compiled from this schema.
So, if you do something like this:
var userSchema = new Schema({
username: String,
age: Number
});
userSchema.method('showAge', function () {
return this.age;
});
, and call your method in a document returned from a query like:
User.findOne({'age':'20'}, function(err, user){
console.log(user.showAge());
});
it should work. Maybe you're having problems because you're calling your method users.function() in an array. Remember: the find method returns an array of documents.

Resources