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
Related
The $and operator in Mongoose isn't working properly in NodeJS.
Let's assume we have this JSON-MongoDB data:
{
"_id" : ObjectId("60bb4cd74a802722d8b0de0f"),
"username" : "System",
"text" : "matan has joined!",
"__v" : NumberInt(0)
}
{
"_id" : ObjectId("60bb4cd74a802722d8b0de10"),
"username" : "System",
"text" : "Welcome!",
"__v" : NumberInt(0)
}
{
"_id" : ObjectId("60bb4cdb4a802722d8b0de11"),
"username" : "matan",
"text" : "Hello World",
"__v" : NumberInt(0)
}
{
"_id" : ObjectId("60bb4ce14a802722d8b0de12"),
"username" : "System",
"text" : "matan has left.",
"__v" : NumberInt(0)
}
I want to filter the data that all the System's Welcome messages will be dropped. For example, this output will be shown:
{
"_id" : ObjectId("60bb4cd74a802722d8b0de0f"),
"username" : "System",
"text" : "matan has joined!",
"__v" : NumberInt(0)
}
{
"_id" : ObjectId("60bb4cdb4a802722d8b0de11"),
"username" : "matan",
"text" : "Hello World",
"__v" : NumberInt(0)
}
{
"_id" : ObjectId("60bb4ce14a802722d8b0de12"),
"username" : "System",
"text" : "matan has left.",
"__v" : NumberInt(0)
}
But instead, only this output is shown:
{
_id: 60bb4cdb4a802722d8b0de11,
username: 'matan',
text: 'Hello World',
__v: 0
}
This is my code (NodeJS):
var msgs = Msg.find({
$and: [
{ username: {$ne: "System"} },
{ text: {$ne: "Welcome!"} }
]
}, (err, retn) => console.log(retn))
Did I miss something important? Thanks for the help.
The $and condition with $ne will match that both fields should not equal to value.
You can just try $or condition,
var msgs = Msg.find({
$or: [
{ username: { $ne: "System" } },
{ text: { $ne: " Welcome!" } }
]
}, (err, retn) => console.log(retn))
Playground
Use the $or operator instead
var msgs = Msg.find({
$or: [
{ username: {$ne: "System"} },
{ text: {$ne: "Welcome!"} }
]
}, (err, retn) => console.log(retn))
You can use Aggregation Pipeline:
$addFields to add new field flag that will be true if both reqirements are met (username equals to System, and text equals to Welcome!), or false otherwise.
$match to filter all documents where new field flag equals to false
$project to remove the new field flag from final result
db.collection.aggregate([
{
"$addFields": {
"flag": {
"$cond": {
if: {
"$and": [
{
"$eq": [
"$username",
"System"
]
},
{
"$eq": [
"$text",
"Welcome!"
]
}
]
},
then: true,
else: false
}
}
}
},
{
"$match": {
"flag": false
}
},
{
"$project": {
"flag": 0
}
}
])
Here is the working example: https://mongoplayground.net/p/ENDzUW8igJP
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
I have a collection with folowing data:
{
"_id" : ObjectId("5b5066b716d3112cfc2a5deb"),
"username" : "admin",
"password" : "123456",
"token" : "0123",
"bots" : [
{
"name" : "mybot",
"installations" : [
{
"date" : ISODate("2018-07-19T10:23:51.774Z")
},
{
"date" : ISODate("2018-07-19T10:23:51.774Z")
}
],
"commands" : [
{
"name" : "read",
"date" : ISODate("2018-07-19T10:23:51.774Z")
},
{
"name" : "answer",
"date" : ISODate("2018-07-19T10:23:51.774Z")
},
{
"name" : "get",
"date" : ISODate("2018-07-19T11:55:28.858Z")
},
{
"name" : "get",
"date" : ISODate("2018-07-19T11:56:47.419Z")
},
{
"name" : "get",
"date" : ISODate("2018-07-19T11:56:48.499Z")
},
{
"name" : "get",
"date" : ISODate("2018-07-19T11:56:49.089Z")
}
]
}
]
},
{
"_id" : ObjectId("5b50bbfe3ed35b6f2bde6923"),
"username" : "user",
"password" : "123456",
"token" : "44444",
"bots" : [
{
"name" : "anotherBotName",
"installations" : [
{
"date" : ISODate("2018-07-19T16:27:42.012Z")
},
{
"date" : ISODate("2018-07-19T16:27:42.012Z")
}
],
"commands" : [
{
"name" : "update",
"date" : ISODate("2018-07-19T16:27:42.012Z")
},
{
"name" : "update",
"date" : ISODate("2018-07-19T16:27:42.012Z")
}
]
}
]
}
I want to execute SQL-equivalent query
SELECT commands.name, COUNT(commands.name), GROUP BY commands.name
and get a result like:
[
{update: 2},
{get: 4},
{read: 1},
{answer: 1}
]
but when I execute this query in mongo:
.collection(collectionName).aggregate({{'$group': {_id: "$bots.commands.name",count:{$sum:1}}}
}).toArray(callback)
I get such a result:
[
{
_id: [
[ 'test', 'test1' ]
],
count: 1
},
{
_id: [
[ 'read', 'answer', 'get', 'get', 'get', 'get' ]
],
count: 1
}
]
I googled and read about agregation in MongoDB and still don't get much. It's hard to move from SQL to NoN-SQL database
My questions are:
Why my query shows not the result I want to see?
How to fix it?
Thanks in advance!
Since you have two nested arrays in your schema you should use $unwind operator twice before you apply your $group. After $unwind you'll get separate document for each name. Try:
db.col.aggregate([
{
$unwind: "$bots"
},
{
$unwind: "$bots.commands"
},
{
$group: {
_id: "$bots.commands.name",
count: { $sum: 1 }
}
},
{
$replaceRoot: {
newRoot: {
$let: {
vars: { obj: [ { k: "$_id", v: "$count" } ] },
in: { $arrayToObject: "$$obj" }
}
}
}
}
])
In the last stage you can use $replaceRoot with $arrayToObject to set _id as keys in your final objects.
Outputs:
{ "update" : 2 }
{ "get" : 4 }
{ "answer" : 1 }
{ "read" : 1 }
I have the following document structure in my MongoDB and I am trying to return an array of objects containing all prices for itemID "5a59c587fa9b4a212b0a1312" across all documents using the following query but unfortunately it is always returning an empty array. Can someone please advice what I might be doing wrong here? and how I can get such a result?
Note: I am using promised-mongo in a Node.js app to access my MongoDB
Query I tried:
{ transDetails: { $elemMatch: { itemID: "5a59c587fa9b4a212b0a1312" } } }
DB sample:
{
"_id" : ObjectId("5a688e7ea52deb6d4a6b6663"),
"transactionID" : "1",
"transDetails" : [
{
"itemID" : "5a59c587fa9b4a212b0a1312",
"price" : "22"
},
{
"itemID" : "5a59c95b081c6c612bd17058",
"price" : "24"
}
] }
{
"_id" : ObjectId("5a6aa99a52deb6d4a67714"),
"transactionID" : "2",
"transDetails" : [
{
"itemID" : "5a59c587fa9b4a212b0a1312",
"price" : "35"
},
{
"itemID" : "5a59c95b081c6c612bd17058",
"price" : "24"
}
] }
Find with projection to have only matched items in the transDetails:
.find({"transDetails.itemID": "5a59c587fa9b4a212b0a1312"}, {_id:0, "transDetails.$": 1})
Will return
{
"transDetails" : [
{
"itemID" : "5a59c587fa9b4a212b0a1312",
"price" : "22"
}
]
},
{
"transDetails" : [
{
"itemID" : "5a59c587fa9b4a212b0a1312",
"price" : "35"
}
]
},
....
Wish you re-shape the documents, you can use aggregation:
.aggregate([
{ $match: { "transDetails.itemID": "5a59c587fa9b4a212b0a1312" } },
{ $project: {
_id: 0,
transDetails: {
$filter: {
input: "$transDetails",
as: "item",
cond: { $eq: [ "$$item.itemID", "5a59c587fa9b4a212b0a1312" ] }
}
}
} },
{ $unwind: "$transDetails"},
{ $project: {price: "$transDetails.price"}}
])
Which will give you
{
"price" : "22"
},
{
"price" : "35"
},
...
How to query :
{
"_id" : Object Id("58787242f7d06edbbb88f46e"),
"name" : "aah",
"values" : [
"1484196300685",
"10"
],
"attributes" : {
}
}
i need time where value=10 result 1484196300685
db.collection.find(
{ values: { $elemMatch: { $eq: "10" } } }
)