How can I updateMany mongodb - node.js

I have three chatrooms and I would like to update the document with MongoDB only if the user matches with the members.user_id. I don't understand, it's updating to all the member's devices the same token. If someone has an idea?
It's my Node.js code :
const _id = req.params._id;
const token = req.body.token;
const user_id = req.body.user_id;
try{
const updateDevice = await ChatRoom.updateMany(
{"members.$[].user_id" : user_id},
{$set:{"members.$[].devices":token}})
res.send(updateDevice)
}catch(err){console.log(err)}
const mongoose = require('mongoose');
const chatRoom_schema = new mongoose.Schema({
name:{
type:Array,
name:String,
},
members:{
user_id:String,
name:String,
devices:String,
type:Array,
required:true
},
lastMessage:{
content:String,
createdAt:Date,
type:Array,
send_by:String,
readBy:Array
}
}, {
collection: "chatRoom"
})
module.exports = chatRoom = mongoose.model("ChatRoom", chatRoom_schema);

According to the schema there is a object of members not an array of object so to access the specific elements only dot(.) operator is used. If you want to access any element from the array or want to update the specific object value in array of object then $ is used.
Try this query to solve the problem
ChatRoom.updateMany(
{"members.user_id" : user_id},
{$set:{"members.devices":token}})
Let there be a record like
members=[
{
"user_id":"1",
"name":"DD",
"type":"ADMIN"
},
{
"user_id":"2",
"name":"HH",
"type":"CUSTOMER"
}
]
To update the type of user_id(2) from CUSTOMER to ADMIN then $ operator can be useful.
The query can be
ChatRoom.updateMany({"members.$.user_id" : 2},
{$set:{"members.$.type":"ADMIN"}})

Related

Populate array inside object in mongoose

I have a company model which looks like this:
const mongoose = require("mongoose");
const Schema = mongoose.Schema;
const CompanySchema = new Schema(
{
companyName: String,
settings: {
type: {
priceVisible: Boolean,
allowPickupAddressAddition: Boolean,
paymentMethodsAvailable: [
{ type: Schema.Types.ObjectId, ref: "PaymentMethod" },
],
},
},
}
);
const Company = mongoose.model("Company", CompanySchema);
module.exports = Company;
And I want to populate the values store in paymentMethodsAvailable array. Here is relevant controller code:
const company = await Company.findOne({ _id: id }).populate([
{
path: "settings",
populate: [{path: "paymentMethodsAvailable"}]
},
]);
But this doesn't work as expected. I can see that it might be trying to populate settings object, and fails there. Is there a way in which I can tell mongoose to populate settings.paymentMethodsAvailable ?
Try this
const company = await Company.findOne({ _id: id }).populate(
"settings.paymentMethodsAvailable"
);
You can find more examples in the documentation. I was using this section as a reference https://mongoosejs.com/docs/populate.html#populating-maps
Mongoose provides clear syntax
The following code will work fine
const company = await Company.findOne({ _id: id }).populate(
"settings.type.paymentMethodsAvailable"
);
if(!company) {
// if there is no company with that id
// this acully not error it's simply
// return `null` to tell you company not found.
res.status(404).send()
}
Also: you can go further and populate specific fields inside settings.paymentMethodsAvailable
const company = await Company.findOne({ _id: id }).populate(
"settings.type.paymentMethodsAvailable",
"field1 filed2 filed3"
);

mongoose#populate returns null in nested object inside array

I have a mongoDB database which is generated using a script that uses only the node.js mongoDB driver without mongoose. Later on, in the application, I want to use mongoose to load a document and have a reference be populated automatically; however, this only ever returns null.
Imagine a task which contains sub-items which each have a title and an assigned person. The assigned person, in this case, is the reference I want to have populated, so the reference lives in an object inside an array in the task schema.
The following code (requiring npm install mongodb mongoose) reproduces the problem (watch out, it destroys a local database named test if you have one already):
const mongodb = require('mongodb');
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
(async () => {
// Step 1: Insert data. This is done using the mongodb driver without mongoose.
const db = await mongodb.MongoClient.connect('mongodb://localhost/test');
await db.dropDatabase();
await db.collection('persons').insertOne({ name: 'Joe' });
const joe = await db.collection('persons').findOne({ name: 'Joe' });
await db.collection('tasks').insertOne({ items: [{ title: 'Test', person: joe._id }] });
await db.close();
// ================
// Step 2: Create the schemas and models.
const PersonSchema = new Schema({
name: String,
});
const Person = mongoose.model('Person', PersonSchema);
const TaskSchema = new Schema({
items: [{
title: String,
person: { type: Schema.Types.ObjectId, ref: 'Person' },
}],
});
const Task = mongoose.model('Task', TaskSchema);
// ================
// Step 3: Try to query the task and have it populated.
mongoose.connect('mongodb://localhost/test');
mongoose.Promise = Promise;
const myTask = await Task.findOne({}).populate('items.person');
// :-( Unfortunately this prints only
// { _id: "594283a5957e327d4896d135", items: [ { title: 'Test', person: null } ] }
console.log(JSON.stringify(myTask, null, 4));
mongoose.connection.close();
})();
The expected output would be
{ _id: "594283a5957e327d4896d135", items: [ { title: 'Test', person: { _id: "594283a5957e327d4896d134", name: "Joe" } } ] }
I have verified that the two _ids actually match using mongo shell:
> db.persons.find({})
{ "_id" : ObjectId("594283a5957e327d4896d134"), "name" : "Joe" }
> db.tasks.find({})
{ "_id" : ObjectId("594283a5957e327d4896d135"), "items" : [ { "title" : "Test", "person" : ObjectId("594283a5957e327d4896d134") } ] }
What am I doing wrong when attempting to populate person? I am using mongoose 4.10.6 and mongodb 2.2.28.
The answer to this problem lies in the fact that the collection name mongoose automatically infers from the model Person is people and not persons.
The problem can be solved either by writing to the people collection in the first part or by forcing mongoose to use the collection name persons:
const Person = mongoose.model('Person', PersonSchema, 'persons');
mongoose plans to remove pluralization in the collection name anyway, see #1350 on Github.

$match _id is not working in Mongoose aggregate function

As my title, $match _id is not working in Mongoose aggregate function.
Could somebody please help me?
Is this related to mongoose version?
I use 4.9.2.
I need to use aggregate because I will group by the result after processing the $match.
I already saw posts before, but manually casting didn't work for me!
Here is my schema:
var mongoose = require("mongoose");
var moment = require("moment");
var Schema = mongoose.Schema;
var AvgDailyCharging = new Schema({
_id : {
date: Date,
storeID: {
type: Schema.Types.ObjectId,
ref: 'store'
}
},
chargers: [{
transmitterID: {
type: Schema.Types.ObjectId,
ref: 'device'
},
minutes: Number
}],
});
mongoose.model('AvgDailyCharging', AvgDailyCharging);
And here is the query:
var Mongoose = require('mongoose');
var Model = require('../db/model');
var Query = require('../db/query');
var RESULT_LIMIT = 2000; // Limit the return data size
exports.getAvgDailyCharging = function(req, res) {
var id = new Mongoose.Types.ObjectId("58b43fdf0fd53910121ca6f4");
var query = new Query("AvgDailyCharging");
query.aggregate([
{
$match: {
"_id.storeID": id, //HELP!!!!!
"_id.date": { //match only by this works fine.
$gte: new Date(req.params.startTime),
$lt: new Date(req.params.endTime)
}
}
}
]).exec(function(error, data) {
if (error) {
res.send({result:'ERROR', message: error});
} else {
res.send(data);
}
});
}
Please help me!!!! I was stuck for several hours! Q_Q
When I was testing in mongoose version 4.4.4, both type casting and string didn't work. However, after I update it to the version 4.9.2, type casting is no needed, and directly using a string in $match _id works!
Update:2017-03-31
I think another problem in my scenario is my schema definition. Since this collection, say A, is created from another one, say B, using $group: { _id: { storeID: "$storeID" } } where the storeID field in collection B is of type ObjectId, then in collection A I find out that _id.store is actually a String not an ObjectId, so the best way is to change the schema I mentioned in the question to:
var AvgDailyCharging = new Schema({
_id : {
date: Date,
storeID: String
},
chargers: [{
transmitterID: {
type: Schema.Types.ObjectId,
ref: 'device'
},
minutes: Number
}],
});

Mongoose populate not returning results

I am trying to use populate to return results that are ref to the Stamp model, under the users array of stamps but for some reason it does not return any results when I see in the database a list of stamp ids in the stamps array...
Here is my code:
var selectQuery = "_id name";
var populateQuery = [{path:'stamps', select: selectQuery, model: 'Stamp', }];
User.findOne({_id: userId}).populate(populateQuery).sort({date: -1}).skip(count).limit(100).exec(function(err, results) {
if(err) {
Here is the User Schema
var mongoose = require('mongoose'),
Schema = mongoose.Schema,
ObjectId = mongoose.Schema.Types.ObjectId,
var Stamp = require('../models/stamp.js');
var User = new Schema({
name: { type: String},
stamps: [{ type: ObjectId, ref: 'Stamp' }],
The "query" form of populate doesn't take an array as argument, but an object:
// `model` can be left out as Mongoose will look that up in the schema
var populateQuery = { path : 'stamps', select : selectQuery };

How to set ObjectId as a data type in mongoose

Using node.js, mongodb on mongoHQ and mongoose. I'm setting a schema for Categories. I would like to use the document ObjectId as my categoryId.
var mongoose = require('mongoose');
var Schema = mongoose.Schema,
ObjectId = Schema.ObjectId;
var Schema_Category = new Schema({
categoryId : ObjectId,
title : String,
sortIndex : String
});
I then run
var Category = mongoose.model('Schema_Category');
var category = new Category();
category.title = "Bicycles";
category.sortIndex = "3";
category.save(function(err) {
if (err) { throw err; }
console.log('saved');
mongoose.disconnect();
});
Notice that I don't provide a value for categoryId. I assumed mongoose will use the schema to generate it but the document has the usual "_id" and not "categoryId". What am I doing wrong?
Unlike traditional RBDMs, mongoDB doesn't allow you to define any random field as the primary key, the _id field MUST exist for all standard documents.
For this reason, it doesn't make sense to create a separate uuid field.
In mongoose, the ObjectId type is used not to create a new uuid, rather it is mostly used to reference other documents.
Here is an example:
var mongoose = require('mongoose');
var Schema = mongoose.Schema,
ObjectId = Schema.ObjectId;
var Schema_Product = new Schema({
categoryId : ObjectId, // a product references a category _id with type ObjectId
title : String,
price : Number
});
As you can see, it wouldn't make much sense to populate categoryId with a ObjectId.
However, if you do want a nicely named uuid field, mongoose provides virtual properties that allow you to proxy (reference) a field.
Check it out:
var mongoose = require('mongoose');
var Schema = mongoose.Schema,
ObjectId = Schema.ObjectId;
var Schema_Category = new Schema({
title : String,
sortIndex : String
});
Schema_Category.virtual('categoryId').get(function() {
return this._id;
});
So now, whenever you call category.categoryId, mongoose just returns the _id instead.
You can also create a "set" method so that you can set virtual properties, check out this link
for more info
I was looking for a different answer for the question title, so maybe other people will be too.
To set type as an ObjectId (so you may reference author as the author of book, for example), you may do like:
const Book = mongoose.model('Book', {
author: {
type: mongoose.Schema.Types.ObjectId, // here you set the author ID
// from the Author colection,
// so you can reference it
required: true
},
title: {
type: String,
required: true
}
});
My solution on using ObjectId
// usermodel.js
const mongoose = require('mongoose')
const Schema = mongoose.Schema
const ObjectId = Schema.Types.ObjectId
let UserSchema = new Schema({
username: {
type: String
},
events: [{
type: ObjectId,
ref: 'Event' // Reference to some EventSchema
}]
})
UserSchema.set('autoIndex', true)
module.exports = mongoose.model('User', UserSchema)
Using mongoose's populate method
// controller.js
const mongoose = require('mongoose')
const User = require('./usermodel.js')
let query = User.findOne({ name: "Person" })
query.exec((err, user) => {
if (err) {
console.log(err)
}
user.events = events
// user.events is now an array of events
})
The solution provided by #dex worked for me. But I want to add something else that also worked for me: Use
let UserSchema = new Schema({
username: {
type: String
},
events: [{
type: ObjectId,
ref: 'Event' // Reference to some EventSchema
}]
})
if what you want to create is an Array reference. But if what you want is an Object reference, which is what I think you might be looking for anyway, remove the brackets from the value prop, like this:
let UserSchema = new Schema({
username: {
type: String
},
events: {
type: ObjectId,
ref: 'Event' // Reference to some EventSchema
}
})
Look at the 2 snippets well. In the second case, the value prop of key events does not have brackets over the object def.
You can directly define the ObjectId
var Schema = new mongoose.Schema({
categoryId : mongoose.Schema.Types.ObjectId,
title : String,
sortIndex : String
})
Note: You need to import the mongoose module
Another possible way is to transform your _id to something you like.
Here's an example with a Page-Document that I implemented for a project:
interface PageAttrs {
label: string
// ...
}
const pageSchema = new mongoose.Schema<PageDoc>(
{
label: {
type: String,
required: true
}
// ...
},
{
toJSON: {
transform(doc, ret) {
// modify ret directly
ret.id = ret._id
delete ret._id
}
}
}
)
pageSchema.statics.build = (attrs: PageAttrs) => {
return new Page({
label: attrs.label,
// ...
})
}
const Page = mongoose.model<PageDoc, PageModel>('Page', pageSchema)
Now you can directly access the property 'id', e.g. in a unit test like so:
it('implements optimistic concurrency', async () => {
const page = Page.build({
label: 'Root Page'
// ...
})
await page.save()
const firstInstance = await Page.findById(page.id)
const secondInstance = await Page.findById(page.id)
firstInstance!.set({ label: 'Main Page' })
secondInstance!.set({ label: 'Home Page' })
await firstInstance!.save()
try {
await secondInstance!.save()
} catch (err) {
console.error('Error:', err)
return
}
throw new Error('Should not reach this point')
})

Resources