I am trying to find and update a subdoc using mongoose and mongdb but I can't seem to find the subdocs using the ids.
My schema
var Attending = new Schema({
users: []
});
var Query = new Schema({
name: [],
url: [],
attending: [Attending],
img: []
});
Using the ids for both docs, just finding returns null for the doc. However, if i only using the general Id I can find the entire doc but not the subdoc.
Queries.findOne(
{ "_id": queryId, "attending._id": attendingId },
function(err, doc) {
if (err) throw err;
console.log(doc);
res.render('index');
}
);
sample doc
{
"_id": {
"$oid": "58d995026d7c8f0a3028a50f"
},
"img": [
"https://pic1",
"https://pic2"
],
"attending": [
{
"_id": {
"$oid": "58d995026d7c8f0a3028a505"
},
"users": [
"somebody"
]
},
{
"_id": {
"$oid": "58d995026d7c8f0a3028a506"
},
"users": [
"another person"
]
}
],
"url": [
"https://www.yelp.com/2",
"https://www.yelp.com/1"
],
"name": [
"The Rivermill",
"Sharkeys"
],
"__v": 0
}
I am using the correct ids and expect to the first subdoc in attending to be returned or whichever one is searched. Instead null is returned.
Queries is made like this:
var names = [];
var urls = [];
var imgUrls = [];
var attendingArray = [];
for (var i=0; i<10; i++) {
names.push(businesses[i].name);
urls.push(businesses[i].url);
imgUrls.push(businesses[i].image_url);
//add attending schema
var newAttending = Attending({
users: ["somebody"]
});
attendingArray.push(newAttending);
}
var newQuery = Queries({
name: names,
url: urls,
attending: attendingArray,
img: imgUrls
});
Related
I have the following Board object:
{
"boardMembers": [
"5f636a5c0d6fa84be48cc19d"
],
"boardLists": [
{
"cards": [],
"_id": "5f6387e077beba2e3c15d15a",
"title": "list one",
"__v": 0
}
],
"_id": "5f63877177beba2e3c15d159",
"boardName": "board1",
"boardPassword": "123456",
"boardCreator": "5f636a5c0d6fa84be48cc19d",
"g_createdAt": "2020-09-17T15:57:37.616Z",
"__v": 2
}
as you can see there is a "boardLists" array, I want to make a post request that post a card to a list inside that array with specific ID.
that my code:
router.post("/add-task/:id", auth, boardAuth, async (req, res) => {
const listId = req.params.id;
try {
const board = await Board.findOne({ _id: req.board._id });
if (!board) return res.status(404).send("no such board");
const list = await List.findOne({ _id: listId });
if (!list) return res.status(404).send("List not found");
const task = new Task({
text: req.body.text,
});
Board.updateOne(
{ _id: req.board._id, "boardLists._id": listId },
{ $push: { "boardLists.$.cards": task } }
);
await board.save();
res.send(board);
} catch (err) {
console.log(err);
}
});
Now the problem is when I make the request call in postman its does the push inside the specific list that i want but its not saving the push inside the parent board object: when do a get request to that object i get an empty cards array.
like that:
{
"boardMembers": [
"5f636a5c0d6fa84be48cc19d"
],
"boardLists": [
{
"cards": [],
"_id": "5f6387e077beba2e3c15d15a",
"title": "list one",
"__v": 0
}
],
"_id": "5f63877177beba2e3c15d159",
"boardName": "board1",
"boardPassword": "123456",
"boardCreator": "5f636a5c0d6fa84be48cc19d",
"g_createdAt": "2020-09-17T15:57:37.616Z",
"__v": 2
}
Why its not saving the push in the board object?
Mongoose is weird. You need to mark the field as modified, otherwise the changes won't be saved. Try this :
board.markModified("boardLists");
await board.save();
I am using the below code to insert data to mongodb
router.post('/NewStory', function (req, res) {
var currentObject = { user: userId , story : story , _id:new ObjectID().toHexString() };
req.db.get('clnTemple').findAndModify({
query: { _id: req.body.postId },
update: { $addToSet: { Stories: currentObject } },
upsert: true
});
});
This code is working fine if i remove the _id:new ObjectID().toHexString()
What i want to achieve here is that for every new story i want a unique _id object to be attached to it
What am i doing wrong?
{
"_id": {
"$oid": "55ae24016fb73f6ac7c2d640"
},
"Name": "some name",
...... some other details
"Stories": [
{
"userId": "105304831528398207103",
"story": "some story"
},
{
"userId": "105304831528398207103",
"story": "some story"
}
]
}
This is the document model, the _id that i am trying to create is for the stories
You should not be calling .toHexString() on this as you would be getting a "string" and not an ObjectID. A string takes more space than the bytes of an ObjectId.
var async = require('async'),
mongo = require('mongodb'),
db = require('monk')('localhost/test'),
ObjectID = mongo.ObjectID;
var coll = db.get('junk');
var obj = { "_id": new ObjectID(), "name": "Bill" };
coll.findAndModify(
{ "_id": new ObjectID() },
{ "$addToSet": { "stories": obj } },
{
"upsert": true,
"new": true
},
function(err,doc) {
if (err) throw err;
console.log(doc);
}
)
So that works perfectly for me. Noting the "new" option there as well so the modified document is returned, rather than the original form of the document which is the default.
{ _id: 55c04b5b52d0ec940694f819,
stories: [ { _id: 55c04b5b52d0ec940694f818, name: 'Bill' } ] }
There is however a catch here, and that is that if you are using $addToSet and generating a new ObjectId for every item, then that new ObjectId makes everything "unique". So you would keep adding things into the "set". This may as well be $push if that is what you want to do.
So if userId and story in combination already make this "unique", then do this way instead:
coll.findAndModify(
{
"_id": docId,
"stories": {
"$not": { "$elemMatch": { "userId": userId, "story": story } }
}
},
{ "$push": {
"stories": {
"userId": userId, "story": story, "_id": new ObjectID()
}
}},
{
"new": true
},
function(err,doc) {
if (err) throw err;
console.log(doc);
}
)
So test for the presence of the unique elements in the array, and where they do not exist then append them to the array. Also noting there that you cannot do an "inequality match" on the array element while mixing with "upserts". Your test to "upsert" the document should be on the primary "_id" value only. Managing array entries and document "upserts" need to be in separate update operations. Do not try an mix the two, otherwise you will end up creating new documents when you did not intend to.
By the way, you can generate an ObjectID just using monk.
var db = monk(credentials.database);
var ObjectID = db.helper.id.ObjectID
console.log(ObjectID()) // generates an ObjectID
I need to help!
I'm creating a website with nodejs and mongo for learning.
I have a problem that I know the best way to do it.
I have two collections codes and tag into table codes I have the tags field is array of tags.
CodeModel:
var CodeSchema = new Schema({
title: { type: 'String', required: true },
text: { type: 'String', required: true },
url: { type: 'String', required: true },
uri: String,
createdAt: { type: Date, default: Date.now },
updatedAt: { type: Date, default: Date.now },
owner: {
type: Schema.ObjectId,
ref: 'User'
},
tags: [
{
type: Schema.ObjectId,
ref: 'Tag'
}
]
});
CodeSchema.pre("save", function (next) {
// if create for first time
if (!this.created_at) {
this.created_at = Date.now();
}
next();
});
module.exports = mongoose.model('Code', CodeSchema);
And My Tag Model:
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var TagSchema = new Schema({
name: 'string'
});
module.exports = mongoose.model('Tag', TagSchema);
when I get the result in my rest I got it:
[
{
"_id": "5540f557bda6c4c5559ef638",
"owner": {
"_id": "5540bf62ebe5874a1b223166",
"token": "7db8a4e1ba11d8dc04b199faddde6a250eb8a104a651823e7e4cc296a3768be6"
},
"uri": "test-save",
"url": "http://www.google.com.br/",
"text": " hello ",
"title": "testing...",
"__v": 0,
"tags": [
{
"_id": "55411700423d29c70c30a8f8",
"name": "GO"
},
{
"_id": "55411723fe083218869a82d1",
"name": "JAVA"
}
],
"updatedAt": "2015-04-29T15:14:31.579Z",
"createdAt": "2015-04-29T15:14:31.579Z"
}
]
This I populate into database, I don't know how I insert it, is there any way automatic with mongoose that to do it or I need to create by myself?
I am testing with this json:
{
"url": "http://www.google.com.br/",
"title": "Test inset",
"text": "insert code",
"tags": [
"ANGULAR",
{
"_id": "55411700423d29c70c30a8f8",
"name": "GO"
}
]
}
I need to do a insert of tags, if I have id or not. Do I need to create it or has way to do it automatically?
and how can I do it?
Sorry my english =x
Generally speaking to create and save a document in a mongo database using mongooseJS is fairly straightforward (assuming you are connected to a database):
var localDocObj = SomeSchemaModel(OPTIONAL_OBJ); // localDocObj is a mongoose document
localDocObj.save(CALLBACK); // save the local mongoose document to mongo
If you have an object that is of the same form as the schema, you can pass that to the constructor function to seed the mongoose document object with the properties of the object. If the object is not valid you will get an invalidation error passed to the callback function on validate or save.
Given your test object and schemas:
var testObj = {
"url": "http://www.google.com.br/",
"title": "Test inset",
"text": "insert code",
"tags": [
"ANGULAR",
{
"_id": "55411700423d29c70c30a8f8",
"name": "GO"
}
]
};
var codeDoc = Code(testObj);
codeDoc.save(function (err, doc) {
console.log(err); // will show the invalidation error for the tag 'Angular'
});
Since you are storing Tag as a separate collection you will need to fetch/create any tags that are string values before inserting the new Code document. Then you can use the new Tag documents in place of the string values for the Code document. This creates an async flow that you could use Promises (available in newer node releases) to manage.
// Create a promise for all items in the tags array to iterate over
// and resolve for creating a new Code document
var promise = Promise.all(testObj.tags.map(function(tag) {
if (typeof tag === 'object') {
// Assuming it exists in mongo already
return tag;
}
// See if a tag already exists
return Tag.findOne({
name: tag
}).exec().then(function(doc) {
if (doc) { return doc; }
// if no tag exists, create one
return (Tag({
name: tag
})).save(); // returns a promise
});
})).then(function(tags) {
// All tags were checked and fetched/created if not an object
// Update tags array
testObj.tags = tags;
// Finally add Code document
var code = Code(testObj);
return code.save();
}).then(function(code) {
// code is the returned mongo document
console.log(code);
}).catch(function(err) {
// error in one of the promises
console.log(err);
});
You can do it like
var checkNewTagAndSave = function(data, doc, next){ // data = req.body (your input json), doc = mongoose document to be saved, next is the callback
var updateNow = function(toSave, newTags){
// save your mongoose doc and call the callback.
doc.set(toSave);
doc.save(next);
};
var data = req.body;
var tagsToCreate = [];
var tagids = [];
data.tags.forEach(function(tag, index){
if(typeof(tag) == 'string') {
tagsToCreate.push({ name: tag });
} else tagids.push(tag._id);
});
data.tags = tagids;
if(tagsToCreate.length === 0) updateNow(data);
else {
mongoose.model('tag').create(tagsToCreate, function(err, models){
if(err || !models) return next(err);
else {
models.forEach(function(model){
data.tags.push(model._id);
});
updateNow(data, models);
}
});
}
};
Hope code is reflecting its logic itself
usage :
after you have found your Code document say aCode
just call
checkNewTagAndSave(req.body, aCode, function(err, doc){
//end your response as per logic
});
I have following data in my Mongodb.
{
"_id" : ObjectId("54a0d4c5bffabd6a179834eb"),
"is_afternoon_scheduled" : true,
"employee_id" : ObjectId("546f0a06c7555ae310ae925a")
}
I would like to use populate with aggregate, and want to fetch employee complete information in the same response, I need help in this. My code is:
var mongoose = require("mongoose");
var empid = mongoose.Types.ObjectId("54a0d4c5bffabd6a179834eb");
Availability.aggregate()
.match( { employee_id : empid} )
.group({_id : "$employee_id",count: { $sum: 1 }})
.exec(function (err, response) {
if (err) console.log(err);
res.json({"message": "success", "data": response, "status_code": "200"});
}
);
The response i am getting is
{"message":"success","data":{"_id":"54a0d4c5bffabd6a179834eb","count":1},"status_code":"200"}
My expected response is:
{"message":"success","data":[{"_id":"54aa34fb09dc5a54232e44b0","count":1, "employee":{fname:abc,lname:abcl}}],"status_code":"200"}
You can call the model form of .populate() on the result objects from an aggregate operation. But the thing is you are going to need a model to represent the "Result" object returned by your aggregation in order to do so.
There are a couple of steps, best explained with a complete listing:
var async = require('async'),
mongoose = require('mongoose'),
Schema = mongoose.Schema;
var employeeSchema = new Schema({
"fname": String,
"lname": String
})
var availSchema = new Schema({
"is_afternoon_scheduled": Boolean,
"employee_id": {
"type": Schema.Types.ObjectId,
"ref": "Employee"
}
});
var resultSchema = new Schema({
"_id": {
"type": Schema.Types.ObjectId,
"ref": "Employee"
},
"count": Number
});
var Employee = mongoose.model( "Employee", employeeSchema );
var Availability = mongoose.model( "Availability", availSchema );
var Result = mongoose.model( "Result", resultSchema, null );
mongoose.connect('mongodb://localhost/aggtest');
async.series(
[
function(callback) {
async.each([Employee,Availability],function(model,callback) {
model.remove({},function(err,count) {
console.log( count );
callback(err);
});
},callback);
},
function(callback) {
async.waterfall(
[
function(callback) {
var employee = new Employee({
"fname": "abc",
"lname": "xyz"
});
employee.save(function(err,employee) {
console.log(employee),
callback(err,employee);
});
},
function(employee,callback) {
var avail = new Availability({
"is_afternoon_scheduled": true,
"employee_id": employee
});
avail.save(function(err,avail) {
console.log(avail);
callback(err);
});
}
],
callback
);
},
function(callback) {
Availability.aggregate(
[
{ "$group": {
"_id": "$employee_id",
"count": { "$sum": 1 }
}}
],
function(err,results) {
results = results.map(function(result) {
return new Result( result );
});
Employee.populate(results,{ "path": "_id" },function(err,results) {
console.log(results);
callback(err);
});
}
);
}
],
function(err,result) {
if (err) throw err;
mongoose.disconnect();
}
);
That's the complete example, but taking a closer look at what happens inside the aggregate result is the main point:
function(err,results) {
results = results.map(function(result) {
return new Result( result );
});
Employee.populate(results,{ "path": "_id" },function(err,results) {
console.log(results);
callback(err);
});
}
The first thing to be aware of is that the results returned by .aggregate() are not mongoose documents as they would be in a .find() query. This is because aggregation pipelines typically alter the document in results from what the original schema looked like. Since it is just a raw object, each element is re-cast as a mongoose document for the Result model type defined earlier.
Now in order to .populate() with data from Employee, the model form of this method is called on the array of results in document object form along with the "path" argument to the field to be populated.
The end result fills is the data as it comes from the Employee model it was related to.
[ { _id:
{ _id: 54ab2e3328f21063640cf446,
fname: 'abc',
lname: 'xyz',
__v: 0 },
count: 1 } ]
Different to how you process with find, but it is necessary to "re-cast" and manually call in this way due to how the results are returned.
This is working like applied populate with aggregate using inner query.
var mongoose = require("mongoose");
var empid = mongoose.Types.ObjectId("54a0d4c5bffabd6a179834eb");
Availability.aggregate()
.match( { employee_id : empid} )
.group({_id : "$employee_id",count: { $sum: 1 }})
.exec(function (err, response) {
if (err) console.log(err);
if (response.length) {
var x = 0;
for (var i=0; i< response.length; i++) {
empID = response[i]._id;
if (x === response.length -1 ) {
User.find({_id: empID}, function(err, users){
res.json({"message": "success", "data": users, "status_code": "200"});
});
}
x++;
}
}
}
);
I've been successfully using $in in my node webservice when my mongo arrays only held ids. Here is sample data.
{
"_id": {
"$oid": "52b1a60ce4b0f819260bc6e5"
},
"title": "Sample",
"team": [
{
"$oid": "52995b263e20c94167000001"
},
{
"$oid": "529bfa36c81735b802000001"
}
],
"tasks": [
{
"task": {
"$oid": "52af197ae4b07526a3ee6017"
},
"status": 0
},
{
"task": {
"$oid": "52af197ae4b07526a3ee6017"
},
"status": 1
}
]
}
Notice that tasks is an array, but the id is nested in "task", while in teams it is on the top level. Here is where my question is.
In my Node route, this is how I typically deal with calling a array of IDs in my project, this works fine in the team example, but obviously not for my task example.
app.get('/api/tasks/project/:id', function (req, res) {
var the_id = req.params.id;
var query = req.params.query;
models.Projects.findById(the_id, null, function (data) {
models.Tasks.findAllByIds({
ids: data._doc.tasks,
query: query
}, function(items) {
console.log(items);
res.send(items);
});
});
});
That communicates with my model which has a method called findAllByIds
module.exports = function (config, mongoose) {
var _TasksSchema = new mongoose.Schema({});
var _Tasks = mongoose.model('tasks', _TasksSchema);
/*****************
* Public API
*****************/
return {
Tasks: _Tasks,
findAllByIds: function(data, callback){
var query = data.query;
_Tasks.find({_id: { $in: data.ids} }, query, function(err, doc){
callback(doc);
});
}
}
}
In this call I have $in: data.ids which works in the simple array like the "teams" example above. Once I nest my object, as with "task" sample, this does not work anymore, and I am not sure how to specify $in to look at data.ids array, but use the "task" value.
I'd like to avoid having to iterate through the data to create an array with only id, and then repopulate the other values once the data is returned, unless that is the only option.
Update
I had a thought of setting up my mongo document like this, although I'd still like to know how to do it the other way, in the event this isn't possible in the future.
"tasks": {
"status0": [
{
"$oid": "52995b263e20c94167000001"
},
{
"$oid": "529bfa36c81735b802000001"
}
],
"status1": [
{
"$oid": "52995b263e20c94167000001"
},
{
"$oid": "529bfa36c81735b802000001"
}
]
}
You can call map on the tasks array to project it into a new array with just the ObjectId values:
models.Tasks.findAllByIds({
ids: data.tasks.map(function(value) { return value.task; }),
query: query
}, function(items) { ...
Have you try the $elemMatch option in find conditions ? http://docs.mongodb.org/manual/reference/operator/query/elemMatch/