I have a collection with date stored as strings YYYY-mm-DD_HH:MM:SS.UUUZ like 2020-10-20_12:15:22.123+0100
My goal is to query on strings treating those as dates.
What am I doing:
I'm unwinding some header data on multiple documents:
{
"$unwind": {
"path": "$events",
"preserveNullAndEmptyArrays": true
}
}
and also
{
"$unwind": {
"path": "$events.hi2",
"preserveNullAndEmptyArrays": true
}
}
I'm adding a new field made with the string parsed as Date
{
"$addFields": {
"events.hi2.ConnectTimets": {
"$dateFromString": {
"dateString": "$events.hi2.ConnectTime",
"format": "%Y-%m-%d_%H:%M:%S.%L%Z"
}
}
}}
then on a $match stage I try to filter all records with date newer than 1 June 2020:
{
"$match":{
"events.hi2.ConnectTimets": {
"$gt": {"$dateFromString": {
"dateString": "2020-06-01",
"format": "%Y-%m-%d"
}
}
}
}
}
my result is Fetched 0 record(s) in 0ms
even though exists (at least a single document) in the database with a date matching the filter:
{
"_id" : ObjectId("5f438dfbf1feb13c4352e9f4"),
"timestamp" : NumberLong(1598262779045),
"attribute1" : [
"common"
],
"events" : [
{
"eventType" : "ty1",
"timestamp" : NumberLong(1598262779018),
"docId" : NumberLong(282578800148736),
"hi2" : {
"Priority" : 3,
"ClientId" : "client1",
"ConnectTime" : "2020-08-24_09:52:58.993+0000",
"Direction" : 1
}
},
{
"eventType" : "ty2",
"timestamp" : NumberLong(1598262781071),
"docId" : NumberLong(282578800148736),
"hi2" : {
"ref" : "bbbb"
}
}
]
}
When I espected something like
{
"_id" : ObjectId("5f438dfbf1feb13c4352e9f4"),
"timestamp" : NumberLong(1598262779045),
"attribute1" : [
"common"
],
"events" : [
{
"eventType" : "ty1",
"timestamp" : NumberLong(1598262779018),
"docId" : NumberLong(282578800148736),
"hi2" : {
"Priority" : 3,
"ClientId" : "client1",
"ConnectTime" : "2020-08-24_09:52:58.993+0000",
"Direction" : 1
}
}
}
Note_: the add field is ok because if i fire it without the match stage outputs a field with the string parsed as date
You should never store date/time values as string, use always proper Date objects.
Then the query is much simpler:
db.logging.aggregate([
{
$addFields: {
"events.hi2.ConnectTimets": {
$dateFromString: {
dateString: "$events.hi2.ConnectTime",
format: "%Y-%m-%d_%H:%M:%S.%L%Z"
}
}
}
},
{ $match: { "events.hi2.ConnectTimets": { $gte: ISODate("2020-06-01") } } }
])
When you have to work with date/time values then I recommend moment.js. Then you query could look like these:
{ $match: { "events.hi2.ConnectTimets": { $gte: moment("2020-06-01").toDate() } } }
{ $match: { "events.hi2.ConnectTimets": { $gte: moment("2020-06-01").tz('Europe/Zurich').toDate() } } }
{ $match: { "events.hi2.ConnectTimets": { $lte: moment.tz('Europe/Zurich').endOf('day').toDate() } } }
Related
I want to group my data based on event date with pagination. However what i am getting is whole record totalcount instead of eventDate count. because of this UI part is not working properly. Here is my collection sample:
{
"_id" : ObjectId("5fc4d0009a25e8cfbe306381"),
"eventDate" : ISODate("2021-11-29T01:00:00.000Z"),
"team1" : {
"tName" : "Chicago Bears",
},
"team2" : {
"tName" : "Green Bay Packers",
}
}
{
"_id" : ObjectId("5fc4d0019a25e8cfbe3063ff"),
"eventDate" : ISODate("2021-11-30T01:00:00.000Z"),
"team1" : {
"tName" : "Nashville SC",
},
"team2" : {
"tName" : "Columbus Crew",
}
}
{
"_id" : ObjectId("5fc4d0019a25e8cfbe3063f4"),
"eventDate" : ISODate("2021-11-30T01:00:00.000Z"),
"team1" : {
"tName" : "yyyy",
},
"team2" : {
"tName" : "xxxx",
}
}
here is my query:
db.getCollection('game').aggregate([
{ $addFields: { "newEventDate": {$dateToString:{ format: "%Y-%m-%d", date: "$eventDate" }}}},
{ "$match": {
"eventDate": { $gte: new Date() }
}
},
{ "$facet": {
"resultData": [
{ "$match": {
"eventDate": { $gte: new Date() }
}
},
{ "$group": {
"_id": "$newEventDate",
"games": {$push: {
team1:"$team1",
team2:"$team2"
}}
}
},
{ $sort: {eventDate: 1} },
{
$limit: 1
}
],
"pageInfo": [
{ "$count": "totalRecords" }
]}
}
]);
After executing this query this is my response:
{
"resultData" : [
{
"_id" : "2021-11-29",
"games" : [
{
"awayTeam" : {
"tName" : "Chicago Bears"
},
"homeTeam" : {
"tName" : "Green Bay Packers"
}
},
]
}
],
"pageInfo" : [
{
"totalRecords" : 3 **[here i want 2 ie total event date]**
}
]
}
$match your condition
move your $group stage outside from $facet, convert your date from string inside group, add you date in group stage because we are going to sort in next stage
$sort by eventDate ascending order
$facet, first get single record using $limit, and second part get total count of the record using $count
db.collection.aggregate([
{ $match: { eventDate: { $gte: new Date() } } },
{
$group: {
_id: {
$dateToString: {
format: "%Y-%m-%d",
date: "$eventDate"
}
},
eventDate: { $first: "$eventDate" },
games: {
$push: {
team1: "$team1",
team2: "$team2"
}
}
}
},
{ $sort: { eventDate: 1 } },
{
$facet: {
resultData: [{ $limit: 1 }],
pageInfo: [{ $count: "totalRecords" }]
}
}
])
Playground
I have a mongodb like that:
{
"_id" : ObjectId("5ece47aa6510a611b47aac5a"),
"array" : [
{
"_id" : ObjectId("5ece47aa6510a611b47aac6e"),
"timestamp" : "1470420945250",
},
{
"_id" : ObjectId("5ece47aa6510a611b47a8895"),
"timestamp" : "1470420945250"
},
{..},
{..}
]
I am trying to make a query to count how many months from timestamp are January for example. Any suggestion?
mongoplayground
mongoplayground2 => Contains the count, just change the $match around
db.collection.aggregate([
{
$unwind: "$array"
},
{
$addFields: {
timestamp: {
$toDate: "$array.timestamp"
}
}
},
{
$project: {
month: {
$month: {
date: "$timestamp"
}
}
}
}
])
You may add a timezone field inside $month.
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
I have such document structure:
{
favourite_shops: [
{
shop_id: "5961352278cba91cc6a6e8cc",
time: "2017-07-11T14:43:35.465Z"
},
{
shop_id: "5964e446c15c760f9b646d99",
time: "2017-07-11T14:44:40.429Z"
},
{
shop_id: "5964e446c15c760f9b646d98",
time: "2017-07-11T14:44:50.988Z"
}
]
}
How can I transform this to something like this:
{
favourite_shops: [
"5961352278cba91cc6a6e8cc",
"5964e446c15c760f9b646d99",
"5964e446c15c760f9b646d98"
]
}
You can $map on the array itself and get only the fields you need. (This is done within the query itself not after you receive the documents)
example.
given documents;
> db.Shopping.find().pretty()
{
"_id" : ObjectId("59651ce38828356e1a39fde9"),
"favourite_shops" : [
{
"shop_id" : "5961352278cba91cc6a6e8cc",
"time" : "2017-07-11T14:43:35.465Z"
},
{
"shop_id" : "5964e446c15c760f9b646d99",
"time" : "2017-07-11T14:44:40.429Z"
},
{
"shop_id" : "5964e446c15c760f9b646d98",
"time" : "2017-07-11T14:44:50.988Z"
}
]
}
{
"_id" : ObjectId("59665cf3d8145b41e5d2f5da"),
"favourite_shops" : [
{
"shop_id" : "2222",
"time" : "2017-07-11T14:43:35.465Z"
},
{
"shop_id" : "4444",
"time" : "2017-07-11T14:44:40.429Z"
},
{
"shop_id" : "6666",
"time" : "2017-07-11T14:44:50.988Z"
}
]
}
$map on favourite_shops array ({$match} block is optional, you can remove if you want shopping id for all documents)
> db.Shopping.aggregate([[
{
"$match": {
"_id": ObjectId("59651ce38828356e1a39fde9")
}
},
{
"$project": {
"my_favourite_shops": {
"$map": {
"input": "$favourite_shops",
"as": "each_shop",
"in": "$$each_shop.shop_id"
}
}
}
}
]).pretty()
{
"_id" : ObjectId("59651ce38828356e1a39fde9"),
"my_favourite_shops" : [
"5961352278cba91cc6a6e8cc",
"5964e446c15c760f9b646d99",
"5964e446c15c760f9b646d98"
]
}
And, with mongodb 3.4.4, I simply could $project on nested field,
db.Shopping.aggregate([{"$project": {"my_favourite_shops": "$favourite_shops.shop_id"}}]).pretty()
{
"_id" : ObjectId("59651ce38828356e1a39fde9"),
"my_favourite_shops" : [
"5961352278cba91cc6a6e8cc",
"5964e446c15c760f9b646d99",
"5964e446c15c760f9b646d98"
]
}
{
"_id" : ObjectId("59665cf3d8145b41e5d2f5da"),
"my_favourite_shops" : [
"2222",
"4444",
"6666"
]
}
Assuming we have such data structure:
const data = {
favourite_shops: [
{
shop_id: "5961352278cba91cc6a6e8cc",
time: "2017-07-11T14:43:35.465Z"
},
{
shop_id: "5964e446c15c760f9b646d99",
time: "2017-07-11T14:44:40.429Z"
},
{
shop_id: "5964e446c15c760f9b646d98",
time: "2017-07-11T14:44:50.988Z"
}
]
};
Just a bit of ES6 magic:
const result = {
favourite_shops: []
};
data.favourite_shops.forEach(el => result.favourite_shops.push(el.shop_id));
Second elegant approach:
const result = {
favourite_shops: data.favourite_shops.map(el => el.shop_id)
};
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" } } }
)