Change value of a field in embed document with condition in mongo DB - mongodb

I try to get a data from the database. But I've had a problem in a section where I can't edit the value of the embedded fields. I want to put the boolean value instead of the mobile number. if it has a value, equal to the true and if it does not have a value, it will be false.
I have document like this in my collection:
{
"_id": ObjectId("606d6ea237c2544324925c61"),
"title": "newwww",
"message": [{
"_id": ObjectId("606d6e1037c2544324925c5f"),
"text": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"user_id": null,
"user_full_name": null,
"user_mobile_number": null,
"submit_date": {
"$date": "2021-04-07T08:32:16.093Z"
}
}, {
"_id": ObjectId("606d6edc546feebf508d75f9"),
"text": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"user_id": null,
"user_full_name": null,
"user_mobile_number": "9653256482",
"submit_date": {
"$date": "2021-04-07T08:35:40.881Z"
}
}],
"user_mobile_number": "9652351489",
}
Do query:
db.ticket.aggregate([{"$match": {"_id": ObjectId("606d6ea237c2544324925c61")}}, {
"$project": {"message.is_admin":{
"$let": {
vars: {
mobile_number: "$message.user_mobile_numebr"
},
in: {
"$cond": [{$eq: ['$$mobile_number', null]},false,true ]
}
}
}
}
}])
and result is:
[
{
"_id": ObjectId("606d6ea237c2544324925c61"),
"message": [
{
"is_admin": true
},
{
"is_admin": true
}
]
}
]
but i want result like this:
[
{
"_id": ObjectId("606d6ea237c2544324925c61"),
"message": [
{
"is_admin": false
},
{
"is_admin": true
}
]
}
]
means I want when message.user_mobile_number has value, get true and when value is null get false.
How can I do this?

Demo - https://mongoplayground.net/p/h_zkRfDbZrS
Use $map
db.collection.aggregate([
{
"$project": {
"message": {
"$map": {
input: "$message",
"as": "message",
"in": {
"$cond": [{ "$eq": [ "$$message.user_mobile_number", null ] }, { is_admin: false }, { is_admin: true } ]
}
}
}
}
}
])

Related

Need help filtering documents with criteria inside array - mongoDB

Need help with query to filter the records in mongoDB. I am using compass to run the que
We have thousands of records/documents where each record/document contains the following array. For few documents, the events in lifeCycleinfo are out of order i.e. payment.completed event comes before 1payment.completed1 event.
I need to filter those records where completed event comes before created event
{
"lifeCycleInfo": [
{
"eventId": "9b8b6adfae",
"eventSubType": "SendTransfer_Receipt",
"eventType": "SendTransfer",
"odsTimestamp": {
"$date": "2023-02-06T14:33:42.308Z"
},
"payload": "{}",
"timestamp": {
"$date": "2023-02-06T14:33:42.271Z"
}
},
{
"eventId": "06e8d144-531b02",
"eventSubType": "payment.created",
"eventType": "Notification",
"odsTimestamp": {
"$date": "2023-02-06T14:33:45.488Z"
},
"payload": "{}",
"timestamp": {
"$date": "2023-02-06T14:33:45.479Z"
}
},
{
"eventId": "9da54454d6",
"eventSubType": "payment.completed",
"eventType": "Notification",
"odsTimestamp": {
"$date": "2023-02-06T14:33:46.698Z"
},
"payload": "{}",
"timestamp": {
"$date": "2023-02-06T14:33:46.689Z"
}
}
]
}
I tried to find it based on array index but not working.
{"lifeCycleInfo[1].eventtype":"payment.completed"}
You can use $reduce with $switch to iterate through the lifeCycleInfo array and keep track of the status with an object {inOrder: <bool>, seenCreated: <bool>}. We can conditionally set the 2 bools and use the final result to get the matched documents.
db.collection.aggregate([
{
$set: {
inOrderResult: {
"$reduce": {
"input": "$lifeCycleInfo",
"initialValue": {
inOrder: false,
seenCreated: false
},
"in": {
"$switch": {
"branches": [
{
"case": {
$eq: [
"$$this.eventSubType",
"payment.created"
]
},
"then": {
inOrder: "$$value.inOrder",
seenCreated: true
}
},
{
"case": {
$and: [
{
$eq: [
"$$this.eventSubType",
"payment.completed"
]
},
{
$eq: [
"$$value.seenCreated",
true
]
}
]
},
"then": {
inOrder: true,
seenCreated: true
}
}
],
default: "$$value"
}
}
}
}
}
},
{
$match: {
"inOrderResult.inOrder": true
}
}
])
Mongo Playground

mongo query for a one-to-many collection where all records has to match the condition and get an unique record

Employee has multiple employeeActions, the employeeActions data looks like this:
[
{
"email": "one#gmail.com",
"companyRegNo": 105,
"event": {
"created": ISODate("2022-09-16T06:42:04.387Z"),
"desc": "COMPLETED_APPLICATIONS",
"note": "Direct apply"
}
},
{
"email": "one#gmail.com",
"companyRegNo": 105,
"event": {
"created": ISODate("2022-09-20T06:42:42.761Z"),
"desc": "ASKED_TO_REVIEW",
}
},
{
"email": "two#gmail.com",
"companyRegNo": 227,
"event": {
"created": ISODate("2022-09-16T06:42:04.387Z"),
"desc": "COMPLETED_APPLICATIONS",
"note": "Direct apply",
}
},
{
"email": "two#gmail.com",
"companyRegNo": 227,
"event": {
"created": ISODate("2022-09-28T06:42:42.761Z"),
"desc": "ASKED_TO_REVIEW",
}
},
{
"email": "three#gmail.com",
"companyRegNo": 157,
"event": {
"created": ISODate("2022-09-16T06:42:04.387Z"),
"desc": "COMPLETED_APPLICATIONS",
"note": "Direct apply",
}
},
{
"email": "four#gmail.com",
"companyRegNo": 201,
"deleted": true,
"event": {
"created": ISODate("2022-09-15T06:42:42.761Z"),
"desc": "COMPLETED_APPLICATIONS",
}
},
]
I need to write an aggregation query to get all email ids where the employee action of the user
- Does not have an ASKED_TO_REVIEW event created before '2022-09-25'
- deleted is either false or does not exist
The out put should have only
{"email": "one#gmail.com"}
{"email": "three#gmail.com"}
The below match and project query did not work
db.collection.aggregate([
{
"$match": {
"$and": [
{
"deleted": {
"$ne": true
}
},
{
"$or": [
{
"$and": [
{
"event.name": {
"$eq": "ASKED_TO_REVIEW"
}
},
{
"event.created": {
"$lt": ISODate("2022-09-25")
}
}
]
},
{
"event.name": {
"$ne": "ASKED_TO_REVIEW"
}
}
]
}
]
}
},
{
"$project": {
"email": 1,
"_id": 0
}
}
])
How do i go about this?
You need to group the events by email and then apply your filtering logic to those groups, something like this:
db.collection.aggregate([
{
"$group": {
"_id": "$email",
"field": {
"$push": "$$ROOT"
}
}
},
{
"$match": {
$expr: {
"$eq": [
0,
{
"$size": {
"$filter": {
"input": "$field",
"as": "item",
"cond": {
"$or": [
{
"$and": [
{
"$eq": [
{
"$getField": {
"field": "desc",
"input": "$$item.event"
}
},
"ASKED_TO_REVIEW"
]
},
{
"$lt": [
{
"$getField": {
"field": "created",
"input": "$$item.event"
}
},
ISODate("2022-09-25")
]
}
]
},
{
"$eq": [
{
"$getField": {
"field": "deleted",
"input": "$$item"
}
},
true
]
}
]
}
}
}
}
]
}
}
},
{
"$project": {
email: "$_id",
"_id": 0
}
}
])
Playground link.
Figured out the working query. After grouping by email, $elemMatch needs to be used for the and condition between "event.desc" and "event.created"
db.collection.aggregate([
{
"$group": {
"_id": "$email",
"field": {
"$push": "$$ROOT"
}
}
},
{
"$match": {
"$and": [
{
"field.deleted": {
"$ne": true
}
},
{
"$or": [
{
"field": {
"$elemMatch": {
"event.desc": "ASKED_TO_REVIEW",
"event.created": {
"$lt": ISODate("2022-09-25")
}
}
}
},
{
"field.event.desc": {
"$ne": "ASKED_TO_REVIEW"
}
}
]
}
]
}
},
{
"$project": {
email: "$_id",
"_id": 0
}
}
])
Playground Link

Filter documents that have id in another collection in MongoDB with aggregation framework

So I have two collection. collectionA and collectionB
collection A has following documents
db={
"collectiona": [
{
"_id": "6173ddf33ed09368a094e68a",
"title": "a"
},
{
"_id": "61wefdf33ed09368a094e6dc",
"title": "b"
},
{
"_id": "61wefdfewf09368a094ezzz",
"title": "c"
},
],
"collectionb": [
{
"_id": "6173ddf33ed0wef368a094zq",
"collectionaID": "6173ddf33ed09368a094e68a",
"data": [
{
"userID": "123",
"visibility": false,
"response": false
},
{
"userID": "2345",
"visibility": true,
"response": true
}
]
},
{
"_id": "6173ddf33ed09368awef4e68g",
"collectionaID": "61wefdf33ed09368a094e6dc",
"data": [
{
"userID": "5678",
"visibility": false,
"response": false
},
{
"userID": "674",
"visibility": true,
"response": false
}
]
}
]
}
So What I need is documents from collection A which has response false in collection B
and document should be sorted by first the ones that have visibility false and then the ones that have visibility true
for eg. userID : 123 should get 3 documents
{
"_id": "6173ddf33ed09368a094e68a",
"title": "a"
},
{
"_id": "61wefdf33ed09368a094e6dc",
"title": "b"
},
{
"_id": "61wefdfewf09368a094ezzz",
"title": "c"
},
whereas userID 2345 should get two
{
"_id": "61wefdf33ed09368a094e6dc",
"title": "b"
},
{
"_id": "61wefdfewf09368a094ezzz",
"title": "c"
},
User 674 will receive 3 objects from collection A but second would be in the last as it has visibility true for that document
{
"_id": "6173ddf33ed09368a094e68a",
"title": "a"
},
{
"_id": "61wefdfewf09368a094ezzz",
"title": "c"
},
{
"_id": "61wefdf33ed09368a094e6dc",
"title": "b"
},
MongoDB Playground link : https://mongoplayground.net/p/3rLry0FPlw-
Really appreciate the help. Thanks
You can start from collectionA:
$lookup the collectionB for the record related to the user specified
filter out collectionB documents according to response
assign a helper sortrank field based on the visibility and whether collectionaID is a match
$sort according to sortrank
wrangle back to the raw collection A
db.collectiona.aggregate([
{
"$lookup": {
"from": "collectionb",
let: {
aid: "$_id"
},
"pipeline": [
{
$unwind: "$data"
},
{
$match: {
$expr: {
$and: [
{
$eq: [
"$data.userID",
"2345"
]
},
{
$eq: [
"$collectionaID",
"$$aid"
]
}
]
}
}
}
],
"as": "collB"
}
},
{
$match: {
"collB.data.response": {
$ne: true
}
}
},
{
"$unwind": {
path: "$collB",
preserveNullAndEmptyArrays: true
}
},
{
"$addFields": {
"sortrank": {
"$cond": {
"if": {
$eq: [
"$collB.data.visibility",
false
]
},
"then": 1,
"else": {
"$cond": {
"if": {
$eq: [
"$collB.collectionaID",
"$_id"
]
},
"then": 3,
"else": 2
}
}
}
}
}
},
{
$sort: {
sortrank: 1
}
},
{
$project: {
collB: false,
sortrank: false
}
}
])
Here is the Mongo playground for your reference.

Sort records by array field values in MongoDb

I have a collection which has documents like;
{
"name": "Subject1",
"attributes": [{
"_id": "security_level1",
"level": {
"value": "100",
"valueKey": "ABC"
}
}, {
"_id": "security_score1",
"level": {
"value": "1000",
"valueKey": "CDE"
}
}
]
},
{
"name": "Subject2",
"attributes": [{
"_id": "security_level1",
"level": {
"value": "99",
"valueKey": "XYZ"
}
}, {
"_id": "security_score1",
"level": {
"value": "2000",
"valueKey": "EDF"
}
}
]
},
......
Each document will have so many attributes generated dynamically, can be different in size.
Is it possible to sort records based on level.value of security_level1? (security_level1 is _id field value)
As per above example, the second document ("name": "Subject2") should come first as the value ('level.value') of _id:security_level1 is 99, which is less than of Subject1's security_level1 value (100) - (Ascending order)
Use $filter and $arrayElemAt to get security_level1 item. Then you can use $toInt to convert that value to an integer so that $sort can be applied:
db.collection.aggregate([
{
$addFields: {
level: {
$let: {
vars: {
level_1: { $arrayElemAt: [ { $filter: { input: "$attributes", cond: { $eq: [ "$$this._id", "security_level1" ] } } } ,0] }
},
in: {
$toInt: "$$level_1.level.value"
}
}
}
}
},
{
$sort: {
level: 1
}
}
])
Mongo Playground

mongodb, Updating a field in an object in array field equal to a document field

let's say I have docs such as
{
"nickname": "my nickname",
"comments": [
{
"id": 1
},
{
"id": 1
}
]
}
how do I update it to look like
{
"nickname": "my nickname",
"comments": [
{
"id": 1,
"nickname": "my nickname"
},
{
"id": 1,
"nickname": "my nickname"
}
]
}
This does not seem to be working
db.getCollection('users').update(
{
"comments.nickname": null
},
{ "$set": { "comments.$.nickname": "$nickname" } });
This is just an example to represent my problem.
I would not like to hear about re-structuring and optimizing the fields.
Thanks!
Try this (v4.2):
db.users.updateMany(
{"comments.nickname":null},
[
{"$set": {"comments.nickname": "$nickname"}}
]
)
Note: It will override if any comments.nickname already exists
db.users.aggregate([
{
$match: {
"comments.nickname": null
}
},
{
$addFields: {
comments: {
$map: {
input: "$comments",
in: {
id: "$$this.id",
nickname: {
$cond: [
"$$this.nickname",
"$$this.nickname",
"$nickname"
]
}
}
}
}
}
},
{
$out: "users"
}
])
Note: It will keep already existing values