MongoDB find documents which have multiple keys in a field - node.js

This is how a documents looks like in my dataset:
{
username: 'stack',
attempts: { 1517761701: false, 1512341532: true }
}
{
username: 'overflow',
attempts: { 1217563721: false }
}
Now, I want to retrieve every document in my dataset where attempts contains more than óne key. So the query should return the document of user 'stack' but not of user 'overflow'. What query can I apply here?

try $objectToArray to convert object to array and count the number of keys if you are using mongo 3.6+
db.cols.aggregate(
[
{$addFields: {count : {$size : {$ifNull : [{$objectToArray : "$attempts"}, []]}}}},
{$match: {count : {$gt : 1}}},
{$project: {count : 0}}
]
)

Use $redact for a single pipeline:
db.collection.aggregate([
{
"$redact": {
"$cond": [
{
"$gt": [
{ "$size": {
"$objectToArray": {
"$ifNull": [
"$attempts",
{ }
]
}
} },
1
]
},
"$$KEEP",
"$$PRUNE"
]
}
}
])

Convert attempts to an array, $unwind and then use $sortByCount to do all the work..
db.collection_name.aggregate( [
{ $addFields : { array : { $objectToArray : "$attempts"} } },
{ $unwind : "$array" },
{ $sortByCount : "$username" },
{ $match : { count : { $gte : 2 } } }
])
Outputs:
{
"_id" : "stack",
"count" : 2
}

Related

How can i do elemMatch inside array using mongodb?

I have below user details in my bookings collection
{
"_id" : ObjectId("609a382b589346973c84c6fe"),
"Name" : "abc",
"UserId":1
"Status" : "Pending",
"BookingData" : {
"Date" : ISODate("2021-04-30T04:00:00.000Z"),
"info" : [],
"BookingDataMethod" : "avf",
"Message" : null,
"products" : [
{
"_id" : ObjectId("60a4e92775e5de3570578820"),
"ProductName" : "Test1",
"ProductID" : ObjectId("60a4e92475e5de357057880a"),
"IsDeliveryFailed" : "Yes"
},
{
"_id" : ObjectId("60a4e92775e5de357057881f"),
"ProductName" : "Test2",
"ProductID" : ObjectId("60a4e92475e5de357057880d")
}
],
}
}
I have prepared a query for the below conditions and when I run the below query I should get the "UserId":1 documents but I got 0 records
condition 1: products should not be null
condition 2: ProductID should exist in the products array and should not be null
condition 3: IsDeliveryFailed should not be "Yes"
Based on the above user only one product got delivery failed(IsDeliveryFailed": "Yes") so when I run this query it should return "UserId":1 document. if both products "IsDeliveryFailed": "Yes" then
we should not get this user
Query
db.getCollection('bookings').find({
"$and": [
{ "BookingData.products": { $ne: [] } },
{ "BookingData.products": {"$elemMatch":{ "ProductID": { "$exists": true ,$ne: null } }} },
{ "BookingData.products": {"$elemMatch":{ "IsDeliveryFailed": { $ne: 'Yes' } }} }
]
})
Could someone please tell me the issue on the above query or please help me to prepare a query for the above condition?
I think you can do it with aggregations
db.collection.aggregate([
{
$match: {
"BookingData.products": { "$exists": true }
}
},
{
$set: {
"BookingData.products": {
"$filter": {
"input": "$BookingData.products",
"cond": {
$and: [
{ $ne: [ "$$this.ProductID", undefined ] },
{ $ne: [ "$$this._id", null ] },
{ $ne: [ "$$this.IsDeliveryFailed", "Yes" ] }
]
}
}
}
}
},
{
$match: {
$expr: {
$ne: [ "$BookingData.products", [] ]
}
}
}
])
Working Mongo playground

How to count specific sub documents on other sub documents in mongoose?

I have a schema like this struct:
{
"user" : "admin",
"exercises" : [
{
"name" : "exercise 1",
"documents" : [
{
"idDoc" : "1",
"name" : "doc1"
},
{
"idDoc" : "2",
"name" : "doc1"
},
{
"idDoc" : "3",
"name" : "doc2"
}
]
}
]
}
How can I count all documents that have name = doc1?
The output is:
{
"_id" : "objectId", --userId
"count" : 2
}
I know this below code can count all document but I don't have any idea to count them with conditions.
Schema.aggregate()
.match({ user: username })
.project({ size: {
$reduce : {
"input" : "$exercises",
"initialValue" : 0,
"in" : {"$add":["$$value",{"$size":"$$this.documents"}]}
}
}});
Please help! Thank you for reading.
You're going the right way by using $reduce, just need some changes in data processing. You can use $reduce with $filter to create an array that has all documents with name doc1 then use $size to count items in that array. Example:
db.collection.aggregate([
{ $match: { user: "admin" } },
{
$addFields: {
count: {
$size: {
$reduce: {
input: "$exercises",
initialValue: [],
in: {
$concatArrays: [
"$$value",
{
$filter: {
input: "$$this.documents",
as: "item",
cond: { $eq: ["$$item.name", "doc1"] }
}
}
]
}
}
}
}
}
},
])
MongoPlayground

Searching in nested arrays Mongodb

I have a mongodb with some JSON data which includes and nested arrays. I am
trying to make a query to count how many documents have a specific
value. For example here is how my json data looks:
{
"_id" : ObjectId("5ecb815bf4b8512918224e71"),
"array1" : [
{
"_id" : ObjectId("5ecb815bf4b8512918224e85"),
"xxxx" : "1450",
"yyyy" : 83,
"array2" : [
{
"_id" : ObjectId("5ecb815bf4b8512918224e88"),
"aaaa" : "1470420945276",
},
{...},
{...}]
}
The query that i am trying is the following:
db.example.aggregate([
{
$project: {
value1: {
$filter: {
input: "$array1",
as: "array",
cond: { $eq: [ "$$array.array2.aaaa" , "1470420945276" ] }
}
}
}
},
{
$project: {
value1Count: { $size: "$value1" }
}
}
])
But doesnt work and returns that value1Count=0. It looks like it doesnt nnavigate into the array2 to
read the value of the 'aaaa'. Any help?
You were almost close to getting the desired value. The problem is $$array.array2.aaaa returns an array value, so we can't use $eq here. Instead, we should use $in operator.
db.example.aggregate([
{
$project: {
value1: {
$filter: {
input: "$array1",
as: "array",
cond: {
$in: [
"1470420945276",
"$$array.array2.aaaa"
]
}
}
}
}
},
{
$project: {
value1Count: {
$size: "$value1"
}
}
}
])
MongoPlayground | Alternative solution

how to get data in mongoose where last element in array?

how to get data in mongoose where last element in array?
I have data looks like this:
[
{
"_id" : ObjectId("5b56eb3deb869312d85a8e69"),
"transactionStatus" : [
{
"status" : "pending",
"createdAt" : ISODate("2018-07-24T09:02:53.347Z")
},
{
"status" : "process",
"createdAt" : ISODate("2018-07-24T09:02:53.347Z")
}
]
},
{
"_id" : ObjectId("5b56eb3deb869312d8589765"),
"transactionStatus" : [
{
"status" : "pending",
"createdAt" : ISODate("2018-07-24T09:02:53.347Z")
},
{
"status" : "process",
"createdAt" : ISODate("2018-07-24T09:03:30.347Z")
},
{
"status" : "done",
"createdAt" : ISODate("2018-07-24T09:04:22.347Z")
}
]
}
]
And, I want to get data above where last object transactionStatus.status = process, so the result should be:
{
"_id" : ObjectId("5b56eb3deb869312d85a8e69"),
"transactionStatus" : [
{
"status" : "pending",
"createdAt" : ISODate("2018-07-24T09:02:53.347Z")
},
{
"status" : "process",
"createdAt" : ISODate("2018-07-24T09:02:53.347Z")
}
]
}
how to do that with mongoose?
You can use $expr (MongoDB 3.6+) inside of match. Using $let and $arrayElemAt passing -1 as second argument you can get the last element as a temporary variable and then you can compare the values:
db.col.aggregate([
{
$match: {
$expr: {
$let: {
vars: { last: { $arrayElemAt: [ "$transactionStatus", -1 ] } },
in: { $eq: [ "$$last.status", "process" ] }
}
}
}
}
])
The same result can be achieved for lower versions of MongoDB using $addFields and $match. You can add $project then to remove that temporary field:
db.col.aggregate([
{
$addFields: {
last: { $arrayElemAt: [ "$transactionStatus", -1 ] }
}
},
{
$match: { "last.status": "process" }
},
{
$project: { last: 0 }
}
])
//Always update new status at Position 0 using $position operator
db.update({
"_id": ObjectId("5b56eb3deb869312d85a8e69")
},
{
"$push": {
"transactionStatus": {
"$each": [
{
"status": "process",
"createdAt": ISODate("2018-07-24T09:02:53.347Z")
}
],
"$position": 0
}
}
}
)
//Your Query for checking first element status is process
db.find(
{
"transactionStatus.0.status": "process"
}
)
refer $position, $each

Search for most common "data" with mongoose, mongodb

My structure.
User:
{
name: "One",
favoriteWorkouts: [ids of workouts],
workouts: [ { name: "My workout 1" },...]
}
I want to get list of favorits/hottest workouts from database.
db.users.aggregate(
{ $unwind : "$favorite" },
{ $group : { _id : "$favorite" , number : { $sum : 1 } } },
{ $sort : { number : -1 } }
)
This returns
{
"hot": [
{
"_id": "521f6c27145c5d515f000006",
"number": 1
},
{
"_id": "521f6c2f145c5d515f000007",
"number": 1
},...
]}
But I want
{
hot: [
{object of hottest workout 1, object of hottest workout 2,...}
]}
How do you sort hottest data and fill the result with object, not just ids?
You are correct to want to use MongoDB's aggregation framework. Aggregation will give you the output you are looking for if used correctly. If you are looking for just a list of the _id's of all users' favorite workouts, then I believe that you would need to add an additional $group operation to your pipeline:
db.users.aggregate(
{ $unwind : "$favoriteWorkouts" },
{ $group : { _id : "$favoriteWorkouts", number : { $sum : 1 } } },
{ $sort : { number : -1 } },
{ $group : { _id : "oneDocumentWithWorkoutArray", hot : { $push : "$_id" } } }
)
This will yield a document of the following form, with the workout ids listed by popularity:
{
"_id" : "oneDocumentWithWorkoutArray",
"hot" : [
"workout6",
"workout1",
"workout5",
"workout4",
"workout3",
"workout2"
]
}

Resources