How to select/find data from inner array in MongoDB? - mongodb

How I can extract (find) tags for date 2013-01-14?
db.test1.insert(
{
date: Date("2013-01-14"),
name: "Roma",
tags: [{Python:14,Ruby:10,C:4}]
}
)
I tried extract all info for current date, but even this request do not work:
db.test1.find({date:Date("2013-01-14")})

The mongo shell wrap objects of Date type with the ISODate helper but the objects remain of type Date. So when inserting dates in MongDB, you could use the
ISODate() constructor which returns a Date object using the ISODate() wrapper instead of the Date() method which returns the current date as a string.
When you query, use the new Date() constructor which returns a Date object using the ISODate() wrapper to get a date object that you can then use in the query, bearing in mind that JavaScript date objects months are zero-based index thus January has the value 0 in the constructor parameter.
Inserting:
db.test1.insert({
"date": ISODate("2013-01-14"),
"name": "Roma",
"tags": [
{ "Python": 14, "Ruby": 10, "C": 4 }
]
})
Querying:
var myDateObj = new Date(2013, 0, 14) // JavaScript Date object months are zero based index
db.test1.find({ "date": myDateObj }, {"_id": 0, "tags": 1})
Result:
/* 0 */
{
"tags" : [
{
"Python" : 14,
"Ruby" : 10,
"C" : 4
}
]
}

Related

MongoDB: subtract months from date, with value from database

Is it possible in MongoDB to somehow subtract only months from a date, with a value from the same document. So for example i have to subtract date from field "valid_to" with months from field "offset", only months need to be changed.
So for example:
2019-11-25 - 3 months = 2019-08-25
2019-12-20 - 5 months = 2019-07-20
Documents in collection:
{
"_id" : ObjectId("5d96f027ad768a1532aa2698"),
"name" : "Q1",
"valid_to" : ISODate("2019-11-25T14:36:32.221+01:00"),
"offset" : 3,
},
{
"_id" : ObjectId("5d96f027ad768a1532aa2699"),
"name" : "Q2",
"valid_to" : ISODate("2019-12-20T14:36:32.221+01:00"),
"offset" : 5,
}
I tried just as an example using JavaScript date function and taking current date and subtracting months but it's not working like this
{
$project: {
"subtractedDate": new Date(new Date().setMonth({ $subtract: [new Date().getMonth(), "$offset"] }))
}
},
])
As a result I am getting ISODate("1970-01-01T01:00:00.000+01:00")
Javascript functions do not work with aggregation framework. The whole aggregation is parsed to BSON format on input, so you can't call any JS inside. The reason is in converting BSON to C++ implementation.
In your example you are using mix of operators and JS, when you try to $subtract inside .setMonth():
$project: {
"subtractedDate": new Date(new Date().setMonth({ $subtract: [new Date().getMonth(), "$offset"] }))
}
So JS doesn't know in advance how to handle Mongo operators and doesn't know about $offset value
Instead you have to use JS, that will be executed before input:
$project: {
"subtractedDate": new Date(new Date().setMonth(new Date().getMonth() - 5))
}
That's the only solution to get you example work. But it won't help in your case since you don't know the exact offset.
What you can do is implement your logic using aggregation operators, there are lots of functions to work with Date like $dateToParts and $dateFromParts where you can easily manipulate with months, years and so on separately.
Or you can add a callback to your aggregation where you can handle all your dates:
db.collection.aggregate([
{
// ...
}
], function( err, results ) {
if ( !err ) {
results.forEach( result => {
result.date = new Date(result.date)
.setMonth(new Date(result.date).getMonth() - result.offset);
});
}
callback(err, results);
});
}

How can I manipulate a date field in an aggregation pipeline?

I'm trying to set the time from a Date field to the start of the day
function getDate(date){ return new Date(date.getYear(), date.getMonth(),
date.getDate(), 0,0,0); }
...
{"$project" : {
"_id" : getDate("$dt"),
...
If I send "$dt" I get TypeError: date.getYear is not a function as I'm passing a string,
If remove the quotation marks, I get Error: "$dt is not defined",
but if I set "$dt" as value for "_id" I get a correct ISO Date.
So how do I pass the date object to the function?
MongoDB's aggregation pipeline does not support JavaScript. To manipulate date values for results in the aggregation pipeline you need to use Date Aggregation Operators.
For example :
db.date.aggregate([
{ $project: {
_id: { $dateToString: { format: "%Y%m%d", date: "$dt" }}
}}
])
Assuming you have a document with a field called dt with a date value of ISODate("2017-07-01T10:01:23.344Z"), the result would look like:
{
"result": [
{
"_id": "20170701"
}
],
"ok": 1
}
Note: if you have multiple documents for the same day, this approach will create duplicate _id values in your results. You may want to project to a different field name or perhaps use a $group stage instead of $project if your intent is to combine values for the same day.

MongoDB: Convert Date String (mm/dd/yyyy) to Unix timestamp

just practicing my MongoDB queries and I've hit a wall with a field data type.
I'm currently using Robomongo as GUI for accessing the production database.
My document structure looks like:
Is there a MongoDB operator or way/method to convert the date field value, currently in mm/dd/yyyy format, to a Unix timestamp so we can perform filter operations?
You can iterate all your items and update one by one with the conversion to Date. Here is an example to convert your date from mm/dd/yyyy to ISODate :
db.test.find().forEach( function(res){
if (typeof(res.date)=="string"){
var arr = res.date.split("/");
res.date = new Date(arr[2], arr[0] - 1, arr[1]);
db.test.save(res)
}
}
)
For Unix timestamp (millis from epoch), you can call getTime() from Date :
db.test.find().forEach( function(res){
if (typeof(res.date)=="string"){
var arr = res.date.split("/");
res.date = new Date(arr[2], arr[0] - 1, arr[1]).getTime();
db.test.save(res)
}
}
)
Note that these dates will be converted into UTC format, so you may want to change temporarily your timezone before doing your conversion
You can also use bulk update if you want to optimize update performance
You can also just convert your date to yyyy-mm-dd which will preserve sorting (check this post). The following will decompose your date field into day,month and year, set date field with the new format and write output in a new collection named test2 :
db.test.aggregate([{
$project: {
startTime: 1,
endTime: 1,
date: {
$let: {
vars: {
year: { $substr: ["$date", 6, 10] },
month: { $substr: ["$date", 0, 2] },
dayOfMonth: { $substr: ["$date", 3, 2] }
},
in : { $concat: ["$$year", "-", "$$month", "-", "$$dayOfMonth"] }
}
}
}
},{
$out :"test2"
}])

Timezone in mongo query

I have data inserted in UTC time format in mongodb. I want timings to be converted based on the timezone. Is there any possibility to do so in mongo query?
In mongo version 3.6 timezone has been added, mongo doc
expression to extract date part with timezone is
{ date: <dateExpression>, timezone: <tzExpression> }
we can either specify the timezone or offset while getting the date parts.
see my answer posted here
to get date from date with timezone America/Chicago
{ $month: {
date: new Date(),
timezone: "America/Chicago"
} }
or with offset
{ $month: {
date: ISODate(),
timezone: "-0500"
} }
Let consider your document contains ISODate as below :
db.collection.insert({"date":new Date()})
Above query insert date in ISODate format now you want to convert this ISODate into give timeZone.
Suppose you want to convert above date to Eastern Daylight Saving Time ( EDT ) epoch time zone conertor then offset converted as 14400 * 1000. First convert ISODate to timeStamp and then use substract EDT OffsetintimeStampand then converttimeStamptoISODate` again.
Check below aggregation query :
db.collection.aggregate({
"$project": {
"timestamp": { //convert ISODate tom timestamp
"$subtract": [{
"$divide": [{
"$subtract": ["$date", new Date("1970-01-01")]
}, 1000]
}, {
"$mod": [{
"$divide": [{
"$subtract": ["$date", new Date("1970-01-01")]
}, 1000]
}, 1]
}]
}
}
}, {
"$project": {
"timeZoneTimeStamp": {
"$subtract": [{ //substract timestamp to given offset if offset will in postive then replace subtract to add
"$multiply": ["$timestamp", 1000]
}, 14400000]
}
}
}, {
"$project": {
"timeZoneTimeStamp": 1, //converted timeZoneTimeStamp if required
"_id": 0,
"newDate": { // newDate is converted timezone ISODate
"$add": [new Date(0), "$timeZoneTimeStamp"]
}
}
})
NOTE :
In above query conversion from ISODATE to timeStamp ref. here
In case if the dates are not changed and constant e.g. something like created_record_date then whichever timezone data you need it, you should pre-calculate and save (as String) along with the same document so that you don't have to run the huge processing at the runtime which could slow down the execution time. in case you have existing records and you want to store the various different timezone data along with the records, think about running a Map-Reduct job and update the documents separately. (let me know if you need the code for that). However, if this date field can be changed as per the business logic then its wise to calculate at runtime. both techniques have their different use cases and their pros and cons.
-$
If you are using mongoose (probably also works in native driver):
import moment from 'moment-timezone'; // this is needed to use .tz() method
import mongoMoment from 'mongodb-moment';
// Initalize mongodb-moment so you can use moment() object directly in mongo query
mongoMoment(moment);
// Add timezone to your_date
const date = moment(your_date)
.tz("Europe/Zagreb");
// Make $gte/$lte queries with date ...

Why are dates in match aggregate query being ignored?

I'm trying to run an aggregation statement in my mongo db. I have a document whose structure is (at least) as follows:
{
"_id": ObjectId,
"date": ISODate,
"keywordGroupId": NumberLong,
"ranking": NumberLong,
}
I would like to run an aggregation statement that aggregates the 'ranking' field for a given 'keywordGroupId' and a given 'date' interval.
I have been trying with the following aggregate command:
{
aggregate : "KeywordHistory",
pipeline : [
{ $match: { keywordGroupId: 75 , "$date": {$gte: ISODate("2013-01-01T00:00:00.0Z"), $lt: ISODate("2013-02-01T00:00:00.0Z")}} },
{ $group: { _id: { null }, count: { $sum: "$ranking" } } }
]
}
This command executes without errors and returns a result. If I try to change the value for the 'keywordGroupId' field, the command returns a different value, so I assume that the $match statement works for that field (NumberLong). Though, if I change the 'date' range and I specify a time interval for which I don't have any data in the database, it still returns a result (I would actually expect an empty result set). So I have to assume that the $match statement is ignoring the date interval specified.
Can anyone help me with this point?
Remove the $ prefix on the $date field of your $match:
{ $match: {
keywordGroupId: 75,
date: {$gte: ISODate("2013-01-01T00:00:00.0Z"), $lt: ISODate("2013-02-01T00:00:00.0Z")}
}},
You only use the $ prefix when the field name is used in a value, not as a key.
Sometimes ISodate does not works . so in Case if you want to match date using only "one" date the best way is:---
ex:-- Let a schema be:---
var storeOrder = new Schema({
store_name:{type:String, required:true},
date :{type:Date ,default:moment(new Date()).format('YYYY-MM-DD')},
orders : [{
vegetable : String,
quantity : Number,
price:Number
}]
});
mongoose.model('storeorder',storeOrder);
now to aggregate by matching date :--
storeOrder.aggregate([$match:{date :new Date("2016-12-26T00:00:00.000Z")} ])
**It is must to use new Date("2016-12-26T00:00:00.000z") instead of Date("2016-12-26T00:00:00.000z") because Date(your_date) !== new Date(your_date).
THANK YOU
The aggregate expects a Javascript Date Object and doesn't work otherwise.
new Date();
new Date(year, month, day);
Please note the month start with 0 and not 1 (Your January is 0 and December 11)