Get documents which expired before today in MongoDB - mongodb

I am using MongoDB 2.6 and I'm making a query that filter the documents by expired date. I want to know which documents expired before today. My persisted data represent publications.
I've made some queries but it doesn't return documents. I know there are many documents who satisfy this condition.
I tried two query but no one works:
1.
{
"domain.ApplicationCase.fields.ExpireDate": { $gte : {
$currentDate: {
lastModified: true,
"cancellation.date": { $type: "timestamp" }
}}}
}
2.
{
"domain.ApplicationCase.fields.ExpireDate": { $gte : new Date()}
}
Part of document records:
{
"_id" : "1234546",
"fields" : {
"Orchestration" : "default",
"Segmentation" : "PFI",
"MatchKey" : "1",
"UserID" : "001"
},
"domain" : {
"ApplicationCase" : {
"_id" : null,
"fields" : {
"ExpireDate" : "2015-11-13T13:47:26Z",
....
What's wrong?

If you want to get records which are expired before today then you should use $lte instead of $gte:
db.myCollection.find({
"domain.ApplicationCase.fields.ExpireDate": { $lte : new Date()}
})
Update:
So the core problem with your documents is that, the value of domain.ApplicationCase.fields.ExpireDate are not in the date format instead they are in simple String format.
So you first need to convert them to the date in order for the query to work since you are comparing an String with the Date.
Probably, you can use a code like this to convert the string to the date:
db.myCollection.find({
"domain.ApplicationCase.fields.ExpireDate": {$exists: true}
}).snapshot().forEach(function(record) {
var stringValue = record.domain.ApplicationCase.fields.ExpireDate;
db.myCollection.update({_id: record._id}, {$set: {
"domain.ApplicationCase.fields.ExpireDate": ISODate(stringValue)
}});
})

Related

Update with $dateToString aggregation causes can't convert from BSON type object to Date in MongoDB

I have following document in the database (Mongodb 4.2)
{
"_id": ObjectId(
"5e58dd49103bba2c961e7d80"
),
"launchProducts": {
"scheduledLaunchDate": {
"$date": "2020-02-03T23:00:00.000Z"
}
}
}
I would like to update document and convert existing date object, into formatted string (dd-mm-yyyy) using following functionality of Mongo 4.2 https://docs.mongodb.com/manual/tutorial/update-documents-with-aggregation-pipeline/
I'm running following query in Mongo Shell:
db.collection.updateMany({}, [{"$set": {"launchProducts.scheduledLaunchDate": {"$dateToString": {"date":"$launchProducts.scheduledLaunchDate","format":"%d-%m-%Y"}}}}])
Unfortunately I'm getting following error:
2020-02-28T11:07:50.375+0100 E QUERY [js] WriteError({
"index" : 0,
"code" : 16006,
"errmsg" : "can't convert from BSON type object to Date",
"op" : {
"q" : {
},
"u" : [
{
"$set" : {
"launchProducts.scheduledLaunchDate" : {
"$dateToString" : {
"date" : "$launchProducts.scheduledLaunchDate",
"format" : "%d-%m-%Y"
}
}
}
}
],
"multi" : true,
"upsert" : false
}
}
Let me know if you have any ideas how to fix this.
You should never store Date as strings, you will just generate problems in future. If you like to get a specific format then you should format the Date value on client side at output.
If you want to get rid of the time part of scheduledLaunchDate you can use this pipeline:
db.collection.aggregate([
{
$set: {
"launchProducts.scheduledLaunchDate": {
$dateFromParts: {
year: {$year: "$launchProducts.scheduledLaunchDate"},
month: {$month: "$launchProducts.scheduledLaunchDate"},
day: {$dayOfMonth: "$launchProducts.scheduledLaunchDate"}
}
}
}
}
])
Problem was that date was stored improperly in MongoDB.
Running db.collection.insert({"launchProducts": {"scheduledLaunchDate": ISODate("2020-02-03T23:00:00.000Z")}}) first, solved my issue.

Querying date range in aggregate query returns nothing or ignores dates

In my aggregate query I'm trying to add conditions in the $match statement to return only records within given date range. Without converting to ISOString, I get a set of records that ignores the date range completely. When I convert to ISOString, I get nothing (returns empty set). I've tried using the $and operator, still nothing.
I've tried all the solutions on stack to no avail. Here's my code:
$match: {
$and: [
{'author.id': { $ne: req.user._id }},
{'blurtDate': { $gte: test1.toISOString() }},
{'blurtDate': { $lte: test2.toISOString() }}
]
}
test1 and test2 are correct, I checked them on console log they reflect as follows:
2019-06-02T12:44:39.000Z -- 2019-07-02T12:44:39.928Z
I also tried without the $and operator like so:
$match: {
'author.id': { $ne: req.user._id },
'blurtDate': { $gte: test1.toISOString() },
'blurtDate': { $lte: test2.toISOString() }
}
Which again returns nothing. Any help much appreciated!
EDIT: Wanted to emphasize that test1 and test2 are new date objects:
test1 = new Date(qryDateFrom); //Tried .toISOString() as well
test2 = new Date(qryDateTo);
Without .toISOString(), I get a return of values that ignores the dates. With .toISOString I get an empty return.
Here's an example document that should be returned:
{
"_id" : ObjectId("5d0a807345c85d00ac4b7217"),
"text" : "<p>Seriously RV style.</p>",
"blurtDate" : ISODate("2019-06-19T18:35:31.156Z"),
"blurtImg" : "04643410-92c1-11e9-80b6-a3262311afff.png",
"vote" : 0,
"author" : {
"id" : ObjectId("5cb5df0ef7a3570bb4ac6e05"),
"name" : "Benjamin Paine"
},
"__v" : 0
}
When I remove .toISOString(), I get documents outside of the expected date range, such as this one in May (query should only return between june 2 and july 2).
{
"_id" : ObjectId("5d07ebaf9a035117e4546349"),
"text" : "<p>A start to something more...</p>",
"blurtDate" : ISODate("2019-05-15T19:36:15.737Z"),
"blurtImg" : "2be7a160-9137-11e9-933f-6966b2e503c7.png",
"vote" : 0,
"author" : {
"id" : ObjectId("5cb5df0ef7a3570bb4ac6e05"),
"name" : "Benjamin Paine"
},
"__v" : 0
}
Your docs contain actual Date objects, so remove the .toISOString()s from your query. But you'll also need to combine your $gte and $lte terms into a single object:
$match: {
'author.id': { $ne: req.user._id },
'blurtDate': { $gte: test1, $lte: test2 }
}

querying Date in mongodb with borders included

I'm evaluating the following query on my collection with fake data:
db.test_result.find({"date": {$gte: ISODate("2021-07-27"), $lte: ISODate("2021-08-31")}}).count()
And despite the fact that I use $lte it does not include the second date value. Is it a bug? If so then how do I make the query so that the left and right borders are included?
Here is what a fake json obj looks like:
{
"nfl": "Some",
"rStatus": false,
"mac": "02:00:00:00:00:00",
"date": "2021-07-27T12:17:57",
"MDCode": "123132132123",
}
With this given input:
{
"_id" : ObjectId("596c6fea53cc7100104628fa"),
"timestamp" : ISODate("2017-07-17T08:06:02.041Z"),
}
{
"_id" : ObjectId("596c7162973f33000fc8bb81"),
"timestamp" : ISODate("2017-07-17T08:12:18.170Z")
}
{
"_id" : ObjectId("596c736c15371f00106b9e3a"),
"timestamp" : ISODate("2017-07-17T08:21:00.291Z")
}
This query:
...find({"timestamp": {$gte: ISODate("2017-07-17T08:06:02.041Z"), $lte: ISODate("2017-07-17T08:12:18.170Z")}})
Would return:
{
"_id" : ObjectId("596c6fea53cc7100104628fa")
"timestamp" : ISODate("2017-07-17T08:06:02.041Z")
}
{
"_id" : ObjectId("596c7162973f33000fc8bb81"),
"timestamp" : ISODate("2017-07-17T08:12:18.170Z")
}
Which is what you would expect, the results match to the dot the 2 borders. So in a nutshell it would be included if it is an exact match, otherwise you would get only the once in between.

In a Mongo collection, how do you query for a specific object in an array?

I'm trying to retrieve an object from an array in mongodb. Below is my document:
{
"_id" : ObjectId("53e9b43968425b29ecc87ffd"),
"firstname" : "john",
"lastname" : "smith",
"trips" : [
{
"submitted" : 1407824585356,
"tripCategory" : "staff",
"tripID" : "1"
},
{
"tripID" : "2",
"tripCategory" : "volunteer"
},
{
"tripID" : "3",
"tripCategory" : "individual"
}
]
}
My ultimate goal is to update only when trips.submitted is absent so I thought I could query and determine what the mongo find behavior would look like
if I used the $and query operator. So I try this:
db.users.find({
$and: [
{ "trips.tripID": "1" },
{ "trips": { $elemMatch: { submitted: { $exists: true } } } }
]
},
{ "trips.$" : 1 } //projection limits to the FIRST matching element
)
and I get this back:
{
"_id" : ObjectId("53e9b43968425b29ecc87ffd"),
"trips" : [
{
"submitted" : 1407824585356,
"tripCategory" : "staff",
"tripID" : "1"
}
]
}
Great. This is what I want. However, when I run this query:
db.users.find({
$and: [
{ "trips.tripID": "2" },
{ "trips": { $elemMatch: { submitted: { $exists: true } } } }
]
},
{ "trips.$" : 1 } //projection limits to the FIRST matching element
)
I get the same result as the first! So I know there's something odd about my query that isn't correct. But I dont know what. The only thing I've changed between the queries is "trips.tripID" : "2", which in my head, should have prompted mongo to return no results. What is wrong with my query?
If you know the array is in a specific order you can refer to a specific index in the array like this:-
db.trips.find({"trips.0.submitted" : {$exists:true}})
Or you could simply element match on both values:
db.trips.find({"trips" : {$elemMatch : {"tripID" : "1",
"submitted" : {$exists:true}
}}})
Your query, by contrast, is looking for a document where both are true, not an element within the trips field that holds for both.
The output for your query is correct. Your query asks mongo to return a document which has the given tripId and the field submitted within its trips array. The document you have provided in your question satisfies both conditions for both tripIds. You are getting the first element in the array trips because of your projection.
I have assumed you will be filtering records by the person's name and then retrieving the elements inside trips based on the field-exists criteria. The output you are expecting can be obtained using the following:
db.users.aggregate(
[
{$match:
{
"firstname" : "john",
"lastname" : "smith"
}
},
{$unwind: "$trips"},
{$match:
{
"trips.tripID": "1" ,
"trips.submitted": { $exists: true }
}
}
]
)
The aggregation pipeline works as follows. The first $match operator filters one document (in this case the document for john smith) The $unwind operator in mongodb aggregation unwinds the specified array (trips in this case), in effect denormalizing the sub-records associated with the parent records. The second $match operator filters the denormalized/unwound documents further to obtain the one required as per your query.

Upsert with pymongo and a custom _id field

I'm attempting to store pre-aggregated performance metrics in a sharded mongodb according to this document.
I'm trying to update the minute sub-documents in a record that may or may not exist with an upsert like so (self.collection is a pymongo collection instance):
self.collection.update(query, data, upsert=True)
query:
{ '_id': u'12345CHA-2RU020130304',
'metadata': { 'adaptor_id': 'CHA-2RU',
'array_serial': 12345,
'date': datetime.datetime(2013, 3, 4, 0, 0, tzinfo=<UTC>),
'processor_id': 0}
}
data:
{ 'minute': { '16': { '45': 1.6693091}}}
The problem is that in this case the 'minute' subdocument always only has the last hour: { minute: metric} entry, the minute subdocument does not create new entries for other hours, it's always overwriting the one entry.
I've also tried this with a $set style data entry:
{ '$set': { 'minute': { '16': { '45': 1.6693091}}}}
but it ends up being the same.
What am I doing wrong?
In both of the examples listed you are simply setting a field ('minute')to a particular value, the only reason it is an addition the first time you update is because the field itself does not exist and so must be created.
It's hard to determine exactly what you are shooting for here, but I think what you could do is alter your schema a little so that 'minute' is an array. Then you could use $push to add values regardless of whether they are already present or $addToSet if you don't want duplicates.
I had to alter your document a little to make it valid in the shell, so my _id (and some other fields) are slightly different to yours, but it should still be close enough to be illustrative:
db.foo.find({'_id': 'u12345CHA-2RU020130304'}).pretty()
{
"_id" : "u12345CHA-2RU020130304",
"metadata" : {
"adaptor_id" : "CHA-2RU",
"array_serial" : 12345,
"date" : ISODate("2013-03-18T23:28:50.660Z"),
"processor_id" : 0
}
}
Now let's add a minute field with an array of documents instead of a single document:
db.foo.update({'_id': 'u12345CHA-2RU020130304'}, { $addToSet : {'minute': { '16': {'45': 1.6693091}}}})
db.foo.find({'_id': 'u12345CHA-2RU020130304'}).pretty()
{
"_id" : "u12345CHA-2RU020130304",
"metadata" : {
"adaptor_id" : "CHA-2RU",
"array_serial" : 12345,
"date" : ISODate("2013-03-18T23:28:50.660Z"),
"processor_id" : 0
},
"minute" : [
{
"16" : {
"45" : 1.6693091
}
}
]
}
Then, to illustrate the addition, add a slightly different entry (since I am using $addToSet this is required for a new field to be added:
db.foo.update({'_id': 'u12345CHA-2RU020130304'}, { $addToSet : {'minute': { '17': {'48': 1.6693391}}}})
db.foo.find({'_id': 'u12345CHA-2RU020130304'}).pretty()
{
"_id" : "u12345CHA-2RU020130304",
"metadata" : {
"adaptor_id" : "CHA-2RU",
"array_serial" : 12345,
"date" : ISODate("2013-03-18T23:28:50.660Z"),
"processor_id" : 0
},
"minute" : [
{
"16" : {
"45" : 1.6693091
}
},
{
"17" : {
"48" : 1.6693391
}
}
]
}
I ended up setting the fields like this:
query:
{ '_id': u'12345CHA-2RU020130304',
'metadata': { 'adaptor_id': 'CHA-2RU',
'array_serial': 12345,
'date': datetime.datetime(2013, 3, 4, 0, 0, tzinfo=<UTC>),
'processor_id': 0}
}
I'm setting the metrics like this:
data = {"$set": {}}
for metric in csv:
date_utc = metric['date'].astimezone(pytz.utc)
data["$set"]["minute.%d.%d" % (date_utc.hour,
date_utc.minute)] = float(metric['metric'])
which creates data like this:
{"$set": {'minute.16.45': 1.6693091,
'minute.16.46': 1.566343,
'minute.16.47': 1.22322}}
So that when self.collection.update(query, data, upsert=True) is run it updates those fields.