How do I convert an array of objects / subdocuments to an array of strings in a MongoDB Aggregation pipeline? - mongodb

I have documents with the following structure
{
"_id":{"$oid":"..."},
"work_type": "ASSIGNMENT",
"materials": [
{
"driveFile": {
"driveFile": {
"id": "...",
"title": "filename",
"alternateLink": "https://drive.google.com/...",
"thumbnailUrl": "https://drive.google.com/..."
},
"shareMode": "STUDENT_COPY"
}
},
{
"youtubeVideo": {
"id": "tfdbq",
"alternateLink": "https://www.youtube.com/watch?v=..."
}
}
]
}
And I want to convert the objects in the array to JSON strings so that I can import the materials field as a repeated string column in SQL
Resulting in:
{
"_id":{"$oid":"..."},
"work_type": "ASSIGNMENT",
"materials": [
"{\"driveFile\":{\"driveFile\":{\"id\":\"...\",\"title\":\"filename\",\"alternateLink\":\"https://drive.google.com/...\",\"thumbnailUrl\":\"https://drive.google.com/...\"},\"shareMode\":\"COPY"}}",
"{\"youtubeVideo\":{\"id\":\"tfdbq\",\"alternateLink\":\"https://www.youtube.com/watch?v=...\"}}""
]
}

I am not sure is there any easy option do this, you can try $function operator starting from MongoDB v4.4, indirectly it is JavaScript code,
using aggregate()
{
$set: {
materials: {
$function: {
body: function(materials) {
let m = [];
for (let i = 0; i < materials.length; i++)
m.push(JSON.stringify(materials[i]));
return m;
},
args: ["$materials"],
lang: "js"
}
}
}
}
Playground
using update with aggregation pipeline starting from MongoDB v4.2
Playground

Related

How to duplicate a document in MongoDB and increment a value in another field?

I need to duplicate a document from my collection orders by filtering the field cd_order with value "7650614875".
Collection orders look like this:
{
...
"cd_order": "7650614875"
...
}
I will copy this document more than 50 times (I will change this value in the query), so I also need to change the value from this field cd_order from this copied document to another value, so I though by converting into int and then use the function inc to increment 1 and then converting back to string.
I tried the query below but only the copy ocorrued, the rest didn't work:
var copy = db.orders.findOne({ cd_order: "7650614875" }, { _id: 0 });
for (var i = 0; i< 3; i++){
db.orders.insert(copy);
{ $convert: { input: $string, to: "int" } }
{ $inc: { "cd_order" : 1 } }
{ $convert: { input: $int, to: "string" } }
}
How can I duplicate this document, increment 1 into field cd_order to not be the same as the previous one, and also to print all new cd_order at the end?
Print example:
cd_order: 7650614875, 7650614876, 7650614877, 76506148758, ...
You can use $range to generate an array of increment. $unwind the array to add to cd_order and $merge to insert/update into the collection
db.collection.aggregate([
{
"$match": {
"cd_order": "7650614875"
}
},
{
$set: {
inc: {
"$range": [
0,
51,
1
]
}
}
},
{
"$unwind": "$inc"
},
{
$set: {
cd_order: {
$toString: {
$add: [
{
"$toLong": "$cd_order"
},
"$inc"
]
}
},
"inc": "$$REMOVE",
_id: "$$REMOVE"
}
},
{
"$merge": {
"into": "collection",
"on": "cd_order",
"whenMatched": "merge",
"whenNotMatched": "insert"
}
}
])
Mongo Playground
I was able to duplicate to convert the value and increment before duplicating the document, it worked well:
var copy = db.orders.findOne({ cd_order: "7650614877" }, { _id: 0 });
for (var i = 0; i < 100; i++) {
copy.cd_order = (parseInt(copy.cd_order) + 1).toString();
db.orders.insert(copy);
}
I was also able to print all values using this query:
var orders = db.orders.find({ "nm_ancestor": {$eq: "Luigi D'arpino"} }, { "cd_order": 1 });
var ordersString = "";
orders.forEach(function(order) {
if(ordersString.length>0) ordersString += ", ";
ordersString += order.cd_order;
});
print("cd_order: " + ordersString);

MongoDB: Can't update in nested arrays

I've been trying to modify a value in multiple arrays for a few arrays and I can't find documentation on how to do this.
My collection looks like this
"rates": [
{
"category": "Web",
"seniorityRates": [
{
"seniority": "junior",
"rate": 100
},
{
"seniority": "intermediate",
"rate": 135
},
{
"seniority": "senior",
"rate": 165
}
]
}
]
I'm just trying to modify "junior" to "beginner", this should be simple.
Thanks to these answers:
How can I update a multi level nested array in MongoDB?
MongoDB updating fields in nested array
I've manage to write that python code (pymongo), but it doesn't works...
result = my_coll.update_many({},
{
"$set":
{
"rates.$[].seniorityRates.$[j].seniority" : new
}
},
upsert=False,
array_filters= [
{
"j.seniority": old
}
]
)
The path 'rates' must exist in the document in order to apply array updates.
It correspond to this command that doesn't work either
db.projects.updateMany({},
{
$set:
{
"rates.$[].seniorityRates.$[j].seniority" : "debutant"
}
},
{ arrayFilters = [
{
"j.seniority": "junior"
}
]
}
)
clone(t={}){const r=t.loc||{};return e({loc:new Position("line"in r?r.line:this.loc.line,"column"in r?r.column:......)} could not be cloned
What am I doing wrong ?
Any help would be very appreciated
The other option could be Sample
db.collection.update({},
{
$set: {
"rates.$[].seniorityRates.$[j].seniority": "debutant"
}
},
{
arrayFilters: [
{
"j.rate": { //As per your data, you can apply the condition o rate field to modify the level
$lte: 100
}
}
]
})
Or
The actual query should work Sample
db.collection.update({},
{
$set: {
"rates.$[].seniorityRates.$[j].seniority": "debutant"
}
},
{
arrayFilters: [
{
"j.seniority": "junior"
}
]
})
The same should work in python, a sample question
So I was just dumb here, I inverted two parameters so I didn't have the correct collection in the python code...
Thanks Gibbs for pointing out where the mistake was in the mongo command.
I will not delete this post as it can help other to know how to do this kind of queries.

MongoDB - Update data type for the nested documents

I have this collection: (see the full collection here https://mongoplayground.net/p/_gH4Xq1Sk4g)
{
"_id": "P-00",
"nombre": "Woody",
"costo": [
{
"tipo": "Cap",
"detalle": "RAC",
"monto_un": "7900 ",
"unidades": "1",
"total": "7900 "
}
]
}
I tried a lot of ways to transform monto_un, unidades and total into int, but I always get an error.
Neither of these works.
db.proyectos.updateMany({}, {'$set': {"costo.monto_un": {'$toInt': 'costo.$.monto_un'}}})
db.collection.update({},
[
{
$set: {
costo: {
monto_un: {
$toInt: {
costo: "$monto_un"
}
}
}
}
}
],
{
multi: true
})
MongoDB 5.0.9
Any suggestions?
$set - Update costo array.
1.1. $map - Iterate each element in the costo array and return a new array.
1.2. $mergeObjects - Merge current document with the document from 1.3.
1.3. A document with the monto_un field. You need to trim space for the monto_un field in the current iterate document via $trim and next convert it to an integer via $toInt.
In case you are also required to convert the unidades and total as int, add those fields with the same operator/function logic as monto_un in 1.3. Those fields in the document (1.3) will override the existing value due to $mergeObjects behavior.
db.collection.update({},
[
{
$set: {
costo: {
$map: {
input: "$costo",
in: {
$mergeObjects: [
"$$this",
{
monto_un: {
$toInt: {
$trim: {
input: "$$this.monto_un"
}
}
}
}
]
}
}
}
}
}
],
{
multi: true
})
Sample Mongo Playground

finding the document based on mongodb query condition

{
"responseType" : "success",
"sentPayload" : "{\"regId\":\"AR34JK\",\"createdOn\":\"2021-12-16T11:17:28.640Z\"}",
"sentUrl" : "DB",
"tat" : 38.044
}
This is my JSON. how to write a MongoDB query based on the sentPayload.createdOn condition?
i have written query {"sentPayload.createdOn":{$gte:new Date("2021-12-16")}}
but this is not returning the document.pls help me
Storing the field as JSON string is considered as an anti-pattern. Consider storing them as proper BSON objects.
Nevertheless, starting with MongoDB v4.4+, you can use $function to do a javascript JSON.parse to convert the JSON string back to JSON object. Then do a $toDate to convert the createdOn field back to proper date field.
db.collection.aggregate([
{
$addFields: {
sentPayload: {
$function: {
body: "function(sentPayload) {return JSON.parse(sentPayload)}",
args: [
"$sentPayload"
],
lang: "js"
}
}
}
},
{
$addFields: {
"sentPayload.createdOn": {
$toDate: "$sentPayload.createdOn"
}
}
},
{
$match: {
"sentPayload.createdOn": {
$gte: ISODate("2021-12-16T00:00:00.000Z")
}
}
}
])
Here is the Mongo playground for your reference.

Mongoose query to list the document based on value from nested document

In this code i am trying to match the document based on product size from mongoose query.
I tried this query but it doesn't works. Can anyone tell me what's wrong in this code?
Query i have passed:
{ $match: { "product_items.product_size": { value: 22, unit: "ml" } } }
** Structure:**
[
{
"product_name": "sample product",
"product_items": [
{
"product_item_number": "796363011028",
"product_size": {
"value": 22,
"unit": "ml"
}
}
]
}
]
It doesn't work this way because product_items.product_size evaluates to an array of objects and you are trying to compare a single object with such array. It is more reliable to use $elemMatch when working with arrays of objects:
db.collection.aggregate([
{
$match: {
"product_items": {
$elemMatch: {
"product_size.value": 22,
"product_size.unit": "ml"
}
}
}
}
])
Mongo Playground