$merge (agregação)
Definição
Observação
Esta página descreve o estágio $merge
, que gera resultados do pipeline de agregação para uma coleção. Para o operador $mergeObjects
, que mescla documentos em um único documento, consulte $mergeObjects
.
$merge
Grava os resultados do pipeline de agregação em uma coleção especificada. O operador
$merge
deve ser o último estágio no pipeline.O estágio
$merge
:Pode enviar para uma coleção no mesmo banco de dados ou em um banco de dados diferente.
Pode gerar saída para a mesma coleção que está sendo agregada. Para obter mais informações,consulte Saída para a mesma coleção que está sendo agregada.
Considere os seguintes pontos ao usar os estágios
$merge
ou$out
em um pipeline de agregação:A partir do MongoDB 5.0, os pipelines com um estágio
$merge
poderão ser executados em nós secundários do conjunto de réplicas se todos os nós do cluster tiverem o featureCompatibilityVersion definido como5.0
ou superior e a preferência de leitura permitir leituras secundárias.nas versões anteriores do MongoDB , os pipelines com estágios
$out
ou$merge
sempre são executados no nó principal, e a preferência de leitura não é considerada.
Cria uma nova coleta se a coleta de saída ainda não existir.
Pode incorporar resultados (inserir novos documentos, mesclar documentos, substituir documentos, manter documentos existentes, falhar a operação, processar documentos com um pipeline de atualização personalizado) a uma coleção existente.
Pode gerar saída para uma coleção fragmentada. A coleta de entrada também pode ser fragmentada.
Para uma comparação com o estágio
$out
, que também gera os resultados da agregação em uma coleção, consulte$merge
e$out
Comparação.
Observação
Visualizações materializadas sob demanda
$merge
pode incorporar os resultados do pipeline em uma coleção de saída existente em vez de executar uma substituição completa da coleção. Essa funcionalidade permite que os usuários criem exibições materializadas sob demanda, em que o conteúdo da coleção de saída é atualizado de forma incremental quando o pipeline é executado.
Para obter mais informações sobre esse caso de uso, consulte Exibições materializadas sob demanda , bem como os exemplos nesta página.
As visualizações materializadas são separadas das exibições somente para leitura. Para obter informações sobre como criar visualizações somente para leitura, consulte exibições somente para leitura.
Compatibilidade
Você pode utilizar o $merge
para implantações hospedadas nos seguintes ambientes:
MongoDB Atlas: o serviço totalmente gerenciado para implantações do MongoDB na nuvem
MongoDB Enterprise: a versão autogerenciada e baseada em assinatura do MongoDB
MongoDB Community: uma versão com código disponível, de uso gratuito e autogerenciada do MongoDB
Sintaxe
$merge
tem a seguinte sintaxe:
{ $merge: { into: <collection> -or- { db: <db>, coll: <collection> }, on: <identifier field> -or- [ <identifier field1>, ...], // Optional let: <variables>, // Optional whenMatched: <replace|keepExisting|merge|fail|pipeline>, // Optional whenNotMatched: <insert|discard|fail> // Optional } }
Por exemplo:
{ $merge: { into: "myOutput", on: "_id", whenMatched: "replace", whenNotMatched: "insert" } }
Se utilizar todas as opções padrão para $merge
, inclusive gravar em uma coleção no mesmo banco de dados, você poderá utilizar o formulário simplificado:
{ $merge: <collection> } // Output collection is in the same database
O $merge
pega um documento com os seguintes campos:
Campo | Descrição | ||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|
A coleção de saída. Especifique uma das seguintes opções:
Se a coleção de saída não existir, o
A coleção de saída pode ser uma coleção fragmentada. | |||||||||||
Opcional. Campo ou campos que atuam como um identificador exclusivo para um documento. O identificador determina se um documento de resultados corresponde a um documento existente na coleção de resultados. Especifique uma das seguintes opções:
Para o campo ou campos especificados:
O valor padrão para ativado depende da coleção de saída:
| |||||||||||
Opcional. O comportamento de Você pode especificar:
| |||||||||||
Opcional. Especifica variáveis para uso no pipeline whenMatched. Especifique um documento com os nomes de variáveis e expressões de valor:
Se não for especificado, o padrão será Para acessar as variáveis no pipeline whenMatched: Especifique o prefixo do sinal de dólar duplo ($$) juntamente com o nome da variável no formulário Para obter exemplos, consulte Usar variáveis para personalizar a mesclagem. | |||||||||||
Opcional. O comportamento de Você pode especificar uma das strings de ação predefinidas: |
Considerações
_id
Geração de campo
Se o campo _id
não estiver presente em um documento dos resultados do pipeline de agregação, o estágio $merge
o gerará automaticamente.
Por exemplo, no seguinte pipeline de agregação, $project
exclui o campo _id
dos documentos passados para $merge
. Quando $merge
grava esses documentos no "newCollection"
, $merge
gera um novo campo e valor _id
.
db.sales.aggregate( [ { $project: { _id: 0 } }, { $merge : { into : "newCollection" } } ] )
Criar uma nova coleção se a coleta de saída não for existente
A operação $merge
cria uma nova coleção se a coleção de saída especificada não existir.
A coleção de saída é criada quando
$merge
grava o primeiro documento na coleção e fica imediatamente visível.Se a agregação falhar, nenhuma das gravações concluídas pelo
$merge
antes do erro será revertida.
Observação
Para um conjunto de réplicas ou um autônomo, se o banco de dados de saída não existir, o $merge
também criará o banco de dados.
Para um cluster fragmentado, o banco de dados de saída especificado já deve existir.
Se a coleção de saída não existir, $merge
exigirá que o identificador ligado seja o campo _id
. Para utilizar um valor de campo diferente do on
para uma coleção que não existe, você pode criar a coleção primeiro criando um índice único no(s) campo(s) desejado(s). Por exemplo, se a coleção de saída newDailySales201905
não existir e você quiser especificar o campo salesDate
como o identificador ligado:
db.newDailySales201905.createIndex( { salesDate: 1 }, { unique: true } ) db.sales.aggregate( [ { $match: { date: { $gte: new Date("2019-05-01"), $lt: new Date("2019-06-01") } } }, { $group: { _id: { $dateToString: { format: "%Y-%m-%d", date: "$date" } }, totalqty: { $sum: "$quantity" } } }, { $project: { _id: 0, salesDate: { $toDate: "$_id" }, totalqty: 1 } }, { $merge : { into : "newDailySales201905", on: "salesDate" } } ] )
Saída para uma coleção compartilhada
O estágio $merge
pode gerar saída para uma coleção fragmentada. Quando a coleção de saída é fragmentada, $merge
utiliza o _id
campo e todos os campos de chave de fragmento como o identificador padrão. Se você substituir o padrão, o identificador ativado deverá incluir todos os campos de chave de fragmento :
{ $merge: { into: "<shardedColl>" or { db:"<sharding enabled db>", coll: "<shardedColl>" }, on: [ "<shardkeyfield1>", "<shardkeyfield2>",... ], // Shard key fields and any additional fields let: <variables>, // Optional whenMatched: <replace|keepExisting|merge|fail|pipeline>, // Optional whenNotMatched: <insert|discard|fail> // Optional } }
Por exemplo, em um banco de dados que tenha sharding enabled
, utilize o método sh.shardCollection()
para criar uma nova coleção fragmentada newrestaurants
com o campo postcode
como a chave fragmentada.
sh.enableSharding("exampledb"); // Sharding must be enabled in the database sh.shardCollection( "exampledb.newrestaurants", // Namespace of the collection to shard { postcode: 1 }, // Shard key );
A coleção newrestaurants
conterá documentos com informações sobre aberturas de novos restaurantes por mês (campo date
) e código postal (chave de fragmento); especificamente, o identificador do campo é ["date", "postcode"]
(a ordem dos campos não importa). Como $merge
exige um índice único com chaves que correspondem aos campos do identificador ligado, crie o índice único (a ordem dos campos não importa): [1]
use exampledb db.newrestaurants.createIndex( { postcode: 1, date: 1 }, { unique: true } )
Com a coleção fragmentada restaurants
e o índice único criado, você pode utilizar $merge
para gerar os resultados da agregação para essa coleção, correspondendo a [ "date", "postcode" ]
, como neste exemplo:
use exampledb db.openings.aggregate([ { $group: { _id: { date: { $dateToString: { format: "%Y-%m", date: "$date" } }, postcode: "$postcode" }, restaurants: { $push: "$restaurantName" } } }, { $project: { _id: 0, postcode: "$_id.postcode", date: "$_id.date", restaurants: 1 } }, { $merge: { into: "newrestaurants", "on": [ "date", "postcode" ], whenMatched: "replace", whenNotMatched: "insert" } } ])
[1] | O método sh.shardCollection() também pode criar um índice exclusivo na chave de estilhaço quando a opção { unique: true
} for passada se: a chave de estilhaço for baseada em intervalo, a coleção estiver vazia e um índice exclusivo na chave de estilhaço ainda não existir. No exemplo anterior, como o identificador on é a chave de estilhaço e outro campo, uma operação separada para criar o índice correspondente é necessária. |
Substituir Documentos () vs Substituir Coleção ()<a class=\" \" href=\" \" title=\" \"><svg xmlns=\" \" width=\" \" height=\" \" fill=\" \" viewbox=\" \" class=\"$merge
\" role=\" \" aria-label=\" \"><path fill=\" \" d=\" \"> <path fill=\" \" d=\"$out
\">
$merge
pode substituir um documento existente na coleção de saída se os resultados da agregação contiverem um documento ou documentos que corresponderem com base na especificação on. Como tal, $merge
pode substituir todos os documentos na coleção existente se os resultados da agregação incluírem documentos correspondentes para todos os documentos existentes na coleção e você especificar "substituir" para whenMatched.
No entanto, para substituir uma coleção existente, independentemente dos resultados da agregação, use $out
em vez disso.
Documentos existentes e _id
e valores da chave de shard
Os erros do $merge
se o $merge
resultar em uma alteração no valor de _id
de um documento existente.
Dica
Para evitar esse erro, se o campo on não contiver o campo _id
, remova o campo _id
dos resultados da agregação para evitar o erro, como em um estágio $unset
anterior e assim por diante.
Além disso, para uma coleção fragmentada, $merge
também gera um erro se resultar em uma alteração no valor da chave de fragmento de um documento existente.
Nenhuma gravação concluída pelo $merge
antes do erro será revertida.
Restrições de índice único
Se o índice único usado por $merge
para o(s) campo(s) for descartado no meio da agregação, não haverá garantias de que a agregação será descartada. Se a agregação continuar, não haverá garantias de que os documentos não tenham valores de campo on
duplicados.
Se o $merge
tentar gravar um documento que violar qualquer índice único na coleção de saída, a operação vai gerar um erro. Por exemplo:
Insira um documento não correspondente que viole um índice exclusivo diferente do índice no(s) campo(s) em.
Falha se houver um documento correspondente na coleção. Especificamente, a operação tenta inserir o documento correspondente que viola o índice exclusivo no (s) campo (s) ativado(s).
Substituir um documento existente por um novo documento que viole um índice exclusivo que não seja o índice do(s) campo(s) ligado(s).
Mescle os documentos correspondentes que resultam em um documento que viola um índice exclusivo diferente do índice no(s) campo(s) ligado(s).
Validação de esquema
Se sua coleção usar validação de esquema e tiver validationAction
definido como error
, inserir um documento inválido ou atualizar um documento com valores inválidos com $merge
gerará um MongoServerError
e o documento não será gravado na coleção de destino. Se houver vários documentos inválidos, apenas o primeiro documento inválido encontrado gerará um erro. Todos os documentos válidos são gravados na coleção de destino e todos os documentos inválidos não são gravados.
whenMatched
Comportamento de pipeline
Se todos os itens a seguir forem verdadeiros para um estágio $merge
, $merge
insere o documento diretamente na coleção de saída:
O valor de whenMatched é um pipeline de agregação,
O valor de whenNotMatched é
insert
, eNão há correspondência para um documento na coleta de saída,
$merge
insere o documento diretamente na coleta de saída.
$merge
e $out
comparação
Com a introdução do $merge
, o MongoDB oferece dois estágios, $merge
e $out
, para gravar os resultados do pipeline de agregação em uma coleção:
|
|
|
|
|
|
|
|
|
|
Saída para a mesma coleção que está sendo agregada
Aviso
Quando $merge
gera saídas para a mesma coleção que está sendo agregada, os documentos podem ser atualizados várias vezes ou a operação pode resultar em um loop infinito. Esse comportamento ocorre quando a atualização executada por $merge
altera a localização física dos documentos armazenados no disco. Quando a localização física de um documento muda, $merge
pode considerá-lo um documento totalmente novo, resultando em atualizações adicionais. Para obter mais informações sobre esse comportamento, consulte Problema de Halloween.
$merge
pode gerar saída para a mesma coleção que está sendo agregada. Você também pode gerar saída para uma coleção que aparece em outros estágios do pipeline, como $lookup
.
Restrições
Restrições | Descrição |
---|---|
Um pipeline de agregação não pode utilizar $merge dentro de uma transação. | |
Um pipeline de agregação não pode utilizar $merge para gerar uma coleção de séries temporais. | |
Separate from materialized view | A definição de visualização não pode incluir o estágio $merge . Se a definição de visualização incluir pipeline aninhado (por exemplo, a definição de visualização inclui o estágio $facet ), essa restrição de estágio $merge também se aplica aos pipelines aninhados. |
$lookup estágio | |
$facet estágio | |
$unionWith estágio | |
"linearizable" Leia a preocupação | O estágio |
Exemplos
Visualização materializada sob demanda: criação inicial
Se a coleção de saída não existir, o $merge
criará a coleção.
Por exemplo, uma coleção chamada salaries
no banco de dados zoo
é preenchida com o salário do funcionário e o histórico do departamento:
db.getSiblingDB("zoo").salaries.insertMany([ { "_id" : 1, employee: "Ant", dept: "A", salary: 100000, fiscal_year: 2017 }, { "_id" : 2, employee: "Bee", dept: "A", salary: 120000, fiscal_year: 2017 }, { "_id" : 3, employee: "Cat", dept: "Z", salary: 115000, fiscal_year: 2017 }, { "_id" : 4, employee: "Ant", dept: "A", salary: 115000, fiscal_year: 2018 }, { "_id" : 5, employee: "Bee", dept: "Z", salary: 145000, fiscal_year: 2018 }, { "_id" : 6, employee: "Cat", dept: "Z", salary: 135000, fiscal_year: 2018 }, { "_id" : 7, employee: "Gecko", dept: "A", salary: 100000, fiscal_year: 2018 }, { "_id" : 8, employee: "Ant", dept: "A", salary: 125000, fiscal_year: 2019 }, { "_id" : 9, employee: "Bee", dept: "Z", salary: 160000, fiscal_year: 2019 }, { "_id" : 10, employee: "Cat", dept: "Z", salary: 150000, fiscal_year: 2019 } ])
Você pode utilizar os estágios $group
e $merge
para criar inicialmente uma coleção chamada budgets
(no banco de dados reporting
) a partir dos dados atualmente na coleção salaries
:
Observação
Para um conjunto de réplicas ou uma implantação autônoma, se o banco de dados de saída não existir, o $merge
também criará o banco de dados.
Para uma implantação de cluster fragmentado, o banco de dados de saída especificado já deve existir.
db.getSiblingDB("zoo").salaries.aggregate( [ { $group: { _id: { fiscal_year: "$fiscal_year", dept: "$dept" }, salaries: { $sum: "$salary" } } }, { $merge : { into: { db: "reporting", coll: "budgets" }, on: "_id", whenMatched: "replace", whenNotMatched: "insert" } } ] )
Etapa
$group
para agrupar os salários porfiscal_year
edept
.O estágio
$merge
grava a saída do estágio$group
anterior na coleçãobudgets
no banco de dadosreporting
.
Para visualizar os documentos na nova coleção budgets
:
db.getSiblingDB("reporting").budgets.find().sort( { _id: 1 } )
A coleção budgets
contém os seguintes documentos:
{ "_id" : { "fiscal_year" : 2017, "dept" : "A" }, "salaries" : 220000 } { "_id" : { "fiscal_year" : 2017, "dept" : "Z" }, "salaries" : 115000 } { "_id" : { "fiscal_year" : 2018, "dept" : "A" }, "salaries" : 215000 } { "_id" : { "fiscal_year" : 2018, "dept" : "Z" }, "salaries" : 280000 } { "_id" : { "fiscal_year" : 2019, "dept" : "A" }, "salaries" : 125000 } { "_id" : { "fiscal_year" : 2019, "dept" : "Z" }, "salaries" : 310000 }
Visualização materializada sob demanda: atualizar/substituir dados
O exemplo a seguir usa as coleções no exemplo anterior.
A coleção do exemplo salaries
contém o salário do funcionário e o histórico do departamento:
{ "_id" : 1, employee: "Ant", dept: "A", salary: 100000, fiscal_year: 2017 }, { "_id" : 2, employee: "Bee", dept: "A", salary: 120000, fiscal_year: 2017 }, { "_id" : 3, employee: "Cat", dept: "Z", salary: 115000, fiscal_year: 2017 }, { "_id" : 4, employee: "Ant", dept: "A", salary: 115000, fiscal_year: 2018 }, { "_id" : 5, employee: "Bee", dept: "Z", salary: 145000, fiscal_year: 2018 }, { "_id" : 6, employee: "Cat", dept: "Z", salary: 135000, fiscal_year: 2018 }, { "_id" : 7, employee: "Gecko", dept: "A", salary: 100000, fiscal_year: 2018 }, { "_id" : 8, employee: "Ant", dept: "A", salary: 125000, fiscal_year: 2019 }, { "_id" : 9, employee: "Bee", dept: "Z", salary: 160000, fiscal_year: 2019 }, { "_id" : 10, employee: "Cat", dept: "Z", salary: 150000, fiscal_year: 2019 }
A coleção do exemplo budgets
contém os orçamentos anuais cumulativos:
{ "_id" : { "fiscal_year" : 2017, "dept" : "A" }, "salaries" : 220000 } { "_id" : { "fiscal_year" : 2017, "dept" : "Z" }, "salaries" : 115000 } { "_id" : { "fiscal_year" : 2018, "dept" : "A" }, "salaries" : 215000 } { "_id" : { "fiscal_year" : 2018, "dept" : "Z" }, "salaries" : 280000 } { "_id" : { "fiscal_year" : 2019, "dept" : "A" }, "salaries" : 125000 } { "_id" : { "fiscal_year" : 2019, "dept" : "Z" }, "salaries" : 310000 }
Durante o ano fiscal atual (2019
neste exemplo), novos funcionários são adicionados à coleção salaries
e novas contagens de funcionários são pré-alocadas para o próximo ano:
db.getSiblingDB("zoo").salaries.insertMany([ { "_id" : 11, employee: "Wren", dept: "Z", salary: 100000, fiscal_year: 2019 }, { "_id" : 12, employee: "Zebra", dept: "A", salary: 150000, fiscal_year: 2019 }, { "_id" : 13, employee: "headcount1", dept: "Z", salary: 120000, fiscal_year: 2020 }, { "_id" : 14, employee: "headcount2", dept: "Z", salary: 120000, fiscal_year: 2020 } ])
Para atualizar a coleção budgets
para refletir as novas informações de salário, o seguinte pipeline de agregação utiliza:
$match
etapa para encontrar todos os documentos comfiscal_year
maior ou igual a2019
.Etapa
$group
para agrupar os salários porfiscal_year
edept
.$merge
para gravar o conjunto de resultados na coleçãobudgets
, substituindo documentos com o mesmo valor de_id
(neste exemplo, um documento com o ano fiscal e o departamento.). Para documentos que não têm correspondências na coleção, o$merge
insere os novos documentos.
db.getSiblingDB("zoo").salaries.aggregate( [ { $match : { fiscal_year: { $gte : 2019 } } }, { $group: { _id: { fiscal_year: "$fiscal_year", dept: "$dept" }, salaries: { $sum: "$salary" } } }, { $merge : { into: { db: "reporting", coll: "budgets" }, on: "_id", whenMatched: "replace", whenNotMatched: "insert" } } ] )
Após a execução da agregação, exiba os documentos na coleção budgets
:
db.getSiblingDB("reporting").budgets.find().sort( { _id: 1 } )
A coleção budgets
incorpora os novos dados de salário para o ano fiscal 2019 e adiciona novos documentos para o ano fiscal 2020:
{ "_id" : { "fiscal_year" : 2017, "dept" : "A" }, "salaries" : 220000 } { "_id" : { "fiscal_year" : 2017, "dept" : "Z" }, "salaries" : 115000 } { "_id" : { "fiscal_year" : 2018, "dept" : "A" }, "salaries" : 215000 } { "_id" : { "fiscal_year" : 2018, "dept" : "Z" }, "salaries" : 280000 } { "_id" : { "fiscal_year" : 2019, "dept" : "A" }, "salaries" : 275000 } { "_id" : { "fiscal_year" : 2019, "dept" : "Z" }, "salaries" : 410000 } { "_id" : { "fiscal_year" : 2020, "dept" : "Z" }, "salaries" : 240000 }
Insira apenas novos dados
Para garantir que o $merge
não substitua os dados existentes na coleção, configure whenMatched para keepExisting ou fail.
A coleção do exemplo salaries
no banco de dados zoo
contém o salário do funcionário e o histórico do departamento:
{ "_id" : 1, employee: "Ant", dept: "A", salary: 100000, fiscal_year: 2017 }, { "_id" : 2, employee: "Bee", dept: "A", salary: 120000, fiscal_year: 2017 }, { "_id" : 3, employee: "Cat", dept: "Z", salary: 115000, fiscal_year: 2017 }, { "_id" : 4, employee: "Ant", dept: "A", salary: 115000, fiscal_year: 2018 }, { "_id" : 5, employee: "Bee", dept: "Z", salary: 145000, fiscal_year: 2018 }, { "_id" : 6, employee: "Cat", dept: "Z", salary: 135000, fiscal_year: 2018 }, { "_id" : 7, employee: "Gecko", dept: "A", salary: 100000, fiscal_year: 2018 }, { "_id" : 8, employee: "Ant", dept: "A", salary: 125000, fiscal_year: 2019 }, { "_id" : 9, employee: "Bee", dept: "Z", salary: 160000, fiscal_year: 2019 }, { "_id" : 10, employee: "Cat", dept: "Z", salary: 150000, fiscal_year: 2019 }
Uma coleção orgArchive
no banco de dados reporting
contém registros históricos da organização departamental dos últimos anos fiscais. Os registros arquivados não devem ser modificados.
{ "_id" : ObjectId("5cd8c68261baa09e9f3622be"), "employees" : [ "Ant", "Gecko" ], "dept" : "A", "fiscal_year" : 2018 } { "_id" : ObjectId("5cd8c68261baa09e9f3622bf"), "employees" : [ "Ant", "Bee" ], "dept" : "A", "fiscal_year" : 2017 } { "_id" : ObjectId("5cd8c68261baa09e9f3622c0"), "employees" : [ "Bee", "Cat" ], "dept" : "Z", "fiscal_year" : 2018 } { "_id" : ObjectId("5cd8c68261baa09e9f3622c1"), "employees" : [ "Cat" ], "dept" : "Z", "fiscal_year" : 2017 }
A coleção orgArchive
tem um índice composto único nos campos fiscal_year
e dept
. Especificamente, deve haver no máximo um registro para a mesma combinação de ano fiscal e departamento:
db.getSiblingDB("reporting").orgArchive.createIndex ( { fiscal_year: 1, dept: 1 }, { unique: true } )
No final do ano fiscal atual (2019
neste exemplo), a coleção salaries
contém os seguintes documentos:
{ "_id" : 1, "employee" : "Ant", "dept" : "A", "salary" : 100000, "fiscal_year" : 2017 } { "_id" : 2, "employee" : "Bee", "dept" : "A", "salary" : 120000, "fiscal_year" : 2017 } { "_id" : 3, "employee" : "Cat", "dept" : "Z", "salary" : 115000, "fiscal_year" : 2017 } { "_id" : 4, "employee" : "Ant", "dept" : "A", "salary" : 115000, "fiscal_year" : 2018 } { "_id" : 5, "employee" : "Bee", "dept" : "Z", "salary" : 145000, "fiscal_year" : 2018 } { "_id" : 6, "employee" : "Cat", "dept" : "Z", "salary" : 135000, "fiscal_year" : 2018 } { "_id" : 7, "employee" : "Gecko", "dept" : "A", "salary" : 100000, "fiscal_year" : 2018 } { "_id" : 8, "employee" : "Ant", "dept" : "A", "salary" : 125000, "fiscal_year" : 2019 } { "_id" : 9, "employee" : "Bee", "dept" : "Z", "salary" : 160000, "fiscal_year" : 2019 } { "_id" : 10, "employee" : "Cat", "dept" : "Z", "salary" : 150000, "fiscal_year" : 2019 } { "_id" : 11, "employee" : "Wren", "dept" : "Z", "salary" : 100000, "fiscal_year" : 2019 } { "_id" : 12, "employee" : "Zebra", "dept" : "A", "salary" : 150000, "fiscal_year" : 2019 } { "_id" : 13, "employee" : "headcount1", "dept" : "Z", "salary" : 120000, "fiscal_year" : 2020 } { "_id" : 14, "employee" : "headcount2", "dept" : "Z", "salary" : 120000, "fiscal_year" : 2020 }
Para atualizar a coleção orgArchive
para incluir o ano fiscal 2019
que acabou de terminar, o seguinte pipeline de agregação usa:
Estágio
$match
para encontrar todos os documentos comfiscal_year
igual a2019
.$group
estágio para agrupar os funcionários porfiscal_year
edept
.Etapa
$project
para suprimir o campo_id
e adicionar camposdept
efiscal_year
separados. Quando os documentos são passados para$merge
,$merge
gera automaticamente um novo campo_id
para os documentos.$merge
para escrever o resultado definido comoorgArchive
.O estágio
$merge
corresponde a documentos nos camposdept
efiscal_year
efails
quando correspondidos. Ou seja, se já existir um documento para o mesmo departamento e ano fiscal, os$merge
erros.
db.getSiblingDB("zoo").salaries.aggregate( [ { $match: { fiscal_year: 2019 }}, { $group: { _id: { fiscal_year: "$fiscal_year", dept: "$dept" }, employees: { $push: "$employee" } } }, { $project: { _id: 0, dept: "$_id.dept", fiscal_year: "$_id.fiscal_year", employees: 1 } }, { $merge : { into : { db: "reporting", coll: "orgArchive" }, on: [ "dept", "fiscal_year" ], whenMatched: "fail" } } ] )
Após a operação, a coleção orgArchive
contém os seguintes documentos:
{ "_id" : ObjectId("5caccc6a66b22dd8a8cc419f"), "employees" : [ "Ahn", "Bess" ], "dept" : "A", "fiscal_year" : 2017 } { "_id" : ObjectId("5caccc6a66b22dd8a8cc419e"), "employees" : [ "Ahn", "Gee" ], "dept" : "A", "fiscal_year" : 2018 } { "_id" : ObjectId("5caccd0b66b22dd8a8cc438e"), "employees" : [ "Ahn", "Zeb" ], "dept" : "A", "fiscal_year" : 2019 } { "_id" : ObjectId("5caccc6a66b22dd8a8cc41a0"), "employees" : [ "Carl" ], "dept" : "Z", "fiscal_year" : 2017 } { "_id" : ObjectId("5caccc6a66b22dd8a8cc41a1"), "employees" : [ "Bess", "Carl" ], "dept" : "Z", "fiscal_year" : 2018 } { "_id" : ObjectId("5caccd0b66b22dd8a8cc438d"), "employees" : [ "Bess", "Carl", "Wen" ], "dept" : "Z", "fiscal_year" : 2019 }
Se a coleção orgArchive
já contiver um documento para 2019 para o departamento "A"
e/ou "B"
, a agregação falhará devido ao erro de chave duplicada. No entanto, nenhum documento inserido antes do erro será revertido.
Se você especificar KeepExisting para o documento correspondente, a agregação não afetará o documento correspondente e não apresentará erro com erro de chave duplicada. Da mesma forma, se você especificar substituir, a operação não falhará; no entanto, a operação substituirá o documento existente.
Mesclar resultados de múltiplas coleções
Por padrão, se um documento nos resultados de agregação corresponder a um documento na coleção, a etapa $merge
funde os documentos.
Uma coleção de exemplo purchaseorders
é preenchida com as informações da ordem de compra por trimestre e regiões:
db.purchaseorders.insertMany( [ { _id: 1, quarter: "2019Q1", region: "A", qty: 200, reportDate: new Date("2019-04-01") }, { _id: 2, quarter: "2019Q1", region: "B", qty: 300, reportDate: new Date("2019-04-01") }, { _id: 3, quarter: "2019Q1", region: "C", qty: 700, reportDate: new Date("2019-04-01") }, { _id: 4, quarter: "2019Q2", region: "B", qty: 300, reportDate: new Date("2019-07-01") }, { _id: 5, quarter: "2019Q2", region: "C", qty: 1000, reportDate: new Date("2019-07-01") }, { _id: 6, quarter: "2019Q2", region: "A", qty: 400, reportDate: new Date("2019-07-01") }, ] )
Outra coleção de exemplo reportedsales
é preenchida com as informações de vendas relatadas por trimestre e regiões:
db.reportedsales.insertMany( [ { _id: 1, quarter: "2019Q1", region: "A", qty: 400, reportDate: new Date("2019-04-02") }, { _id: 2, quarter: "2019Q1", region: "B", qty: 550, reportDate: new Date("2019-04-02") }, { _id: 3, quarter: "2019Q1", region: "C", qty: 1000, reportDate: new Date("2019-04-05") }, { _id: 4, quarter: "2019Q2", region: "B", qty: 500, reportDate: new Date("2019-07-02") }, ] )
Suponha que, para fins de relatório, você queira visualizar os dados por trimestre no seguinte formato:
{ "_id" : "2019Q1", "sales" : 1950, "purchased" : 1200 } { "_id" : "2019Q2", "sales" : 500, "purchased" : 1700 }
Você pode utilizar o $merge
para mesclar os resultados da coleção purchaseorders
e da coleção reportedsales
para criar uma nova coleção quarterlyreport
.
Para criar a coleção quarterlyreport
, você pode utilizar o seguinte pipeline:
db.purchaseorders.aggregate( [ { $group: { _id: "$quarter", purchased: { $sum: "$qty" } } }, // group purchase orders by quarter { $merge : { into: "quarterlyreport", on: "_id", whenMatched: "merge", whenNotMatched: "insert" } } ])
- Primeiro estágio:
O estágio
$group
agrupa por trimestre e usa$sum
para adicionar os camposqty
em um novo campopurchased
. Por exemplo:Para criar a coleção
quarterlyreport
, você pode usar este pipeline:{ "_id" : "2019Q2", "purchased" : 1700 } { "_id" : "2019Q1", "purchased" : 1200 } - Segundo estágio:
- O estágio
$merge
grava os documentos na coleçãoquarterlyreport
no mesmo banco de dados. Se o estágio encontrar um documento existente na coleção que corresponda ao campo_id
, o estágio mesclará os documentos correspondentes. Caso contrário, o estágio vai inserir o documento. Para a criação inicial, nenhum documento deve corresponder.
Para visualizar os documentos na coleção, execute a seguinte operação:
db.quarterlyreport.find().sort( { _id: 1 } )
A collection contém os seguintes documentos:
{ "_id" : "2019Q1", "sales" : 1200, "purchased" : 1200 } { "_id" : "2019Q2", "sales" : 1700, "purchased" : 1700 }
Da mesma forma, execute o seguinte pipeline de agregação na coleção reportedsales
para mesclar os resultados de vendas na coleção quarterlyreport
.
db.reportedsales.aggregate( [ { $group: { _id: "$quarter", sales: { $sum: "$qty" } } }, // group sales by quarter { $merge : { into: "quarterlyreport", on: "_id", whenMatched: "merge", whenNotMatched: "insert" } } ])
- Primeiro estágio:
O estágio
$group
agrupa por trimestre e usa$sum
para adicionar os camposqty
em um novo camposales
. Por exemplo:{ "_id" : "2019Q2", "sales" : 500 } { "_id" : "2019Q1", "sales" : 1950 } - Segundo estágio:
- O estágio
$merge
grava os documentos na coleçãoquarterlyreport
no mesmo banco de dados. Se o estágio encontrar um documento existente na coleção que corresponda ao campo_id
(o quarto), o estágio mesclará os documentos correspondentes. Caso contrário, o estágio insere o documento.
Para visualizar os documentos na coleção quarterlyreport
após os dados terem sido mesclados, execute a seguinte operação:
db.quarterlyreport.find().sort( { _id: 1 } )
A collection contém os seguintes documentos:
{ "_id" : "2019Q1", "sales" : 1950, "purchased" : 1200 } { "_id" : "2019Q2", "sales" : 500, "purchased" : 1700 }
Use o pipeline para personalizar a fusão
O $merge
pode utilizar um pipeline de atualização personalizado quando os documentos corresponderem. O pipeline whenMatched pode ter os seguintes estágios:
$addFields
e seu alias$set
$replaceRoot
e seu alias$replaceWith
Um exemplo de votes
de coleta é preenchido com a contagem diária de votos. Cria a coleção com os seguintes documentos:
db.votes.insertMany( [ { date: new Date("2019-05-01"), "thumbsup" : 1, "thumbsdown" : 1 }, { date: new Date("2019-05-02"), "thumbsup" : 3, "thumbsdown" : 1 }, { date: new Date("2019-05-03"), "thumbsup" : 1, "thumbsdown" : 1 }, { date: new Date("2019-05-04"), "thumbsup" : 2, "thumbsdown" : 2 }, { date: new Date("2019-05-05"), "thumbsup" : 6, "thumbsdown" : 10 }, { date: new Date("2019-05-06"), "thumbsup" : 13, "thumbsdown" : 16 } ] )
Outra coleção de exemplo monthlytotals
tem os totais de votos mensais atualizados. Cria a coleção com o seguinte documento:
db.monthlytotals.insertOne( { "_id" : "2019-05", "thumbsup" : 26, "thumbsdown" : 31 } )
No final de cada dia, os votos desse dia são inseridos na coleção votes
:
db.votes.insertOne( { date: new Date("2019-05-07"), "thumbsup" : 14, "thumbsdown" : 10 } )
Você pode utilizar $merge
com um pipeline personalizado para atualizar o documento existente na coleção monthlytotals
:
db.votes.aggregate([ { $match: { date: { $gte: new Date("2019-05-07"), $lt: new Date("2019-05-08") } } }, { $project: { _id: { $dateToString: { format: "%Y-%m", date: "$date" } }, thumbsup: 1, thumbsdown: 1 } }, { $merge: { into: "monthlytotals", on: "_id", whenMatched: [ { $addFields: { thumbsup: { $add:[ "$thumbsup", "$$new.thumbsup" ] }, thumbsdown: { $add: [ "$thumbsdown", "$$new.thumbsdown" ] } } } ], whenNotMatched: "insert" } } ])
- Primeiro estágio:
O estágio
$match
encontra os votos do dia específico. Por exemplo:{ "_id" : ObjectId("5ce6097c436eb7e1203064a6"), "date" : ISODate("2019-05-07T00:00:00Z"), "thumbsup" : 14, "thumbsdown" : 10 } - Segundo estágio:
O estágio
$project
define o campo_id
como uma sequência de ano-mês. Por exemplo:{ "thumbsup" : 14, "thumbsdown" : 10, "_id" : "2019-05" } - Terceiro estágio:
O estágio
$merge
grava os documentos na coleçãomonthlytotals
no mesmo banco de dados. Se o estágio encontrar um documento existente na coleção que corresponder ao campo_id
, o estágio usará um pipeline para adicionar os votosthumbsup
e os votosthumbsdown
.Esse pipeline não pode acessar diretamente os campos do documento de resultados. Para acessar o campo
thumbsup
e o campothumbsdown
no documento de resultados, o pipeline utiliza a variável$$new
; ou seja,$$new.thumbsup
e$new.thumbsdown
.Este pipeline pode acessar diretamente o campo
thumbsup
e o campothumbsdown
no documento existente na coleção; ou seja,$thumbsup
e$thumbsdown
.
O documento resultante substitui o documento existente.
Para visualizar documentos na coleção monthlytotals
após a operação de mesclagem, execute a seguinte operação:
db.monthlytotals.find()
A coleção contém o seguinte documento:
{ "_id" : "2019-05", "thumbsup" : 40, "thumbsdown" : 41 }
Usar variáveis para personalizar a fusão
Você pode utilizar variáveis no campo $merge
stage whenMatched. As variáveis precisam ser definidas para poderem ser usadas.
Defina variáveis em uma ou em ambas as opções a seguir:
Para usar variáveis em whenMatched:
Especifique o prefixo do sinal de dólar duplo ($$) juntamente com o nome da variável no formulário $$<variable_name>
. Por exemplo, $$year
. Se a variável estiver definida para um documento, você também poderá incluir um campo de documento no formulário $$<variable_name>.<field>
. Por exemplo, $$year.month
.
As guias abaixo demonstram o comportamento quando as variáveis são definidas no estágio de mesclagem, no comando de agregação ou em ambos.
Usar variáveis definidas no estágio de mesclagem
Você pode definir variáveis no estágio $merge
let e usar as variáveis no campo whenMatched.
Exemplo:
db.cakeSales.insertOne( [ { _id: 1, flavor: "chocolate", salesTotal: 1580, salesTrend: "up" } ] ) db.runCommand( { aggregate: db.cakeSales.getName(), pipeline: [ { $merge: { into: db.cakeSales.getName(), let : { year: "2020" }, whenMatched: [ { $addFields: { "salesYear": "$$year" } } ] } } ], cursor: {} } ) db.cakeSales.find()
O exemplo:
cria uma coleção chamada
cakeSales
executa um comando
aggregate
que define uma variávelyear
no$merge
let e adiciona o ano acakeSales
usando WhenMatchedretrieves the
cakeSales
document
Saída:
{ "_id" : 1, "flavor" : "chocolate", "salesTotal" : 1580, "salesTrend" : "up", "salesYear" : "2020" }
Usar variáveis definidas no comando de agregação
Novidades na versão 5.0.
Você pode definir variáveis no comando aggregate
let e usar as variáveis no campo $merge
estágio whenMatched.
Exemplo:
db.cakeSales.insertOne( { _id: 1, flavor: "chocolate", salesTotal: 1580, salesTrend: "up" } ) db.runCommand( { aggregate: db.cakeSales.getName(), pipeline: [ { $merge: { into: db.cakeSales.getName(), whenMatched: [ { $addFields: { "salesYear": "$$year" } } ] } } ], cursor: {}, let : { year: "2020" } } ) db.cakeSales.find()
O exemplo:
cria uma coleção chamada
cakeSales
executa um comando
aggregate
que define uma variávelyear
no comandoaggregate
let e adiciona o ano acakeSales
usando WhenMatchedretrieves the
cakeSales
document
Saída:
{ "_id" : 1, "flavor" : "chocolate", "salesTotal" : 1580, "salesTrend" : "up", "salesYear" : "2020" }
Usar variáveis definidas no estágio de mesclagem e comando de agregação
Você pode definir variáveis no estágio $merge
e, a partir do MongoDB 5.0, no comando aggregate
.
Se duas variáveis com o mesmo nome forem definidas no estágio $merge
e no comando aggregate
, a variável de estágio $merge
será usada.
Neste exemplo, a variável de estágio year: "2020"
$merge
é usada em vez da variável de comando year: "2019"
aggregate
:
db.cakeSales.insertOne( { _id: 1, flavor: "chocolate", salesTotal: 1580, salesTrend: "up" } ) db.runCommand( { aggregate: db.cakeSales.getName(), pipeline: [ { $merge: { into: db.cakeSales.getName(), let : { year: "2020" }, whenMatched: [ { $addFields: { "salesYear": "$$year" } } ] } } ], cursor: {}, let : { year: "2019" } } ) db.cakeSales.find()
Saída:
{ _id: 1, flavor: 'chocolate', salesTotal: 1580, salesTrend: 'up', salesYear: '2020' }