Crie uma ferramenta de linha de comando com Swift e MongoDB
Michael Lynn13 min read • Published Feb 17, 2022 • Updated Sep 11, 2024
APLICATIVO COMPLETO
Construir algo com as próprias mãos dá uma sensação de satisfação, como poucas outras tarefas. Mas realmente não há comparação com a sensação que você tem ao criar algo que não apenas realiza a tarefa imediata em questão, mas também permite que você realize a mesma tarefa com mais eficiência no futuro. Ou, melhor ainda, quando outra pessoa pode usar o que você criou para realizar suas tarefas com mais facilidade. Isso é o que faremos hoje. Vamos construir algo que automatize o processo de importação de dados para o MongoDB.
Um programa executável é poderoso porque é independente e transportável. Não há necessidade de compilá-lo ou garantir que outros elementos estejam presentes no ambiente. Ele apenas corre. Você pode compartilhá-lo com outras pessoas e, supondo que elas tenham um sistema relativamente semelhante, ele também será executado para elas. Vamos nos concentrar em atingir nosso objetivo usando o Swift, a linguagem de programação fácil de aprender da Apple. Também mostraremos o uso do nosso novo MongoDB Swift Driver, que permite criar, ler, atualizar e excluir dados em um MongoDB database.
Em vez disso, um vídeo demonstrando esse conteúdo? Confira o vídeo do YouTube em que meu colega Nic RaBoy e eu conversamos sobre esse mesmo conteúdo.
Aqui estão os objetivos deste artigo.
- Aumente sua familiaridade com o MongoDB Atlas
- Apresentar você ao Swift Driver do MongoDB
- Apresentar você ao Swift Package Manager
Ao final deste artigo, se tivermos atingido nossas metas, você será capaz de fazer o seguinte:
- Use o Xcode para começar a fazer experiências com o Swift
- Criar um projeto básico.
- Integre o MongoDB Swift Driver em seu projeto
- Crie um executável no seu Mac.
Antes de começarmos, vamos esclarecer algumas das coisas que você precisará ter para começar.
- Um Mac e macOS (não um dispositivo iOS). Você pode estar lendo isto em seu PC com Windows ou um iPad. Pessoal, este tutorial foi escrito para que você possa acompanhar em sua máquina Mac: macbook, macbook pro, imac etc. Você pode querer conferir o macincloud se estiver interessado em uma experiência virtual com o Mac.
- Acesso a um MongoDB database – Visite MongoDB Atlas para começar gratuitamente. Leia mais sobre MongoDB Atlas.
Se você não tem muita experiência com o Xcode ou o desenvolvimento de aplicativos MacOS, confira os guias no Hub do Desenvolvedor da Apple. Começar é muito fácil e é grátis!
A tarefa que estou tentando automatizar envolve a importação de dados para um banco de MongoDB database. Antes de avançarmos muito no caminho da criação de uma solução, vamos documentar nosso conjunto de requisitos para o que criaremos.
Aqui está um resumo rápido das etapas em que trabalharemos para concluir nossa tarefa.
- Inicie um cluster do Atlas.
- Adicione um usuário/senha de banco de dados e uma entrada de exceção de rede para que você possa acessar seu banco de dados a partir do seu endereço IP.
- Crie um projeto Swift usando o Swift Package Manager (
swift package init --type=executable
) - Gerar um projeto Xcode usando o Swift Package Manager (
swift package generate-xcodeproj
) - Crie um (
for loop
) usando (String) para acessar e imprimir os dados em seu arquivoexample.csv
. (Consulte csvread.swift) - Teste.(
swift build; swift run
) Erros? Consulte a seção de perguntas frequentes abaixo. - Modifique seu código para incorporar o driver MongoDB Swift e escreva documentos. (Veja Fontes/linha de comando-swift-mongodb/main.swift)
- Teste.(
swift build; swift run
) Erros? Consulte a seção de perguntas frequentes abaixo. - Criar executável e liberar.(
swift package release
)
- A solução deve importar um conjunto de dados que comece no formato CSV (ou tabular/excel) para um MongoDB database existente.
- Cada linha dos dados no CSV deve se tornar um documento separado no MongoDB database. Além disso, cada novo documento deve incluir um novo campo com a data/hora de importação.
- Deve ser feito com o mínimo de conhecimento do MongoDB , ou seja, alguém com relativamente pouca experiência e conhecimento do MongoDB deve ser capaz de executar a tarefa em alguns minutos.
Poderemos simplesmente usar o mongoimport com a seguinte linha de comando:
1 mongoimport --host localhost:27017 --type csv --db school --collection students --file example.csv --headerline
Se você estiver familiarizado com o MongoDB, a linha de comando acima não parecerá nada complicada. No entanto, isso não atenderá aos nossos requisitos pelos seguintes motivos:
- Requisito 1: Aprovado - Isso resultará na importação de dados para o MongoDB.
- Requisito 2: Falha - Embora cada linha se torne um documento separado, não obteremos nosso campo de data adicional nesses documentos.
- Requisito 3: Fail - Embora a sintaxe aqui possa parecer bastante simples se você já tiver usado o MongoDB antes, para um novato, ela pode ser um pouco confusa. Por exemplo, estou usando localhost aqui... quando executarmos esse executável em outro host, precisaremos substituí-lo pelo nome real do host do nosso MongoDB database. A sintaxe do comando ficará um pouco mais complexa quando isso acontecer.
Então, como criaremos algo que atenda a todos os nossos requisitos?
Podemos construir um executável de linha de comando que usa o MongoDB Swift Driver para realizar a tarefa. Construir um programa para realizar nossa tarefa nos permite abstrair grande parte da complexidade associada à nossa tarefa. Felizmente, há um driver para o Swift e usá-lo para ler CSV, manipulá-los e escrevê-los em um MongoDB database é realmente simples.
Você precisará criar um novo cluster e carregá-lo com dados de amostra. Meu colega Maxime Beugnet criou um tutorial em vídeo para ajudar você, mas também explico as etapas abaixo:
- Insira seus detalhes cadastre-se com sua conta do Google, se tiver uma.
- Aceite os Termos de Serviço
- Crie um cluster Starter.
- Selecione o provedor de nuvem onde você gostaria de armazenar seu banco de dados MongoDB.
- Escolha uma região que faça sentido para você.
- Você pode alterar o nome do cluster, se quiser. Chamei o meu de "MyFirstCluster".
Depois que seu cluster for iniciado, adicione uma entrada deexceção de rede para seu IP atual e, em seguida,adicione um nome de usuário e senha de banco de dados. Anote o nome de usuário e a senha. Você precisará deles em breve.
Começaremos nossa jornada criando um pacote Swift usando o Swift Package Manager. Essa ferramenta nos dará um projeto de modelo e estabelecerá a estrutura de diretórios e alguns andaimes necessários para começar. Vamos usar a ferramenta de linha de comando swift com o subcomando
package
.Existem várias variações que podemos usar. Antes de pular, vamos dar um exemplo da diferença em algumas das bandeiras.
1 swift package init
Esta variação mais básica nos fornecerá um projeto de uso geral. Mas, como estamos criando um MacOS executável, vamos adicionar o sinalizador
--type
para indicar o tipo de projeto em que estamos trabalhando.1 swift package init --type=executable
Isso criará um projeto que define o "produto" de uma construção -- que é, em essência, nosso executável. Lembre-se de que, se você estiver criando um executável, normalmente para o Swift do lado do servidor, convém incorporar o sinalizador
--type=executable
.O Xcode é onde a maioria dos desenvolvedores iOS e Apple em geral escreve e mantém o código, então vamos preparar um projeto para que possamos usar o Xcode também. Agora que já temos nossa estrutura básica de projeto pronta, vamos criar um projeto do Xcode no qual poderemos modificar nosso código.
Para criar um projeto Xcode, basta executar o seguinte comando:
1 swift package generate-xcodeproj
Em seguida, podemos abrir o arquivo
.xcproject
. Seu mac deve abrir automaticamente o Xcode como resultado de tentar abrir um arquivo do Xcode Project.1 open <your project name>.xcodeproj/ # change this to the name that was created by the previous command.
Com a estrutura do nosso projeto pronta, vamos nos concentrar nos dados que manipularemos com o nosso executável. Vamos dar uma olhada nos dados brutos primeiro. Digamos que haja uma lista de alunos que saem todos os meses e que eu preciso colocar em meu banco de dados. Pode ser mais ou menos assim:
1 firstname,lastname,assigned 2 Michael,Basic,FALSE 3 Dan,Acquilone,FALSE 4 Eli,Zimmerman,FALSE 5 Liam,Tyler,FALSE 6 Jane,Alberts,FALSE 7 Ted,Williams,FALSE 8 Suzy,Langford,FALSE 9 Paulina,Stern,FALSE 10 Jared,Lentz,FALSE 11 June,Gifford,FALSE 12 Wilma,Atkinson,FALSE
Neste exemplo de dados, temos 3 campos básicos de informações: Nome, Sobrenome e um valor booleano indicando se o aluno foi ou não atribuído a uma classe específica.
Queremos obter esses dados do formulário atual (CSV) em documentos dentro do banco de dados e, ao longo do caminho, adicionar um campo para registrar a data em que o documento foi importado. Isso exigirá que leiamos o arquivo CSV dentro de nosso aplicativo Swift. Antes de prosseguir, certifique-se de ter dados semelhantes em um arquivo para o qual você sabe o caminho. Estaremos criando algum código ao lado para acessar esse arquivo com Swift.
Quando terminarmos, os dados ficarão assim, representados em um JSON document:
1 { 2 "_id": { 3 "$oid": "5f491a3bf983e96173253352" // this will come from our driver. 4 }, 5 "firstname": "Michael", 6 "lastname": "Basic", 7 "date": { 8 "$date": "2020-08-28T14:52:43.398Z" // this will be set by our Struct default value 9 }, 10 "assigned": false 11 }
Para colocar as linhas e campos de nomes no MongoDB, usaremos a classeStringintegrada do Swift. Este é um utilitário poderoso que pode fazer de tudo, desde ler o conteúdo de um arquivo até interpolar variáveis incorporadas e fazer comparações entre dois ou mais conjuntos de strings. O método da classeconcernsOfFile da classe String acessará o arquivo com base em um caminho de arquivo que fornecemos, abrirá o arquivo e nos permitirá acessar seu conteúdo. Veja como nosso código poderia ficar se tivéssemos apenas que percorrer o arquivo CSV e imprimir as linhas que ele contém.
Você pode ficar tentado a apenas copiar/cole o código abaixo. Sugiro que você digite à mão ... lendo na tela. Isso permitirá que você experimente o poder da correção automática e da sugestão de código dentro do Xcode. Além disso, certifique-se de modificar o valor da variável
path
para apontar para o local onde você coloca seu arquivoexample.csv
.1 import Foundation 2 3 let path = "/Users/mlynn/Desktop/example.csv" // change this to the path of your csv file 4 do { 5 let contents = try String(contentsOfFile: path, encoding: .utf8) 6 let rows = contents.components(separatedBy: NSCharacterSet.newlines) 7 for row in rows { 8 if row != "" { 9 print("Got Row: \(row)") 10 } 11 } 12 }
Vamos dar uma olhada no que está acontecer aqui.
- Linha 1: usaremos a biblioteca principal do Foundation. Isso nos dá acesso a alguns métodos básicos de string, caracteres e comparação. A declaração de importação nos dá acesso a bibliotecas e módulos nativos e de terceiros.
- Linha 3: codifique uma variável de caminho para o arquivo CSV.
- Linhas 6a7: use o método String para acessar o conteúdo do arquivo CSV.
- Linha 8: Percorra cada linha em nosso arquivo e exiba o conteúdo.
Para executar este exemplo simples, vamos abrir o arquivo
main.swift
que o comando swift package init
criou para nós. Para editar este arquivo, no Xcode, Para começar, vamos abrir o arquivo principal.swift que nosso comando swift package init
criou para nós. Para editar este arquivo, no Xcode, percorra a árvore de pastas em Project->Sources-Project name... e abra main.swift
. Substitua o simples hello world
pelo código acima.Executando isso em nosso arquivo
example.csv
, você verá algo parecido com o seguinte resultado. Usaremos os comandosswift build
e swift run
.Com esta construção básica implementada, podemos agora começar a incorporar o código necessário para inserir um documento em nosso banco de dados para cada linha de dados no arquivo csv. Vamos começar configurando o Swift Package Manager para integrar o Swift Driver do MongoDB.
Navegue no explorador de projetos para encontrar o arquivo Package.swift. Substitua o conteúdo pelo arquivo Package.swift do repositório:
1 // swift-tools-version:5.2 2 // The swift-tools-version declares the minimum version of Swift required to build this package. 3 import PackageDescription 4 5 let package = Package( 6 name: "csvimport-swift", 7 platforms: [ 8 .macOS(.v10_15), 9 ], 10 dependencies: [ 11 .package(url: "https://github.com/mongodb/mongo-swift-driver.git", from: "1.0.1"), 12 ], 13 targets: [ 14 .target( 15 name: "csvimport-swift", 16 dependencies: [.product(name: "MongoSwiftSync", package: "mongo-swift-driver")]), 17 .testTarget( 18 name: "csvimport-swiftTests", 19 dependencies: ["csvimport-swift"]), 20 ] 21 )
Estamos incluindo uma declaração informando ao Swift Package Manager que estamos criando esse executável para um conjunto específico de versões do MacOS.
1 platforms: [ 2 .macOS(.v10_15) 3 ],
Dica: se você deixar esta declaração de fora, receberá uma mensagem informando que o pacote foi projetado para ser criado para MacOS 10.10 ou similar.
Em seguida, incluímos referências aos pacotes necessários em nosso software para inserir e manipular dados do MongoDB. Neste exemplo, nos concentraremos em uma implementação assíncrona. Ou seja, o mongo-swift-driver.
Agora que incluímos nossas dependências, vamos construir o projeto. Construa o projeto com frequência para detectar quaisquer erros que possa ter introduzido inadvertidamente no início.
1 swift package build
Você deverá obter uma resposta semelhante à seguinte:
1 [3/3] Linking cmd
Agora vamos modificar nosso projeto de programa básico para usar nosso driver MongoDB.
1 import Foundation 2 import MongoSwiftSync 3 4 var murl: String = "mongodb+srv://<username>:\(ProcessInfo.processInfo.environment["PASS"]!)@myfirstcluster.zbcul.mongodb.net/<dbname>?retryWrites=true&w=majority" 5 let client = try MongoClient(murl) 6 7 let db = client.db("students") 8 let session = client.startSession(options: ClientSessionOptions(causalConsistency: true)) 9 10 struct Person: Codable { 11 let firstname: String 12 let lastname: String 13 let date: Date = Date() 14 let assigned: Bool 15 let _id: BSONObjectID 16 } 17 18 let path = "/Users/mlynn/Desktop/example.csv" 19 var tempAssigned: Bool 20 var count: Int = 0 21 var header: Bool = true 22 23 let personCollection = db.collection("people", withType: Person.self) 24 25 do { 26 let contents = try String(contentsOfFile: path, encoding: .utf8) 27 let rows = contents.components(separatedBy: NSCharacterSet.newlines) 28 for row in rows { 29 if row != "" { 30 var values: [String] = [] 31 values = row.components(separatedBy: ",") 32 if header == true { 33 header = false 34 } else { 35 if String(values[2]).lowercased() == "false" || Bool(values[2]) == false { 36 tempAssigned = false 37 } else { 38 tempAssigned = true 39 } 40 try personCollection.insertOne(Person(firstname: values[0], lastname: values[1], assigned: tempAssigned, _id: BSONObjectID()), session: session) 41 count.self += 1 42 print("Inserted: \(count) \(row)") 43 44 } 45 } 46 } 47 }
A linha 2 importa o driver de que precisaremos (mongo-swift).
Em seguida, configuramos o driver.
1 var murl: String = "mongodb+srv://<username>:\(ProcessInfo.processInfo.environment["PASS"]!)@myfirstcluster.zbcul.mongodb.net/<dbname>?retryWrites=true&w=majority" 2 let client = try MongoClient(murl) 3 4 let db = client.db("students") 5 let session = client.startSession(options: ClientSessionOptions(causalConsistency: true))
Lembre-se de substituir
<username>
pelo usuário que você criou no Atlas.Para ler e escrever dados de e para MongoDB no Swift, precisaremos aproveitar uma estrutura Codable. Os codeables são uma funcionalidade surpreendente do Swift e definitivamente úteis para escrever código que gravará dados no MongoDB. Codables é, na verdade, um alias para dois protocolos: Encodablee Decodable. Quando deixamos nosso
Struct
conformidade com o protocolo Codable, podemos codificar nossos dados de string em JSON e depois decodificá-los de volta em um Struct
simples usando JSONEncoder e JSONDecoder, respectivamente. Vamos precisar dessa estrutura porque o formato usado para armazenar dados no MongoDB é ligeiramente diferente da representação que você vê dessa estrutura de dados no Swift. Criaremos uma estrutura para descrever como deve ser o esquema de documentos dentro do MongoDB. Veja como deve ser o nosso esquema Struct
:1 struct Code: Codable { 2 let code: String 3 let assigned: Bool 4 let date: Date = Date() 5 let _id: BSONObjectID 6 }
Observe que temos todos os elementos do nosso arquivo CSV e um campo de data.
Também precisaremos de algumas variáveis temporárias que usaremos ao processar os dados.
count
e uma variável temporária especial que usarei quando determino se um aluno está ou não designado para umatempAssigned
classe... . Por fim, neste bloco de código, criarei uma variável para armazenar o estado da nossa posição no arquivo . ocabeçalho será definido como verdadeiro inicialmente porque queremos pular a primeira linha de dados. É aí que ficam os cabeçalhos das colunas .1 let path = "/Users/mlynn/Desktop/example.csv" 2 var tempAssigned: Bool 3 var count: Int = 0 4 var header: Bool = true
Agora podemos criar uma referência para a collection em nosso MongoDB database que usaremos para armazenar os dados dos alunos. Na falta de um nome melhor, estou chamando o meu
personCollection
. Além disso, observe que estamos fornecendo um link de volta para nosso Struct
utilizando o argumentowithType
para o método de collection. Isso garante que o motorista saiba com que tipo de dados estamos lidando.1 let personCollection = db.collection("people", withType: Person.self)
O próximo bit de código está no centro da nossa tarefa. Vamos percorrer cada linha e criar um documento. Comentei e expliquei cada linha in-line.
1 let contents = try String(contentsOfFile: path, encoding: .utf8) // get the contents of our csv file with the String built-in 2 let rows = contents.components(separatedBy: NSCharacterSet.newlines) // get the individual rows separated by newline characters 3 for row in rows { // Loop through all rows in the file. 4 if row != "" { // in case we have an empty row... skip it. 5 var values: [String] = [] // create / reset the values array of type string - to null. 6 values = row.components(separatedBy: ",") // assign the values array to the fields in the row of data 7 if header == true { // if it's the first row... skip it and. 8 header = false // Set the header to false so we do this only once. 9 } else { 10 if String(values[2]).lowercased() == "false" || Bool(values[2]) == false { 11 tempAssigned = false // Above: if its the string or boolean value false, so be it 12 } else { 13 tempAssigned = true // otherwise, explicitly set it to true 14 } 15 try personCollection.insertOne(Person(firstname: values[0], lastname: values[1], assigned: tempAssigned, _id: BSONObjectID()), session: session) 16 count.self += 1 // Above: use the insertOne method of the collection class form 17 print("Inserted: \(count) \(row)") // the mongo-swift-driver and create a document with the Person ``Struct``. 18 } 19 } 20 }
A importação de dados é um desafio comum. Ainda mais comum é quando queremos automatizar a tarefa de inserção ou manipulação de dados com o MongoDB. Neste tutorial, expliquei como você pode começar a usar o Swift e realizar a tarefa de simplificar a importação de dados criando uma ferramenta executável de linha de comando que pode ser compartilhada com um colega para que ele possa importar dados para você. Embora esse exemplo seja bastante simples em termos de como ele resolve o problema em questão, você certamente pode dar o próximo passo e começar a desenvolvê-lo para dar suporte a argumentos de linha de comando e até mesmo usá-lo não apenas para inserir dados, mas também para remover e mesclar ou atualizar dados.
Preparei uma seção abaixo intitulada Solução de problemas caso você se depare com alguns erros comuns. Fizemos o possível para pensar em todos os problemas usuais que você pode encontrar. No entanto, se você encontrar outro problema, informe-me. A melhor maneira de fazer isso é Inscrever-se na MongoDB Community e não deixar de visitar a seção de Drivers e ODMs.
Use esta seção para ajudar a resolver alguns problemas comuns. Se você ainda tiver problemas depois de ler essas soluções comuns, visite-me na MongoDB Community.
Isso ocorre quando o Swift não conseguiu criar o módulo
mongo-swift-driver
. Isso normalmente ocorre quando um desenvolvedor está tentando usar o Xcode e não especificou uma versão mínima do sistema operacional de destino. Revise a imagem anexa e observe a sequência de cliques para obter a configuração apropriada. Altere essa configuração para 10.15 ou superior.