Uma das principais atrações do bancos de dados de documentos é a flexibilidade da estrutura ou do esquema do documento. Na maioria das vezes, a flexibilidade do esquema do documento é benéfica. No entanto, pode haver algumas situações em que ter alguma estrutura para o documento pode ser útil. Este artigo mostra como validar seus documentos JSON em relação a um esquema especificado usando a popular biblioteca Python pydantic.
Quando você precisa validar documentos?
Uma concepção errônea comum sobre o uso de bancos de dados NoSQL é que não são necessárias estruturas ou esquemas de documentos. Na maioria dos casos, os aplicativos tendem a ter algumas restrições para os dados, mesmo que não os validem especificamente. Por exemplo, pode haver alguns campos no documento dos quais o aplicativo depende para sua funcionalidade. Um aplicativo pode simplesmente não funcionar corretamente quando alguns desses campos JSON pidêmicos estiverem faltando.
Um exemplo do mundo real desse problema poderia ser um aplicativo que lê dados de outro aplicativo não confiável que envia periodicamente dados ruins. Seria prudente destacar todos os documentos que poderiam quebrar o aplicativo nesses casos. Os desenvolvedores podem fazer isso no aplicativo ou no nível do documento.
Nessa abordagem, especificamos um esquema de JSON para pidântica para os documentos, a fim de ajudar a identificar aqueles que não correspondem às especificações do aplicativo no nível do documento.
Gerar dados de teste JSON
Usamos a biblioteca de código aberto, Fakerpara gerar alguns perfis de usuário falsos para este tutorial.
Essa é a estrutura de um único documento:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
{ "trabalho": "Radiologista, diagnóstico", "empresa": "Klein, Dillon e Neal", "residência": "2232 Jackson Forks\nLake Teresa, CO 46959", "website": [ "http://bishop-torres.net/" ], "nome de usuário": "aabbott", "name" (nome): "Madison Mitchell", "endereço": "1782 Moore Hill Apt. 717\nWest Stephaniestad, NM 75293", "mail": "amberrodriguez@hotmail.com", "telefone": { "casa": "677-197-4239x889", "móvel": "001-594-038-9255x99138" } } |
Para simular os documentos quebrados, modifico uma pequena parte dos perfis de usuário, excluindo alguns dos campos de telefone celular e e-mail. Nosso objetivo é identificar esses registros que, no mundo real, seriam armazenados em um banco de dados de documentos como o Couchbase.
Carrego os dados pidânticos gerados a partir do JSON em um bucket em nosso servidor hospedado Couchbase Capella cluster usando o importação na interface integrada do console da Web para nossos testes. Eu especifico o nome de usuário como a chave para identificar exclusivamente cada documento.
Como especificar um esquema para documentos JSON?
Nos dados do perfil do usuário, espero que os documentos estejam em conformidade com a seguinte estrutura em meus aplicativos:
- Campos obrigatórios:
- nome de usuário
- nome
- phone - com elementos JSON para "home" e "mobile"
- correio
- website - uma matriz
- Campos opcionais:
- empresa
- residência
- trabalho
- endereço
Pydantic é uma das bibliotecas mais populares em Python para validação de dados. A sintaxe para especificar o esquema é semelhante ao uso de dicas de tipos para funções em Python. Os desenvolvedores podem especificar o esquema definindo um modelo. O Pydantic tem um rico conjunto de recursos para fazer uma variedade de validações de JSON. Examinaremos a representação de algumas especificações de documentos de perfil de usuário.
Uma observação sobre o pydantic é que, por padrão, ele tenta coagir os tipos de dados fazendo conversões de tipo quando possível - por exemplo, convertendo a string '1' em um 1 numérico. No entanto, há uma opção para ativar a verificação rigorosa de tipos sem realizar conversões.
Neste exemplo de código, você vê uma configuração básica para o esquema UserProfile usando a sintaxe pydantic:
1 2 3 4 5 6 7 8 9 10 11 |
classe Perfil do usuário(BaseModel): """Esquema para perfis de usuário""" nome de usuário: str nome: str telefone: Telefone correio: str empresa: Opcional[str] residência: Opcional[str] site: Lista[HttpUrl] trabalho: Opcional[str] endereço: Opcional[str] |
Cada campo é especificado junto com o tipo de dados esperado. Os campos opcionais são marcados como Opcional. Uma matriz é especificada pelo parâmetro Lista seguida pelo tipo de dados desejado.
O nome de usuário precisa ser uma string, enquanto o campo empresa é uma cadeia de caracteres opcional. Se observarmos as outras linhas do snippet, veremos que o site é uma lista do tipo HttpUrl - um dos muitos tipos personalizados fornecidos pelo pydantic imediatamente. HttpUrl é usado para validar se o URL é válido e não cadeias de caracteres aleatórias. Da mesma forma, há outros campos como e-mails que poderíamos usar para garantir que os campos de e-mail sejam um formulário válido.
Se você observar o telefone ele é marcado como um campo Telefone que é um tipo personalizado que definiremos no próximo trecho de código:
1 2 3 4 5 6 7 8 9 10 11 |
classe Telefone(BaseModel): """Esquema para números de telefone""" casa: str móvel: str @validador("móvel", "casa") def does_not_contain_extension(cls, v): """Verifique se os números de telefone contêm extensões""" se "x" em v: aumentar ExtensionError(valor_errado=v) retorno v |
Aqui especificamos que o Telefone é composto de dois campos que são cadeias de caracteres: casa e móvel. Isso seria verificado dentro do Perfil do usuário e interpretado como o modelo Perfil do usuário contendo um modelo telefone que contém o campo casa e móvel campos.
O Pydantic também nos permite validar o conteúdo dos dados e o tipo e a presença dos dados. Você pode fazer isso definindo funções que validam campos específicos. No exemplo acima, validamos o campo móvel e casa para verificar se há extensões. Se eles contiverem uma extensão, não a suportaremos e lançaremos um erro personalizado. Esses erros de esquema são então mostrados aos usuários que fazem a validação pidântica.
Você pode visualizar a definição do esquema especificando o parâmetro Model.schema_json() como mostrado aqui:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 |
{ "título": "UserProfile" (Perfil do usuário), "description" (descrição): "Esquema para perfis de usuário", "tipo": "objeto", "propriedades": { "nome de usuário": { "título": "Nome de usuário", "tipo": "string" }, "name" (nome): { "título": "Nome", "tipo": "string" }, "telefone": { "$ref": "#/definitions/Phone" }, "mail": { "título": "Correio", "tipo": "string" }, "empresa": { "título": "Empresa", "tipo": "string" }, "residência": { "título": "Residência", "tipo": "string" }, "website": { "título": "Site", "tipo": "array", "itens": { "tipo": "string", "minLength": 1, "maxLength": 2083, "formato": "uri" } }, "trabalho": { "título": "Trabalho", "tipo": "string" }, "endereço": { "título": "Endereço", "tipo": "string" } }, "obrigatório": [ "nome de usuário", "name" (nome), "telefone", "mail", "website" ], "definições": { "Telefone": { "título": "Telefone", "description" (descrição): "Esquema para números de telefone", "tipo": "objeto", "propriedades": { "casa": { "título": "Casa", "tipo": "string" }, "móvel": { "título": "Móvel", "tipo": "string" } }, "obrigatório": [ "casa", "móvel" ] } } } |
Validação de documentos JSON em relação a um esquema Python pidântico
Agora que definimos o esquema, vamos explorar como podemos validar os documentos em relação ao esquema.
A validação pode ser feita usando o pydantic parse_obj do modelo. Você especifica o documento como um dicionário e verifica se há exceções de validação. Nesse caso, buscamos todos os documentos (até o limite especificado) usando uma consulta do Couchbase, testamos um a um e informamos os erros.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
senha_autenticador = PasswordAuthenticator(DB_USER, DB_PWD) agrupamento = Aglomerado( CONN_STR, ClusterOptions(senha_autenticador), ) validation_error_count = 0 consulta = f"select profile.* from `{BUCKET}`.{SCOPE}.{COLLECTION} profile LIMIT {DOCUMENT_LIMIT}" tentar: resultados = agrupamento.consulta(consulta) para fila em resultados: tentar: Perfil do usuário.parse_obj(fila) validation_error_count += 1 exceto ValidationError como e: impressão(f"Erro encontrado no documento: {row['username']}\n", e.json()) exceto Exceção como e: impressão(e) impressão(f"Contagem de erros de validação: {validation_error_count}") |
O esquema destaca alguns dos erros observados nos documentos:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
Erro encontrado em documento: aarias [ { "loc": [ "telefone", "casa" ], "msg": "value is an extension, got \"(932)532-4001x319\"", "tipo": "value_error.extension", "ctx": { "wrong_value" (valor errado): "(932)532-4001x319" } }, { "loc": [ "telefone", "móvel" ], "msg": "campo obrigatório", "tipo": "value_error.missing" } ] |
O documento com a ID aarias tem extensões no campo doméstico, e o telefone → móvel também está faltando.
Resumo
Este exemplo de perfil de usuário mostra como podemos criar facilmente esquemas personalizados para nossos documentos JSON. Esta postagem também mostra como usar os recursos de teste e validação do Python e do módulo pydantic. No entanto, é apenas a ponta do iceberg - há muito mais tipos que podemos validar.
Outras abordagens também são possíveis; por exemplo, você pode validar os dados no momento da gravação. Isso pode ser feito com bastante facilidade integrando o esquema que definimos aqui ao aplicativo e verificando os dados antes de inseri-los/uprimi-los no Couchbase.
O código completo dessa demonstração pode ser encontrado em Github. As instruções para gerar os dados e executar os scripts também podem ser encontradas lá.
Recursos
- Validação do esquema JSON - Repositório do GitHub para o código desta publicação
- Couchbase Capella – DBaaS NoSQL totalmente hospedado
- Módulo Python pydantic
- Módulo Python Faker