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

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.

Related

Getting item in a nested array mongoldb

I am trying to aggregate an object but getting null. Would appreciate help.
here is my sample object
[
{
"_id": 1,
"item": "sweatshirt",
"price.usd": 45.99,
"am": [
{
"one": 100
}
],
qty: 300
},
{
"_id": 2,
"item": "winter coat",
"price.usd": 499.99,
"am": [
{
"one": 50
}
],
qty: 200
},
{
"_id": 3,
"item": "sun dress",
"price.usd": 199.99,
"am": [
{
"one": 10
}
],
qty: 250
}
]
This is my query
db.collection.aggregate([
{
$group: {
_id: {
$getField: {
$literal: "am.one"
}
},
}
}
])
currently I am getting
[
{
"_id": null
}
]
any help is appreciated.
I want my response to look like
{
_id: 1
anotherThin: anotherValue
}
Here's one way you could do it.
db.collection.aggregate([
{ // only pass docs that have a numerical "am.one"
"$match": {
"am.one": {"$type": "number"}
}
},
{ // unwind in case multiple objects in "am"
"$unwind": "$am"
},
{ // group by "id" and average "one" values
"$group": {
"_id": "$am.id",
"am1avg": {"$avg": "$am.one"}
}
}
])
Try it on mongoplayground.net.

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

How to use `$size` with embedded document?

I'm having difficulty getting the size of arrays within embedded documents.
Given a collection with some document:
{
"_id": { "$oid": "60e26137600b71f2939ebbe7" },
"name": "Outer doc",
"inner_docs": [
{ "name": "one", "vals": [1,2,3,4,5] },
{ "name": "two", "vals": [1,2,3,4] },
{ "name": "three", "vals": [1,2,3] }
]
}
By applying a $project stage in aggregation with $size like:
{
"outer_num": { "$size": "$inner_docs" },
"inner_docs.num_vals": { "$size": "$inner_docs.vals" }
}
I get the resultant doc:
{
"_id": { "$oid": "60e26137600b71f2939ebbe7" },
"inner_docs": [
{ "name": "one", "num_vals": 3 },
{ "name": "two", "num_vals": 3 },
{ "name": "three", "num_vals": 3 }
],
"outer_num": 3,
}
outer_num is correct, but num_vals is not, it should be the values 5, 4 and 3 respectively.
How could I fix this?
Demo - https://mongoplayground.net/p/dpsp907E_fa
Use $map
Applies an expression to each item in an array and returns an array with the applied results.
db.collection.aggregate([
{
$project: {
"outer_num": { $size: "$inner_docs"},
"inner_docs": {
$map: {
input: "$inner_docs",
as: "doc",
in: { name: "$$doc.name", num_vals: { $size: [ "$$doc.vals" ] } }
}
}
}
}
])

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

Fetch mongo documents based on multiple fields

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
})