PHP MongoDB getting data from a date range using dateFromString - php

I have data stored in the following format in MongoDB:
[
{
meta: {
id: 1
},
data: {
date: "03/01/2020"
}
}
],
[
{
meta: {
id: 1
},
data: {
date: "12/19/2019"
}
}
]
And I want to use PHP to get all entries that are greater than or equal to the current date, and less than or equal to the current date plus 6 months.
To do that, my query looks like this:
$query = [
'meta.id' => 1,
'$expr' => [
'$and' => [
[
'$gte' =>
[
['$dateFromString' => ['dateString' => '$data.date']],
time()
]
],
[
'$lte' =>
[
['$dateFromString' => ['dateString' => '$data.date']],
strtotime('+6 months')
]
]
]
]
];
With this query, I should get back the first entry, with a date of "03/01/2020", however I get back no results.
I've also tried using 'format' in the dateString but it doesn't appear to be supported yet, as it gives me an error.
How can I get this query to work correctly?

Related

How to filter elasticsearch by date field with date modification

To filter by date, I use the following queries:
'body' => [
'query' => [
'bool' => [
'filter' => [
'range' => [
'expire_at' => [
'gte' => now()
]
]
]
]
]
]
UPD: All records have another date field - last_checked. The question is how to select records in which, for example, (expire_at - 7 days) > last_checked?
Check the range query documentation: you can use date math in the range parameters. E.g., expire_at - 7 days < now() means that the expiration will be within the next 7 days. Then you can do:
"range": {
"expire_at": {
"lt": "now+7d/d"
}
}
Note that this will include also already expired items. If you want to avoid that, you can add the condition that the expiration date is not met yet:
"range": {
"expire_at": {
"lt": "now+7d/d",
"gte": "now/d"
}
}
use this code
"expire_at" => array(
"lt" => "now+7d/d"
)

Merging doc_count result from keyed buckets

I have a query like
'aggs' => [
'deadline' => [
'date_histogram' => [
'field' => 'deadline',
'interval' => 'month',
'keyed' => true,
'format' => 'MMM'
]
]
]
the result I am getting are buckets with keys as month names.
The problem I am facing is the buckets with the month names as keys for a previous year are over written by another month of the next year (because obviously the key is same).
I want results where doc-count of buckets of previous which are over written merge with the doc_count of the next.
You can either add a separate month field during indexing and perform aggregation on it or use below script
{
"size": 0,
"aggs": {
"deadline": {
"histogram": {
"script": { "inline" : "return doc['deadline'].value.getMonthOfYear()" },
"interval": 1
}
}
}
}
Creating a separate month field will have better performance
Replace the format from MMM to YYYY-MMM as below:
'aggs' => [
'deadline' => [
'date_histogram' => [
'field' => 'deadline',
'interval' => 'month',
'keyed' => true,
'format' => 'YYYY-MMM'
]
]
]
After this you can handle the merging process at your application level

MongoDB using UTCDateTime in $gte doesn't work

I'm writing a query to search for users created at specific date e.g.
$date = '2018-05-02 15:46:41.000Z';
User::raw(function ($col) use($date) {
return $col->aggregate([
['$match' =>
'created_at' => ['$gte' => new \MongoDB\BSON\UTCDateTime(strtotime($date))]
]
]);
});
Given an sample data like
{
"_id" : ObjectId("5ae9dd61ef7a5754f159c656"),
"name" : "Effie Larkin",
"created_at" : ISODate("2018-05-02 15:46:41.000Z"),
}...
Expected result should be
{
"users" : [
"_id" : {"$oid": "5ae9dd61ef7a5754f159c656"},
"name" "Effie Larkin",
"created_at" : "2018-05-02 15:46:41"
]
}...
But I get an empty json array []. I think the problem is in UTCDateTime function but I don't how to pass dateTime to it. I want to pass a time interval like 1 week ago or yesterday etc... if anyone can help me will be much appreciated, thanks
Update, the example below fixed it
$date = '2018-05-02';
User::raw(function ($col) use($date) {
return $col->aggregate([
['$match' =>
'created_at' => ['$gte' => new \MongoDB\BSON\UTCDateTime(strtotime($date)*1000)]
]
]);
});

Mongodb string to date conversion

Below is my sample mongodb collection
{
"_id" : ObjectId("57ed32f4070577ec56a56b9f"),
"log_id" : "180308",
"issue_id" : "108850",
"author_key" : "priyadarshinim_contus",
"timespent" : NumberLong(18000),
"comment" : "Added charts in the dashboard page of the application.",
"created_on" : "2017-08-16T18:22:04.816+0530",
"updated_on" : "2017-08-16T18:22:04.816+0530",
"started_on" : "2017-08-16T18:21:39.000+0530",
"started_date" : "2017-08-02",
"updated_date" : "2017-08-02",
"role" : "PHP",
"updated_at" : ISODate("2017-09-29T15:27:48.069Z"),
"created_at" : ISODate("2017-09-29T15:27:48.069Z"),
"status" : 1.0
}
I need to get records with help of started_date , by default I will give two dates in that i will check $gt and $lt of started date .
$current_date = '2017-08-31';
$sixmonthfromcurrent ='2017-08-01';
$worklogs = Worklog::raw ( function ($collection) use ($issue_jira_id, $current_date, $sixmonthfromcurrent) {
return $collection->aggregate ( [
['$match' => ['issue_id' => ['$in' => $issue_jira_id],
'started_date' => ['$lte' => $current_date,'$gte' => $sixmonthfromcurrent]
]
],
['$group' => ['issue_id' => ['$push' => '$issue_id'],
'_id' => ['year' => ['$year' => '$started_date'],
'week' => ['$week' => '$started_date'],'resource_key' => '$author_key'],
'sum' => array ('$sum' => '$timespent')]
],
[ '$sort' => ['_id' => 1]
]
] );
} );
If I run this query I am getting this type of error:
Can't convert from BSON type string to Date
How to rectify this error?
The only field in your $group that I see as troubling is the field week.
The year you could extract by doing a $project before your $group aggregation:
$project: {
year: { $substr: [ "$started_date", 0, 4 ] },
issue_id: 1,
author_key: 1,
timespent: 1
}
if you know that the date string will always come at this format. Of course you cannot do a substr operation for finding out the week.
It would be easy though if your field started_date would be an actual ISODate(), then you could use exactly what you wrote as you probably already saw in the documentation.
If you need the field week very bad, which I imagine you do, then I'd suggest you convert your field started_date to an ISODate().
You can do that with a bulkWrite:
db = db.getSiblingDB('yourDatabaseName');
var requests = [];
db.yourCollectionName.find().forEach(doc => {
var date = yourFunctionThatConvertsStringToDate(doc.started_date);
requests.push( {
'updateOne': {
'filter': { '_id': doc._id },
'update': { '$set': {
"started_date": date
} }
}
});
if (requests.length === 500) {
db.yourCollectionName.bulkWrite(requests);
requests = [];
}
});
if(requests.length > 0) {
db.yourCollectionName.bulkWrite(requests);
}
Load this script directly on your mongodb server and execute there.
Hope this helps.

Query where timestamp field is older than another timestamp field in MongoDB with PHP

How can I obtain an object from a MongoDB collection where a specific field1 (timestamp or date) is older/newer than another specific field2 (timestamp or date)?
Given the following example object:
// MongoDB 3.2
{
name: 'test',
updated_on: Timestamp(1474416000, 0),
export: {
active: true,
last_exported_on: Timestamp(1474329600, 0)
}
}
This object should match a query like: where export.active is true and updated_on > export.last_exported_on
I've tried it with the aggregation framework, since I've read that $where can be very slow, but without any success.
// PHP 5.4 (and MongoDB PHP lib. http://mongodb.github.io/mongo-php-library)
$collection->aggregate([
['$project' => [
'dst' => ['$cmp' => ['updated_on', 'export.last_exported_on']],
'name' => true
]],
['$match' => ['dst' => ['$gt' => 0], 'export.active' => ['$eq' => true]]],
['$limit' => 1]
]);
I can change timestamps into date or anything else, but I don't see the problem in the type.
Edit: Not all objects have the last_exported_on or the export fields at all. Besides that both can be null or empty or 000000.
That's because after you do the $project you end up only with the dst and _id fields, so you cannot $match on export.active. You need to match on export.active before the projection. After that you need another match on the dst field.
[
{
$match: {
"export.active": true
}
},
{
$project: {
dst: {
$cmp: [
"$updated_on",
"$export.last_exported_on"
]
}
}
},
{
$match: {
dst: 1
}
}
]
Edit
Alternatively, you can make sure to preserve export.active and to spare another $match:
[
{
$project: {
"export.active": 1,
cmp: {
$cmp: [
"$updated_on",
"$export.last_exported_on"
]
}
}
},
{
$match: {
cmp: 1,
"export.active": true
}
}
]

Categories