Basicamente, coloque um
$addToSet
O operador não pode trabalhar para você porque seus dados não são um "set"
verdadeiro por definição sendo uma coleção de objetos "completamente distintos". A outra parte do sentido lógico aqui é que você trabalharia nos dados à medida que eles chegassem, seja como um único objeto ou um feed. Presumo que seja um feed de muitos itens de alguma forma e que você possa usar algum tipo de processador de fluxo para chegar a essa estrutura por documento recebido:
{
"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
}
Convertendo para um formato decimal padrão, bem como uma data UTC, já que qualquer configuração de localidade realmente deve ser o domínio do seu aplicativo, uma vez que os dados são recuperados do armazenamento de dados, é claro.
Eu também pelo menos achataria seu "intraDayQuoteSchema" um pouco removendo a referência à outra coleção e apenas colocando os dados lá. Você ainda precisaria de uma pesquisa na inserção, mas a sobrecarga do preenchimento adicional na leitura pareceria mais cara do que a sobrecarga de armazenamento:
intradayQuotesSchema = Schema({
symbol:{
name: String,
code: String
},
day:Date,
quotes:[quotesSchema]
});
Depende de seus padrões de uso, mas é provável que seja mais eficaz dessa maneira.
O resto realmente se resume ao que é aceitável para
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) {
}
);
}
);
}
);
});
});
Se você realmente não precisar do documento modificado na resposta, obterá algum benefício implementando a API de operações em massa aqui e enviando todas as atualizações neste pacote em uma única solicitação de banco de dados:
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
});
});
});
O ponto é que apenas uma das instruções lá realmente modificará os dados e, como tudo isso é enviado na mesma solicitação, há menos idas e vindas entre o aplicativo e o servidor.
O caso alternativo é que pode ser mais simples neste caso ter os dados reais referenciados em outra coleção. Isso se torna uma simples questão de processar 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) {
}
);
}
}
);
});
});
Realmente se resume a quão importante é para você ter os dados de cotações aninhados no documento "dia". A principal distinção é se você deseja consultar esses documentos com base nos dados de alguns desses campos "quote" ou viver com a sobrecarga de usar
.populate()
para puxar as "aspas" da outra coleção. É claro que, se referenciados e os dados de cotação forem importantes para a filtragem de sua consulta, você sempre poderá consultar essa coleção para o
_id
valores que correspondem e usam um $in
consulta nos documentos de "dia" para corresponder apenas aos dias que contêm esses documentos de "cotação" correspondentes. É uma grande decisão onde mais importa qual caminho você toma com base em como seu aplicativo usa os dados. Espero que isso o guie sobre os conceitos gerais por trás de fazer o que você deseja alcançar.
P.S. A menos que você tenha "certeza" de que seus dados de origem são sempre uma data arredondada para um "minuto" exato, você provavelmente deseja empregar o mesmo tipo de matemática de arredondamento de data usada para obter o "dia" discreto também.