Fetch mongo documents based on multiple fields - mongodb

Given mongo document of the following form in a collection:
{
"_id":"ObjectId",
"value":{
"id": 1,
"payment": [
{
"status": {
"id": "1.1",
"value": "Paid"
}
},
{
"status": {
"id": "1.2",
"value": "Scheduled"
}
},
{
"status": {
"id": "1.3",
"value": "Recorded"
}
}
]
}
}
ids = [1,2,3,4]
How can i fetch all documents having id in ids and at least one of payments.status.value equal to Scheduled state ?
I am using the following query but it's returning 0 records,
db.collectionName.find({$and:[{"value.id":{$in:ids}},{"value.payment.status.value":"Scheduled"}]})`

you can specify the name of the collection
so instead of this:
db.collection.find({
$and:
[
{"value.id": {$in:ids}},
{"value.payment.status.value":"Scheduled"}
]
})
you can write:
db.payments.find({
$and:
[
{"value.id":{$in:ids}},
{"value.payment.status.value":"Scheduled"}
]
})

db.collection.find({
'value.payment': {
$elemMatch: {
'status.value': 'Scheduled'
}
},
'value.id': {
$in: [1, 2, 3, 4]
}
}, {
'value.id': 1,
'value.payment.$.status': 1
})

Related

mongodb - query to count number of array elements inside an object

I've a mongodb collection "customer_vehicle_details" as below:
{
"_id": ObjectId("5660c2a5b6fcba2d47baa2d9"),
"customer_id": 4,
"customer_vehicles": {
"cars": [
{
"id": 1,
"name": "abc"
},
{
"id": 2,
"name": "xyz"
}
],
"bikes": [
{
"id": 1,
"name": "pqr"
},
{
"id": 2,
"name": "asdf"
}
]
}
}
I want to count total number of "cars" and total number of "bikes" separately in "customer_vehicles" collections. Not a sum of cars and bikes.
I tried with
db.customer_vehicle_details.aggregate(
{
$group: {
_id: "$customer_vehicles.cars",
total: { $sum: { $size:"$cars" } }
}
}
)
but this is giving me an error ""errmsg" : "The argument to $size must be an array, but was of type: missing"
How do I count total number of array elements inside an object in mongoDB?
Does this helps:
db.collection.aggregate([
{
"$project": {
carsCount: {
"$size": "$customer_vehicles.cars"
},
bikesCount: {
"$size": "$customer_vehicles.bikes"
}
}
}
])
Here's the playground link.

MongoDB returning two array while using alias

I am a json in mongodb. The structure is below --
{
id: "1",
name: "sample",
user: [
{
data_alias: "ex",
value: "efg"
}
]
}
Now I want the the data_alias to be data after mongodb returns the result.
When I am using below query --
db.coll.find(
{"id":"1"},
{"data": "$user.data_alias","_id": 0,"value":1}
)
Now it is retuning data like --
{
"user": [
{
"value": "efg",
},
],
"data": [
"ex"
]
}
But I want the returning value should be like --
{
"data": "ex",
"name": "sample"
}
Also I have tried with aggregate function
db.colls.aggregate([
{
$match: {
"id": "1"
}
},
{
"$project": {
"_id": 0,
"data": "$user.data_alias"
}
}
]);
Both the queries returning same result.
Just $unwind the user array and $project to your expected output.
db.collection.aggregate([
{
$match: {
name: "sample"
}
},
{
"$unwind": "$user"
},
{
"$project": {
_id: 0,
"name": 1,
"data": "$user.data_alias"
}
}
])
Here is the Mongo playground for your reference.

Query maximum N records of each group base on a condition in MongoDB?

I have a question regarding querying data in MongoDB. Here is my sample data:
{
"_id": 1,
"category": "fruit",
"userId": 1,
"name": "Banana"
},
{
"_id": 2,
"category": "fruit",
"userId": 2,
"name": "Apple"
},
{
"_id": 3,
"category": "fresh-food",
"userId": 1,
"name": "Fish"
},
{
"_id": 4,
"category": "fresh-food",
"userId": 2,
"name": "Shrimp"
},
{
"_id": 5,
"category": "vegetable",
"userId": 1,
"name": "Salad"
},
{
"_id": 6,
"category": "vegetable",
"userId": 2,
"name": "carrot"
}
The requirements:
If the category is fruit, returns all the records match
If the category is NOT fruit, returns maximum 10 records of each category grouped by user
The category is known and stable, so we can hard-coded in our query.
I want to get it done in a single query. So the result expected should be:
{
"fruit": [
... // All records of
],
"fresh-food": [
{
"userId": 1,
"data": [
// Top 10 records of user 1 with category = "fresh-food"
]
},
{
"userId": 2,
"data": [
// Top 10 records of user 2 with category = "fresh-food"
]
},
...
],
"vegetable": [
{
"userId": 1,
"data": [
// Top 10 records of user 1 with category = "vegetable"
]
},
{
"userId": 2,
"data": [
// Top 10 records of user 2 with category = "vegetable"
]
},
]
}
I've found the guideline to group by each group using $group and $slice, but I can't apply the requirement number #1.
Any help would be appreciated.
You need to use aggregation for this
$facet to categorize incoming data, we categorized into two. 1. Fruit and 2. non_fruit
$match to match the condition
$group first group to group the data based on category and user. Second group to group by its category only
$objectToArray to make the object into key value pair
$replaceRoot to make the non_fruit to root with fruit
Here is the code
db.collection.aggregate([
{
"$facet": {
"fruit": [
{ $match: { "category": "fruit" } }
],
"non_fruit": [
{
$match: {
$expr: {
$ne: [ "$category", "fruit" ]
}
}
},
{
$group: {
_id: { c: "$category", u: "$userId" },
data: { $push: "$$ROOT" }
}
},
{
$group: {
_id: "$_id.c",
v: {
$push: {
uerId: "$_id.u",
data: { "$slice": [ "$data", 3 ] }
}
}
}
},
{ $addFields: { "k": "$_id", _id: "$$REMOVE" } }
]
}
},
{ $addFields: { non_fruit: { "$arrayToObject": "$non_fruit" } }},
{
"$replaceRoot": {
"newRoot": {
"$mergeObjects": [ "$$ROOT", "$non_fruit" ]
}
}
},
{ $project: { non_fruit: 0 } }
])
Working Mongo playground

Add field to every object in array of objects in mongo aggregation

I have one field in schema at root level and want to add it in every object from array which matches a condition.
Here is a sample document....
{
calls: [
{
"name": "sam",
"status": "scheduled"
},
{
"name": "tom",
"status": "cancelled"
},
{
"name": "bob",
"status": "scheduled"
},
],
"time": 1620095400000.0,
"call_id": "ABCABCABC"
}
Required document is as follows:
[
{
"call_id": "ABCABCABC",
"calls": [
{
"call_id": "ABCABCABC",
"name": "sam",
"status": "scheduled"
},
{
"name": "tom",
"status": "cancelled"
},
{
"call_id": "ABCABCABC",
"name": "bob",
"status": "scheduled"
}
],
"time": 1.6200954e+12
}
]
The call_id should be added to all objects in array whose status is "scheduled".
Is it possible to do this with mongo aggregation? I have tried $addFields but was unable to achieve above result.
Thanks in advance!
Here is how I would do it using $map and $mergeObjects
db.collection.aggregate([
{
"$addFields": {
calls: {
$map: {
input: "$calls",
as: "call",
in: {
$cond: [
{
$eq: [
"$$call.status",
"scheduled"
]
},
{
"$mergeObjects": [
"$$call",
{
call_id: "$call_id"
}
]
},
"$$call"
]
}
}
}
}
}
])
Mongo Playground

mongodb update multiple Array elements using db.update()

I compiled 2 update queries by referring to related stackoverflow answers, however, it doesn't seem to work, query updates all elements while only elements matching the criteria are expected to update.
Document:
[
{
"_id": 259,
"members": [
{
"email": "test1#gmail.com",
"added_by": "javapedia.net",
"status": "pending"
},
{
"email": "test2#gmail.com",
"added_by": "javapedia.net",
"status": "pending"
},
{
"email": "test3#gmail.com",
"status": "pending"
}
]
}
]
Query1: Using elemMatch operator, mongodb playground: https://mongoplayground.net/p/4cNgWJse86W
db.collection.update({
_id: 259,
"members": {
"$elemMatch": {
"email": {
"$in": [
"test3#gmail.com",
"test4#gmail.com"
]
}
}
}
},
{
"$set": {
"members.$[].status": "active"
}
},
{
"multi": true
})
Query2: using $in, mongodb playground : https://mongoplayground.net/p/tNu395B2RFx
db.collection.update({
_id: 259,
"members.email": {
"$in": [
"test3#gmail.com",
"test4#gmail.com"
]
}
},
{
"$set": {
"members.$[].status": "active"
}
},
{
"multi": true
})
Expected result: only one element with test3#gmail.com status should be updated to active.
Actual result: both queries update all records.
Is this what you're looking for?
db.collection.update({
_id: 259,
},
{
"$set": {
"members.$[el].status": "active"
}
},
{
arrayFilters: [
{
"el.email": {
$in: [
"test3#gmail.com",
"test4#gmail.com"
]
}
}
]
})
You can put the initial conditions back if needed, I just keep this short (and to me they make no sense).
multi:true isn't needed for one document
Maybe better semantically to use updateOne()