I am currently trying to query a MongoDB collection using Mongoose, and I am having trouble trying to convert this query into a useable Mongoose query.
The MongoDB CLI query is db.events.find({}, {'first.points': 1, '_id': 0}).
This works fine and returns what I would expect when I run this in the command line, I have tried several methods of converting this to a Mongoose query, my attempts so far are:
Attempt #1
Events.find({}).populate('first').exec(function(err, events){
if(err) { console.log(err) }
console.log(events);
});
This does not work and throws the error Cast to ObjectId failed for value "10" at path "_id" for model "Event" when the node server is started.
Attempt #2
Event.find({'first.points': "10"}).populate('first').exec(function(err, events)
This does not throw any errors, and it does return the values I would expect, however I am trying to return all the first.points values for all events, and I cannot seem to do this.
Attempt #3
Event.find({'first.points': "$all"}).populate('first').exec(function(err, events)
This also does not work, and was my most recent attempt at this issue, it again throws an error this time saying Cast to number failed for value "$all" at path "first.points" for model "Event"
I am not sure what else to try for this, I am unsure how to return all of the values without specifying which to look for.
EDIT
The model for Events is included below
var eventsSchema = new mongoose.Schema({
name: String, // The event name, with data type String
date: Date, // Date with data type Date
first: {
points: Number, // The points awarded to the first place with data type Number
house: String
},
second: {
points: Number,
house: String
},
third: {
points: Number,
house: String
},
fourth: {
points: Number,
house: String
}
});
Any help is appreciated.
Credit to naga - elixir - jar for this answer.
Event.find({}, {'first.points': 1, '_id': 0}, function(err, events) {...})
This code returns the values that I needed, without errors and in the correct format.
Note I have converted this away from an arrow function for clarity. Arrow function is here Event.find({}, {'first.points': 1, '_id': 0}, (err, events) => {...})
Related
I'm trying to query an object that's inside an item which is inside a mongoose model, when I'm trying to find that object with the find() method or _.find() method, I can't get access to the object for some reason and when I console.log() it, it gives me undefined or when I use array.filter() it gives me an empty array, which means the object that I'm trying to access does not meet the criteria that I give it in the lodash find method, but then when I look at my database I see that the object does actually have the properties to meet the criteria. So I don't know what I'm doing wrong, here's my code: as you can see I'm trying to get the information of the item that the user clicked on and want to see:
router.get("/:category/:itemId", (req, res) => {
console.log(req.params.itemId);
//gives the item id that the user clicked on
console.log(req.params.category);
//gives the name of category so I can find items inside it
Category.findOne({ name: req.params.category }, (err, category) => {
const items = category.items; //the array of items
console.log(items); //gives an array back
const item = _.find(items, { _id: req.params.itemId });
console.log(item); //gives the value of 'undefined' for whatever reason
});
});
The category Schema:
const catSchema = new mongoose.Schema({
name: {
type: String,
default: "Unlisted",
},
items: [
{
name: String,
price: Number,
description: String,
img: String,
dateAdded: Date,
lastUpdated: Date,
},
],
dateCreated: Date,
lastUpdate: Date,
});
well the answer is a little bit obvious, you are using MongoDB and in Mongo you have "_ID" you can use that "_ID" only with Mongoose! so you just have to remove the underscore and that is it! do it like this const item = _.find(items, { id: req.params.itemId });
hope you are doing better.
When I look at your Schema I see that the item field is an array of objects which doesn't have an _id inside so when you create a new instance of catShema it just generates an _id field for the new instance but not for each item inside the items array, just also enter the id of the item in question because according to my understanding, you must also have a model called items in your database
When you save these records in your database, you will generate an element with this structure
{
_id: String, // auto generated
name: String,
items: [ {"name1", "price1", "description1", "imgUrl1", "dateAdded1", "lastUpdated1"},{"name2", "price2", "description2", "imgUrl2", "dateAdded1", "lastUpdated2"}, ...],
dateCreated: Date,
lastUpdate: Date
}
Note : the code provided at the top is only an illustration of the object which will be registered in the database and not a valid code
Here you can notice that there is not a field called _id in the object sent inside the database.
My suggestion to solve this issue is
To create an additional field inside the items array called _id like :
{
...,
items: [
{
_id: {
type: String,
unique : true,
required: true // to make sure you will always have it when you create a new instance
},
...
... // the rest of fields of items
},
...
Now when you create a new instance make sure in the object you enter in the database the _id is recorded when you call the catInstance.save() so inside the catInstance object you have to add the current Id of the element to be able to filter using this field.
Hope my answer helped, if you have any additional questions, please let me know
Happy coding ...
I've a schema for a store inventory. The location has locationcategories array in the schema and inside that array, I have items[] array.
My issue is updating this items[] inside the locationcategories[]. I use the mongodb shell to update that array (using updateOne()) and it works (update my items[] inside locationcategories[]) but when I do it using mongoose model ("Locations") it doesn't update, in my console, it just shows { n: 1, nModified: 0, ok: 1 } which indicate that everything was found but didn't modify anything. When I check the db, it doesn't have any new items added.
Here is code I use in mongoshell which updates items[] inside locationcategories[]
db.locations.updateOne({"locationname" :"My Town", "locationcategories":{"$elemMatch":{"categoryname": "Media"}}},{$push:{"locationcategories.$.items": {"test":"test"}}})
In my application server route (expressjs) I enter the following:
Location.updateOne(
{ "locationname": "My Town", "locationcategories": {"$elemMatch":{"categoryname": "Media"}}},
{$push: {"locationcategories.$.items": {"test":"test"}}},
{new : true, upsert: true },
function (error, results) {
console.log("findByIdAndUpdate results :", results);
}
);
my mongooose schema-model:
var Schema = mongoose.Schema;
var locationSchema = new Schema({
locationname: String,
locationdescription: String,
locationcategories: [{ categoryorder: Number, categoryname: String,
categorydescription: String, items[{itemname: String }]}],
items: [{categoryid : String, itemorder: Number, itemname: String, itemdescription: String, itemprice: Number }],
created_at: Date
});
var Location = mongoose.model('Location', locationSchema);
export default Location;
I'm using mognodb version 4.02
Model.updateOne should work but it is not adding anything to my db. I read many of the stackoverflow.com issues but I couldn't find anything that indicate an issue except this old question from years ago which he had an issue updating array inside array. I find it hard to believe that there is an issue and no one addressed it after all those years but I might be wrong.
If the mongo shell execute and add to db correctly, it leads me to believe that the mongoose model or schema are responsible for the issue or my updateOne() code is wrong.
Any idea why?
Got it. So I changed items inside the locationcategories[] to categoryItems[] and it works.
The .$ operator looks for the first array with the name items which was items outside but I was posting the data to the model's first array.
Make sure you name the arrays in your model with unique names and point the $push to the correct array.
Also upgrded mongodb from version 4.0 to the latest one 4.41.
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.
I am working through a MEAN stack tutorial. It contains the following code as a route in index.js. The name of my Mongo collection is brandcollection.
/* GET Brand Complaints page. */
router.get('/brands', function(req, res) {
var db = req.db;
var collection = db.get('brandcollection');
collection.find({},{},function(e,docs){
res.render('brands', {
"brands" : docs
});
});
});
I would like to modify this code but I don't fully understand how the .find method is being invoked. Specifically, I have the following questions:
What objects are being passed to function(e, docs) as its arguments?
Is function(e, docs) part of the MongoDB syntax? I have looked at the docs on Mongo CRUD operations and couldn't find a reference to it. And it seems like the standard syntax for a Mongo .find operation is collection.find({},{}).someCursorLimit(). I have not seen a reference to a third parameter in the .find operation, so why is one allowed here?
If function(e, docs) is not a MongoDB operation, is it part of the Monk API?
It is clear from the tutorial that this block of code returns all of the documents in the collection and places them in an object as an attribute called "brands." However, what role specifically does function(e, docs) play in that process?
Any clarification would be much appreciated!
The first parameter is the query.
The second parameter(which is optional) is the projection i.e if you want to restrict the contents of the matched documents
collection.find( { qty: { $gt: 25 } }, { item: 1, qty: 1 },function(e,docs){})
would mean to get only the item and qty fields in the matched documents
The third parameter is the callback function which is called after the query is complete. function(e, docs) is the mongodb driver for node.js syntax. The 1st parameter e is the error. docs is the array of matched documents. If an error occurs it is given in e. If the query is successful the matched documents are given in the 2nd parameter docs(the name can be anything you want).
The cursor has various methods which can be used to manipulate the matched documents before mongoDB returns them.
collection.find( { qty: { $gt: 25 } }, { item: 1, qty: 1 })
is a cursor you can do various operations on it.
collection.find( { qty: { $gt: 25 } }, { item: 1, qty: 1 }).skip(10).limit(5).toArray(function(e,docs){
...
})
meaning you will skip the first 10 matched documents and then return a maximum of 5 documents.
All this stuff is given in the docs. I think it's better to use mongoose instead of the native driver because of the features and the popularity.
What is the best way to update a value within an array saved in a mongodb record? Currently, I'm trying it this way:
Record.find({ 'owner': owner}, {}, {sort: { date: -1 }}, function(err, record){
if(!err){
for (var i = 0; i < record[0].array.length; i++){
record[0].array[i].score = 0;
record[0].array[i].changed = true;
record[0].save();
}
}
});
And the schema looks like this:
var recordSchema = mongoose.Schema({
owner: {type: String},
date: {type: Date, default: Date.now},
array: mongoose.Schema.Types.Mixed
});
Right now, I can see that the array updates, I get no error in saving, but when I query the database again, the array hasn't been updated.
It would help if you explained your intent here as naming a property "array" conveys nothing about its purpose. I guess from your code you hope to go and set the score of each item there to zero. Note your save is currently being ignored because you can only save top-level mongoose documents, not nested documents.
Certain find-and-modify operations on arrays can be done with a single database command using the Array Update Operators like $push, $addToSet, etc. However I don't see any operators that can directly make your desired change in a single operation. Thus I think you need to find your record, alter the array date, and save it. (Note findOne is a convenience function you can use if you only care about the first match, which seems to be the case for you).
Record.findOne({ 'owner': owner}, {}, {sort: { date: -1 }}, function(err, record){
if (err) {
//don't just ignore this, log or bubble forward via callbacks
return;
}
if (!record) {
//Record not found, log or send 404 or whatever
return;
}
record.array.forEach(function (item) {
item.score = 0;
item.changed = true;
});
//Now, mongoose can't automatically detect that you've changed the contents of
//record.array, so tell it
//see http://mongoosejs.com/docs/api.html#document_Document-markModified
record.markModified('array');
record.save();
});
If you have a mongoose object of a document, you can of course update the array as in the question, with the following Caveat.
This is in fact a mongoose gotcha. Mongoose cannot track changes in the array of mixed, one has to use markModified:
doc.mixed.type = 'changed';
doc.markModified('mixed.type');
doc.save() // changes to mixed.type are now persisted