J'essaie d'agréger des enregistrements dans une collection MongoDB par heure et j'ai besoin de convertir la date stockée sous forme d'horodatage (millisecondes) en ISODate afin de pouvoir utiliser les opérateurs de date intégrés au cadre global ($ heure, $ mois, etc.).
Les enregistrements sont stockés comme
{
"data" : { "UserId" : "abc", "ProjId" : "xyz"},
"time" : NumberLong("1395140780706"),
"_id" : ObjectId("532828ac338ed9c33aa8eca7")
}
J'essaie d'utiliser une requête globale du type suivant:
db.events.aggregate(
{
$match : {
"time" : { $gte : 1395186209804, $lte : 1395192902825 }
}
},
{
$project : {
_id : "$_id",
dt : {$concat : (Date("$time")).toString()} // need to project as ISODate
}
},
// process records further in $project or $group clause
)
qui produit des résultats de la forme:
{
"result" : [
{
"_id" : ObjectId("5328da21fd207d9c3567d3ec"),
"dt" : "Fri Mar 21 2014 17:35:46 GMT-0400 (EDT)"
},
{
"_id" : ObjectId("5328da21fd207d9c3567d3ed"),
"dt" : "Fri Mar 21 2014 17:35:46 GMT-0400 (EDT)"
},
...
}
Je souhaite extraire l'heure, le jour, le mois et l'année de la date, mais comme l'heure est projetée en tant que chaîne, je ne peux pas utiliser les opérateurs de date intégrés au framework global ($ hour, etc.).
Comment puis-je convertir le temps en millisecondes en date ISO comme suit:
db.events.aggregate(
{
$match : {
"time" : { $gte : 1395186209804, $lte : 1395192902825 }
}
},
{
$project : {
_id : "$_id",
dt : <ISO date from "$time">
}
},
{
$project : {
_id : "$_id",
date : {
hour : {$hour : "$dt"}
}
}
}
)
Je suppose qu'il n'y a aucun moyen de le faire. Parce que la structure d'agrégation est écrite en code natif. ne pas utiliser le moteur V8. Ainsi, tout le JavaScript ne fonctionnera pas dans le cadre (et c’est aussi pourquoi le cadre d’agrégation est beaucoup plus rapide).
Mapper/Réduire est une façon de résoudre ce problème, mais le cadre d’agrégation a nettement mieux performé.
À propos des performances de la carte/réduction, lisez ce fil de discussion .
Une autre solution consiste à obtenir un résultat "brut" du framework d'agrégation, à le placer dans un tableau JSON. Ensuite, effectuez la conversion en exécutant JavaScript. Un peu comme:
var results = db.events.aggregate(...);
reasult.forEach(function(data) {
data.date = new Date(data.dateInMillionSeconds);
// date is now stored in the "date" property
}
En fait, c’est possible, l’astuce consiste à ajouter votre temps en millisecondes à un objet Date () à zéro millisecondes en utilisant une syntaxe semblable à celle-ci:
dt : {$add: [new Date(0), "$time"]}
J'ai modifié votre agrégation d'en haut pour produire le résultat:
db.events.aggregate(
{
$project : {
_id : "$_id",
dt : {$add: [new Date(0), "$time"]}
}
},
{
$project : {
_id : "$_id",
date : {
hour : {$hour : "$dt"}
}
}
}
);
Le résultat est (avec une entrée de vos données d'échantillon):
{
"result": [
{
"_id": ObjectId("532828ac338ed9c33aa8eca7"),
"date": {
"hour": 11
}
}
],
"ok": 1
}
Pour renvoyer une date BSON valide, il vous suffit d'une petite date "mathématique" à l'aide de l'opérateur $add
. Vous devez ajouter new Date(0)
à l'horodatage. La new Date(0)
représente le nombre de millisecondes écoulées depuis l’époque Unix (1er janvier 1970) et est un raccourci pour new Date("1970-01-01")
.
db.events.aggregate([
{ "$match": { "time": { "$gte" : 1395136209804, "$lte" : 1395192902825 } } },
{ "$project": {
"hour": { "$hour": { "$add": [ new Date(0), "$time" ] } },
"day": { "$dayOfMonth": { "$add": [ new Date(0), "$time" ] } },
"month": { "$month": { "$add": [ new Date(0), "$time" ] } },
"year": { "$year": { "$add": [ new Date(0), "$time" ] } }
}}
])
Quels rendements:
{
"_id" : ObjectId("532828ac338ed9c33aa8eca7"),
"hour" : 11,
"day" : 18,
"month" : 3,
"year" : 2014
}
utiliser ceci si la fonction
{$add: [new Date(0), "$time"]
} renvoiestring type
et non unISO date type
J'utilise toute cette option mais j'échoue quand même, car ma nouvelle date de $project
renvoie un string type
tel que '2000-11-2:xxxxxxx'
pas date type
comme ISO('2000-11-2:xxxxxxx')
pour tous ceux qui ont le même problème que moi.
db.events.aggregate(
{
$project : {
_id : "$_id",
dt : {$add: [new Date(0), "$time"]}
}
},
{
$project : {
_id : "$_id",
"year": { $substr: [ "$dt", 0, 4 ] },
"month": { $substr: [ "$dt", 5, 2] },
"day": { $substr: [ "$dt", 8, 2 ] }
}
}
);
le résultat sera
{ _id: '59f940eaea87453b30f42cf5',
year: '2017',
month: '07',
day: '04'
},
vous pouvez obtenir des heures ou des minutes si vous le souhaitez en fonction de la string
que vous souhaitez subset
, vous pouvez alors group
définir à nouveau selon la même date, mois ou année