Usando o MongoDB – parte 3

mongodb-3_abertura

Esta é a continuação da segunda parte com a sequência das operações do CRUD dentro do MongoDB Shell e contemplando atualização e remoção de documentos dentro de coleções. E assim como foi feito com inserção e recuperação também incluirei, para comparação, os comandos análogos no SQLite3.

Alteração de documentos

Os métodos para a alteração de documentos no MongoDB são:

  • update() — para alterar um ou mais documentos na coleção;
  • updateOne() — para a alteração de vários documentos da coleção e
  • updateMany() — para alterar um único documento.

Em ambos os casos é preciso passar como primeiro parâmetro o critério de pesquisa, tal qual  como nos métodos find() e findOne(), seguido da lista de alterações a ser realizada. E igual ao que acontece nos métodos de pesquisa, um documento vazio, significará (em tese) todos os documentos da coleção.

Inclusão antes da alteração

No MongoDB a atualização de documentos funciona de modo um pouco diferente de como ocorre nos registros dos bancos SQL e para mostrar esta diferença uma nova inserção naquela coleção dos sistemas operacionais:

> use teste
switched to db teste
> db.oses.insertOne({name:"Windows Server"})
...
> db.oses.findOne({name:"Windows Server"})
{
    "_id" : ObjectId("5a8c8b6fefd2b5423fc002ca"),
    "name" : "Windows Server"
}

Assim mesmo deixando de preencher todas as chaves e fazendo a mesma coisa no SQLite3:

sqlite> INSERT INTO oses (name) VALUES ("Windows Server");
sqlite> SELECT * FROM oses WHERE name='Windows Server';
3|Windows Server||

Também com os valores de alguns campos sendo omitidos.

Alterando um documento

Invertendo a ordem e atualizando o valor do campo “kind” primeiro no SQLite3:

sqlite> update oses set kind='Windows' where name='Windows Server';

Por associação é de se esperar que no MongoDB seja algo assim:

> db.oses.update({name:"Windows Server"},{kind:"Windows"})

Certo? Mais ou menos, o primeiro parâmetro do método update() é o critério de pesquisa porém o segundo parâmetro não funciona como a projeção no método find() e sim como um documento contendo uma lista de como se deve alterar no documento.

Que especificamente neste caso significará “troque o documento por este aqui”:

> db.oses.find({_id:ObjectId("5a8c8b6fefd2b5423fc002ca")})
{ "_id" : ObjectId("5a8c8b6fefd2b5423fc002ca"), "kind" : "Windows" }

Ou seja, o conteúdo do documento foi totalmente substituído e não somente o valor da chave.

Diferente do que acontece nos bancos d dados SQL o MongoDB não tem uma estrutura definida para cada documento dentro da coleção e exceto pelo “_id”, que é obrigatório, pode conter qualquer conjunto de chaves.

> db.documentos.find({},{_id:0})
{ "nome" : "Fulano" }
{ "camisas" : 10 }
{ "frutas" : [ "banana", "laranja" ] }
{ "cor" : { "nome" : "azul", "rgba" : "#1020c0ff" } }

E a forma de fazê-lo, sem necessariamente ser obrigado a reescrever todo o documento, é utilizando os operadores de atualização:

> db.oses.update({name:"Windows Server"},{$set:{kind:"Windows"}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.oses.find({_id:ObjectId("5a8c8b6fefd2b5423fc002ca")}).pretty()
{
 "_id" : ObjectId("5a8c8b6fefd2b5423fc002ca"),
 "name" : "Windows Server",
 "kind" : "Windows"
}

Neste caso, em cada documentos cuja chave “name” contendo “Windows Server” a chave “kind” será definida com o valor “Windows” caso já exista ou criada com ele valor em caso contrário.

Neste exemplo o uso dos métodos update() ou updateOne() funcionam da mesma forma.

Alterando diversos documentos

Alterações em diversos documentos seguem a mesma lógica de se alterar somente um, porém há uma diferença de comportamento no método update() do MongoDB quando comparado ao seu análogo em SQL.

> db.oses.update({},{$set:{download:0}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })

Por padrão o método update() executará este tipo de operação em apenas um documento e não em toda a coleção — observe as chaves “nMatched” e “nModified” nos exemplos.

Para fazer o MongoDB executar a operação em todos os documentos basta acrescentar o parâmetro “multi” na chamada do método:

> db.oses.update({},{$set:{download:0}},{multi:true})
WriteResult({ "nMatched" : 3, "nUpserted" : 0, "nModified" : 2 })

No caso do método updateMany() isto não é necessário:

> db.oses.updateMany({ },{$set:{download:0}})
{ "acknowledged" : true, "matchedCount" : 3, "modifiedCount" : 0 }

Neste caso a operação será executada em todos os documentos sem a necessidade de se explicitar uma operação em vários documentos.

É possível com os operadores de atualização realizar algumas ações interessantes nos documentos.

> db.oses.find({},{_id:0,name:1,download:1})
{ "name" : "Ubuntu Linux", "download" : 0 }
{ "name" : "CentOS", "download" : 0 }
{ "name" : "Windows Server", "download" : 0 }

Os valores da chave “download” em ambos os documentos foram criadas com valor 0 mas que tal incrementá-las todas de uma vez?

> db.oses.updateMany({ },{$inc:{download:1}})
{ "acknowledged" : true, "matchedCount" : 3, "modifiedCount" : 3 }
> db.oses.find({},{ _id:0,name:1,download:1 })
{ "name" : "Ubuntu Linux", "download" : 1 }
{ "name" : "CentOS", "download" : 1 }
{ "name" : "Windows Server", "download" : 1 }

O operador $inc serve para adicionar 1 (ou o outro valor definido) ao valor já existente dentro da chave “download” ou criando-a caso não exista.

Sincronizando o SQL…

Antes de seguir adiante é bom deixar a estrutura da tabela utilizada no SQLite3 parecida com o que está sendo armazenado no MongoDB:

> ALTER TABLE oses ADD 'download' INTEGER;
> UPDATE oses SET download=1;

Infelizmente o SQL precisa de um schema para entender o mundo! 😀

Atualizando ou Inserindo um novo documento

Uma característica interessante da atualização de documentos no MongoDB é o upsert (“update” + “insert”) e que basicamente consiste na inserção automática do documento no caso dele não ser encontrado na coleção:

> db.oses.update({"name":"FreeBSD"},
... {name:"FreeBSD",version:"11.1",kind:"BSD",download:1},
... {upsert:true})
WriteResult({
 "nMatched" : 0,
 "nUpserted" : 1,
 "nModified" : 0,
 "_id" : ObjectId("5a8d6e290ca01068e813bb7c")
})

O que é bastante útil para em uma única operação verificar a existência de um documento e ao mesmo tempo já inseri-lo — note que o retorno desta operação contém o “id” do documento que foi criado.

Bônus : fazendo “upsert” no SQLite3

Já que me prontifiquei a mostrar o equivalente em SQL, é possível fazer o upsert no SQLite3 — e em outros bancos de dados relacionais mas sintaxe poderá variar um pouco — a partir dos comandos REPLACE ou INSERT OR REPLACE (ambos são um apelido para o comando INSERT):

sqlite> REPLACE INTO oses (id,name,version,kind,download)
   ...> VALUES (
   ...> (SELECT id FROM oses WHERE name="FreeBSD"),
   ...> "FreeBSD","11.1","BSD",1);

O resultado é o mesmo mas a forma de obtê-lo que é diferente e a “mágica” fica por conta do SELECT usado para descobrir o provável “id” do registro dentro da tabela.

Removendo documentos

O método para remover documentos é o remove() e tal qual os demais tem como parâmetro o critério de pesquisa para remoção, ou seja, um documento vazio apagará todos os documentos da coleção.

Para remover da coleção dos sistemas operacionais da coleção os documentos com valor “Windows” na chave “kind”:

> db.oses.remove({ kind: "Windows"})
WriteResult({ "nRemoved" : 1 })

Claro, em SQL o comando é

sqlite> DELETE FROM oses WHERE kind='Windows';

Como segurança é possível usar a chave justOne com valor true como garantia contra eventuais acidentes:

> db.oses.remove({ kind: "Windows"},{justOne: true})

E que funciona tal qual o “LIMIT 1” em SQL.

Fim desta parte

E isto encerra a viagem pelo CRUD dentro do MongoDB e na próxima parte será a vez de tratar de replica sets e também de shards, até!

Anúncios