Remove from array of objectId using update mongoDB - arrays

I have a model named courier, it has an array named order and orders has some ObjectIds of model order, how can I remove an element from orders array using update or something else ?
(for example removing an order with specific id)
Here is my model:
courier:
var courierSchema = new Schema({
name: { type: String },
orders:[{type:Schema.Types.ObjectId,ref:'order'}],
});
I tried this code but it fails :
courier.update({
name: 'Mahan'
}, {
$pull : {
orders: {
_id: order._id
}
}
}, (err, count, obj) => {
if(err) {
console.log(err);
return handleError(err, reply);
}
console.log(count);
});
Is there any way to do this not using find, remove and then save ?

This would be the most efficient format to achieve what you are trying to do
db.collection.update({<cond to identify document}, {$pull: {'orders': {'id': <id>}}} )

Related

How to compare 2 collections and fetch matched field data in mongodb?

I have a two collection as given below.
Collection 1: List
{
"_id" : ObjectId("5b618589d6edcc135c1aee0a"),
"name" : "ABC",
"special" : "Golmal",
"dim" : "2122",
}
Collection 2: Data
Document 1:
{
"_id" : ObjectId("5b6023d9589ff6bfb5dd75d7"),
"date" : "2018/08/13",
"special" : "Golmal",
}
Document 2:
{
"_id" : ObjectId("5b602447589ff6bfb5dd7606"),
"date" : "2018/08/13",
"special" : "Godman",
}
Here I want compare both the collection and if "special":"Golmal" of Collection 1 is present in Collection 2 documents I want to display/fetch all the related date from Collection 1 document.
I have tried aggregate & $lookup as below
getAvailableList(): Promise<any> {
return this._mongoUtility.Db
.then(db => {
let list= db.collection('list');
let data= db.collection('data');
list.aggregate([{$lookup:
{
from: data,
localField: "special",
foreignField: "special",
as: "matches"
}
},
{
$match: { "matches": { $ne: [{}] } }
}
]);
return Promise.resolve();
})
.catch(err => {
return Promise.reject({message: 'Not found', err: err});
});
}
But I'm getting null.
I have also tied by using .find & forEach as below
let abc = [];
list.find({}).toArray()
.then(arr => {
arr.forEach(obj1 => {
data.find({special: obj1.special}).toArray()
.then(secondArr => {
console.log(secondArr);
if (secondArr.length > 0) {
abc.push(obj1);
}
});
});
});
return Promise.resolve();
But no result. I need value in JSON format. As MongoDB dont have joins. Is there any ways to crack it?
Well, I changed my way of doing that query by not using forEach and its working for me.
I come across $in in find in mongoDB website. Here's the answer
return new Promise((res, rej) => {
this._mongoUtility.Db
.then(db => {
let list= db.collection('List');
let data= db.collection('Data');
data.find({}).toArray()
.then(arr => {
let maps = arr.map(data => data.special);
list.find({special: {$in: maps}}).toArray()
.then(secondArr => {
res(secondArr);
});
});
})
.catch(err => {
return rej({message: 'Not found', err: err});
});
});
As of MongoDB 3.2, joins become possible.
https://www.mongodb.com/blog/post/joins-and-other-aggregation-enhancements-coming-in-mongodb-3-2-part-1-of-3-introduction

Add array values into MongoDB where element is not in array

In MongoDB, this is the simplified structure of my account document:
{
"_id" : ObjectId("5a70a60ca7fbc476caea5e59"),
"templates" : [
{
"name" : "Password Reset",
"content" : "AAAAAAAA"
},
{
"name" : "Welcome Message",
"content" : "BBBBBB"
}
]
}
There's a similar default_templates collection
let accnt = await Account.findOne({ _id: req.account._id }, { templates: 1 });
let defaults = await DefaultTemplate.find({}).lean();
My goal is to find the missing templates under account and grab them from defaults. (a) I need to upsert templates if it doesn't exist in an account and (b) I don't want to update a template if it already exists in an account.
I've tried the following:
if (!accnt.templates || accnt.templates.length < defaults.length) {
const accountTemplates = _.filter(accnt.templates, 'name');
const templateNames = _.map(accountTemplates, 'name');
Account.update({ _id: req.account._id, 'templates.name' : { $nin: templateNames } },
{ '$push': { 'templates': { '$each' : defaults } } }, { 'upsert' : true },
function(err, result) {
Logger.error('error %o', err);
Logger.debug('result %o', result);
}
);
}
This succeeds at the upsert but it will enter all default templates even if there's a matching name in templateNames. I've verified that templateNames array is correct and I've also tried using $addToSet instead of $push, so I must not understand Mongo subdoc queries.
Any ideas on what I'm doing wrong?
Edit: I've gotten this to work by simply removing elements from the defaults array before updating, but I'd still like to know how this could be accomplished with Mongoose.
You can try with bulkWrite operation in mongodb
Account.bulkWrite(
req.body.accountTemplates.map((data) =>
({
updateOne: {
filter: { _id: req.account._id, 'templates.name' : { $ne: data.name } },
update: { $push: { templates: { $each : data } } },
upsert : true
}
})
)
})

$push to an array in MongoDB with mongoose doesn't work

errmsg: 'The field \'weight\' must be an array but is of type int in
document
My Schema:
weight: [{
type: Number
}]
and my post request:
app.post('/edit', function(req, res){
var update = { $push: {"weight": req.body.weight}};
User.findOneAndUpdate(conditions, update, options, function (err)
{
if (err)
{
console.log(err);
}
else
{
console.log('yep');
}
})
});
If there are multiple documents in the collection that match your conditions, you can update only suitable one by adding { weight: { $type: 4 } } to your conditions.
Otherwise your application's schema doesn't match data in the database.
This might work.
//Schema
weight: [Number]
http://mongoosejs.com/docs/schematypes.html
//Or this way too if pushing objects into array
//Schema
weight: [{
weight: {
type: Number
}
}]
//Then in API
var update = { $push: {"weight": { "weight": req.body.weight }}};

get array if array contains id mongoose

It is a tricky one. I thought I could use $in, but after querying, it wasn't where I was looking for.
This is my schema
var gameSchema = new mongoose.Schema({
state: {
type: String,
default: "invited"
},
finished: {
type: Boolean,
default: false
},
players: {
type: [{
type: mongoose.Schema.Types.ObjectId,
ref: 'users'
}],
required: true,
},
scores: [scoreSchema],
chat : [chatSchema]
});
The request I'm trying to make is the following, I send a user Id, if the players array contains this Id, return the other id (the array will always have length 2) in the array.
The context is that you can lookup against whom you have played before.
This is what I had, but "players" should be an array and it's not games that I want to return, so
exports.getFriends = function(id, cb){
gameSchema.find({ id: { "$in": "players"} }, function(err, games){
if(err){
return cb(err, null);
}
else{
return cb(null, games);
}
});
};
Can you try this?
exports.getFriends = function(id, cb){
gameSchema.find({ players: id }, function(err, games) {
if (err) {
return cb(err);
}
const players = games.map(game => game.players);
const mergedPlayers = [].concat.apply([], players);
const mappedPlayers = mergedPlayers.map(String); // convert ObjectIds to strings for comparisons.
const idString = String(id);
const filteredPlayers = mappedPlayers.filter(player => player !== idString);
const uniquePlayers = filteredPlayers.filter((player, index, arr) => arr.indexOf(player) === index);
return cb(null, uniquePlayers);
});
};
I'm operating under the assumption that you want an array of the unique player ids that are not the player id you passed in. I kept the vars split apart instead of chaining all of the array methods, in an attempt to improve readability.

Meteor Autoform, collection hooks - How to insert into user profile array after a collection insert?

I'm trying to insert into a user profile array after an autoform inserts into another collection (Meteor.users).
My simple schema array is set up like this -
(within the profile schema)
listings: {
type: [String],
optional: true
},
"listings.$.id": {
type: String,
optional: true
}
And this is my collection-hook method that should be inserting after listing insert.
//Add listing to user collection on submit
Listings.after.insert(function(userId, doc) {
console.log("STUFF");
Meteor.users.update({_id : userId},
{
$push :
{
'profile.listings.$.id' : this._id
}
}
In my eyes, this should work. The form inserts properly without the collection-hook, but now when I submit the form I get this error in my JS console:
Error: After filtering out keys not in the schema, your modifier is now empty(…)
The console.log("stuff") triggers, I see that in the console before the error.
Anybody have any ideas on how to do this?
EDIT - fixed a few things by switching it to :
Listings.after.insert(function(userId, doc) {
console.log("STUFF" + userId + ' ' + this._id);
Meteor.users.update({_id: userId },
{
$set :
{
"profile.listings.$.id" : this._id
}
}
)
});
Now I can't insert into the array because of the $ operator.
Assuming listings is just an array of objects with the id field, you could do:
listings: {
type: [Object],
optional: true
},
"listings.$.id": {
type: String,
optional: true
}
Listings.after.insert(function(userId, doc) {
var id = this._id;
Meteor.users.update({_id: userId }, {
$push : {
"profile.listings" : { id: id }
}
});
});
This changes your listings from an array of Strings to an array of objects - you can't have a property of id on a String. This then allows you to do $push on the profile.listings array with the object in question. If you're literally just storing an ID on listings though, you could simplify this further:
listings: {
type: [String],
optional: true
}
Listings.after.insert(function(userId, doc) {
var id = this._id;
Meteor.users.update({_id: userId }, {
$push : {
"profile.listings" : id
}
});
});
Maybe you're leaving some code out, but with your current schema you don't need anything but an array of strings - no need for an id property.

Resources