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
Related
I have the following DB structure :
{
"uploadedAt": "2021-09-22T22:09:12.133Z",
"paidAt: "2021-09-30T22:09:12.133Z",
"amount": {
"currency": "EUR",
"expected": 70253,
"paid": 0
},
}
I would like to know how do I calculate the total amount that still need to be paid (expected - paid), and the average date between uploadedAt and paidAt. This for multiple records.
My function for getting the data is (the criteria should be updated to get this data).
const invoiceParams = new FindParams();
invoiceParams.criteria = { company: company._id }
const invoices = await this.findAll(invoiceParams);
FindAll function looks like:
async findAll(
params: FindParams,
ability?: Ability,
includeDeleted: boolean = false,
): Promise<Entity[]> {
let queryCriteria: Criteria = params.criteria;
let query: DocumentQuery<Entity[], Entity> = null;
if (!includeDeleted) {
queryCriteria = {
...queryCriteria,
deleted: { $ne: true },
};
}
try {
if (ability) {
ability.throwUnlessCan('read', this.entityModel.modelName);
queryCriteria = {
...toMongoQuery(ability, this.entityModel.modelName),
...queryCriteria,
};
}
query = this.entityModel.find(queryCriteria);
if (params.populate) {
query = query.populate(params.populate);
}
if (params.sort) {
query = query.sort(params.sort);
}
if (params.select) {
query = query.select(params.select);
}
return query.exec();
} catch (error) {
if (error instanceof ForbiddenError) {
throw new ForbiddenException(error.message);
}
throw error;
}
}
Update:
const paymentTime = await this.invoiceModel.aggregate([
{
$group: {
_id: "$account",
averageSpread: { $avg: { $subtract: ["$paidAt", "$uploadedAt"] } },
count: { $sum: 1 }
}
}
]);
Try this aggregation pipeline:
db.invoiceParams.aggregate([
{
$set: {
expectedPaid: { $subtract: ["$amount.expected", "$amount.paid"] },
averageDate: { $toDate: { $avg: [{ $toLong: "$uploadedAt" }, { $toLong: "$paidAt" }] } }
}
}
])
I have a schema.
const placeSchema = new Schema({
description: {
fr: String,
en: String,
},
comment: {
fr: String,
en: String,
},
...
...
});
const Place= mongoose.model('Place', placeSchema);
module.exports = Place;
If I want to get only 'en' value I am currently using
await Place.find({}, '-description.fr -comment.fr ...')
If the number of similar fields increases so does the length of the query. Is there a way to select all the similar fields like maybe $field.fr?
Technically yes there is a way. using $objectToArray and doing some structure manipulation.
It would look something like this:
db.collection.aggregate([
{
$match: {} //match your document.
},
{
$addFields: {
rootArr: {
$objectToArray: "$$ROOT"
}
}
},
{
$unwind: "$rootArr"
},
{
$match: {
"rootArr.v.en": {
$exists: true
}
}
},
{
$group: {
_id: "$_id",
data: {
$push: {
k: "$rootArr.k",
v: "$rootArr.v.en"
}
}
}
},
{
$replaceRoot: {
newRoot: {
$arrayToObject: "$data"
}
}
}
])
Mongo Playground
It's a little "hacky" thought, how strict are your schema needs?
Have you considered building it under the following structure?:
const placeSchema = new Schema({
data: [
{
lang: String,
description: String,
comment: String,
...
}
]
});
The following aggregation will check all the top level fields for a subfield en. If it's truthy (should work if you strictly have string values for the language properties), the subfield will be { field: { en: fieldValue.en } } otherwise it will be { field: fieldValue }
db.collection.aggregate([
{
$replaceRoot: {
newRoot: {
$arrayToObject: {
$map: {
input: { $objectToArray: "$$ROOT" },
in: {
k: "$$this.k",
v: {
$cond: [
"$$this.v.en", // works for string values, otherwise you will have to check more explicitly
{
en: "$$this.v.en"
},
"$$this.v"
]
}
}
}
}
}
}
}
])
Mongo Playground
Both the answers above are exactly what the question was looking for. This might be a more 'hacky' way of doing things.
First create a function that generates the query string '-description.fr -comment.fr ...'
let select = '';
const selectLanguage = (fields, lang) => {
switch (true) {
case lang === 'fr':
fields.forEach(field => {
select= `${select} -${field}.en `;
});
break;
case lang === 'en':
fields.forEach(field => {
select = `${select} -${field}.fr `;
});
break;
default:
break;
}
return select;
}
This generates a string like ' -fieldName1.fr -fieldName2.fr ..' for english and and ' -fieldName1.en ..' for french. Then we can use this statement in the query above.
const select = selectLanguage(['description', 'comment', ..], 'en')
await Place.find({}, select) //await Place.find({}, ' -description.fr -comment.fr ..')
This question already has answers here:
Update nested subdocuments in MongoDB with arrayFilters
(2 answers)
Closed 3 years ago.
My collection is like this: https://mongoplayground.net/p/91InBXrUq7R
With this query I can update replies.likes
db.getCollection("posts").updateOne(
{
"_id": ObjectId("5da832caeb173112348e509b"), //posts._id
"comments.replies._id":ObjectId("5db6a88f7c6cfb0d0c2b689b"),//replies._id
},
{ "$push": { "comments.$[outer].replies.$[inner].likes": "10000012" } },
{
"arrayFilters": [
{ "outer._id": ObjectId("5db06e11d0987d0aa2cd5593") },//comments._id
{ "inner._id": ObjectId("5db6a88f7c6cfb0d0c2b689b") }//replies._id
]
}
)
But when I code using mongoose, express, collection not update
//Like Reply toggle
router.post("/toggleLikeReply", function(req, res, next) {
var id_post = req.body.id_post;
var id_comment = req.body.id_comment;
var id_reply = req.body.id_reply;
var id_user = req.user._id;
console.log("id_post: "+id_post+" id_comment: "+id_comment+" id_reply: "+id_reply+" id_user: "+id_user);
//todo
Post.aggregate([
{ $match: {_id: ObjectId(id_post),"comments._id": ObjectId(id_comment)}},
{ $unwind: "$comments"},
{ $match: { "comments._id": ObjectId(id_comment)}},
{ $project: {"replies": "$comments.replies", _id: 0}},
{ $match: { "replies._id": ObjectId(id_reply)}},
{ $project: {"likes": "$replies.likes", _id: 0}},
]).exec((err, users_liked) => {
var index = users_liked[0].likes[0].indexOf(id_user);
console.log(users_liked[0].likes[0]);
//todo
if (index == -1) {
const updatePost = async () => {
try {
await Post.updateOne({
_id: ObjectId(req.body.id_post),
"comments.replies._id": ObjectId(req.body.id_reply)},
{ $push: {"comments.$[outer].replies.$[inner].likes": ObjectId(req.user._id)} },
{
"arrayFilters": [
{ "outer._id": ObjectId(req.body.id_comment) },
{ "inner._id": ObjectId(req.body.id_reply) }
]
}
);
} catch (error) {
console.log("error", error);
}
};
updatePost().then(function(data) {res.send({ like: true, success: true})});
}else{
const updatePost = async () => {
try {
await Post.updateOne({
_id: ObjectId(req.body.id_post),
"comments.replies._id": ObjectId(req.body.id_reply)},
{ $pull: {"comments.$[outer].replies.$[inner].likes": ObjectId(req.user._id)} },
{
"arrayFilters": [
{ "outer._id": ObjectId(req.body.id_comment) },
{ "inner._id": ObjectId(req.body.id_reply) }
]
}
);
} catch (error) {
console.log("💥", error);
}
};
updatePost().then(function(data) {res.send({ like: false, success: true})});
}
})
});
I logged the all the id is come and the same as I did with mongo query directly .
id_post: 5da832caeb173112348e509b
id_comment: 5db06e11d0987d0aa2cd5593
id_reply: 5db6a88f7c6cfb0d0c2b689b
id_user: 5da85558886aee13e4e7f044
What is wrong with my code using mongoose and express?
Try This Query
var mongoose = require('mongoose');
const Schema = mongoose.Schema
const ObjectId = Schema.Types.ObjectId
const updatePost = async () => {
try {
await Post.updateOne({
_id: ObjectId(req.body.id_post),
"comments.replies._id": ObjectId(req.body.id_reply)},
{ $push: {"comments.$[outer].replies.$[inner].likes": req.user._id} },
{
"arrayFilters": [
{ "outer._id": ObjectId(req.body.id_comment) },
{ "inner._id": ObjectId(req.body.id_reply) }
]
}
);
} catch (error) {
console.log("error", error);
}
};
updatePost().then(function(data) {res.send({ like: true, success: true})});
I'm trying to group all this object in one, the idea is to combine all the object.
My function know is like this:
app.get('/stats/:id(\\d+)/weapon/:weapon', function(req, res, next) {
db.collection('stats').aggregate( [
{ $match: { _id: parseInt(req.params.id, 10) } },
{ $unwind: "$session" },
{ $addFields: { weapon: { $objectToArray: '$session.weapons.' + sanitize(req.params.weapon) }, _id: false } },
{ $addFields: { weapon: { $arrayToObject: "$weapon" } } },
{ $project: { weapon: "$weapon", _id: false } }
], function(err, doc) {
if( !err ) {
res.json(doc);
}
else {
console.log(err);
res.end();
}
});
});
and return something like this:
[
{
"weapon":{
"shots":30,
"hitbox":{
"head":7,
"chest":4
},
"kills":4,
"dmg":590
}
},
{
"weapon":{
"shots":46,
"kills":4,
"hitbox":{
"head":3,
"chest":4,
"stomach":3,
"left_leg":2
},
"hs":3,
"dmg":479
}
},
{
"weapon":{
"shots":30,
"hitbox":{
"head":7,
"chest":4
},
"kills":4,
"dmg":590
}
}
]
My idea is to return only one instance of weapon with the sum key by key.
I already try $group and concat array but i can't get the result that i want...
I want like this:
[
{
"weapon":{
"shots":160,
"hitbox":{
"head":17,
"chest":12,
"stomach":3,
"left_leg":2
},
"kills":12,
"hs":3,
"dmg":1659
}
}
]
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