Document 1: Need to covert object to Arrays. I have to iterate on each element of the key and value, how I can convert from an object to Array.
{
"Data":{
"A" :{
"name" : alpha,
"Score" : 199
},
"B" :{
"name" : Beta,
"Score" : 122
}
}
}
Expected Output :
{
"name" :[
alpha,
Beta
],
"score": [
199,
122
]
}
You can achieve this using mongo aggregation framework.
[
{
"$addFields": {
"Data": {
"$objectToArray": "$Data"
}
}
},
{
$unwind: "$Data"
},
{
"$group": {
"_id": "_id",
"name": {
$push: "$Data.v.name"
},
"score": {
$push: "$Data.v.Score"
},
}
}
]
Mongo Playground: https://mongoplayground.net/p/JVTW5rEoJoa
Related
I'm working in MongoDB and getting stuck at one aggregation case. Let me show you my collection.
First collection (data):
[
{
"_id": "8e7b3fa0-4230-448c-8f70-1d7300632834",
"data": [
{
"animal" : "7d44251a-b308-4deb-875a-33ef0a69fe2b",
"place": "Chennai"
},
{
"animal" : "fcfdd527-5885-48b0-a91f-03f72f78528f",
"place": "Kolkata"
}
]
}
]
Second collection (Animal):
[
{
"_id": "7d44251a-b308-4deb-875a-33ef0a69fe2b",
"name": "Dog"
},
{
"_id": "7d44251a-b308-4deb-875a-33ef0a69fe2b",
"name": "Cat"
}
]
I'm using this query:
db.data.aggregate([
{
"$lookup": {
"from": "animal",
"localField": "data.animal",
"foreignField": "_id",
"as": "doc"
}
},
{
"$unwind": "$doc"
},
{
"$project": {
"_id": 1,
"data.animal": "$doc.name",
"data.place": 1
}
}
])
and it result me this
[
{
"_id": "8e7b3fa0-4230-448c-8f70-1d7300632834",
"data": [
{
"animal": "Dog",
"place": "Chennai"
},
{
"animal": "Dog",
"place": "Kolkata"
}
]
},
{
"_id": "8e7b3fa0-4230-448c-8f70-1d7300632834",
"data": [
{
"animal": "Cat",
"place": "Chennai"
},
{
"animal": "Cat",
"place": "Kolkata"
}
]
}
]
Where I'm expecting like this
[
{
"_id": "8e7b3fa0-4230-448c-8f70-1d7300632834",
"data": [
{
"animal": "Dog",
"place": "Chennai"
},
{
"animal": "Cat",
"place": "Kolkata"
}
]
}
]
Mongo Playground
Also sharing this question in Mongo playgroud. Thanks in advance!!
Solution 1
$unset - Deconstruct the data array into multiple documents.
$lookup - Perform join with animal collection.
$project - Decorate the output document. For data.animal field, get the first value via $first.
$group - Group by _id. Push the data document into the data array.
db.data.aggregate([
{
"$unwind": "$data"
},
{
"$lookup": {
"from": "animal",
"localField": "data.animal",
"foreignField": "_id",
"as": "doc"
}
},
{
"$project": {
"_id": 1,
"data.animal": {
$first: "$doc.name"
},
"data.place": 1
}
},
{
$group: {
_id: "$_id",
data: {
$push: "$data"
}
}
}
])
Demo Solution 1 # Mongo Playground
Solution 2
$lookup
$set - Set data field.
2.1. $map - Iterate the data array and returns a new array.
2.1.1. $mergeObjects - Merge current iterated document with place field and the document from 2.1.1.1.
2.1.1.1. $first - Get the first document from the filtered doc arrays by matching the ids via $filter.
$unset - Remove _id and animals._id fields.
db.data.aggregate([
{
"$lookup": {
"from": "animal",
"localField": "data.animal",
"foreignField": "_id",
"as": "doc"
}
},
{
$set: {
data: {
$map: {
input: "$data",
as: "data",
in: {
$mergeObjects: [
{
place: "$$data.place"
},
{
$first: {
$filter: {
input: "$doc",
cond: {
$eq: [
"$$this._id",
"$$data.animal"
]
}
}
}
}
]
}
}
}
}
},
{
$unset: [
"doc",
"data._id"
]
}
])
Demo Solution 2 # Mongo Playground
I want to $match in a MongoDB, the number of documents in thousands, so looking for dynamic sol:
$doc.k equal to $info.data.k after k, $match then we have a document that contains only information that $matchs, see the expected output document. If you have any questions let me know.
[
{
"doc": {
"k": "ABC",
"v": {
"sec": 0
}
},
"info": [
{
"data": [
{
"k": "XYZ",
"v": {
"know": "alpha"
}
},
{
"k": "ABC",
"v": {
"know": "alpha"
}
}
]
}
]
}
]
The expected output document will look like this
[
{
"doc": {
"k": "ABC",
"v": {
"sec": 0
}
},
"info": [
{
"data": [
{
"k": "ABC",
"v": {
"know": "alpha"
}
}
]
}
]
}
]
$set - Update info field.
1.1. $map - As info is an array, need to iterate each document in the info array and return a new array.
1.1.1. $filter - Filter the document by doc.k and current iterate k value.
db.collection.aggregate([
{
$set: {
info: {
$map: {
input: "$info",
in: {
data: {
$filter: {
input: "$$this.data",
cond: {
$eq: [
"$doc.k",
"$$this.k"
]
}
}
}
}
}
}
}
}
])
Sample Mongo Playground
"sourceList": [
{
"source" : "hello world, how are you?",
"_id" : ObjectId("5f0eb9946db57c0007841153")
},
{
"source" : "hello world, I am fine",
"_id" : ObjectId("5f0eb9946db57c0007841153")
},
{
"source" : "Is it raining?",
"_id" : ObjectId("5f0eb9946db57c0007841153")
}
]
Total words in hello world, how are you? = 5, in hello world, I am fine = 5, and in Is it raining?= 3.
Thus the total number of words = 13
Is there a mongo query to do this calculation? I could do this using javascript, but is there a direct way to query via mongo?
EDIT
Is there a way I can do this query across the documents? For documents obeying specific criteria, I want to run a similar calculation with an added constraint, that words of duplicate sentences are not counted twice. For example,
Document - 1
"sourceList": [
{
"source" : "hello world, how are you?",
"_id" : ObjectId("5f0eb9946db57c0007841153")
},
{
"source" : "hello world, I am fine",
"_id" : ObjectId("5f0eb9946db57c0007841153")
},
{
"source" : "Is it raining?",
"_id" : ObjectId("5f0eb9946db57c0007841153")
}
]
Document - 2
"sourceList": [
{
"source" : "hello world, how are you?",
"_id" : ObjectId("5f0eb9946db57c0007841153")
},
{
"source" : "hello world, I am fine",
"_id" : ObjectId("5f0eb9946db57c0007841153")
},
{
"source" : "Is it raining?",
"_id" : ObjectId("5f0eb9946db57c0007841153")
}
]
Here the count still remains the same. The reason being, sentences are exactly same in both the documents. But if we combine Document 1 + Document 3 (given as follows)
"sourceList": [
{
"source" : "Look at the beautiful tiger!",
"_id" : ObjectId("5f0eb9946db57c0007841153")
}
]
The count would come as 13 + 5 (document 3) = 18.
Yes, You can do that with the help of powerful aggregate framework.
mongo play-ground
db.collection.aggregate([
{
"$unwind": "$sourceList" //For each array element
},
{
$project: {
"sp": {
$split: [
"$sourceList.source", //split by spaces
" "
]
}
}
},
{
"$project": {
"sizes": {
"$size": "$sp". //count the words in each array
}
}
},
{
"$group": {
"_id": "$_id",
"count": {
"$sum": "$sizes" //group by id to reverse unwind and add the sizes
}
}
}
])
Update:
play
db.collection.aggregate([
{
"$unwind": "$sourceList"
},
{
$project: {
"sp": {
$split: [
"$sourceList.source",
" "
]
}
}
},
{
"$project": {
"sizes": {
"$size": "$sp"
}
}
},
{
"$group": {
"_id": null,
"count": {
"$sum": "$sizes"
}
}
}
])
For huge collections, you may need to use allowDiskUse but it is very heavy operation for larger collections.
Update:
play
db.collection.aggregate([
{
"$unwind": "$sourceList"
},
{
$project: {
"sp": {
$split: [
"$sourceList.source",
" "
]
}
}
},
{
"$group": {
"_id": null,
"elements": {
$addToSet: "$sp"
}
}
},
{
"$unwind": "$elements"
},
{
"$project": {
"sizes": {
"$size": "$elements"
}
}
},
{
"$group": {
"_id": null,
"count": {
"$sum": "$sizes"
}
}
}
])
I am trying to aggregate 2 collections in MongoDB based on a student's ID. One collection consists of student personal information, another one consists of the students logs. The issue is that the data is in array which is why I think my aggregation is not working. Any help will be appreciated.
student collection
{
"_id" : ObjectId("(Object ID here"),
"data" : [
{
"name" : "John",
"id" : 1
},
{
"name" : "Sandy",
"id" : 2
}
]
}
logs collection
{
"_id" : ObjectId("(Object ID here"),
"logs" : [
{
"studentId" : 1,
"activity" : "11112,334,123"
},
{
"studentId" : 2,
"activity" : "11112,334,123"
}
]
}
Here is what I have tried:
dbo.collection("student").aggregate([
{ "$lookup": {
"localField": "data.id",
"from": "logs",
"foreignField": "logs.studentId",
"as": "studentInfo"
}
}]).toArray(function(err, results) {
console.log(results);
});
Expected result:
studentinfo: {
id: 1,
name: "John",
activity" : "11112,334,123"
}
You can use below aggregation with mongodb 3.6
So basically your foreign field is an array you need to use $lookup with the pipeline to $unwind the foreign array inside the $lookup pipeline and to match the corresponding ids.
db.students.aggregate([
{ "$lookup": {
"from": "logs",
"let": { "dataId": "$data.id" },
"pipeline": [
{ "$unwind": "$logs" },
{ "$match": { "$expr": { "$in": ["$logs.studentId", "$$dataId"] }}},
{ "$replaceRoot": { "newRoot": "$logs" }}
],
"as": "students"
}}
])
or use this to merge both the arrays
db.students.aggregate([
{ "$lookup": {
"from": "logs",
"let": { "dataId": "$data.id" },
"pipeline": [
{ "$unwind": "$logs" },
{ "$match": { "$expr": { "$in": ["$logs.studentId", "$$dataId"] }}},
{ "$replaceRoot": { "newRoot": "$logs" }}
],
"as": "students"
}},
{ "$project": {
"students": {
"$map": {
"input": "$students",
"in": {
"studentId": "$$this.studentId",
"activity": "$$this.activity",
"name": { "$arrayElemAt": ["$data.name", { "$indexOfArray": ["$data.id", "$$this.studentId"] }]}
}
}
}
}}
])
Output
[
{
"students": [
{
"activity": "11112,334,123",
"name": "John",
"studentId": 1
},
{
"activity": "11112,334,123",
"name": "Sandy",
"studentId": 2
}
]
}
]
How to realize count array in array using aggregate? I have this document structure:
{
"_id" : 1,
"link" : [
{
"linkHistory" : [
{
"_id" : 1,
},
{
"_id" : 2,
}
]
}
]
}
and MongoDB code:
db.emailGroup.aggregate([
{
"$lookup":
{
"from": "link",
"localField": "_id",
"foreignField": "emailGroupId",
"as": "link"
},
},
{
"$unwind": "$link"
},
{
"$match": {
'link.originalLink': ""
}
},
{
"$group" : {
_id: '$_id',
link: {
$push: '$link'
}
}
}
])
I want to get count in other field for linkHistory. Is this possible?