How can i set alias for each "_id" in array of students.
I want to set an alias for _id in this json output.
{
"items": [
{
"id": "5d7aa7c1cba3435ebcb069c6",
"start": "2019-01-01T10:00:00.000Z",
"end": "2019-01-01T10:00:00.000Z",
"description": "test",
"students": [
{
"_id": "5d7aa779cba3435ebcb069c5", // <- alias _id to id
"name": "Jon",
"surname": "Snow"
}
]
}
]
}
How can i do that with Aggregation operations?
Use the native $map operator in your aggregate pipeline to transform the array. You would need to nest two $map operations; one
for the items array and an inner $map for the students array:
const studentsMap = {
'$map': {
'input': '$$this.students',
'as': 'student',
'in': {
'id': '$$student._id',
'name': '$$student.name',
'surname': '$$student.surname'
}
}
}
db.collection.aggregate([
{ '$addFields': {
'items': {
'$map': {
'input': '$items',
'in': {
'id': '$$this.id',
'start': '$$this.start',
'end': '$$this.end',
'description': '$$this.description',
'students': studentsMap
}
}
}
} }
])
Use map to transform.
items = items.map((item)=>{
item.students = item.students.map((student)=>{
student.id = student._id;
delete student._id;
return student;
})
return item;
})
A better approach would be to avoid hard coding of the fields in the $map stage as the maintenance of such queries becomes difficult when document size grows. The following query can get us the expected output and it's independent of the fields present in the document. As it would only focus on replacing the _id present in items.students.
db.collection.aggregate([
{
$addFields:{
"items":{
$map:{
"input":"$items",
"as":"item",
"in":{
$mergeObjects:[
"$$item",
{
"students":{
$map:{
"input":"$$item.students",
"as":"student",
"in":{
$mergeObjects:[
"$$student",
{
"id":"$$student._id"
}
]
}
}
}
}
]
}
}
}
}
},
{
$project:{
"items.students._id":0
}
}
]).pretty()
Related
Array field in collection:
"fruits": [ "fruits": [ "fruits": [
{"fruit1": "banana"}, {"fruit2": "apple"}, {"fruit3": "pear"},
{"fruit2": "apple"}, {"fruit4": "orange"}, {"fruit2": "apple"},
{"fruit3": "pear"}, {"fruit1": "banana"}, {"fruit4": "orange"},
{"fruit4": "orange"} {"fruit3": "pear"} {"fruit1": "banana"}
]
I need to find those documents in collections, where "banana" signed before "apple". Does mongodb allows to compare elements in array just like :
if (fruits.indexOf('banana') < fruits.indexOf('apple')) return true;
Or maybe there is any other method to get result i need?
MongoDB's array query operations do not support any positional search as you want.
You can, however, write a $where query to do what you want:
db.yourCollection.find({
$where: function() {
return (this.fruits.indexOf('banana') < this.fruits.indexOf('apple'))
}
})
Be advised though, you won't be able to use indexes here and the performance will be a problem.
Another approach you can take is to rethink the database design, if you can specify what it is you're trying to build, someone can give you specific advise.
One more approach: pre-calculate the boolean value before persisting to DB as a field and query on true / false.
Consider refactoring your schema if possible. The dynamic field names(i.e. fruit1, fruit2...) make it unnecessarily complicated to construct a query. Also, if you require frequent queries by array index, you should probably store your array entries in individual documents with some sort keys to facilitate sorting with index.
Nevertheless, it is achievable through $unwind and $group the documents again. With includeArrayIndex clause, you can get the index inside array.
db.collection.aggregate([
{
"$unwind": {
path: "$fruits",
includeArrayIndex: "idx"
}
},
{
"$addFields": {
fruits: {
"$objectToArray": "$fruits"
}
}
},
{
"$addFields": {
"bananaIdx": {
"$cond": {
"if": {
$eq: [
"banana",
{
$first: "$fruits.v"
}
]
},
"then": "$idx",
"else": "$$REMOVE"
}
},
"appleIdx": {
"$cond": {
"if": {
$eq: [
"apple",
{
$first: "$fruits.v"
}
]
},
"then": "$idx",
"else": "$$REMOVE"
}
}
}
},
{
$group: {
_id: "$_id",
fruits: {
$push: {
"$arrayToObject": "$fruits"
}
},
bananaIdx: {
$max: "$bananaIdx"
},
appleIdx: {
$max: "$appleIdx"
}
}
},
{
$match: {
$expr: {
$lt: [
"$bananaIdx",
"$appleIdx"
]
}
}
},
{
$unset: [
"bananaIdx",
"appleIdx"
]
}
])
Mongo Playground
I have the following set of data in my MongoDB collection called orders:
{
"_id" : ObjectId("618e0e1b17687316dcdd6246"),
"groupUID": "abc",
"orderData" : {
"charges" : {
"total" : 18480.0,
"subtotal" : 13980.0
},
"items" : [
{
"name" : "Chocolate cookies",
"imageURL": "domainURL2.com/cookies"
},
{
"name" : "Chocolate muffins",
"imageURL": "domainURL2.com/muffins"
}
]
}
}
Now I want to update the imageURL substring part of "domainURL2" to "domainURL1" field in every document of this collection. I have the following query so far:
db.orders.update(
{
"groupUID" : "abc"
},
{ "$set": { "orderData.items.$.imageURL":"myURL.com" } } )
I also have the query in JavaScript form but I want this to be in pure Mongo query. So the query below will not do it for me unfortunately.
db.getCollection("orders").find({"groupUID" : "abc"}).forEach(function(aRow) {
if (aRow.orderDetails !== undefined) {
var updated = false;
aRow.orderData.items.forEach(function(item) {
item.imageURL = item.imageURL.replace("eddress/", "noknok-app/");
})
db.getCollection("orders").save(aRow);
}
});
I want to update all records' imageURL field's substring part. I am unable to figure out the rest of the query. Can anyone please help me?
My answer may look complex (Welcome for suggestion/improvement).
Work the update with Aggegration Pipeline.
$set - Update orderData.items field.
1.1. $map - Iterate orderData.items and returns new array.
1.1.1. $mergeObjects - Merge current object and imageURL field from 1.1.1.1.
1.1.1.1. $cond - With $regexMatch to find the imageURL starts with "domainURL2.com".
1.1.1.2. If true, then replace "domainURL2.com" with "domainURL1.com".
1.1.1.3. If false, remain existing value.
db.collection.update({
"groupUID": "abc"
},
[
{
"$set": {
"orderData.items": {
$map: {
input: "$orderData.items",
in: {
$mergeObjects: [
"$$this",
{
imageURL: {
$cond: {
if: {
$regexMatch: {
input: "$$this.imageURL",
regex: "^domainURL2.com"
}
},
then: {
$concat: [
"domainURL1.com",
{
$arrayElemAt: [
{
$split: [
"$$this.imageURL",
"domainURL2.com"
]
},
-1
]
}
]
},
else: "$$this.imageURL"
}
}
}
]
}
}
}
}
}
])
Sample Mongo Playground
Another approach is using $replaceOne (suggested by #rickhg12hs) which will be much easier.
$replaceOne to replace for 1.1.1.1.
db.collection.update({
"groupUID": "abc"
},
[
{
"$set": {
"orderData.items": {
$map: {
input: "$orderData.items",
in: {
$mergeObjects: [
"$$this",
{
imageURL: {
$replaceOne: {
input: "$$this.imageURL",
find: "domainURL2.com",
replacement: "domainURL1.com"
}
}
}
]
}
}
}
}
}
])
Sample Mongo Playground ($replaceOne)
I am having the below document structure:
[
{
"network_type": "ex",
"rack": [
{
"xxxx": {
"asn": 111111,
"nodes": {
"business": [
"sk550abcc1eb01.abc.com",
"sk550abcc1eb10.abc.com",
"sk550abcc1eb19.abc.com",
"sk550abcc1eb28.abc.com"
]
},
"region": "ex-01",
"zone": "01a"
}
}
]
}
]
I need to rename/update the key array element "xxxx" to "details".
I tried the below command, but it doesn't seem to work.
db.collection.update({},
{
$rename: {
"rack.xxxx": "details"
}
})
Link: https://mongoplayground.net/p/9dcDP-VKZ55
Please help me.
You can't direct $rename the field name which is within the array.
Instead,
Iterate with document(s) in the rank array, create the details field with the value of xxxx and next append this field to each document.
Remove the path with $rank.xxxx to remove the xxxx field from the document(s) in the rank array.
db.collection.update({},
[
{
$set: {
rack: {
$map: {
input: "$rack",
in: {
$mergeObjects: [
{
"details": "$$this.xxxx"
},
"$$this"
]
}
}
}
}
},
{
$unset: "rack.xxxx"
}
])
Sample Mongo Playground
Hello Experts,
I am trying to parse a MongoDB collection row, and after using $unwind,
one of the remaining fields looks like that:
[
{
"account_id": "1234",
"cities": {
"cityname1": {
"param1": 1,
"param2": 2
}
}
},
{
"account_id": "2345",
"cities": {
"cityname2": {
"param1": 3,
"param2": 3
}
}
},
{
"account_id": "3456",
"cities": {
"cityname3": {
"param1": 8,
"param2": 6
}
}
}
]
Now, I would like to continue parsing this field, so I can extract the fieldname/value for account_id, for param1 and for param2, hoping then to sum up the param1 and param2 values.
However, when I try to use a second $unwind, I receive those fields with "null" value.
How should I parse this field correctly?
$set change cities into array by using $objectToArray
$unwind unwind cities array
$group group by account_id and cityname then sum up param1 and param2
(you can only group by account_id or cityname, just remove one of them)
aggregate
db.collection.aggregate([
{
"$set": {
cities: {
"$objectToArray": "$cities"
}
}
},
{
"$unwind": "$cities"
},
{
"$group": {
"_id": {
account_id: "$account_id",
"cityname": "$cities.k"
},
"sumOfParam1": {
"$sum": "$cities.v.param1"
},
"sumOfParam2": {
"$sum": "$cities.v.param2"
}
}
}
])
mongoplayground
I have an aggregation pipeline with result like that.
[
{
"_id":"1621",
"count":567
},
{
"_id":"1658",
"count":1089
}
...
]
How i can convert this result into key:value pair ("_idValue": "countValue"), like this?
{
"1621": 567,
"1658": 1089
...
}
My pipeline is:
pipeline = [
{'$match': {
'date': {'$gte': start_date, '$lt': end_date}
}},
{
'$group': {
'_id': '$networkId',
'count': {'$sum': 1}
}
},
{
'$sort': {'_id': 1}
},
]
Add below stages after your pipeline stages,
$group by null and construct the array of key-value pair
$arrayToObject convert above formatted array to object
$repalceRoot to replace above converted object to root
pipeline = [
// .. add your pipeline stages here
{
$group: {
_id: null,
object: {
$push: { k: "$_id", v: "$count" }
}
}
},
{
$replaceRoot: { newRoot: { $arrayToObject: "$object" } }
}
]
Playground