Explore o novo chatbot do Developer Center! O MongoDB AI chatbot pode ser acessado na parte superior da sua navegação para responder a todas as suas perguntas sobre o MongoDB .

Junte-se a nós no Amazon Web Services re:Invent 2024! Saiba como usar o MongoDB para casos de uso de AI .
Desenvolvedor do MongoDB
Central de desenvolvedor do MongoDBchevron-right
Idiomaschevron-right
Swiftchevron-right

Trabalhando com o padrão de coleção única do MongoDB em Swift

Andrew Morgan6 min read • Published Jan 18, 2023 • Updated Jan 18, 2023
MongoDBSwift
Ícone do FacebookÍcone do Twitterícone do linkedin
Avalie esse Início rápido
star-empty
star-empty
star-empty
star-empty
star-empty
É um axioma do MongoDB que você obtém o melhor desempenho e escalabilidade armazenando juntos os dados que são mais comumente acessados em conjunto.
A abordagem mais simples e óbvia para conseguir isso é incorporar todos os dados relacionados em um único documento. Isso funciona muito bem em muitos casos, mas há alguns cenários em que pode se tornar ineficiente:
  • Relacionamentos de muitos para muitos (muito). Isso pode levar a dados duplicados. Essa duplicação geralmente é aceitável – apesar de tudo, o armazenamento é comparativamente barato. Fica mais doloroso quando os dados duplicados são modificados com frequência. Em seguida, você tem o custo de atualizar todos os documentos que incorporam esses dados.
  • Leitura de pequenas partes de documentos grandes. Mesmo que sua query esteja interessado apenas em uma pequena fração de campos em um documento, todo o documento é colocado no cache — ocupando memória que poderia ser usada de forma mais eficaz.
  • Documentos grandes e mutáveis. Sempre que seu aplicativo faz uma alteração em um documento, todo o documento deve ser gravado em disco em algum momento (pode ser combinado com outras alterações no mesmo documento). O WiredTiger grava dados no disco em blocos de 4 KB após a compressão - o que normalmente mapeia para um documento não compactado de 16a20 KB. Se você estiver fazendo muitas edições pequenas em um documento 20+ KB, pode estar desperdiçando E/S de disco.
Se incorporar todos os dados em um único documento não for o padrão certo para seu aplicativo, considere o design de collection única. O padrão de collection única pode oferecer desempenho de leitura comparável ao de documentos incorporados, além de otimizar as atualizações.
Existem variantes no padrão de collection única, mas para esta publicação, concentrei-me nos aspectos principais:
  • Os dados relacionados consultados juntos são armazenados na mesma collection.
  • Os documentos podem ter estruturas diferentes.
  • Os índices são adicionados para que todos os dados de suas queries frequentes possam ser obtidos com uma única pesquisa de índice.
Nesse momento, seu cérebro de desenvolvedor pode estar levantando questões sobre como o código do seu aplicativo pode lidar com isso. É comum ler os dados de uma collection específica e, em seguida, fazer com que o driver do MongoDB converta esse documento em um objeto de uma classe específica. Como isso funciona se o driver estiver buscando documentos com diferentes formas da mesma collection? Essa é a principal coisa que gostaria de demonstrar nesta postagem.
Usarei o Swift, mas os mesmos princípios se aplicam a outros idiomas. Para ver como fazer isso com Java/Spring Data, dê uma olhada em Designs de collection no MongoDB com Spring Data.

Executando o código de exemplo

Comece recentemente a usar o MongoDB Swift Driver pela primeira vez. Decidi criar um aplicativo de desktop Mac supersimples que permita navegar por suas coleções (o que o MongoDB Compass faz um trabalhomuito melhor) e exibir eventos de Change Stream em tempo real (o que o Compass não faz atualmente).
Você pode baixar o código do repositório Swift-Change-Streams. Basta criar e executar a partir do Xcode .
Forneça sua connection string e navegue pelas suas collections. Selecione a opção " Ativar fluxos de alteração " para exibir eventos de alteração em tempo real.
Um navegador de desktop simples, conectando-se a um cluster MongoDB, navegando pelas collection e habilitando change stream. Em seguida, vemos os eventos de change stream mostrados na ferramenta. As alterações são exibidas como um JSON document amarelo que mostra as alterações, bem como o novo documento. As inserções são mostradas em verde e as exclusões como vermelho.
O aplicativo exibirá dados da maioria das collection como documentos JSON genéricos, sem conhecimento do esquema. Há um caso especial para uma collection chamada "Collection" em um banco de dados chamado "Single" — vamos ver isso a seguir.

Dados de amostra

A collection Simple.Collection precisa conter estes documentos (ou semelhantes):
1{ _id: 'basket1', docType: 'basket', customer: 'cust101' }
2{ _id: 'basket1-item1', docType: 'item', name: 'Fish', quantity: 5 }
3{ _id: 'basket1-item2', docType: 'item', name: 'Chips', quantity: 3 }
Esses dados representam uma bandeja de compras com uma _id de "cesta1". Existem dois itens associados a basket1 — basket1-item1 e basket1-item2. Uma única query buscará todos os três documentos para a bandeja (encontre todos os documentos onde _id começa com "cesta1"). Sempre há um índice no atributo_id e, portanto, esse índice será usado.
Observe que todos os dados de uma cesta nesse conjunto de dados são extremamente pequenos - bem abaixo do limite 16-20K - e, portanto, em um exemplo real, eu aconselharia incorporar tudo em um único documento. O padrão de collection única faria mais sentido se houvesse um grande número de itens de linha e cada um deles fosse grande (por exemplo, se fossem incorporadas várias imagens em miniatura).
Cada documento também tem um atributo docTypepara identificar se o documento se refere à própria cesta ou a um dos itens associados. Se o seu aplicativo incluísse uma consulta comum para buscar apenas a cesta ou apenas os itens associados à cesta, você poderia adicionar um índice composto: { _id: 1, docType: 1}.
Outros usos do campodocType incluem:
  • Um prompt para ajudar os humanos a entender o que estão vendo na collection.
  • Filtrar os dados retornados de uma query apenas para determinados tipos de documentos da collection.
  • Filtrar quais tipos de documentos são incluídos ao usar o MongoDB Compass para examinar o schema de uma collection.
  • Permitir que um aplicativo identifique o tipo de documento recebido. O código do aplicativo pode fazer com que o driver MongoDB desmarque o documento em um objeto da classe correta. Isso é o que veremos a seguir.

Manipulação de diferentes tipos de documentos da mesma collection

Usaremos o mesmo aplicativo de desktop para ver como seu código pode discriminar entre diferentes tipos de documentos da mesma collection.
O aplicativo tem conhecimento codificado da aparência de uma cesta e de documentos de itens. Isso permite renderizar os dados do documento em formatos específicos, em vez de um JSON document:
A ferramenta de desktop está mostrando documentos da coleção "Single.Collection". Em vez de mostrar o documento como JSON genérico, o aplicativo mostra blocos para a bandeja e para cada um dos 2 itens. Cada bloco renderiza dados do documento associado em um formato que faz sentido para esse tipo de documento.
O código para determinar o documento docType e converter o documento em um objeto da classe apropriada pode ser encontrado em CollectionView.swift.
CollectionView busca todos os documentos correspondentes do MongoDB e os armazena em uma array de BSONDocuments:
1@State private var docs = [BSONDocument]()
O aplicativo pode então fazer um loop sobre cada documento em docs, verificar o atributodocType e decidir o que fazer com base nesse valor:
1List(docs, id: \.hashValue) { doc in
2 if path.dbName == "Single" && path.collectionName == "Collection" {
3 if let docType = doc["docType"] {
4 switch docType {
5 case "basket":
6 if let basket = basket(doc: doc) {
7 BasketView(basket: basket)
8 }
9 case "item":
10 if let item = item(doc: doc) {
11 ItemView(item: item)
12 }
13 default:
14 Text("Unknown doc type")
15 }
16 }
17 } else {
18 JSONView(doc: doc)
19 }
20}
Se docType == "basket", o código converte o documento genérico em um objetoBasket e o passa para BasketView para renderização.
Esta é a classe Basket, incluindo o inicializador para criar umBasket a partir de um BSONDocument:
1struct Basket: Codable {
2 let _id: String
3 let docType: String
4 let customer: String
5
6 init(doc: BSONDocument) {
7 do {
8 self = try BSONDecoder().decode(Basket.self, from: doc)
9 } catch {
10 _id = "n/a"
11 docType = "basket"
12 customer = "n/a"
13 print("Failed to convert BSON to a Basket: \(error.localizedDescription)")
14 }
15 }
16}
Da mesma forma para Items:
1struct Item: Codable {
2 let _id: String
3 let docType: String
4 let name: String
5 let quantity: Int
6
7 init(doc: BSONDocument) {
8 do {
9 self = try BSONDecoder().decode(Item.self, from: doc)
10 } catch {
11 _id = "n/a"
12 docType = "item"
13 name = "n/a"
14 quantity = 0
15 print("Failed to convert BSON to a Item: \(error.localizedDescription)")
16 }
17 }
18}
As subexibições podem então usar os atributos do objeto com o tipo correto para renderizar os dados adequadamente:
1struct BasketView: View {
2 let basket: Basket
3
4 var body: some View {
5 VStack {
6 Text("Basket")
7 .font(.title)
8 Text("Order number: \(basket._id)")
9 Text("Customer: \(basket.customer)")
10 }
11 .padding()
12 .background(.secondary)
13 .clipShape(RoundedRectangle(cornerRadius: 15.0))
14 }
15}
1struct ItemView: View {
2 let item: Item
3
4 var body: some View {
5 VStack {
6 Text("Item")
7 .font(.title)
8 Text("Item name: \(item.name)")
9 Text("Quantity: \(item.quantity)")
10 }
11 .padding()
12 .background(.secondary)
13 .clipShape(RoundedRectangle(cornerRadius: 15.0))
14 }
15}

Conclusão

O padrão de coleção única é uma maneira de fornecer desempenho de leitura e gravação quando a incorporação ou outros padrões de design não são adequados.
Esse padrão quebra o mapeamento 1-1 entre classes de aplicativos e collections do MongoDB que muitos desenvolvedores podem presumir. Esta publicação mostra como contornar isso:
  • Extraia um único campo docType do documento BSON retornado pelo driver do MongoDB.
  • Verifique o valor de docType e faça com que o driver MongoDB mapeie o documento BSON em um objeto da classe apropriada.
Perguntas? comentários? Acesse nossa Comunidade de desenvolvedores para continuar a conversa!

Ícone do FacebookÍcone do Twitterícone do linkedin
Avalie esse Início rápido
star-empty
star-empty
star-empty
star-empty
star-empty
Relacionado
Tutorial

Adicionar Realm como uma dependência a um framework do iOS


Aug 12, 2024 | 4 min read
Notícias e Anúncios

Interrompendo o desenvolvimento do driver Swift do MongoDB


Sep 11, 2024 | 1 min read
Artigo

Uma atualização sobre o compromisso contínuo do MongoDB com Swift


Jul 12, 2024 | 4 min read
exemplo de código

Crie uma ferramenta de linha de comando com Swift e MongoDB


Sep 11, 2024 | 13 min read
Sumário
  • Executando o código de exemplo