Fondamentalement, mettez un $addToSet
l'opérateur ne peut pas fonctionner pour vous car vos données ne sont pas un vrai "set"
étant par définition une collection d'objets "complètement distincts".
L'autre élément logique ici est que vous travailleriez sur les données au fur et à mesure qu'elles arrivent, soit en tant qu'objet unique, soit en tant que flux. Je suppose qu'il s'agit d'un flux de nombreux éléments sous une forme et que vous pouvez utiliser une sorte de processeur de flux pour arriver à cette structure par document reçu :
{
"date": new Date("2015-03-09 13:23:00.000Z"),
"symbol": "AAPL",
"open": 127.14
"high": 127.17,
"low": 127.12
"close": 127.15,
"volume": 19734
}
Conversion vers un format décimal standard ainsi qu'une date UTC puisque tous les paramètres régionaux doivent vraiment être le domaine de votre application une fois que les données sont récupérées à partir du magasin de données bien sûr.
Je voudrais aussi au moins aplatir un peu votre "intraDayQuoteSchema" en supprimant la référence à l'autre collection et en y mettant simplement les données. Vous auriez toujours besoin d'une recherche lors de l'insertion, mais la surcharge du peuplement supplémentaire lors de la lecture semblerait être plus coûteuse que la surcharge de stockage :
intradayQuotesSchema = Schema({
symbol:{
name: String,
code: String
},
day:Date,
quotes:[quotesSchema]
});
Cela dépend de vos habitudes d'utilisation, mais cela sera probablement plus efficace de cette façon.
Le reste se résume vraiment à ce qui est acceptable pour
stream.on(function(data) {
var symbol = data.symbol,
myDay = new Date(
data.date.valueOf() -
( data.date.valueOf() % 1000 * 60 * 60 * 24 ));
delete data.symbol;
symbol.findOne({ "code": symbol },function(err,stock) {
intraDayQuote.findOneAndUpdate(
{ "symbol.code": symbol , "day": myDay },
{ "$setOnInsert": {
"symbol.name": stock.name
"quotes": [data]
}},
{ "upsert": true }
function(err,doc) {
intraDayQuote.findOneAndUpdate(
{
"symbol.code": symbol,
"day": myDay,
"quotes.date": data.date
},
{ "$set": { "quotes.$": data } },
function(err,doc) {
intraDayQuote.findOneAndUpdate(
{
"symbol.code": symbol,
"day": myDay,
"quotes.date": { "$ne": data.date }
},
{ "$push": { "quotes": data } },
function(err,doc) {
}
);
}
);
}
);
});
});
Si vous n'avez pas réellement besoin du document modifié dans la réponse, vous bénéficierez de l'implémentation de l'API Bulk Operations ici et de l'envoi de toutes les mises à jour de ce package dans une seule requête de base de données :
stream.on("data",function(data) {
var symbol = data.symbol,
myDay = new Date(
data.date.valueOf() -
( data.date.valueOf() % 1000 * 60 * 60 * 24 ));
delete data.symbol;
symbol.findOne({ "code": symbol },function(err,stock) {
var bulk = intraDayQuote.collection.initializeOrderedBulkOp();
bulk.find({ "symbol.code": symbol , "day": myDay })
.upsert().updateOne({
"$setOnInsert": {
"symbol.name": stock.name
"quotes": [data]
}
});
bulk.find({
"symbol.code": symbol,
"day": myDay,
"quotes.date": data.date
}).updateOne({
"$set": { "quotes.$": data }
});
bulk.find({
"symbol.code": symbol,
"day": myDay,
"quotes.date": { "$ne": data.date }
}).updateOne({
"$push": { "quotes": data }
});
bulk.execute(function(err,result) {
// maybe do something with the response
});
});
});
Le fait est qu'une seule des déclarations ici modifiera réellement les données, et comme tout est envoyé dans la même requête, il y a moins de va-et-vient entre l'application et le serveur.
L'autre cas est qu'il pourrait être plus simple dans ce cas d'avoir les données réelles référencées dans une autre collection. Cela devient alors une simple question de traitement des upserts :
intradayQuotesSchema = Schema({
symbol:{
name: String,
code: String
},
day:Date,
quotes:[{ type: Schema.Types.ObjectId, ref: "quote" }]
});
// and in the steam processor
stream.on("data",function(data) {
var symbol = data.symbol,
myDay = new Date(
data.date.valueOf() -
( data.date.valueOf() % 1000 * 60 * 60 * 24 ));
delete data.symbol;
symbol.findOne({ "code": symbol },function(err,stock) {
quote.update(
{ "date": data.date },
{ "$setOnInsert": data },
{ "upsert": true },
function(err,num,raw) {
if ( !raw.updatedExisting ) {
intraDayQuote.update(
{ "symbol.code": symbol , "day": myDay },
{
"$setOnInsert": {
"symbol.name": stock.name
},
"$addToSet": { "quotes": data }
},
{ "upsert": true },
function(err,num,raw) {
}
);
}
}
);
});
});
Cela dépend vraiment de l'importance pour vous d'avoir les données des devis imbriquées dans le document "jour". La principale distinction est si vous souhaitez interroger ces documents en fonction des données de certains de ces champs "citation" ou autrement vivre avec la surcharge de l'utilisation de .populate()
pour extraire les "citations" de l'autre collection.
Bien sûr, si elles sont référencées et que les données de devis sont importantes pour le filtrage de votre requête, vous pouvez toujours interroger cette collection pour le _id
valeurs qui correspondent et utilisent un $in
requête sur les documents "jour" pour faire correspondre uniquement les jours qui contiennent ces documents "devis" correspondants.
C'est une grande décision où il importe le plus de choisir le chemin que vous empruntez en fonction de la façon dont votre application utilise les données. J'espère que cela devrait vous guider sur les concepts généraux derrière ce que vous voulez réaliser.
P.S À moins que vous ne soyez "sûr" que vos données source sont toujours une date arrondie à une "minute" exacte, vous souhaiterez probablement utiliser le même type de calcul d'arrondi de date que celui utilisé pour obtenir également le "jour" discret.