In my collection i need to change the firstname and lastname to be in Titlecase.since its in nested array i couldn't proceed.
db.users.find()
{
"users" : {
"assigned" :[
{
"firstName" : "naveen",
"lastName" : "bala",
},
{
"firstName" : "SHAJU",
"lastName" : "HARI",
},
{
"firstName" : "PADMANESH",
"lastName" : "NC",
}
]
}
}
I need the result to be like
{
"firstName" : "Padmanesh",
"lastName" : "Nc",
}
Tried this code below
function titleCase(str) {
return str && str.toLowerCase().split(/\s/).map(function(word) {
return word && word.replace(word[0], word[0].toUpperCase());
}).join(' ');
}
db.users.find().forEach(function(doc){
db.users.updateOne(
{ "_id": doc._id },
{ "$set": { "firstName": titleCase(doc.firstName) } }
);
});
The most efficient way is to use updateMany(). You can see how the titleCase operators work here: https://mongoplayground.net/p/xdePfeBvIQ1
https://docs.mongodb.com/master/reference/method/db.collection.updateMany/index.html
This should do it for you, you can match using the first arg if needed.
Please double check the user schema is correct in your question. If its not this will need to be tweaked. It expects each user doc contains a users object with an assigned property.
db.users.updateMany({}, [{
$set: {
"users.assigned": {
$map: {
input: "$users.assigned",
in: {
firstName: {
$concat:[
{$toUpper: {$substrCP: ["$$this.firstName", 0, 1]}},
{$toLower: {$substrCP: ["$$this.firstName", 1, {$strLenCP: "$$this.firstName"}]}},
]
},
lastName: {
$concat:[
{$toUpper: {$substrCP: ["$$this.lastName", 0, 1]}},
{$toLower: {$substrCP: ["$$this.lastName", 1, {$strLenCP: "$$this.lastName"}]}},
]
}
}
}
}
}
}])
An alternative, to do it on the mongo shell :
var titleCase = function (str) {
return (
str &&
str
.toLowerCase()
.split(/\s/)
.map(function (word) {
return word && word.replace(word[0], word[0].toUpperCase());
})
.join(" ")
);
};
db.users.find().forEach(function (doc) {
var a = doc.users.assigned;
a.forEach(function (person, index) {
var setop = `users.assigned.` + index + `.firstName`;
var uppered = titleCase(person.firstName);
db.users.updateOne(
{ _id: doc._id, "users.assigned.firstName": person.firstName },
{ $set: { [setop]: uppered } }
);
});
});
I find my document from whole collection like this:
const account = await Account.findOne({ "buildings.gateways.devices.verificationCode": code })
const buildings = account.buildings
const gateways = buildings[0].gateways;
const devices = gateways[0].devices;
const device = _.filter(devices, d => d.verificationCode === code);
now I want to change one of the property "patientLastName" and then save the whole document. I am doing as below.
device.patientLastName = lastName;
const updated = await account.save();
This simply does not change anything. I have tried many solutions given but none of them working.
not sure if I can save parent document just like that?
I have few other calls where same code works but only change for this is that this is in my post call while working ones are in put call.
My Schema:
const accountSchema = new mongoose.Schema({
email: { type: String, unique: true, required: true },
password: { type: String, required: true },
userName: { type: String, unique: true, required: true },
companyName: { type: String, required: true },
apiKey: { type: String, unique: true, required: true },
apiCallCount: { type: Number, default: 0 },
solutionType: String,
parentCompany: String,
buildings:
[
new mongoose.Schema({
buildingName: String,
address: String,
suite: String,
floor: String,
timeZone: String,
gateways:
[
new mongoose.Schema({
gatewayName: String,
gatewayKey: { type: String, sparse: true },
suite: String,
devices: [
new mongoose.Schema({
serialNumber: { type: String, sparse: true },
area: String,
connectionStatus: Number,
gatewayKey: String,
applicationNumber: Number,
firmwareVersion: String,
needsAttention: Boolean,
verificationCode: String,
patientRiskStatus: String,
patientFirstName: String,
patientLastName: String
}, { timestamps: true })
]
}, { timestamps: true })
]
}, { timestamps: true })
]
}, { timestamps: true });
Update:
I am trying this:
it gives me error message -
"message": "Converting circular structure to JSON"
const updated = account.update(
{
"_id" : ObjectId(accountId),
"buildings.gateways.devices.verificationCode": code
},
{
"$set": {
"buildings.$.gateways.0.devices.0.patientFirstName": "name1",
"buildings.$.gateways.0.devices.0.patientLastName": "name2",
}
}
)
Your help is appreciated. Thanks
UPDATED -
complete call for your reference.
// Register User
loginRouter.post('/register', async (req, res, next) => {
try {
var { email, userName, password, firstName, lastName, role, deviceIds, code } = req.body;
console.log(req.body)
// checking if email or username already exist before registering.
const verifyEmail = await User.find({
$or: [
{ 'email': email },
{ 'userName': userName },
]
})
if (verifyEmail.length > 0) {
throw new BadRequestError('DuplicateEmailOrUserName', {
message: 'Email or Username already exists'
});
}
// finding accountId for verification code first
const account = await Account.findOne({ "buildings.gateways.devices.verificationCode": code })
//console.log(account)
if (account.length === 0) {
console.log("Invalid registration code")
throw new BadRequestError('InvalidVerificationCode', {
message: 'Invalid registration code'
});
}
var accountId = account ? account._id : null
const buildings = account.buildings
const gateways = buildings[0].gateways;
const devices = gateways[0].devices;
//console.log("devices", devices)
// finding deviceId to insert for user from that account
const device = _.filter(devices, d => d.verificationCode === code);
// console.log("device", device)
if (!deviceIds) {
deviceIds = device.map(item => item._id)
// console.log("deviceIds", deviceIds)
}
const hashedPassword = await hasher.hashPassword(password);
const newUser = new User({
accountId: accountId ? accountId : undefined,
userName: userName,
password: hashedPassword,
email: email,
firstName: firstName,
lastName: lastName,
role: role,
refreshToken: uuidv4(),
refreshTokenExpiryDate: moment().add(process.env.REFRESH_TOKEN_EXPIRY_IN_DAYS, 'days'),
deviceIds: deviceIds ? deviceIds : [],
isActive: true,
});
const newlySavedUser = await newUser.save();
const {
refreshToken,
refreshTokenExpiryDate,
password: pwd,
...userWithoutSensitiveInfo
} = newlySavedUser.toObject();
**// solutions by #SuleymanSah** <----
try {
let result = await Account.findByIdAndUpdate(
accountId,
{
$set: {
"buildings.$[building].gateways.$[gateway].devices.$[device].patientFirstName": "userName"
}
},
{
arrayFilters: [
{ "building._id": ObjectId("5d254bb179584ebcbb68b712") },
{ "gateway._id": ObjectId("5d254b64ba574040d9632ada") },
{ "device.verificationCode": "4144" }
],
new: true
}
);
if (!result) return res.status(404);
console.log(result)
//res.send(result);
} catch (err) {
console.log(err);
res.status(500).send("Something went wrong");
}
res.json(newlySavedUser);
next();
} catch (err) {
next(err);
}
});
Let me know if you need more information. Thanks
You can use the filtered positional operator $ for this.
Note that we also need to have the buildingId and gatewayId to make it work dynamically.
router.put("/account/:accountId/:buildingId/:gatewayId", async (req, res) => {
const { patientFirstName, verificationCode } = req.body;
try {
let result = await Account.findByIdAndUpdate(
req.params.accountId,
{
$set: {
"buildings.$[building].gateways.$[gateway].devices.$[device].patientFirstName": patientFirstName
}
},
{
arrayFilters: [
{ "building._id": req.params.buildingId },
{ "gateway._id": req.params.gatewayId },
{ "device.verificationCode": verificationCode }
],
new: true
}
);
if (!result) return res.status(404);
res.send(result);
} catch (err) {
console.log(err);
res.status(500).send("Something went wrong");
}
});
TEST
Let's have this document:
{
"_id" : ObjectId("5e0da052b4b3fe5188602e11"),
"apiCallCount" : 1,
"email" : "abc#def.net",
"password" : "123123",
"userName" : "username",
"companyName" : "companyName",
"apiKey" : "apiKey",
"solutionType" : "solutionType",
"parentCompany" : "parentCompany",
"buildings" : [
{
"_id" : ObjectId("5e0da052b4b3fe5188602e12"),
"buildingName" : "buildingName 1",
"address" : "address",
"suite" : "suite",
"floor" : "floor",
"timeZone" : "String",
"gateways" : [
{
"_id" : ObjectId("5e0da052b4b3fe5188602e13"),
"gatewayName" : "gatewayName 1",
"gatewayKey" : "gatewayKey",
"suite" : "suite",
"devices" : [
{
"_id" : ObjectId("5e0da052b4b3fe5188602e15"),
"serialNumber" : "serialNumber 1",
"area" : "area",
"connectionStatus" : 0,
"gatewayKey" : "gatewayKey",
"applicationNumber" : 11,
"firmwareVersion" : "firmwareVersion",
"needsAttention" : true,
"verificationCode" : "123456",
"patientRiskStatus" : "patientRiskStatus",
"patientFirstName" : "patientFirstName",
"patientLastName" : "patientLastName",
"createdAt" : ISODate("2020-01-02T10:48:34.287+03:00"),
"updatedAt" : ISODate("2020-01-02T10:48:34.287+03:00")
},
{
"_id" : ObjectId("5e0da052b4b3fe5188602e14"),
"serialNumber" : "serialNumber 2",
"area" : "area",
"connectionStatus" : 0,
"gatewayKey" : "gatewayKey",
"applicationNumber" : 22,
"firmwareVersion" : "firmwareVersion",
"needsAttention" : true,
"verificationCode" : "987654",
"patientRiskStatus" : "patientRiskStatus",
"patientFirstName" : "patientFirstName",
"patientLastName" : "patientLastName",
"createdAt" : ISODate("2020-01-02T10:48:34.288+03:00"),
"updatedAt" : ISODate("2020-01-02T10:48:34.288+03:00")
}
],
"createdAt" : ISODate("2020-01-02T10:48:34.288+03:00"),
"updatedAt" : ISODate("2020-01-02T10:48:34.288+03:00")
}
],
"createdAt" : ISODate("2020-01-02T10:48:34.288+03:00"),
"updatedAt" : ISODate("2020-01-02T10:48:34.288+03:00")
}
],
"createdAt" : ISODate("2020-01-02T10:48:34.289+03:00"),
"updatedAt" : ISODate("2020-01-02T10:48:34.289+03:00"),
"__v" : 0
}
To update the device patientFirstName with verificationCode 123456, we need to send a PUT request to the url http://..../account/5e0da052b4b3fe5188602e11/5e0da052b4b3fe5188602e12/5e0da052b4b3fe5188602e13
5e0da052b4b3fe5188602e11 is accountId.
5e0da052b4b3fe5188602e12 is buildingId.
5e0da052b4b3fe5188602e13 is gatewayId.
Request body:
{
"verificationCode": "123456",
"patientFirstName": "UPDATED!!!"
}
Result will be like this:
{
"apiCallCount": 1,
"_id": "5e0da052b4b3fe5188602e11",
"email": "abc#def.net",
"password": "123123",
"userName": "username",
"companyName": "companyName",
"apiKey": "apiKey",
"solutionType": "solutionType",
"parentCompany": "parentCompany",
"buildings": [
{
"gateways": [
{
"devices": [
{
"_id": "5e0da052b4b3fe5188602e15",
"serialNumber": "serialNumber 1",
"area": "area",
"connectionStatus": 0,
"gatewayKey": "gatewayKey",
"applicationNumber": 11,
"firmwareVersion": "firmwareVersion",
"needsAttention": true,
"verificationCode": "123456",
"patientRiskStatus": "patientRiskStatus",
"patientFirstName": "UPDATED!!!",
"patientLastName": "patientLastName",
"createdAt": "2020-01-02T07:48:34.287Z",
"updatedAt": "2020-01-02T07:48:34.287Z"
},
{
"_id": "5e0da052b4b3fe5188602e14",
"serialNumber": "serialNumber 2",
"area": "area",
"connectionStatus": 0,
"gatewayKey": "gatewayKey",
"applicationNumber": 22,
"firmwareVersion": "firmwareVersion",
"needsAttention": true,
"verificationCode": "987654",
"patientRiskStatus": "patientRiskStatus",
"patientFirstName": "patientFirstName",
"patientLastName": "patientLastName",
"createdAt": "2020-01-02T07:48:34.288Z",
"updatedAt": "2020-01-02T07:48:34.288Z"
}
],
"_id": "5e0da052b4b3fe5188602e13",
"gatewayName": "gatewayName 1",
"gatewayKey": "gatewayKey",
"suite": "suite",
"createdAt": "2020-01-02T07:48:34.288Z",
"updatedAt": "2020-01-02T07:48:34.288Z"
}
],
"_id": "5e0da052b4b3fe5188602e12",
"buildingName": "buildingName 1",
"address": "address",
"suite": "suite",
"floor": "floor",
"timeZone": "String",
"createdAt": "2020-01-02T07:48:34.288Z",
"updatedAt": "2020-01-02T07:48:34.288Z"
}
],
"createdAt": "2020-01-02T07:48:34.289Z",
"updatedAt": "2020-01-02T09:10:25.200Z",
"__v": 0
}
And if you always want to update in the first building's in the first gateway, you may use this:
router.put("/account/:accountId", async (req, res) => {
const { patientFirstName, verificationCode } = req.body;
try {
let result = await Account.findByIdAndUpdate(
req.params.accountId,
{
$set: {
"buildings.0.gateways.0.devices.$[device].patientFirstName": patientFirstName
}
},
{
arrayFilters: [{ "device.verificationCode": verificationCode }],
new: true
}
);
if (!result) return res.status(404);
res.send(result);
} catch (err) {
console.log(err);
res.status(500).send("Something went wrong");
}
});
Now you need to send only the accountId in the url like this: http://../account/5e0da052b4b3fe5188602e11
How do I add an item to Mongoose, if I want to push it to an item of the array?
I want to push it to the document with predefined _id, to the 'productList' array with predefined 'id', to the 'items' array.
{
"_id" : ObjectId("5ba94316a48a4c828788bcc9"),
"productList" : [
{
"id" : 1,
"items" : [
{
"id" : 1,
"name" : "FLOSS 500",
}
]
}
]
}
I thought that it should be something like this, but it did not work:
Products.findOneAndUpdate({_id: req.body._id, productList: {id: req.body.id}}, {$push: {'items': req.body.product}})
You can try this with positional operator $. For search by nested array property use dot-separated syntax:
Products.findOneAndUpdate({
_id: req.body._id,
'productList.id': req.body.id
}, { $push: { 'productList.$.items': req.body.product } });
Full example:
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const Products = mongoose.model('Test', new Schema({
productList: []
}));
mongoose.connect("mongodb://localhost:27017/myapp");
let item = new Products({
"_id": mongoose.Types.ObjectId("5ba94316a48a4c828788bcc9"),
"productList": [
{
"id": 1,
"items": [
{
"id": 1,
"name": "FLOSS 500",
}
]
}
]
});
Products.deleteMany({}).then(() => {
return Products.create(item);
}).then(() => {
return Products.findOneAndUpdate({
_id: mongoose.Types.ObjectId("5ba94316a48a4c828788bcc9"),
'productList.id': 1
}, {
$push: {
'productList.$.items': {
"id": 2,
"name": "FLOSS 600",
}
}
});
}).then(() => {
return Products.find({
_id: mongoose.Types.ObjectId("5ba94316a48a4c828788bcc9"),
'productList.id': 1
});
}).then(data => {
console.log(data);
if (data) {
console.log(data[0].productList);
/* [{"id":1,"items":[{"id":1,"name":"FLOSS 500"},{"id":2,"name":"FLOSS 600"}]}] */
}
}).catch(err => {
console.error(err);
});
I have this schema
var postSchema = new Schema({
category: 'Home',
body: 'sometext',
comments: [{
nickName: 'Meron',
comment: 'Hello',
date: 'Feb 2 2017, 23:32',
commentUnders: [{
nickName: 'Storseses',
comment: 'It's our dat',
date: 'Feb 5 2017, 22:03'
}]
}]
});
How can I findByIdAndUpdate commentUnders? I already have in comments one comment which has id "5897518c0a913a842cc041cf". I tried this solve but doesnt work:
Post.findByIdAndUpdate( {'comments._id': '5897518c0a913a842cc041cf'},
{
$push: {
commentUnders: {
"comment" : "Так, что мне делать-то?",
"nickName" : "Great",
"date" : Date.now()
}
}
}, function(err, data) {
if (err) {console.log(err); }
console.log(data);
});
Use the $ positional operator with findOneAndUpdate() function instead of findByIdAndUpdate() to update the matching comments array:
Post.findOneAndUpdate(
{ "comments._id": "5897518c0a913a842cc041cf" },
{
"$push": {
"comments.$.commentUnders": {
"comment" : "Так, что мне делать-то?",
"nickName" : "Great",
"date" : Date.now(),
}
}
},
function(err, data) {
if (err) console.log(err);
console.log(data);
}
);
Situation:
kendo DataSource
var ordersDataSource = new kendo.data.DataSource({
type: "odata",
transport: {
read: {
url: "http://localhost/odata.svc/Orders?$expand=OrderDetails"
}
},
schema: {
type: "json",
data: function(response){
return response.value;
}
total: function(response){
return response['odata.count'];
}
},
serverPaging: true,
serverFiltering: true,
serverSorting: true
})
the json data read from the odata source is like:
{
odata.metadata: "xxxx",
odata.count: "5",
value: [
{
OrderId: 1,
OrderedDate: "2013-02-20",
OrderInfoA: "Info A",
OrderInfoB: "Info B"
OrderDetails: [
{
OrderDetailId: 6,
OrderDetailInfoC: "Info C",
OrderDetailInfoD: "Info D"
},
{
//Another OrderDetail's data
}
]
},
{
// Another Order's data
}
]
}
Question 1:
1.If I wanna define a "computed" property: OrderedDateRelative, which should be the number of days between Today(2013-02-25) and the Day the Order was Created(2013-02-20), Like: "5 days ago", HOW can i achieve this in the client side?
Answer to Question1: http://jsbin.com/ojomul/7/edit
Question 2 --UPDATE--
2.Every Order has its Nested Property OrderDetails, so is it possible to define a Calculated Field for the Nested OrderDetails Property? Like: OrderDetailInfoCAndD for each OrderDetail, and the value should be something like: OrderDetailInfoC + OrderDetailInfoD, which is "Info C Info D"?
Thanks,
dean
You can create a calculated field by specifying the model of the data source:
dataSource = new kendo.data.DataSource({
data: [
{ first: "John", last: "Doe" },
{ first: "Jane", last: "Doe" }
],
schema: {
model: {
// Calculated field
fullName: function() {
return this.get("first") + " " + this.get("last");
}
}
}
});
Here is a live demo: http://jsbin.com/ojomul/1/edit
Here is a way to use calculated field in Kendo Grid.
var crudServiceBaseUrl = "http://demos.telerik.com/kendo-ui/service",
dataSource = new kendo.data.DataSource({
transport: {
read: {
url: crudServiceBaseUrl + "/Products",
dataType: "jsonp"
}
},
pageSize: 20,
schema: {
model: {
total: function (item) {
return this.UnitPrice * this.UnitsInStock;
}
}
}
});
$("#grid").kendoGrid({
dataSource: dataSource,
pageable: true,
height: 550,
sortable: true,
filterable: true,
toolbar: ["create"],
columns: [
{ field: "UnitPrice", title: "Unit Price"},
{ field: "UnitsInStock", title: "Units In Stock", width: "120px" },
{ field: "total()", title: "Total" }]
});
Below an example to use it in a grid. It can then also sort the column.
$("#grid").kendoGrid({
dataSource: {
data: [
{ first: "John", last: "Doe" },
{ first: "Jane", last: "Doe" }
],
schema: {
model: {
// Calculated field
fullName: function() {
return this.first + " " + this.last;
},
fields: {
first: { type: "string" },
last: { type: "string" }
}
}
}
},
columns: [
{
// Trigger function of the Calculated field
field: "fullName()",
title: "Fullname"
},
{
field: "first",
title: "firstname"
}
]
});