How to update array in mongodb using mongoose - node.js

when i try to update it dose not throw back any error it goes OK but when i check my datebase nothing i their updated nothing is modified pls help
this is my db
{
"_id" : ObjectId("56651f0e4905bd041cad0413"),
"creator" : ObjectId("566299dd17990464160ae27a"),
"content" : "this is my joke 2",
"created" : ISODate("2015-12-07T05:54:22.858Z"),
"__v" : 15,
"comments" : [
{
"posteruserId" : "5665e6867185d87c1e71dbdc",
"postedBy" : "lawrence nwoko",
"postterscomment" : "good joke",
"_id" : ObjectId("56660745f644c2501116acce")
},
{
"posteruserId" : "5665e6867185d87c1e71dbdc",
"postedBy" : "lawrence nwoko",
"postterscomment" : "good joke",
"_id" : ObjectId("56660b6d33c245c012104fdc")
}
]
}
this is my schema
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var JokesSchema = new Schema({
creator: {type: Schema.Types.ObjectId, ref: 'User'},
content: String,
created:{type:Date, default: Date.now},
comments: [{
text: String,
postedBy: String,
posteruserId :String,
date: String,
postterscomment:String
}]
})
module.exports = mongoose.model('Jokes_db', JokesSchema)
here i my post funtion
api.post('/update', function(req, res) {
// Joke.findById("56651f0e4905bd041cad0413", function (err, meeting) {
Joke.update({_id: "5665e6867185d87c1e71dbdc", 'comments._id' : "56660745f644c2501116acce"},
{'$set': {
'comments.$.postterscomment': "working"
}},
function(err, numAffected) {
if(err){
console.log(err)
}else{
res.json(numAffected)
}
}
);
});

It has been three days of trying to fix this problem but by his grace I have done it without help the problem user that I was not using the right id to make the query thanks for your help guys I hope this helps another user
api.post('/editecomments', function(req, res) {
Joke.update({_id: "56651f0e4905bd041cad0413", 'comments._id' : "56660745f644c2501116acce"},
{'$set': {'comments.$.postterscomment': 'working'}},
function(err, numAffected) {
if(err){
console.log(err)
}else{
res.json(numAffected)
}
}
);
});

Related

find nested (embedded) object in the collection

while i was going through my problem on StackOverflow,i noticed same question was asked before aswell,but none of them had got a good response,or an actual answer.
Mongoose Find One on Nested Object
How can I find nested objects in a document?
well back to my question: i wanted to find the object that is nested in the schema. trying findMany gives all the objects,and findOne give just the first one,but i want particular objects whose id i pass through req.body.checkbox.
my JS code goes like..
app.post("/data", uploads, function (req, res) {
User.findById(req.user.id, function (err, foundUser) {
if (err) {
console.log(err);
} else {
if (foundUser) {
var checkedBox = req.body.checkbox;
console.log(checkedBox);
User.findMany({_id:foundUser._id},{comments:{$elemMatch:{_id:checkedBox}}} ,function(err,checkedobj){
if(err){
console.log(err);
}
else{
console.log(checkedobj.comments);
if (Array.isArray(checkedobj.comments)) {
res.render("checkout",{SIMG: checkedobj.comments});
} else {
res.render("checkout",{SIMG: [checkedobj.comments]});
}
}
})
}
}
});
});
here is my schema,for reference
const commentSchema = new mongoose.Schema({
comment: String,
imagename: String,
permission:{type:Number,default:0},
});
const Comment = new mongoose.model("Comment", commentSchema);
const userSchema = new mongoose.Schema({
firstname: String,
lastname: String,
email: String,
password: String,
comments: [commentSchema],
permission:{type:Number,default:0},
});
userSchema.plugin(passportLocalMongoose);
const User = new mongoose.model("User", userSchema);
example
{
"_id" : ObjectId("5ec3f54adfaa1560c0f97cbf"),
"firstname" : "q",
"lastname" : "q",
"username" : "q#q.com",
"salt" : "***",
"hash" : "***",
"__v" : NumberInt(2),
"comments" : [
{
"permission" : NumberInt(0),
"_id" : ObjectId("5ec511e54db483837885793f"),
"comment" : "hi",
"imagename" : "image-1589973477170.PNG"
}
],
"permission" : NumberInt(1)
}
also when i check 3 checkboxes, console.log(checkBox) logs:
[
'5ec543d351e2db83481e878e',
'5ec589369d3e9b606446b776',
'5ec6463c4df40f79e8f1783b'
]
but console.log(checkedobj.comments) gives only one object.
[
{
permission: 0,
_id: 5ec543d351e2db83481e878e,
comment: 'q',
imagename: 'image-1589986259358.jpeg'
}
]
When you want multiple matching elements from an array you should use $filter aggregation operator
And as a precaution, first check req.body.checkbox is an array or not and convert it into an array of ObjectIds
app.post("/data", uploads, function (req, res) {
var ObjectId = mongoose.Types.ObjectId;
User.findById(req.user.id, function (err, foundUser) {
if (err) {
console.log(err);
} else {
if (foundUser) {
var checkedBox = req.body.checkbox;
if (!Array.isArray(checkedBox)) {
checkedBox = [checkedBox]
}
console.log(checkedBox);
var checkedBoxArray = checkedBox.map(id => ObjectId(id))
User.aggregate([
{$match: {_id: foundUser._id}},
{
$project: {
comments: {
$filter: {
input: "$comments",
as: "comment",
cond: { $in: [ "$$comment._id", checkedBoxArray ] }
}
}
}
}
],function(err,checkedobj){
if(err){
console.log(err);
}
else{
console.log(checkedobj[0].comments);
if (Array.isArray(checkedobj[0].comments)) {
res.render("checkout",{SIMG: checkedobj[0].comments});
} else {
res.render("checkout",{SIMG: [checkedobj[0].comments]});
}
}
})
}
}
});
});
Working example - https://mongoplayground.net/p/HnfrB6e4E3C
Above example will return only 2 comments matching the ids
You can make use of findById() method, more documentation about it is provided here
You can use something like this to search by object id:-
var id = "123";
userSchema.findById(id, function (err, user) { ... } );
Hope this helps!

Response output not showing entire information present in mongoose schema nodejs

I have designed a Mongoose schema like this :
const metricsSchema = mongoose.Schema({
_id : mongoose.Schema.Types.ObjectId,
level : String,
details: {
demo: String,
full: String
}
});
Also, I have handled the response as such :
router.post('/',(req, res, next)=>{
const metrics = new Metrics({
_id : new mongoose.Types.ObjectId(),
level : req.body.level,
details:{
demo: req.body.demo,
full: req.body.full
}
});
res.status(201).json({
metrics: metrics
})
});
However, when I use Postman to post JSON data like this :
{
"level" :"schema" ,
"details":{
"demo" : "2465",
"full" : "1211234"
}
}
I get output like this :
{
"metrics": {
"_id": "5e09c156b0ce8a4a54a3ecca",
"level": "schema"
}
}
I do not get the rest of the output : demo and full in the response json. I wish to get the output like this :
{
"metrics": {
"_id": "5e09c156b0ce8a4a54a3ecca",
"level": "schema"
"details": {
"demo": "2465",
"full": "1211234"
}
}
}
Update: I found one solution in which the Mongoose schema was divided into two parts :
const detailsSchema = mongoose.Schema({
_id : mongoose.Schema.Types.ObjectId,
demo: String,
full: String
});
mongoose.model('Details',detailsSchema );
const metricsSchema = mongoose.Schema({
_id : mongoose.Schema.Types.ObjectId,
level : String,
details: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Details'
}
});
However, this did not work as well.
You have to change the code as below:
router.post('/',(req, res, next)=>{
const metrics = new Metrics({
_id : new mongoose.Types.ObjectId(),
level : req.body.level,
details:{
demo: req.body.details.demo, <----- see the change here
full: req.body.details.full
}
});
res.status(201).json({
metrics: metrics
})
});

selecting data from several collections after finding the proper id in documents

I have an almost working solution, but I believe my solution is bad programmed and I don't know how to make it better. Maybe it should be done with mongoose population, but I can't figure out how it works and how to adjust my code.
I have 2 collections: author and books. They are imported from MySQL with data - so I can't change the structure.
author:
{ "_id" : ObjectId("59492addd80eb0f9c1b42fd9"), "id_a" : 1, "name" : "Agatha Christie", "gender" : "female", "born" : 1890, "birthplace" : "England", "genre" : "crime"
}
{ "_id" : ObjectId("594935e1d80eb0f9c1b42fdb"), "id_a" : 2, "name" : "Stephen King", "gender" : "male", "born" : 1947, "birthplace" : "U.S.", "genre" : "horror" }
books:
{ "_id" : ObjectId("59492cd1d80eb0f9c1b42fda"), "id_b" : 1, "title" : "Murder on the Orient Express", "id_a" : 1, "pub_date" : 1934, "publisher" : "Collins Crime Club",
"pages" : 256, "description" : "Hercule Poirot, the internationally famous detective, boards the Orient Express (Simplon-Orient-Express) in Istanbul. The train is unus
ually crowded for the time of year. Poirot secures a berth only with the help of his friend Monsieur Bouc, a director of the Compagnie Internationale des Wagons-Lits. W
hen a Mr. Harris fails to show up, Poirot takes his place. On the second night, Poirot gets a compartment to himself..." }
{ "_id" : ObjectId("59493779d80eb0f9c1b42fdc"), "id_b" : 2, "title" : "The A.B.C. Murders", "id_a" : 1, "pub_date" : 1936, "publisher" : "Collins Crime Club", "pages" :
256, "description" : "The novel follows the eponymous murders and their investigation as seen by Arthur Hastings, Poirot's old friend. Poirot receives typed letters si
gned by A.B.C. In each is given the date and location of the next murder. A.B.C. moves alphabetically: Alice Ascher is a tobacco shop owner killed in her shop in Andove
r, Betty Barnard is a flirty waitress killed in Bexhill, and Sir Carmichael Clarke is a wealthy man killed at his home in Churston..." }
{ "_id" : ObjectId("59493858d80eb0f9c1b42fdd"), "id_b" : 3, "title" : "The Shining", "id_a" : 2, "pub_date" : 1977, "publisher" : "Doubleday", "pages" : 447, "descripti
on" : "The Shining mainly takes place in the fictional Overlook Hotel, an isolated, haunted resort located in the Colorado Rockies. The history of the hotel, which is d
escribed in backstory by several characters, includes the deaths of some of its guests and of former winter caretaker Delbert Grady, who succumbed to cabin fever and ki
lled his family and himself..." }
I want to find with author's name his id in the collection author and use his id to find all his books in the collection books. But the json-result should be a combination of selected field from both collections. For example I search for Agatha Christie and want get following selected fields as one json-object (name and genger from author + title and description from books as one object) Desired Api result in postman:
[ {
"name": "Agatha Christie",
"gender": "femail",
"title" : "Murder on the Orient Express",
"description" : "Hercule Poirot, the internationally famous detective, boards the Orient Express (Simplon-Orient-Express) in Istanbul...."
},
{
"name": "Agatha Christie",
"gender": "femail",
"title" : "The A.B.C. Murders",
"description" : "The novel follows the eponymous murders and their investigation as seen by Arthur Hastings, Poirot's old friend..."
}]
here is my code:
api.js
var express = require('express');
var app = express();
var bodyParser = require('body-parser');
var mongoose = require('mongoose');
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
mongoose.connect('mongodb://localhost/books');
var db = mongoose.connection;
db.on('connected', function() {
console.log('MongoDB connection successful');
});
Author = require('./models/books');
Book = require('./models/books');
app.post('/api/Books', function(req, res){
Author.getAuthor({name : req.body.name}, 10, function(err, data){
if (err){
throw err;
}
var tmp = data[0].id_a;
Book.getBook({id_a : tmp}, 10, function(err, data2){
if (err){
throw err;
}
var result = [data, data2];
console.log(result);
res.json(result);
});
});
});
app.listen(3000);
console.log('server started and waits on port 3000');
books.js
var mongoose = require('mongoose');
var authorSchema = mongoose.Schema({
id_a:{
type: Number,
required: true
},
name:{
type: String,
required: true
},
gender:{
type: String,
required: true
},
born:{
type: Number,
required: true
},
birthplace:{
type: String,
required: true
},
genre:{
type: String,
required: true
}},
{ collection: 'author'}
);
var booksSchema = mongoose.Schema({
id_b:{
type: Number,
required: true
},
title:{
type: String,
required: true
},
id_a:{
type: Number,
required: true
},
pub_date:{
type: Number,
required: true
},
publisher:{
type: String,
required: true
},
pages:{
type: Number,
required: true
},
description:{
type: String,
required: true
}},
{ collection: 'books'}
);
var Author = module.exports = mongoose.model('author', authorSchema);
var Book = module.exports = mongoose.model('books', booksSchema);
module.exports.getAuthor = function(query, limit, callback){
Author.find(query, {'_id': 0}).select('id_a').limit(limit).exec(callback);
}
module.exports.getBook = function(query, limit, callback){
Book.find(query).select('-_id id_a title').limit(limit).exec(callback);
}
With my app I can find the proper books to particular author, but my result is without author's name and gender - I don't know how to do it. Also I make a request with nested functions - there might be much better solution for it. My solution feels very dirty. How can I improve my code and get data from both collections? A working adjusted example would be realy great!
First you have to add those changes:
model:
var booksSchema = mongoose.Schema({
...
},
{ collection: 'books', toJSON: { virtuals: true } })
// Foreign keys definitions
// http://mongoosejs.com/docs/populate.html#populate-virtuals
booksSchema.virtual('author', {
ref: 'author',
localField: 'id_a',
foreignField: 'id_a',
justOne: true // for many-to-1 relationships
});
module.exports.getAuthor = function (query) {
return Author.findOne(query).exec();
}
// this help you get books with author
module.exports.getBook = function (query) {
return Book.find(query)
.populate('author')
.exec();
}
app code:
app.post('/api/Books', function (req, res) {
Author.getAuthor({ name: req.body.name }, 10)
.then(author => {
return Book.getBook({ id_a: author.id_a });
})
.then(result => {
console.log(result);
res.json(result);
})
.catch(error => {
// TODO:
});
});
result should be:
[{...book info,...author info }]
I hope this help you

Updating multiple sub-documents with Mongoose and Node

I have a Model wich contains an array of sub-documents. This is a Company:
{
"_id" : ObjectId(":58be7c236dcb5f2feff91ac0"),
"name" : "sky srl",
"contacts" : [
{
"_id" : ObjectId("58be7c236dcb5f2feff91ac2"),
"name": { type: String, required: true },
"company" : ObjectId("58be7c236dcb5f2feff91ac0"),
"email" : "sky#gmail.com",
"chatId" : "",
"phone" : "123456789",
"name" : "John Smith"
},
{
"_id" : ObjectId("58be7f3a6dcb5f2feff91ad3"),
"company" : ObjectId("58be7f3a6dcb5f2feff91ad1"),
"email" : "beta#gmail.com",
"chatId" : "",
"phone" : "987654321",
"name" : "Bill Gaset"
}
],
"__v" : 1
}
I have several companies, and I want to update the field chatId of all the contacts in all the companies, that matches the phone I am searching for.
My Schema definitions (simplified, for focusing on question):
var contactSchema = new Schema({
[...]
phone: { type: String, required: true },
email: { type: String },
chatId: { type: String },
company: Schema.Types.ObjectId,
});
var companySchema = new Schema({
name: { type: String, required: true },
type: { type: String, default: "company" },
contacts: [contactSchema]
});
I tried
var conditions = { "contacts.phone": req.body.phone };
var partialUpdate = req.body; //it contains 'req.body.phone' and 'req.body.chatId' attributes
Company.find(conditions).then(
function (results) {
results.map( function(companyFound) {
companyFound.contacts.forEach(function (contactContainer){
if (contactContainer.phone == partialUpdate.phone) {
contactContainer.chatId = partialUpdate.chatId;
Company.save();
companyFound.save();
contactContainer.save();
results.save();
}
//not sure of what to save, so i save everything
companyFound.save();
contactContainer.save();
results.save();
});
});
});
following this answer; but it doesn't works. It does not save anything, what I'm doing wrong?
I have never done this before, but worth a try.
Maybe you need to use $elemMatch.
// find the companies that have contacts having the phone number
Company.find().where('contacts', { $elemMatch: { phone: req.body.phone }}).exec(function (err, companies) {
if (err) {
console.log(err);
return;
}
// see if you can at least get the query to work
console.log(companies);
async.eachSeries(companies, function updateCompany(company, done) {
// find and update the contacts having the phone number
company.contacts.forEach(function (contact, i, arr) {
if (contact.phone == req.body.phone) {
arr[i].chatId = req.body.chatId;
}
});
company.save(done);
}, function allDone (err) {
console.log(err);
});
});
Note, I am using async.js to do async operations on multiple items.
Honestly, I would have simply made contacts an array of Contact references -- much easier to query and update.
Just for the records: I did this to make it work without async.js:
Company.find().where('contacts', { $elemMatch: { phone: req.body.phone } })
.exec(function (err, companies) {
if (err) {
console.log(err);
return;
}
console.log("companies: " + JSON.stringify(companies, null, 4));
companies.forEach(function (company) {
company.contacts.map(function (contact, i, arr) {
if (contact.phone == req.body.phone) {
arr[i].telegramChatId = req.body.telegramChatId;
}
});
company.save();
},
function allDone(err) {
console.log(err);
});
});`

$unwind nested document and $match

I have a nested document which looks like:
var User = new Schema({
id: String,
position: [{
title: String,
applied:[{
candidate_id: String,
name: String
}],
}],
What I am looking to do is return all of the 'applied' subdocuments which match a certain 'candidate_id'
What I have so far:
app.get('/applied', function(req, res){
var position = "58dc2bd4e7208a3ea143959e";
User.aggregate(
{$unwind : "$position"},
{$unwind : "$position.applied"},
{$match:{'position.applied.candidate_id': position}}).exec(function (err, result) {
console.log(result);
});
res.render('applied', { title: 'applied',layout:'candidate'});
});
I have another function which returns all the positions that match, and that code works:
app.post('/search', function (req, res) {
var position = new RegExp(req.body.position, 'i');
var location = new RegExp(req.body.location, 'i');
User.aggregate(
{$unwind : "$position"},
{$match:{'position.title': position,'position.location':location}}).exec(function (err, result) {
console.log(result);
res.send({ results: result });
});
});
So basically I am struggling with getting a sub-sub-document. Any idea where I'm going wrong?
Sample data:
{
"_id" : ObjectId("58c2871414cd3d209abf5fc9"),
"position" : [
{
"_id" : ObjectId("58d6b7e11e793c9a506ffe8f"),
"title" : "Software Engineer",
"applied" : [
{
"candidate_id" : "58d153e97e3317291gd80087",
"name" : "Sample user"
},
{
"candidate_id" : "58d153e97e3317291fd99001",
"name" : "Sample User2"
}
]
},
{
"_id" : ObjectId("58c2871414cd3d209abf5fc0"),
"title" : "Software Engineer",
}
],
}
What is going on above is there 2 positions, one of which (first entry) has 2 applied candidates, What I need to do is return the nested object if it matches the mongoose query.
Your code seems fine to me I have implemented same and it works for me only possible issue can be that your position="58dc2bd4e7208a3ea143959e" it might be talking it as a string just convert it to objectId by using the following code and check it should work for you.
var mongoose = require('mongoose');
var position = mongoose.Types.ObjectId("58dc2bd4e7208a3ea143959e");
User.aggregate(
{$unwind : "$position"},
{$unwind : "$position.applied"},
{$match:{'position.applied.candidate_id': position}}).exec(function (err, result) {
console.log(result);
});
res.render('applied', { title: 'applied',layout:'candidate'});
});

Resources