Array not added to object in array - javascript

I want to combine two arrays from MongoDB: an array of contact objects and an array of appointments.
My current solution is to query all contacts and appointments, loop over each array, compare the email and if they match, add each appointment to its corresponding contact.
var contacts, appointments;
Contact.find(query, function(err, result1) {
if (err) {
console.log(err)
}
else {
contacts = result1;
Appointment.find({isArchived : false}, function(err, result2) {
if (err) {
console.log(err);
}
else {
appointments = result2;
for (var i=0; i<contacts.length;i++) {
contacts[i]["appointments"] = [];
for (var j=0; j<appointments.length;j++) {
if (contacts[i].email === appointments[j].owner) {
contacts[i].appointments.push(appointments[j]);
}
}
}
res.send(contacts);
}
});
}
});
Contacts:
[{
email: "example1#example1.com"
}, {
email: "example2#example2.com"
}]
[{
email: "example1#example1.com",
start: "2015-01-01",
end: "2015-02-01"
}, {
email: "example1#example1.com",
start: "2015-02-01",
end: "2015-03-01"
}]
// Desired output
[{
email: "example1#example1.com",
appointments: [{
email: "example1#example1.com",
start: "2015-01-01",
end: "2015-02-01"
}, {
email: "example1#example1.com",
start: "2015-02-01",
end: "2015-03-01"
}]
}, {
email: "example2#example2.com",
appointments: []
}]
I know it's a mess, and I'm also unable to append the appointment array to each contact. I can only assign values to existing object keys.
So it's really two questions: 1) Why can't I append the array to an existing contact, 2) what is a more efficient solution?

Apparently you cannot extend Mongoose results directly. For that to work, you must call .toObject() on the object you wish to extend.
I would love to have suggestions for a cleaner solution, preferrably one that only involves one mongodb call, and no manual array operations.

Related

How to add an object to an array of object, using addToSet, or push operators in mongodb

I have an array of reviews, I want to add a review using addToSet that will check if user is present in the array, then we do not want to add since one user can only review once.
My schema looks like this:
const sellerSchema = new mongoose.Schema({
user: {
type: mongoose.Schema.Types.ObjectId,
ref: "User",
unique: true,
},
reviews: [
{
by: {
type: mongoose.Schema.Types.ObjectId,
ref: "User",
unique: true,
},
title: {
type: String,
},
message: {
type: String,
},
rating: Number,
imagesUri: [{ String }],
timestamp: {
type: Date,
default: Date.now,
},
},
],
});
I might be doing the query wrong, but can't figure out how to add a review and check if current user has not reviewed before.
Here is the query where I add the review:
router.post("/review/:_id/", async (req, res) => {
try {
const stylist_id = mongoose.Types.ObjectId(req.params._id);
const review = {
by: req.user._id,
title: req.body.title,
message: req.body.message,
rating: parseFloat(req.body.rating),
};
if (req.body.imagesUri) {
//if there is images, we need to set it up
review.imagesUri = req.body.imagesUri;
}
await Seller.updateOne(
{ _id: seller_id },
{ $addToSet: { reviews: review } } //get the review that matches to the user_id
);
return res.status(200).send(true);
}catch(err){
res.status(502).send({
error: "Error creating a review.",
});
}
});
I'm thinking of checking for seller's id and also check that no review is by current user, but it is not working.
const userID = req.user._id;
await Seller.updateOne(
{ _id: seller_id, reviews: { $elemMatch: { by: { $ne: { userID } } } } },
{ $addToSet: { reviews: review } } //get the review that matches to the user_id
);
ANSWER:
I was able to solve the issue, in case other people have same issue. I did this:
await Seller.updateOne(
{
_id: seller_id,
"reviews.by": { $nin: [req.user.id] },
//knowing req.user._id is a mongoose.Types.ObjectId.
//You can also use [id1, id2, ...] to the array to check for other id's
},
{ $addToSet: { reviews: review } } //get the review that matches to the user_id
);
Here is the documentation for $nin operator: https://www.mongodb.com/docs/manual/reference/operator/query/nin/
You are pushing the review object inside an object.
Instead do this:
await Seller.updateOne(
{ _id: seller_id },
{ $addToSet: { reviews: review } }
);

How to insert array items in mongoose?

i'm making a web site with MEAN Stack
I made a schema.
below is the schema.
const StoreSchema = mongoose.Schema({
storename: {
type: String
},
storenumber: {
type: Number
},
product: [{
pname: String,
pcode: Number,
price: String,
detail: String
}]
});
product is array.
when i make this schema, mongoose make it like this.
_id:5c3afaa96649e84364aab24b
storenumber:1
product:Array
__v:0
now there is a problem .
i want to insert values to pname, pcode, price, detail in product array.
but i can't push the values to the array.
below is the inserting value code.
module.exports.addProd = function(newProd, callback) {
Store.updateOne({
storenumber: number
}, {
$push: {
pname: newProd.pname
},
$push: {
pcode: newProd.pcode
},
$push: {
price: newProd.price
},
$push: {
detail: newProd.detail
}
}, callback);
}
but it doesn't work.
even i wrote product.
the values in newProd are all correct
what i want is like this
product[0] = {
pname:blah,
pcode:1,
price:5,
detail:blah blah
}
how can i insert values to array in mongoose?
please help me
You have to push the whole object together rather than each field like this -
Store.updateOne({
storenumber: number
}, { $push: { "product": newProduct } }, callback);
and also rather than using callback you should use async await -
module.exports.addProd = async newProd => {
let res = await Store.updateOne({
storenumber: number
}, { $push: { "product": newProduct } });
return res;
}

How do I search one array for items from another array in MongoDB?

I have a users table that has the following data structure:
[{
userID: 0,
username: 'test0',
petsForSale: [
{ type: 'fish' },
{ type: 'cats' }
],
seekingPets: [
{ type: 'dogs' },
{ type: 'birds' }
]
},
{
userID: 1,
username: 'test1',
petsForSale: [
{ type: 'guinea pigs' },
{ type: 'dogs' },
{ type: 'hamsters' }
],
seekingPets: [
{ type: 'ferrets' }
]
}]
I'm trying to execute a GET that returns matched users based on petsForSale and seekingPets. For example, if a user is selling dogs, they will show up on the list of matched results for any user with dogs in seekingPets. I'm very close, here's my router code so far:
router.get('/:id/findmatches', function(req, res) {
var db = req.db;
var collection = db.get('users');
var uid = req.params.id;
//var seeking_pets = collection.find({ userID: uid }, { seekingPets: 1 });
//var seeking_pets = collection.find({ userID: uid }, { seekingPets: { type: 1 }});
var seeking_pets = [ 'dogs', 'birds' ]; // *Hard-coded is the only way I can get it to work
collection.find({ petsForSale: { $elemMatch: { type: { $in: seeking_pets }}}}, function(e, docs) {
res.json(docs);
});
});
This code compiles and works just fine with seeking_pets hard-coded - visiting /users/0/findmatches returns user test1 as expected. I'm stuck extracting the list of seekingPets from the userID in the request and searching through it in collection.find. The two commented lines are what I've tried without success. I've also tried converting the collection to an array.
As find is an async method, you must implement a callback in order to get the values returned from the database. Also notice that your hardcoded values are strings, but the seekingPets array is of objects.
Try this:
router.get('/:id/findmatches', function(req, res) {
var db = req.db;
var collection = db.get('users');
var uid = req.params.id;
collection.findOne({ userID: uid }, function(err, user)
var seeking_pets = [].
for (var i = 0; i < user.seekingPets.length; i++) {
seeking_pets.push(user.seekingPets[i].type);
}
collection.find({ petsForSale: { $elemMatch: { type: { $in: seeking_pets }}}}, function(e, docs) {
res.json(docs);
});
});
});
Notice that I use findOne in order to get an object instead of an array, and from this object I get the seekingPets array. Of course you can improve this code, eliminate some dispensable variables like seeking_pets and handle the database error if the id is not found.

set array object from other array object

I have 2 arrays array1, array2.
array 1 objects: userName, userId
array 2 objects: userId, msg
i want to get array3: userId, userName, msg
Array1:
[{ userName: 'vimal', userId: 789 },
{ userName: 'kabilan', userId: 456 },
{ userName: 'yathavan', userId: 123 }]
Array2:
[ { userId: '123', msg: 'hi' },
{ userId: '789', msg: 'yth' },
{ userId: '789', msg: 'hu' } ]
i want to compare 2 arrays and get output like this.
Array3:
[ { userId: '123', userName: 'yathavan', msg: 'hi' },
{ userId: '789', userName: 'vimal', msg: 'yth' },
{ userId: '789', userName: 'vimal', msg: 'hu' } ]
An off-the-shelf, "functional programming" approach:
var users = [{ userName: 'vimal', userId: 789 },
{ userName: 'kabilan', userId: 456 },
{ userName: 'yathavan', userId: 123 }]
var messages = [ { userId: '123', msg: 'hi' },
{ userId: '789', msg: 'yth' },
{ userId: '789', msg: 'hu' } ]
var user_message_list = [];
messages.map(function (message) {
return users.
filter(function (user) {
return user.userId == message.userId
}).
map(function (user) {
return {
"userId": user.userId,
"userName": user.userName,
"msg": message.msg
}
})
})
.forEach(function (item) { // eliminate nested layers
user_message_list.push.apply(user_message_list, item)
})
JSFiddle Functional
Explanation:
Two arrays of objects, one a list of users, and the other a list of messages by some of those users.
You're wanting to flesh out a report of the messages showing the usernames, so start with the messages array and loop through it. Now, for each message loop through the users list and retrieve the corresponding username.
The "loopy" approach is like this:
var messages_users = []
var message_user = {}
for (ii=0; ii < messages.length; ii++) {
message_user = {
"userId": messages[ii].userId,
"msg": messages[ii].msg
}
for (iii=0; iii < users.length; iii++) {
if ( messages[ii].userId == users[iii].userId ) {
message_user.userName = users[iii].userName
}
}
messages_users.push(message_user)
}
JSFiddle Loopy
Alternatively, using Functional Programming concepts, start by maping a function to each item in the messages array. That function takes the users array and filters it to find the corresponding user object for the current message, and map on that result to combine the current message information with the filtered user result. At this point you have an object wrapped in an array, since the map and filter methods return arrays. Therefore, the final operation is to loop using the forEach method to remove the extra array layer. Some JavaScript libraries have a concatAll or better yet concatMap method that hides that extra loop. In that case you'd have something like this:
var user_message_list = messages.
concatMap(function (message) {
return users.
filter(function (user) {
return user.userId == message.userId
}).
map(function (user) {
return {
"userId": user.userId,
"userName": user.userName,
"msg": message.msg
}
})
})
The benefit here is tighter coupling between the language nomenclature and the procedural concepts. For example: filter(... vs. for (i=0; ... if ( arr[i] ===.... Both constructs loop and select items based on criteria, hence filter.
More on Functional Programming in JavaScript
I'd make a users array indexed my the userid that contains the username;
var users = [];
for(var i=0; i<arr1.length; i++)
users[arr1[i].userId] = arr1[i].userName;
now make your output array, and go through the 2nd array. using the users array to insert;
var arr3 = [];
for(var i=0; i<arr2.length; i++)
arr3.push({userId:arr2[i].userId, userName:users[arr2[i].userId], msg:arr2[i].msg});
You would do something like this, if userId value was not a String in ary2:
var ary1 =[{userName:'vimal', userId:789}, {userName:'kabilan', userId:456}, {userName:'yathavan', userId:123}];
var ary2 = [{userId:123, msg:'hi'}, {userId:789, msg:'yth'}, {userId:789, msg:'hu'}];
function specialMerge(ar1, ar2){
var r = [];
for(var i=0,l=ar1.length; i<l; i++){
var p = ar1[i];
for(var n=0,c=ar2.length; n<c; n++){
var m = ar2[n];
if(p.userId === m.userId){
r.push({userId:p.userId, userName:p.userName, msg:m.msg});
}
}
}
return r;
}
var resultArrayOfObjects = specialMerge(ary1, ary2);

Mongodb: Can't append to array using string field name

i am trying to push inside a subarray using $push but got a Mongo error, and not able to get through this after considerable search on google, and findOneAndUpdate didn't worked out so i used find and update separately
{ [MongoError: can't append to array using string field name: to]
name: 'MongoError',
err: 'can\'t append to array using string field name: to',
code: 13048,
n: 0,
lastOp: { _bsontype: 'Timestamp', low_: 2, high_: 1418993115 },
Schema:
var NetworkSchema = new Schema({
UserID: {
type: Schema.Types.ObjectId,
ref: 'User'
},
NetworkList: [{
type: Schema.Types.ObjectId,
ref: 'User'
}],
NetworkRequest: [{
from: [{
type:Schema.Types.ObjectId,
ref: 'User'
}],
to: [{
type: Schema.Types.ObjectId,
ref: 'User'
}]
}]
});
Document:
{
"UserID" : ObjectId("549416c9cbe0e42c1adb42b5"),
"_id" : ObjectId("549416c9cbe0e42c1adb42b6"),
"NetworkRequest" : [
{
"from" : [],
"to" : []
}
],
"NetworkList" : [],
"__v" : 0
}
Controller:
exports.update = function(req,res) {
var network = req.network;
var query={'UserID':req.body.UserID};
var update = {$push:{'NetworkRequest.to': req.body.FriendID}};
Network.find(query,function(err){
if (err) {
console.log(err);
return err;
} else {
}
});
Network.update(query,update,{upsert:true},function(err,user){
console.log(user);
if (err) {
console.log(err);
return err;
} else {
console.log('User'+user);
}
});
};
Everything #cbass said in his answer is correct, but since you don't have a unique identifier in your NetworkRequest element to target, you need to do it by position:
var query = {'UserID': req.body.UserID};
var update = {$push:{'NetworkRequest.0.to': req.body.FriendID}};
Test.update(query, update, {upsert: true}, function(err, result) { ... });
'NetworkRequest.0.to' identifies the to field of the first element of the NetworkRequest array.
Your query var query={'UserID':req.body.UserID}; identifies the document you want to edit. Then you need another query to identify which object in the NetworkRequest array that the UserID should be pushed into. Something like below:
var query = {
'UserID':req.body.UserID,
'NetworkRequest._id': ObjectId(someNetworkRequestId)
};
Then use this update query containing $ which is the index of the object in the nested array(NetworkRequest)
var update = {
$push:{
'NetworkRequest.$.to': req.body.FriendID
}
};

Categories

Resources