How sort according to given order and calculation 1st document with second one in MongoDB? - mongodb

I want to get which members not present when state change and which member is present.
My given array for state order like:
[{order:1,text:'CS'}, {order:2,text:'IP'}, {order:3,text:'AC'}]
so I want to sort according to this array want to perform some operation with each pare of documents
My documents like:
{
"count" : 2,
"state" : "CS",
"members" : [
{
"email" : "builuu1998#gmail.com",
"date" : ISODate("2016-12-24T03:39:05.720Z")
},
{
"email" : "bactv.hn#gmail.com",
"date" : ISODate("2016-12-25T02:32:48.698Z")
}
]
},
{
"count" : 1,
"state" : "AC",
"members" : [
{
"email" : "builuu1998#gmail.com",
"date" : ISODate("2016-12-24T03:39:05.720Z")
}
]
},
{
"count" : 3,
"state" : "IP",
"members" : [
{
"email" : "builuu1998#gmail.com",
"date" : ISODate("2016-12-24T03:39:05.720Z")
},
{
"email" : "bactv.hn#gmail.com",
"date" : ISODate("2016-12-25T02:32:48.698Z")
},
{
"email" : "abc.hn#gmail.com",
"date" : ISODate("2016-12-25T02:32:48.698Z")
}
]
}
So I want to know which members are not present from one state to another state and which members are present .
In my exam 1st state to 2nd state means (CS - IP): present 2 members and not present 1 member
{
"state": "CS_IP",
"present": [
{
"email" : "builuu1998#gmail.com",
"date" : ISODate("2016-12-24T03:39:05.720Z")
},
{
"email" : "bactv.hn#gmail.com",
"date" : ISODate("2016-12-25T02:32:48.698Z")
}
],
"notPresent": [
{
"email" : "abc.hn#gmail.com",
"date" : ISODate("2016-12-25T02:32:48.698Z")
}
]
}
and 2nd state to 3rd state means (IP - AC): present 1 members and not present 2 member
{"state": "IP_AC",
"present": [
{
"email" : "builuu1998#gmail.com",
"date" : ISODate("2016-12-24T03:39:05.720Z")
}
],
"notPresent": [
{
"email" : "bactv.hn#gmail.com",
"date" : ISODate("2016-12-25T02:32:48.698Z")
},
{
"email" : "abc.hn#gmail.com",
"date" : ISODate("2016-12-25T02:32:48.698Z")
}
]}
How can I achieve this using aggregate query because I need some aggregate operation after this stage complete

You can perform one aggregation request per "couple" of state like this for ["IP", "AC"] :
db.exams.aggregate([{
$match: {
"state": {
$in: ["IP", "AC"]
}
}
}, {
"$group": {
"_id": 1,
"group1": { "$first": "$members" },
"group2": { "$last": "$members" }
}
}, {
"$project": {
"present": { "$setIntersection": ["$group1", "$group2"] },
"notPresent": {
$setUnion: [
{ "$setDifference": ["$group1", "$group2"] },
{ "$setDifference": ["$group2", "$group1"] }
]
}
}
}])
Note that this will only work for 2 elements to match (here ["IP", "AC"]) since we have to create two new fields group1 & group2 to $setIntersection and $setDifference (as there is no $group with $setIntersection and $setDifference)
The query above will give :
{
"_id": 1,
"present": [
{ "email": "builuu1998#gmail.com", "date": ISODate("2016-12-24T03:39:05.720Z") }
],
"notPresent": [
{ "email": "abc.hn#gmail.com", "date": ISODate("2016-12-25T02:32:48.698Z") },
{ "email": "bactv.hn#gmail.com", "date": ISODate("2016-12-25T02:32:48.698Z") }
]
}

Related

match element in the array with aggregation

i have mongo db collection the follwing structure
{
{
"_id" : ObjectId("63e37afe7a3453d5014c011b"),
"schemaVersion" : NumberInt(1),
"Id" : "ObjectId("63e37afe7a3453d5014c0112")",
"Id1" : "ObjectId("63e37afe7a3453d5014c0113")",
"Id2" : "ObjectId("63e37afe7a3453d5014c0114")",
"collectionName" : "Country",
"List" : [
{
"countryId" : NumberInt(1),
"name" : "Afghanistan",
},{
"countryId" : NumberInt(1),
"name" : "India",
},
{
"countryId" : NumberInt(1),
"name" : "USA",
}
}
i need to match the value with id, id1, id2, collectionName and name in the list to get country id for example if match the below value
"Id" : "ObjectId("63e37afe7a3453d5014c0112")",
"Id1" : "ObjectId("63e37afe7a3453d5014c0113")",
"Id2" : "ObjectId("63e37afe7a3453d5014c0114")",
"collectionName" : "Country",
"name" : "Afghanistan",
i need result
{
"countryId" : 1,
"name" : "Afghanistan",
}
i tried like below
db.country_admin.aggregate([
{ $match: { collectionName: "Country" } },
{ $unwind : '$countryList' },
{ $project : { _id : 0, 'countryList.name' : 1, 'countryList.countryId' : 1 } }
]).pretty()
and i have following output
[
{
"List" : {
"countryId" : 1.0,
"name" : "Afghanistan"
}
},
{
"List" : {
"countryId" : 2.0,
"name" : "india"
}
},
{
"List" : {
"countryId" : 3.0,
"name" : "USA"
}
}]```
You can try using $filter to avoid $unwind like this example:
First $match by your desired condition(s).
Then $filter and get the first element (as "List.name": "Afghanistan" is used into $match stage there will be at least one result).
And output only values you want using $project.
db.collection.aggregate([
{
"$match": {
"Id": ObjectId("63e37afe7a3453d5014c0112"),
"Id1": ObjectId("63e37afe7a3453d5014c0113"),
"Id2": ObjectId("63e37afe7a3453d5014c0114"),
"collectionName": "Country",
"List.name": "Afghanistan",
}
},
{
"$project": {
"country": {
"$arrayElemAt": [
{
"$filter": {
"input": "$List",
"cond": {
"$eq": [
"$$this.name",
"Afghanistan"
]
}
}
},
0
]
}
}
},
{
"$project": {
"_id": 0,
"countryId": "$country.countryId",
"name": "$country.name"
}
}
])
Example here
By the way, using $unwind is also possible and you can check this example

How to copy a value between fields within each object in an embedded array of objects in MongoDB?

In my 'assemblies' collection, each document contains an embedded array of objects called 'partlist':
{
"_id" : ObjectId("0001"),
"pn" : "01",
"title" : "MyAssembly",
"partlist" : [
{
"id" : "",
"pn" : "1234",
"desc" : "myPart1",
},
{
"id" : "",
"pn" : "5678",
"desc" : "myPart2",
}]
}
Within each object, I need to copy the value from 'partlist.pn' into 'partlist.id'. I used:
db.assemblies.aggregate([{$set:{"partlist.id":"$partlist.pn"}}])
hoping to achieve this:
{
"_id" : ObjectId("0001"),
"pn" : "01",
"title" : "MyAssembly",
"partlist" : [
{
"id" : "1234",
"pn" : "1234",
"desc" : "myPart1",
},
{
"id" : "5678",
"pn" : "5678",
"desc" : "myPart2",
}]
}
Instead it returned to 'id' an array of ALL the 'pn' values in 'partlist':
{
"_id" : ObjectId("0001"),
"pn" : "01",
"title" : "MyAssembly",
"partlist" : [
{
"id" : [
"1234",
"5678"
],
"pn" : "1234",
"desc" : "myPart1",
},
{
"id" : [
"1234",
"5678"
],
"pn" : "5678",
"desc" : "myPart2",
}]
}
What is the correct syntax for copying the one value within each object?
What you can do is, you can use $map to modify each elements and mearg the id with the help of $mergeObject
db.collection.aggregate([
{
$addFields: {
"partlist": {
$map: {
input: "$partlist",
in: {
"$mergeObjects": [
"$$this",
{
id: "$$this.pn"
}
]
}
}
}
}
}
])
Working Mongo playground
If the pn does not exist it retrieves initial object.
db.collection.aggregate([
{
$addFields: {
partlist: {
$map: {
input: "$partlist",
as: "p",
in: {
$cond: [
"$$p.pn",
{
$mergeObjects: [
"$$p",
{
"id": "$$p.pn"
}
]
},
"$$p"
]
}
}
}
}
}
])
Playground

Inner array total count in mongodb

I have a journal doc. Which contains details of journal_volumes, journal issues and journal articles. I have to list the journals along with the count of volumes, issues and articles in each journal.
Here is my doc:
{
"_id" : ObjectId("5c470fc3135edb4413b0ea24"),
"jnl_code" : "KEG",
"jnl_volumes" : [
{
"name" : "1",
"created_date" : "2019-03-01",
"status" : "0",
"issue_flag" : "0",
"jnl_issues" : [
{
"issue_name" : "1",
"created_date" : "2019-03-04",
"jnl_articles" : [
"test",
"test2"
]
},
{
"issue_name" : "2",
"created_date" : "2019-03-04",
"jnl_articles" : [
"a"
]
},
{
"issue_name" : "3",
"created_date" : "2019-03-04",
"jnl_articles" : [
"b"
]
},
{
"issue_name" : "3",
"created_date" : "2019-03-05",
"jnl_articles" : [
"Q"
]
}
]
},
{
"name" : "2",
"created_date" : "2019-03-01",
"status" : "0",
"issue_flag" : "0",
"jnl_issues" : [
{
"issue_name" : "1",
"created_date" : "2019-03-05",
"jnl_articles" : [
"W"
]
},
{
"issue_name" : "1",
"created_date" : "2019-03-05",
"jnl_articles" : [
"S"
]
},
{
"issue_name" : "1",
"created_date" : "2019-03-05",
"jnl_articles" : [
"R"
]
},
{
"issue_name" : "1",
"created_date" : "2019-03-05",
"jnl_articles" : [
"R"
]
}
]
},
{
"name" : "3",
"created_date" : "2019-03-05",
"status" : "0",
"issue_flag" : "0"
}
]
}
My requirement is to get the count of jnl_volumes, total jnl_issues count and total jnl_articles count in single query..
Thanks to Neil Lunn to redirect me to the similar question (Calculate the count of nested objects with C#
). I referred the answer and wrote a query:
db.getCollection('rvh_journals').aggregate([
{
$project: {
"volumes" : { "$size" : { "$ifNull" : [ "$jnl_volumes", [] ] } },
"issues" : {
"$sum" : {
"$map" : {
"input" : "$jnl_volumes",
"in": { "$size" : { "$ifNull" : [ "$$this.jnl_issues", [] ] } }
}
}
},
"articles" : {
"$sum" : {
"$map" : {
"input" : "$jnl_volumes.jnl_issues.jnl_articles",
"in" : { "$size" : { "$ifNull" : [ "$$this", [] ] } }
}
}
}
}
}
])
This returns with an incorrect article count. Actual article count is 9 but the query returns 8
{
"_id" : ObjectId("5c470fc3135edb4413b0ea24"),
"volumes" : 3,
"issues" : 8,
"articles" : 8
}
Yes I got curious after your edit to the previous question, and noticed your statement was incorrect.
This one is correct:
db.getCollection('rvh_journals').aggregate([
{ "$project": {
"volumes": { "$size": "$jnl_volumes" },
"issues": {
"$sum": {
"$map": {
"input": "$jnl_volumes",
"in": { "$size": { "$ifNull": ["$$this.jnl_issues", [] ] } }
}
}
},
"articles": {
"$sum": {
"$map": {
"input": "$jnl_volumes",
"in": {
"$sum": {
"$map": {
"input": { "$ifNull": [ "$$this.jnl_issues", [] ] },
"in": { "$size": { "$ifNull": [ "$$this.jnl_articles", [] ] } }
}
}
}
}
}
}
}}
])
Returns:
{
"_id" : ObjectId("5c470fc3135edb4413b0ea24"),
"volumes" : 3,
"issues" : 8,
"articles" : 9
}
Note the traversal of the arrays.
You might go and read some of the actual words I used on that original linked answer, because I would have explained that nesting arrays like this is not a good idea. More details on why it's not a good idea and practical approaches to take otherwise are on Updating a Nested Array with MongoDB
enter image description here
.itcount() - is showing distinct count.
Need to show total count of count variable which is declare in $group
Currently it is showing based on company.

How to take the duplicate records in mongodb

Here i have two document ,in this documents childNodes array ID is duplicate means , i want to take the userID and pedagogyID of the record,as per my documents second document under childNodes array 798 is coming duplicate, so i want to take the records
Documents
{
"userID" : "A",
"pedagogyID" : "100",
"summary" : {
"LearnProgress" : {
"childNodes" : [
{
"ID" : "123",
"status" : "in-progress"
},
{
"ID" : "456",
"status" : null
},
{
"ID" : "333",
"status" : null
}
],
}
}
}
{
"userID" : "B",
"pedagogyID" : "200",
"summary" : {
"LearnProgress" : {
"childNodes" : [
{
"ID" : "789",
"status" : "in-progress"
},
{
"ID" : "1010",
"status" : null
},
{
"ID" : "789",
"status" : null
}
],
}
}
}
Expected Output
{
"userID" : "B",
"pedagogyID" : "200",
}
MY Code
db.collectionname.aggregate(
[
{"$unwind":"$summary.LearnProgress.childNodes"},
{"$group":{
"_id":{"_id":"$_id","ID":"$summary.LearnProgress.childNodes.ID"},
"userID":{"$first":"$userID"},
"pedagogyID":{"$first":"$pedagogyID"},
"count":{"$sum":1}
}},
{"$match":{"count":{"$gt":1}}},
{"$group":{"_id":{"userID":"$userID","pedagogyID":"$pedagogyID"}}},
{"$replaceRoot":{"newRoot":"$_id"}}
],
{ allowDiskUse:true }
)
You can use below aggregation.
db.colname.aggregate([
{"$unwind":"$summary.LearnProgress.childNodes"},
{"$group":{
"_id":{"_id":"$_id","ID":"$summary.LearnProgress.childNodes.ID"},
"userID":{"$first":"$userID"},
"pedagogyID":{"$first":"$pedagogyID"},
"count":{"$sum":1}
}},
{"$match":{"count":{"$gt":1}}},
{"$group":{"_id":{"userID":"$userID","pedagogyID":"$pedagogyID"}}},
{"$replaceRoot":{"newRoot":"$_id"}}
],{"allowDiskUse":true})
db.collectionname.aggregate(
// Pipeline
[
// Stage 1
{
$unwind: {
path : "$summary.LearnProgress.childNodes",
}
},
// Stage 2
{
$group: {
_id:'$summary.LearnProgress.childNodes.ID',
count:{$sum:1},
pedagogyID:{$first:'$pedagogyID'},
userID:{$first:'$userID'}
}
},
// Stage 3
{
$match: {
count:{$gt:1}
}
},
// Stage 4
{
$project: {
userID:1,
pedagogyID:1,
_id:0
}
},
]
);

mongodb aggregation match multiple $and on the same field

i have a document like this :
{
"ExtraFields" : [
{
"value" : "print",
"fieldID" : ObjectId("5535627631efa0843554b0ea")
},
{
"value" : "14",
"fieldID" : ObjectId("5535627631efa0843554b0eb")
},
{
"value" : "POLYE",
"fieldID" : ObjectId("5535627631efa0843554b0ec")
},
{
"value" : "30",
"fieldID" : ObjectId("5535627631efa0843554b0ed")
},
{
"value" : "0",
"fieldID" : ObjectId("5535627631efa0843554b0ee")
},
{
"value" : "0",
"fieldID" : ObjectId("5535627731efa0843554b0ef")
},
{
"value" : "0",
"fieldID" : ObjectId("5535627831efa0843554b0f0")
},
{
"value" : "42",
"fieldID" : ObjectId("5535627831efa0843554b0f1")
},
{
"value" : "30",
"fieldID" : ObjectId("5535627831efa0843554b0f2")
},
{
"value" : "14",
"fieldID" : ObjectId("5535627831efa0843554b0f3")
},
{
"value" : "19",
"fieldID" : ObjectId("5535627831efa0843554b0f4")
}
],
"id" : ObjectId("55369e60733e4914550832d0"), "title" : "A product"
}
what i want is to match one or more sets from the ExtraFields array. For example, all the products that contain the values print and 30. Since a value may be found in more than one fieldID (like 0 or true) we need to create a set like
WHERE (fieldID : ObjectId("5535627631efa0843554b0ea"), value : "print")
Where i'm having problems is when querying more than one fields. The pipeline i came up with is :
db.products.aggregate([
{'$unwind': '$ExtraFields'},
{
'$match': {
'$and': [{
'$and': [{'ExtraFields.value': {'$in': ["A52A2A"]}}, {
'ExtraFields.fieldID': ObjectId("5535627631efa0843554b0ea")
}]
}
,
{
'$and': [{'ExtraFields.value': '14'}, {'ExtraFields.fieldID': ObjectId("5535627631efa0843554b0eb")}]
}
]
}
},
]);
This returns zero results, but this is what i want to do in theory. Match all items that contain set 1 AND all that contain set 2.
The end result should look like a faceted search output :
[
{
"_id" : {
"values" : "18",
"fieldID" : ObjectId("5535627831efa0843554b0f3")
},
"count" : 2
},
{
"_id" : {
"values" : "33",
"fieldID" : ObjectId("5535627831efa0843554b0f2")
},
"count" : 1
}
]
Any ideas?
You could try the following aggregation pipeline
db.products.aggregate([
{
"$match": {
"ExtraFields.value": { "$in": ["A52A2A", "14"] },
"ExtraFields.fieldID": {
"$in": [
ObjectId("5535627631efa0843554b0ea"),
ObjectId("5535627631efa0843554b0eb")
]
}
}
},
{
"$unwind": "$ExtraFields"
},
{
"$match": {
"ExtraFields.value": { "$in": ["A52A2A", "14"] },
"ExtraFields.fieldID": {
"$in": [
ObjectId("5535627631efa0843554b0ea"),
ObjectId("5535627631efa0843554b0eb")
]
}
}
},
{
"$group": {
"_id": {
"value": "$ExtraFields.value",
"fieldID": "$ExtraFields.fieldID"
},
"count": {
"$sum": 1
}
}
}
])
With the sample document provided, this gives the output:
/* 1 */
{
"result" : [
{
"_id" : {
"value" : "14",
"fieldID" : ObjectId("5535627631efa0843554b0eb")
},
"count" : 1
}
],
"ok" : 1
}