I am trying an aggregation in MongoDB in order to convert:
[
{
"_id": ObjectId("5a934e000102030405000000"),
"acronym": "MC",
"tickers": [
{
"name": "Sensor1-front",
"symbol": "s1f"
},
{
"name": "Sensor1-back",
"symbol": "s1b"
}
]
}
]
into something like this:
['s1f.MC','s1b.MC']
Where each symbol in tickers is concat to acronym.
I am trying it with $concat, $group, etc but I am not going anywhere.
Thanks!
You can use reduce
db.collection.aggregate([
{
$addFields: {
newField: {
"$reduce": {
"input": "$tickers",
"initialValue": [],
"in": {
"$concatArrays": [
[
{
"$concat": [
"$$this.symbol",
".",
"$acronym"
]
}
],
"$$value"
]
}
}
}
}
}
])
Working Mongo playground
Related
My database looks like this:
[
{
"title": "man",
"articlesType": [
{
"title": "shoes",
"articles": [
{
"title": "shoes1",
"id": "randomId"
},
{
"title": "shoes2",
"id": "alsoRandomId"
}
]
}
]
},
{
"title": "woman",
"articlesType": [
{
"title": "pants",
"articles": [
{
"title": "pants1",
"id": "anotherRandomId"
},
{
"title": "pants1",
"id": "justId"
}
]
}
]
}
]
I expect something like this: , is it possible to get whole object in this nested just using an ID?
{
"title": "shoes2",
"id": "alsoRandomId"
}
I found this, but does not work for me
You can try an aggregation pipeline using double $unwind to deconstruct the array twice and then filter by your desired id (I'm assuming you want to match by the id).
And the last step, $project is to output the result in the same way as you want.
db.collection.aggregate([
{
"$unwind": "$articlesType"
},
{
"$unwind": "$articlesType.articles"
},
{
"$match": {
"articlesType.articles.id": "alsoRandomId"
}
},
{
"$project": {
"title": "$articlesType.articles.title",
"id": "$articlesType.articles.id"
}
}
])
Example here
I have 2 collections:
Vehicles:
[
{
"_id": "a1",
"type:": "car",
"make": "Honda",
"specifications": ["1", "2"]
},
{
"_id": "a2",
"type:": "car",
"make": "Toyota",
"specifications": ["3", "4"]
},
{
"_id": "a3",
"type:": "car",
"make": "Honda",
"specifications": []
},
{
"_id": "a4",
"type:": "car",
"make": "Toyota"
}
]
Specifications:
[
{
"_id": "1",
"color": "Black"
},
{
"_id": "2",
"sunroof": "yes"
},
{
"_id": "3",
"engine": "1800 CC"
},
{
"_id": "4",
"bodyType": "Sedan"
}
]
I want to fetch those records which has at least one specification.
And also the details from specifications collections should appear in Vehicles collections somewhere.
Expected response:
[
{
"_id": "a1",
"make": "Honda",
"type:": "car",
"carSpecifications": [
{
"color": "Black"
},
{
"sunroof": "yes"
}
],
},
{
"_id": "a2",
"make": "Toyota",
"type:": "car",
"specifications": [
{
"engine": "1800 CC"
},
{
"bodyType": "Sedan"
}
]
}
]
Now what I tried so far is:
db.vehicles.find({type: "car", "specifications": {$exists: true}}, {fields: {"specifications.$": 1}}).fetch()
this query is returning all the records from Vehicles.
After getting all the records I put a loop on the records I get and check manually if specifications.length > 0 than I query from Specifications collection accordingly.
Can I achieve all this with a single query?
You should look for an aggregation query.
$match - Filter documents with "type:" "car" and specifications is not an empty array (with $ifNull, default as [] when specifications field is null or not existed).
$lookup - Vehicles collection join specifications collection (Refer to Use $lookup with an Array). Work with pipeline to return the array without the _id field (Refer to Correlated Subqueries Using Concise Syntax).
MongoDB v5 query
db.vehicles.aggregate({
$match: {
$and: [
{
"type:": "car"
},
{
$expr: {
$ne: [
{
$ifNull: [
"$specifications",
[]
]
},
[]
]
}
}
]
}
},
{
$lookup: {
from: "specifications",
localField: "specifications",
foreignField: "_id",
pipeline: [
{
$project: {
_id: 0
}
}
],
as: "specifications"
}
})
Sample Mongo Playground (v5)
MongoDB v4 query
db.vehicles.aggregate({
$match: {
$and: [
{
"type:": "car"
},
{
$expr: {
$ne: [
{
$ifNull: [
"$specifications",
[]
]
},
[]
]
}
}
]
}
},
{
$lookup: {
from: "specifications",
let: {
specifications: "$specifications"
},
pipeline: [
{
$match: {
$expr: {
$in: [
"$_id",
"$$specifications"
]
}
}
},
{
$project: {
_id: 0
}
}
],
as: "specifications"
}
})
Sample Mongo Playground (v4)
I have a below collection
[
{
"Array1": [
{
"Array2": [
{
"name": "6666",
"Array3": [
{ "_id": 128938120, "nest": "samsung" },
{ "_id": 12803918239, "nest": "nokia" }
]
},
{
"name": "5555",
"Array3": [
{ "_id": 48102938109, "nest": "iphone" },
{ "_id": 501293890, "nest": "micromax" }
]
}
],
"name": "old apartment"
},
{
"Array2": [
{
"_id": 410923810,
"name": "3333",
"Array3": [
{ "_id": 48102938190, "nest": "airtel" },
{ "_id": 48102938190, "nest": "jio" }
]
},
{
"_id": 41092381029,
"name": "2222",
"Array3": [
{ "_id": 10293182309, "nest": "master" },
{ "_id": 38190238, "nest": "cub" }
]
}
],
"name": "new apartment"
}
]
}
]
I want to fo $filter to 3 nested level... I want only nokia element from 3rd array and 6666 element from second and old apartment from the first
I want this output
[
{
"Array1": [
{
"Array2": [
{
"name": "6666",
"Array3": [
{
"_id": 12803918239,
"nest": "nokia"
}
]
}
],
"name": "old apartment"
}
]
}
]
And also I want to do using $filter and $map only... Don't want to use $unwind here
Basically you have to use $filter for each level to apply your condition and $map for each array that has nested array inside. That's because you want to pass filtered array to the output. So there will be 3 filters and 2 maps. Indentations might be really helpful in this case. Try:
db.collection.aggregate([
{
$project: {
Array1: {
$map: {
input: { $filter: { input: "$Array1", as: "a1", cond: { $eq: ["$$a1.name", "old apartment" ] } } },
as: "a1",
in: {
name: "$$a1.name",
Array2: {
$map: {
input: { $filter: { input: "$$a1.Array2", as: "a2", cond: { $eq: [ "$$a2.name", "6666" ] } } },
as: "a2",
in: {
name: "$$a2.name",
Array3: { $filter: { input: "$$a2.Array3", as: "a3", cond: { $eq: [ "$$a3.nest", "nokia" ] } } }
}
}
}
}
}
}
}
}
])
Mongo playground
I have a mongodb aggregation framework query as shown below.I am unable to parse the output of the below query
myModel.aggregate(
[
{
"$match": { "$and": [{ "serviceActiveFlag": "Y" }, { "hospitalName": hospitalName }] }
},
//decompile array
{ $unwind: "$Treatment" },
{
$group: {
_id: "$Treatment.departmentName", "procedureList": {
$push: { "procedureName": "$Treatment.name", "cost": "$Treatment.costLowerBound" }
}
}
},
{
$project: {
"_id": 0,
"department": '$_id',
"procedureList": 1
}
}
], function (err, result) {
})
Output of the above query is shown below
{
"data": [
{
"procedureList": [
{
"procedureName": "Root Canal",
"cost": 10200
}
],
"department": "Dental"
},
{
"procedureList": [
{
"procedureName": "Bone Grafting",
"cost": 20000
}
],
"department": "Ortho"
}
]
}
How do i retrieve the value corresponding to key data?
I tried result.data[0],but I got undefined error
Expected output is given below
Expected output
[
{
"procedureList": [
{
"procedureName": "Root Canal",
"cost": 10200
}
],
"department": "Dental"
},
{
"procedureList": [
{
"procedureName": "Bone Grafting",
"cost": 20000
}
],
"department": "Ortho"
}
]
I think you forgot to parse the json-data, that's why it isn't able to index, Please use the code below to parse the json and then try accessing the array. if you encounter any problems, please mention them.
result = JSON.parse(result)
result.data[0]
Result is an object so,
let data = result.data
Given a collection like:
{
"_id": "XXXX",
"JobId": [
100
],
"PersonalDetails": [
{
"Level": 1,
"Zone": [
{
"Id": 1,
"Code": "XXXXXXXX",
"IsAvailable": true
},
{
"Id": 45,
"Code": "ZZZZZZZZZ",
"IsAvailable": false
}
]
}
],
"Timestamp": ISODate("2015-11-01T00:00:00.000Z")
}
I need to get all Zone ids and codes that have the IsAvailable flag set to true.
I have tried the following:
var details = db.test.find(
{
JobId: {$in: [100]},
'PersonalDetails': {$elemMatch: {Zone : {$elemMatch: {IsAvailable: true}}}}
},
{
'PersonalDetails.Zone.Id': 1,
'PersonalDetails.Zone.Code': 1,
'PersonalDetails.Zone.IsAvailable': 1
});
details.forEach(function(doc){
var myDetails = doc.PersonalDetails;
myDetails.forEach(function(doc2){
var myZones = doc2.Zone;
print(myZones);
This gives me
{
"0" : {
"Id": 1,
"Code": "XXXXXXXX",
"IsAvailable": true
},
"1" : {
"Id": 45,
"Code": "ZZZZZZZZZ",
"IsAvailable": false
}
}
But I just want only where the IsAvailable flag is set to true returned.
Am I going about this the wrong way?? I tried using aggregate but ran into the same problem - returning all and not filtering the IsAvailable flag.
You need to use the .aggregate() method.
First of all you need to reduce the size of the documents to process using the $match operator. From there you will need to denormalize your "PersonalDetails" array using the $unwind operator.
You can then use the $project operator to return only sub-documents that match your criteria.
The $map operator in the project stage is used to return array of sub-documents.
db.collection.aggregate([
{ "$match": {
"JobId": 100,
"PersonalDetails.Zone.IsAvailable": true
}},
{ "$unwind": "$PersonalDetails" },
{ "$project": {
"zone": {
"$setDifference": [
{ "$map": {
"input": "$PersonalDetails.Zone",
"as": "z",
"in": { "$cond": [ "$$z.IsAvailable", "$$z", false ] }
}},
[false]
]
}
}}
])
Which returns:
{
"_id" : "XXXX",
"zone" : [
{
"Id" : 1,
"Code" : "XXXXXXXX",
"IsAvailable" : true
}
]
}
Starting from MongoDB 3.2 we can use the $filter operator to do this efficiently
db.collection.aggregate([
{ "$match": {
"JobId": 100,
"PersonalDetails.Zone.IsAvailable": true
}},
{ "$unwind": "$PersonalDetails" },
{ "$project": {
"zone": {
"$filter": {
"input": "$PersonalDetails.Zone",
"as": "z", "cond": "$$z.IsAvailable"
}
}
}}
])