Agrupar dados com padrão de Bucket
Nesta página
O padrão de bucket separa longas séries de dados em objetos distintos. Separar grandes séries de dados em grupos menores pode melhorar os padrões de acesso à consulta e simplificar a lógica do aplicativo. O bucketing é útil quando você tem objetos semelhantes relacionados a uma entidade central, como negociações de ações feitas por um único usuário.
Você pode utilizar o padrão bucket para paginação, agrupando seus dados com base nos elementos que seu aplicativo mostra por página. Essa abordagem utiliza o modelo de dados flexível do MongoDB para armazenar dados de acordo com os dados que seus aplicativos precisam.
Dica
As coleções de séries temporais aplicam o padrão de bucket automaticamente e são adequadas para a maioria das aplicações que envolvem o agrupamento de dados de séries temporais.
Sobre esta tarefa
Considere o esquema a seguir que acompanha as negociações de ações. O esquema inicial não utiliza o padrão de bucket e armazena cada negociação em um documento individual.
db.trades.insertMany( [ { "ticker" : "MDB", "customerId": 123, "type" : "buy", "quantity" : 419, "date" : ISODate("2023-10-26T15:47:03.434Z") }, { "ticker" : "MDB", "customerId": 123, "type" : "sell", "quantity" : 29, "date" : ISODate("2023-10-30T09:32:57.765Z") }, { "ticker" : "GOOG", "customerId": 456, "type" : "buy", "quantity" : 50, "date" : ISODate("2023-10-31T11:16:02.120Z") } ] )
O aplicativo mostra transações de ações feitas por um único cliente de cada vez e mostra 10 transações por página. Para simplificar a lógica do aplicativo, use o padrão bucket para agrupar as transações por customerId
em grupos de 10.
Passos
Agrupar os dados por customerId
Reorganizar o esquema para ter um único documento para cada customerId
:
{ "customerId": 123, "history": [ { "type": "buy", "ticker": "MDB", "qty": 419, "date": ISODate("2023-10-26T15:47:03.434Z") }, { "type": "sell", "ticker": "MDB", "qty": 29, "date": ISODate("2023-10-30T09:32:57.765Z") } ] }, { "customerId": 456, "history": [ { "type" : "buy", "ticker" : "GOOG", "quantity" : 50, "date" : ISODate("2023-10-31T11:16:02.120Z") } ] }
Com o padrão de bucket:
Os documentos com valores
customerId
comuns são condensados em um único documento, no qualcustomerId
é um campo de nível superior.As negociações desse cliente são agrupadas em um campo de array incorporado, chamado
history
.
Adicione um identificador e conte para cada bucket
1 db.trades.drop() 2 3 db.trades.insertMany( 4 [ 5 { 6 "_id": "123_1698349623", 7 "customerId": 123, 8 "count": 2, 9 "history": [ 10 { 11 "type": "buy", 12 "ticker": "MDB", 13 "qty": 419, 14 "date": ISODate("2023-10-26T15:47:03.434Z") 15 }, 16 { 17 "type": "sell", 18 "ticker": "MDB", 19 "qty": 29, 20 "date": ISODate("2023-10-30T09:32:57.765Z") 21 } 22 ] 23 }, 24 { 25 "_id": "456_1698765362", 26 "customerId": 456, 27 "count": 1, 28 "history": [ 29 { 30 "type" : "buy", 31 "ticker" : "GOOG", 32 "quantity" : 50, 33 "date" : ISODate("2023-10-31T11:16:02.120Z") 34 } 35 ] 36 }, 37 ] 38 )
O valor do campo _id
é uma concatenação de customerId
mais o tempo da primeira negociação em segundos (a partir da era UNIX) no campo history
.
O campo count
indica quantos elementos estão na array history
desse documento. O campo count
é usado para implementar a lógica de paginação.
Próximos passos
Depois de atualizar o esquema para usar o padrão de bucket, atualize a lógica do aplicativo para ler e gravar dados. Consulte as seções a seguir:
Consultar dados com o padrão de bucket
No esquema atualizado, cada documento contém dados para uma única página no aplicativo. Você pode usar os campos _id
e count
para determinar como retornar e atualizar dados.
Para consultar dados na página apropriada, use uma query regex para obter dados de um customerId
especificado e use skip
para retornar aos dados da página correta. A query regex em _id
usa índice _id padrão, o que resulta em consultas de alto desempenho sem a necessidade de um índice adicional.
A consulta a seguir retorna dados da primeira página de negociações para o cliente 123
:
db.trades.find( { "_id": /^123_/ } ).sort( { _id: 1 } ).limit(1)
Para retornar dados para páginas posteriores, especifique um valor de skip
menor do que a página para a qual você deseja mostrar dados. Por exemplo, para mostrar dados para a página 10, execute a seguinte query:
db.trades.find( { "_id": /^123_/ } ).sort( { _id: 1 } ).skip(9).limit(1)
Observação
A query anterior não retorna resultados porque os dados de amostra contêm apenas documentos da primeira página.
Inserir dados com o padrão Bucket
Agora que o esquema usa o padrão de bucket, atualize a lógica do aplicativo para inserir novas negociações no bucket correto. Use o comando update para inserir a negociação no bucket com o customerId
e o bucket apropriados.
O comando a seguir insere uma nova negociação para customerId: 123
:
db.trades.updateOne( { "_id": /^123_/, "count": { $lt: 10 } }, { "$push": { "history": { "type": "buy", "ticker": "MSFT", "qty": 42, "date": ISODate("2023-11-02T11:43:10") } }, "$inc": { "count": 1 }, "$setOnInsert": { "_id": "123_1698939791", "customerId": 123 } }, { upsert: true } )
O aplicação exibe 10 transações por página. O filtro de atualização procura um documento para customerId: 123
onde o count
é menor que 10, o que significa que o bucket não contém uma página inteira de dados.
Se houver um documento que corresponda a
"_id": /^123_/
e seucount
for menor que 10, o comando de atualização empurrará a nova transação para a arrayhistory
do documento correspondente.Se não houver um documento correspondente, o comando de atualização inserirá um novo documento com a nova transação (porque
upsert
étrue
). O campo_id
do novo documento é uma concatenação docustomerId
e o tempo em segundos desde a Era UNIX da transação.
A lógica para comandos de atualização evita arrays ilimitadas, garantindo que nenhuma array history
contenha mais de 10 documentos.
Depois de executar a operação de atualização, a coleção trades
tem os seguintes documentos:
[ { _id: '123_1698349623', customerId: 123, count: 3, history: [ { type: 'buy', ticker: 'MDB', qty: 419, date: ISODate("2023-10-26T15:47:03.434Z") }, { type: 'sell', ticker: 'MDB', qty: 29, date: ISODate("2023-10-30T09:32:57.765Z") }, { type: 'buy', ticker: 'MSFT', qty: 42, date: ISODate("2023-11-02T11:43:10.000Z") } ] }, { _id: '456_1698765362', customerId: 456, count: 1, history: [ { type: 'buy', ticker: 'GOOG', quantity: 50, date: ISODate("2023-10-31T11:16:02.120Z") } ] } ]
Resultados
Depois de implementar o padrão de bucket, você não precisa incorporar a lógica de paginação para retornar resultados em seu aplicação. A forma como os dados são armazenados corresponde à forma como são utilizados no aplicação.