MongoDb Titlecase in Collection - mongodb

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 } }
);
});
});

Related

MongoDB update a specific nested field

Hi I am trying to update nested filed , but couldn't able to do so.
Here is the sample data,
[{
"_id": {
"$oid": "632ec4128f567511dcd80ed9"
},
"company_id": 1,
"contact_id": 1001,
"roles_to_be_accepted": {
"ROLE#04": {
"assigned_data": {
"assigned_3HFui": {
"is_idle": false,
"send_for_acceptance_date": 1664009233,
"action_date": ""
},
"assigned_b1J9t": {
"is_idle": false,
"send_for_acceptance_date": 1664009233,
"action_date": ""
}
}
},
"ROLE#02": {
"assigned_data": {
"assigned_uPJI1": {
"is_idle": false,
"send_for_acceptance_date": 1664009233,
"action_date": ""
}
}
}
}
}]
Now I want to update that is_idle field to true. I have tried in this way
let query = { contact_id: 1, company_id: 1001};
const db = this.client.db("dbname");
const col= db.collection("collection_name");
col.update(query, {
'$set': { "roles_to_be_accepted.assigned_data.is_idle": true }
});

MongoDB - convert double to string with aggregation

I am new to MongoDB and I am trying to convert double to string. I am not sure why my results are not as needed.
export function StoreSettings(req, res) {
var id = req.params.id;
var id = mongoose.Types.ObjectId(id);
Setting.aggregate([
{
$match: { restaurantID: id }
},
{
$addFields: {
"appTheme.appBanner": {
$concat: [
"/App/Carousel/",
{ $toString: "$appTheme.appBanner" },
".png"
]
}
}
}
])
.exec()
.then(data => {
return res.json(data);
})
.catch(err => res.json({ data: "Data Not Found", err }));
}
==OUTPUT==
{
"_id": "5e3379be06558d0c40d035ee",
"appTheme": {
"appBanner": "/App/Carousel/1.58078e+12.png"
}}
=== i NEED it to be like this: ====
{
"_id": "5e3379be06558d0c40d035ee",
"appTheme": {
"appBanner": "/App/Carousel/1580782209156.png"
}}
what am i doing wrong?
Thanks!
As $appTheme.appBanner :1580782209156 is a double in database, then using $toString would result in 1.58078e+12. You need to convert it into NumberLong() using $toLong & then convert it to string, Try below :
Setting.aggregate([
{
$match: { restaurantID: id }
},
{
$addFields: {
"appTheme.appBanner": {
$concat: [
"/App/Carousel/",
{ $toString: { $toLong: "$appTheme.appBanner" } },
".png"
]
}
}
}
])
Test : MongoDB-Playground

MongoDB push to array with predefined index

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);
});

Change capital letters in mongo to camel casing?

I have a collection named User, which contains the the fields firstName and secondName. But the data is in capital letters.
{
firstName: 'FIDO',
secondName: 'JOHN',
...
}
I wanted to know whether it is possible to make the field to camel case.
{
firstName: 'Fido',
secondName: 'John',
...
}
You can use a helper function to get your desired answer.
function titleCase(str) {
return str.toLowerCase().split(' ').map(function(word) {
return word.replace(word[0], word[0].toUpperCase());
}).join(' ');
}
db.User.find().forEach(function(doc){
db.User.update(
{ "_id": doc._id },
{ "$set": { "firstName": titleCase(doc.firstName) } }
);
});
Run an update operation with aggregate pipeline as follows:
const titleCase = key => ({
$concat: [
{ $toUpper: { $substrCP: [`$${key}`,0,1] } },
{ $toLower: {
$substrCP: [
`$${key}`,
1,
{ $subtract: [ { $strLenCP: `$${key}` }, 1 ] }
]
} }
]
});
db.User.updateMany(
{},
[
{ $set: {
firstName: titleCase('firstName'),
secondName: titleCase('secondName')
} }
]
)
Mongo Playground

How to calculate ratios for an additive attribute with mongodb?

Using the sample mongodb aggregation collection (http://media.mongodb.org/zips.json), I would like to output the population share of every city in California.
In SQL, it could look like this:
SELECT city, population/SUM(population) as poppct
FROM (
SELECT city, SUM(population) as population
FROM zipcodes
WHERE state='CA'
GROUP BY city
) agg group by state;
This can be done using mongodb map/reduce:
db.runCommand({
mapreduce : "zipcodes"
, out : { inline : 1}
, query : {state: "CA"}
, map : function() {
emit(this.city, this.pop);
cache.totalpop = cache.totalpop || 0;
cache.totalpop += this.pop;
}
, reduce : function(key, values) {
var pop = 0;
values.forEach(function(value) {
if (value && typeof value == 'number' && value > 0) pop += value;
});
return pop;
}
, finalize: function(key, reduced) {
return reduced/cache.totalpop;
}
, scope: { cache: { } }
});
Can this be also achieved using the new aggregation framework (v2.2)? This would require some form of global scope, as in the map/reduce case.
Thanks.
Is this what you're after?
db.zipcodes.remove();
db.zipcodes.insert([
{ city:"birmingham", population:1500000, state:"AL" },
{ city:"London", population:10000, state:"ON" },
{ city:"New York", population:1000, state:"NY" },
{ city:"Denver", population:100, state:"CO" },
{ city:"Los Angeles", population:1000000, state:"CA" },
{ city:"San Francisco", population:2000000, state:"CA" },
]);
db.zipcodes.runCommand("aggregate", { pipeline: [
{ $match: { state: "CA" } }, // WHERE state='CA'
{ $group: {
_id: "$city", // GROUP BY city
population: { $sum: "$population" }, // SUM(population) as population
}},
]});
produces
{
"result" : [
{
"_id" : "San Francisco",
"population" : 2000000
},
{
"_id" : "Los Angeles",
"population" : 1000000
}
],
"ok" : 1
}
you could try:
db.zipcodes.group( { key: { state:1 } ,
reduce: function(curr, result) {
result.total += curr.pop;
result.city.push( { _id: curr.city, pop: curr.pop } ); },
initial: { total: 0, city:[] },
finalize: function (result) {
for (var idx in result.city ) {
result.city[idx].ratio = result.city[idx].pop/result.total;
}
} } )