MongoDB
 sql >> Base de Dados >  >> NoSQL >> MongoDB

Mongoose encontra um e empurra para a matriz de documentos


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.