MongoDB - sort by words - mongodb

Having data like below.
{
"summary": "testing api1",
"UpdatedDate": {
"$date": "2018-12-30T00:00:00.000Z"
},
"status": "draft"
},
{
"summary": "testing api2",
"UpdatedDate": {
"$date": "2018-12-30T00:00:00.000Z"
},
"status": "published"
},
{
"summary": "testing api3",
"UpdatedDate": {
"$date": "2018-12-30T00:00:00.000Z"
},
"status": "delete"
}
I want to sort based "status"
For ex: If I have given sort by status "published" then I need data like below
{
"summary": "testing api2",
"UpdatedDate": {
"$date": "2018-12-31T00:00:00.000Z"
},
"status": "published"
},
{
"summary": "testing api1",
"UpdatedDate": {
"$date": "2018-12-30T00:00:00.000Z"
},
"status": "draft"
},
{
"summary": "testing api3",
"UpdatedDate": {
"$date": "2019-01-01T00:00:00.000Z"
},
"status": "delete"
}
It's possible in mongodb? or anything available similar like this?

Ideally, you would assign a numerical value. But you can do it just as you have it with Aggregation framework.
Something like this with $addFields and $cond:
db.getCollection("yourcollection").aggregate([
{
$addFields: {
sortNum: {
$cond: [
{ $eq: ["$status", "published"] },
0,
{ $cond: [{ $eq: ["$status", "draft"] }, 1, 2] }
]
}
}
},
{
$sort: {
sortNum: 1
}
}
]);
OR With $switch
db.collection.aggregate([{
$addFields: {
sortNum: {
$switch: {
branches: [
{
case: {
$eq: ["$status", "published"]
},
then: 1
},
{
case: {
$eq: ["$status", "delete"]
},
then: 2
},
{
case: {
$eq: ["$status", "draft"]
},
then: 3
}
],
default: 0
}
}
}
},
{
$sort: {
sortNum: 1
}
}]);

Related

MongoDB aggregate using $match with $expr with array

MongoDB 5.0.9
I am trying to get
value of application within course and their specification
value of paid application ( status : paid) based on course and their specification
courses collection having multiple courses with specification which might be there maybe not
[
{
"_id": {
"$oid": "62aab6669b3740313d881a30"
},
"course_name": "Master",
"fees": "Rs.1000.0/-",
"course_specialization": [
{
"spec_name": "Social Work",
"is_activated": true
}
],
"college_id": {
"$oid": "628dfd41ef796e8f757a5c13"
},
"is_pg": true
},
{
"_id": {
"$oid": "62aab6669b3740313d881a38"
},
"college_id": {
"$oid": "628dfd41ef796e8f757a5c13"
},
"course_name": "BBA",
"fees": "Rs.1000.0/-",
"is_pg": false,
"course_specialization": null
},
{
"_id": {
"$oid": "628f3967cb69fc0789e69181"
},
"course_name": "BTech",
"fees": "Rs.1000.0/-",
"course_specialization": [
{
"spec_name": "Computer Science and Engineering",
"is_activated": true
},
{
"spec_name": "Mutiple Specs",
"is_activated": true
}
],
"college_id": {
"$oid": "628dfd41ef796e8f757a5c13"
},
"is_pg": false
},
{
"_id": {
"$oid": "628f35a1cb69fc0789e6917e"
},
"course_name": "Bachelor",
"fees": "Rs.1000.0/-",
"course_specialization": [
{
"spec_name": "Social Work",
"is_activated": true
}
],
"college_id": {
"$oid": "628dfd41ef796e8f757a5c13"
},
"is_pg": false
}
],
Student Application forms collection where we are storing student application forms details
[
{
"_id": {
"$oid": "62cd476adbc878a0490e20ee"
},
"spec_name1": "Social Work",
"spec_name2": "",
"spec_name3": "",
"student_id": {
"$oid": "62cd1374dbc878a0490e20a5"
},
"course_id": {
"$oid": "62aab6669b3740313d881a30"
},
"current_stage": 2.5,
"declaration": true,
"payment_info": {
"payment_id": "123458",
"status": "paid"
},
"enquiry_date": {
"$date": {
"$numberLong": "1657620330432"
}
},
"last_updated_time": {
"$date": {
"$numberLong": "1657621796062"
}
}
},
{
"_id": {
"$oid": "62cd476adbc878a0490e20ef"
},
"spec_name1": "",
"spec_name2": "",
"spec_name3": "",
"student_id": {
"$oid": "62cd1374dbc878a0490e20a5"
},
"course_id": {
"$oid": "62aab6669b3740313d881a38"
},
"current_stage": 2.5,
"declaration": true,
"payment_info": {
"payment_id": "123458",
"status": "paid"
},
"enquiry_date": {
"$date": {
"$numberLong": "1657620330432"
}
},
"last_updated_time": {
"$date": {
"$numberLong": "1657621796062"
}
}
},
{
"_id": {
"$oid": "62cdc12000b820f5ea58cc60"
},
"spec_name1": "Social Work",
"spec_name2": "",
"spec_name3": "",
"student_id": {
"$oid": "62cdad90a9b64d58b15e6976"
},
"course_id": {
"$oid": "628f35a1cb69fc0789e6917e"
},
"current_stage": 6.25,
"declaration": false,
"payment_info": {
"payment_id": "",
"status": ""
},
"enquiry_date": {
"$date": {
"$numberLong": "1657651488511"
}
},
"last_updated_time": {
"$date": {
"$numberLong": "1657651987155"
}
}
}
]
Desired output with every specification within the course
[
"_id": {
"coursename": "Master",
"spec": "Social Work",
"Application_Count": 1,
"Paid_Application_Count:0
},
{
"_id": {
"coursename": "Bachelor"
"spec":"" ,
"Application_Count": 1,
"Paid_Application_Count:0
},
{
"_id": {
"coursename": "BBA"
"spec":"" ,
"Application_Count": 1,
"Paid_Application_Count:1
},
]
Aggregation Query
[{
$match: {
college_id: ObjectId('628dfd41ef796e8f757a5c13')
}
}, {
$project: {
_id: 1,
course_name: 1,
course_specialization: 1
}
}, {
$unwind: {
path: '$course_name',
includeArrayIndex: 'course_index',
preserveNullAndEmptyArrays: true
}
}, {
$unwind: {
path: '$course_specialization',
includeArrayIndex: 'course_specs_index',
preserveNullAndEmptyArrays: true
}
}, {
$lookup: {
from: 'studentApplicationForms',
'let': {
id: '$_id',
spec: '$course_specialization.spec_name'
},
pipeline: [
{
$match: {
$expr: {
$and: [
{
$eq: [
'$course_id',
'$$id'
]
},
{
$eq: [
'$spec_name1',
'$$spec'
]
}
]
}
}
},
{
$project: {
student_id: 1,
payment_info: 1,
spec_name1: 1,
spec_name2: 1,
spec_name3: 1
}
}
],
as: 'student_application'
}
}, {
$unwind: {
path: '$student_application',
includeArrayIndex: 'application',
preserveNullAndEmptyArrays: true
}
}, {
$facet: {
course: [
{
$group: {
_id: {
course_name: '$course_name',
spec: '$course_specialization'
},
count: {
$count: {}
}
}
}
],
declatration: [
{
$group: {
_id: {
course_name: '$course_name',
spec: '$course_specialization'
},
count_dec: {
$sum: {
$cond: [
'$student_application.declaration',
1,
0
]
}
}
}
}
],
payment: [
{
$group: {
_id: {
course_name: '$course_name',
spec: '$course_specialization'
},
payment: {
$sum: {
$eq: [
'$student_application.payment_info.status',
'paid'
]
}
}
}
}
]
}
}]
Problem :
I am able to get application count but it is not getting unique value if 2 specs are same then duplicate value is coming as you can see on sample application collection Social Work is in two different course . So my aggregations is not grouping them based in course name.specs
Not able to find correct Paid_Application_Count and Application_Count
Update :
Updated JSON Data Matching use cases with different type of data
MongoDB Playground
You can do it in several different ways, I took the liberty to simplify the pipeline a little bit.
I will just mention that the structure does not fully make sense to me, and there are some additional contradictions between the sample input you provided and the "text" description/pipeline description.
Just a tiny example is payment_info_status being paid in the sample and capture in the pipeline.
These things will not change the pipeline structure, will just need to be fixed by you based on the actual needs.
db.courses.aggregate([
{
$project: {
_id: 1,
course_name: 1,
course_specialization: 1
}
},
{
$unwind: {
path: "$course_specialization",
preserveNullAndEmptyArrays: true
}
},
{
$lookup: {
from: "studentApplicationForms",
"let": {
courseId: "$_id",
spec: {
$ifNull: [
"$course_specialization.spec_name",
""
]
}
},
pipeline: [
{
$match: {
$expr: {
$and: [
{
$eq: [
"$spec_name1",
"$$spec"
]
},
{
$eq: [
"$$courseId",
"$course_id"
]
}
]
}
}
},
{
$project: {
student_id: 1,
payment_info: 1,
spec_name1: 1,
spec_name2: 1,
spec_name3: 1,
declaration: 1,
}
},
{
$group: {
_id: null,
count: {
$sum: 1
},
declatration: {
$sum: {
$cond: [
"$declaration",
1,
0
]
}
},
paid: {
$sum: {
$cond: [
{
$eq: [
"$payment_info.status",
"paid"
]
},
1,
0
]
}
},
}
}
],
as: "student_application"
}
},
{
$project: {
_id: {
coursename: "$course_name",
spec: "$course_specialization.spec_name",
Application_count: {
$ifNull: [
{
$first: "$student_application.count"
},
0
]
},
Declaration_count: {
$ifNull: [
{
$first: "$student_application.declatration"
},
0
]
},
Paid_Application_Count: {
$ifNull: [
{
$first: "$student_application.paid"
},
0
]
},
}
}
}
])
Mongo Playground

MongoDB match filters with grouping and get total count

My sample data:
{
"_id": "random_id_1",
"priority": "P1",
"owners": ["user-1", "user-2"],
},
{
"_id": "random_id_2",
"priority": "P1",
"owners": ["user-1", "user-2"],
},
{
"_id": "random_id_3",
"priority": "P2",
"owners": ["user-1", "user-2"],
},
I want to run an aggregation pipeline on the data involving match filters and grouping, also I want to limit the number of groups returned as well as the number of items in each group.
Essentially, if limit=2, limit_per_group=1, group_by=owner, priority=P1, I want the following results:
[
{
"data": [
{
"group_key": "user-1",
"total_items_in_group": 2,
"limited_items": [
{
"_id": "random_id_1",
"priority": "P1",
"owners": ["user-1", "user-2"],
},
],
},
{
"group_key": "user-2",
"total_items_in_group": 2,
"limited_items": [
{
"_id": "random_id_1",
"priority": "P1",
"owners": ["user-1", "user-2"],
},
],
},
]
},
{
"metadata": {
"total_items_matched": 2,
"total_groups": 2
}
},
]
Need some help on how to write an aggregation pipeline to get the required result.
My current query is as follows:
{
"$match": {
"priority": "P1"
}
},
{
"$facet": {
"data": [
{
$addFields: {
"group_by_owners": "$owners"
}
},
{
$unwind: "$group_by_owners"
},
{
$group: {
"_id": "$group_by_owners",
"total_items_in_group": {
$sum: 1
},
"items": {
$push: "$$ROOT"
}
}
},
{
$sort: {
"total": -1
}
},
{
$unset: "items.group_by_owners"
},
{
$project: {
"_id": 1,
"total_items_in_group": 1,
"limited_items": {
$slice: [
"$items",
1
]
}
}
},
{
"$limit": 2
}
],
"metadata": [
{
$count: "total_items_matched"
}
]
}
}
Mongo playground link
I am unable to calculate the total number of groups.
add new stage of $addfields at the end of pipeline
db.collection.aggregate([
{
"$match": {
"priority": "P1"
}
},
{
"$facet": {
"data": [
{
$addFields: {
"group_by_owners": "$owners"
}
},
{
$unwind: "$group_by_owners"
},
{
$group: {
"_id": "$group_by_owners",
"total_items_in_group": {
$sum: 1
},
"items": {
$push: "$$ROOT"
}
}
},
{
$sort: {
"total": -1
}
},
{
$unset: "items.group_by_owners"
},
{
$project: {
"_id": 0,
"group_key": "$_id",
"total_items_in_group": 1,
"limited_items": {
$slice: [
"$items",
1
]
}
}
},
{
"$limit": 2
}
],
"metadata": [
{
$count: "total_items_matched",
}
]
}
},
{
"$addFields": {
"metadata.total_groups": {
"$size": "$data"
}
}
}
])
https://mongoplayground.net/p/y5a0jvr6fxI

MongoDb aggregation with arrays inside an array possible

I am struggling to find some examples of using the mongo aggregation framework to process documents which has an array of items where each item also has an array of other obejects (array containing an array)
In the example document below what I would really like is an example that sums the itemValue in the results array of all cases in the document and accross the collection where the result.decision was 'accepted'and group by the document locationCode
However, even an example that found all documents where the result.decision was 'accepted' to show or that summmed the itemValue for the same would help
Many thanks
{
"_id": "333212",
"data": {
"locationCode": "UK-555-5566",
"mode": "retail",
"caseHandler": "A N Other",
"cases": [{
"caseId": "CSE525666",
"items": [{
"id": "333212-CSE525666-1",
"type": "hardware",
"subType": "print cartridge",
"targetDate": "2020-06-15",
"itemDetail": {
"description": "acme print cartridge",
"quantity": 2,
"weight": "1.5"
},
"result": {
"decision": "rejected",
"decisionDate": "2019-02-02"
},
"isPriority": true
},
{
"id": "333212-CSE525666-2",
"type": "Stationery",
"subType": "other",
"targetDate": "2020-06-15",
"itemDetail": {
"description": "staples box",
"quantity": 3,
"weight": "1.66"
},
"result": {
"decision": "accepted",
"decisionDate": "2020-03-03",
"itemValue": "23.01"
},
"isPriority": true
}
]
},
{
"caseId": "CSE885655",
"items": [{
"id": "333212-CSE885655-1",
"type": "marine goods",
"subType": "fish food",
"targetDate": "2020-06-04",
"itemDetail": {
"description": "fish bait",
"quantity": 5,
"weight": "0.65"
},
"result": {
"decision": "accepted",
"decisionDate": "2020-03-02"
},
"isPriority": false
},
{
"id": "333212-CSE885655-4",
"type": "tobacco products",
"subType": "cigarettes",
"deadlineDate": "2020-06-15",
"itemDetail": {
"description": "rolling tobbaco",
"quantity": 42,
"weight": "2.25"
},
"result": {
"decision": "accepted",
"decisionDate": "2020-02-02",
"itemValue": "48.15"
},
"isPriority": true
}
]
}
]
},
"state": "open"
}
You're probably looking for $unwind. It takes an array within a document and creates a separate document for each array member.
{ foos: [1, 2] } -> { foos: 1 }, { foos: 2}
With that you can create a flat document structure and match & group as normal.
db.collection.aggregate([
{
$unwind: "$data.cases"
},
{
$unwind: "$data.cases.items"
},
{
$match: {
"data.cases.items.result.decision": "accepted"
}
},
{
$group: {
_id: "$data.locationCode",
value: {
$sum: {
$toDecimal: "$data.cases.items.result.itemValue"
}
}
}
},
{
$project: {
_id: 0,
locationCode: "$_id",
value: "$value"
}
}
])
https://mongoplayground.net/p/Xr2WfFyPZS3
Alternative solution...
We group by data.locationCode and sum all items with this condition:
cases[*].items[*].result.decision" == "accepted"
db.collection.aggregate([
{
$group: {
_id: "$data.locationCode",
itemValue: {
$sum: {
$reduce: {
input: "$data.cases",
initialValue: 0,
in: {
$sum: {
$concatArrays: [
[ "$$value" ],
{
$map: {
input: {
$filter: {
input: "$$this.items",
as: "f",
cond: {
$eq: [ "$$f.result.decision", "accepted" ]
}
}
},
as: "item",
in: {
$toDouble: {
$ifNull: [ "$$item.result.itemValue", 0 ]
}
}
}
}
]
}
}
}
}
}
}
}
])
MongoPlayground

Mongodb multi level aggregation

Data in mongo
[{
"_id": "5d71d1432f7c8151c58c4481",
"payment": {
"transactions": [
{
"_id": "5d71d1ff2f7c8151c58c44cf",
"method": "paytm",
"amount": 100,
"paymentOn": "2019-09-06T03:26:44.959Z"
},
{
"_id": "5d71d1ff2f7c8151c58c44ce",
"method": "cash",
"amount": 650,
"paymentOn": "2019-09-06T03:26:55.531Z"
}
],
"status": "partial"
},
"customer": "5d66c434c24f2b1fb6772014",
"order": {
"orderNumber": "WP-ORD-06092019-001",
"total": 770,
"balance": 20
}
},
{
"_id": "5d71d1432f7c8151c58c4481",
"payment": {
"transactions": [
{
"_id": "5d71d1ff2f7c8151c58c44cf",
"method": "paytm",
"amount": 100,
"paymentOn": "2019-09-06T03:26:44.959Z"
}
],
"status": "partial"
},
"customer": "5d66c434c24f2b1fb6772014",
"order": {
"orderNumber": "WP-ORD-06092019-001",
"total": 200,
"balance": 100
}
}]
I want to aggregate payments by method.
So the result would look like below:
Output:
Paytm: 200
Cash : 650
Unpaid(Balance): 120
I have tried:
[
{
'$unwind': {
'path': '$payment.transactions',
'preserveNullAndEmptyArrays': true
}
}, {
'$project': {
'amount': '$payment.transactions.amount',
'method': '$payment.transactions.method'
}
}, {
'$group': {
'_id': '$method',
'amount': {
'$sum': '$amount'
}
}
}
]
But how to include balance calculation as well
Using the above dataset, use the aggregate pipeline for calculation using aggregate as:
db.collection.aggregate([
{
$facet: {
paidAmounts: [
{ '$unwind': { 'path': '$payment.transactions', 'preserveNullAndEmptyArrays': true } },
{
$group: {
_id: "$payment.transactions.method",
amount: {
$sum: "$payment.transactions.amount"
}
}
}
],
leftAmounts: [
{
$group: {
_id: null,
balance: {
$sum: "$order.balance"
}
}
}
]
}
}
])
giving output:
here leftAmounts has left balance and paidAmounts having grouped paid data on basis of payment type
[
{
"leftAmounts": [
{
"_id": null,
"balance": 120
}
],
"paidAmounts": [
{
"_id": "cash",
"amount": 650
},
{
"_id": "paytm",
"amount": 200
}
]
}
]
Working solution : https://mongoplayground.net/p/7IWELKKMsWe
db.collection.aggregate([
{
"$unwind": "$payment.transactions"
},
{
"$group": {
"_id": "$_id",
"balance": {
"$first": "$order.balance"
},
"paytm": {
"$sum": {
"$cond": [
{
"$eq": [
"$payment.transactions.method",
"paytm"
]
},
"$payment.transactions.amount",
0
]
}
},
"cash": {
"$sum": {
"$cond": [
{
"$eq": [
"$payment.transactions.method",
"cash"
]
},
"$payment.transactions.amount",
0
]
}
}
}
},
{
"$group": {
"_id": null,
"balance": {
"$sum": "$balance"
},
"cash": {
"$sum": "$cash"
},
"paytm": {
"$sum": "$paytm"
}
}
}
])

Use $Redact with a regular expression

In an aggregation pipeline, I am trying to filter some elements of an array of objects, based on the value of a field in this object.
Let's say that I have this entry:
{
"_id": "5b8911d346d19645f8a66bf4",
"title": "test task",
"creation_date": "2018-08-31T10:00:51.598Z",
"logs": [
{
"_id": "5b89126c46d19645f8a66bfb",
"content": "Running"
},
{
"_id": "5b89128646d19645f8a66bfd",
"content": "Stopping"
},
{
"_id": "5b89128646d19645f8a66bfd",
"content": "Stopped"
}
]
}
My objectif is to filter only the logs containing the stop word in their content:
{
"_id": "5b8911d346d19645f8a66bf4",
"title": "test task",
"creation_date": "2018-08-31T10:00:51.598Z",
"logs": [
{
"_id": "5b89128646d19645f8a66bfd",
"content": "Stopping"
},
{
"_id": "5b89128646d19645f8a66bfd",
"content": "Stopped"
}
]
}
I tried to use $redact to eliminate all the logs that does not contain the stop word:
$redact: {
$cond: {
if: { $match: { "logs.content": { $regex: "stop", $options: 'i' }}},
then: "$$KEEP",
else: "$$PRUNE"
}
}
but I keep getting the error:
Unrecognized expression '$match'
You can try below aggregation
db.collection.aggregate([
{ "$addFields": {
"logs": {
"$filter": {
"input": "$logs",
"cond": {
"$ne": [
{ "$indexOfBytes": [
{ "$toUpper": "$$this.content" },
{ "$toUpper": "stop" }
]},
-1
]
}
}
}
}}
])
Output
[
{
"_id": "5b8911d346d19645f8a66bf4",
"creation_date": "2018-08-31T10:00:51.598Z",
"logs": [
{
"_id": "5b89128646d19645f8a66bfd",
"content": "Stopping"
},
{
"_id": "5b89128646d19645f8a66bfd",
"content": "Stopped"
}
],
"title": "test task"
}
]
As per your requirement below query is working and it is properly tested
db.users.aggregate(
// Pipeline
[
// Stage 1
{
$unwind: {
path : "$logs",
preserveNullAndEmptyArrays : true // optional
}
},
// Stage 2
{
$group: {
_id: "$_id",
"title" :{$last:"$title"} ,
"creation_date" :{$last:"$creation_date"},
logs: {
$push: {
$cond: [ {$or:[{"$eq":[{ "$substr": [ "$logs.content", 0, 4 ] }, "Stop"]},{"$eq":[{ "$substr": [ "$logs.content", 0, 4 ] }, "stop"]}]},{"_id":"$logs._id","content":"$logs.content"},null]
}
}
}
},
// Stage 3
{
$project: {
logs: {
$filter: {
input: "$logs",
as: "log",
cond: { $ne: [ "$$log", null ] }
}
}
}
},
]
// Created with Studio 3T, the IDE for MongoDB - https://studio3t.com/
);