Estruturando dados com Serde em Rust
Avalie esse Artigo
Este post detalha novas atualizações no Rust MongoDB Driver e biblioteca BSON para melhorar nossa integração com o Serde. Na publicação do blog Rust Quick Start, discute-se a complexidade de trabalhar com o BSON, que tem um esquema dinâmico, no Rust, que usa um sistema de tipos estáticos. O driver MongoDB Rust e a biblioteca BSON usam oSerde para facilitar a conversão entre estruturas e enums BSON e Rust. No 1.2.0 versões dessas duas bibliotecas, incluímos a nova integração do Serde para tornar o trabalho direto com seus próprios tipos de dados Rust mais fácil e fácil de usar.
Esta publicação pressupõe que você tenha uma versão recente da cadeia de ferramentas Rust instalada (v1.44+) e que esteja familiarizado com a sintaxe Rust. Ele também pressupõe que você esteja familiarizado com a biblioteca Rust Serde.
O 1.2.0 A versão do driver Rust introduz um parâmetro de tipo genérico para o tipocollection. O parâmetro genérico representa o tipo de dados que você deseja inserir e localizar de sua coleção MongoDB. Qualquer tipo de dados Rust que derive/implemente as características Serde Serialize e Deserialize pode ser usado como um parâmetro de tipo para uma collection.
Por exemplo, estou trabalhando com a seguinte estrutura que define o esquema dos dados em minha coleção
students
:1 2 struct Student { 3 name: String, 4 grade: u32, 5 test_scores: Vec<u32>, 6 }
Posso criar um
Collection
genérico usando o métodoDatabase::collection_with_type e especificando Student
como o tipo de dados com o qual estou trabalhando.1 let students: Collection<Student> = db.collection_with_type("students");
Antes da introdução do
Collection
genérico, os vários métodos CRUD Collection
aceitavam e retornavam o tipo dedocumento. Isso significa que eu precisaria serializar minhas estruturasStudent
para Document
s antes de inseri-las na coleção dos alunos. Agora, posso inserir um Student
diretamente na minha coleção:1 let student = Student { 2 name: "Emily".to_string(), 3 grade: 10, 4 test_scores: vec![98, 87, 100], 5 }; 6 let result = students.insert_one(student, None).await;
Também posso encontrar um aluno diretamente em meu
Collection
:1 // student is of type Student 2 let student = students.find_one(doc! { "name": "Emily" }, None).await?;
Talvez eu decida inserir um tipo diferente de dados na collection
students
em algum momento. Embora minha coleçãostudents
esteja restrita ao tipo de dadosStudent
, posso criar facilmente um clone da coleçãostudents
com um novo parâmetro de tipo:1 let students: Collection<CollegeStudent> = students.clone_with_type();
O tipo genérico padrão para
Collection
é Document
. Isso significa que qualquer Collection
criado sem um tipo genérico continuará a localizar e retornar o tipoDocument
, e qualquer código existente que use Collection
não será afetado por essas alterações.O 1.2. A versão 0 também inclui alterações na biblioteca Rust BSON que melhoram a usabilidade ao trabalhar com o Serde.
Às vezes, você pode querer serializar ou desserializar dados em suas estruturas ou enums de forma diferente do comportamento padrão. Serde fornece atributosserialize_with e deserialize_with que permitem especificar funções a serem usadas para serialização e desserialização em campos e variantes específicos.
A biblioteca BSON agora inclui um conjunto de funções que implementam estratégias comuns para serialização e desserialização personalizadas ao trabalhar com BSON. Você pode usar essas funções importando-as do módulo
serde_helpers
na caixabson-rust
e usando os atributosserialize_with
e deserialize_with
. Algumas dessas funções são detalhadas abaixo.Alguns usuários preferem representar o campo de ID do objeto em seus dados com uma string hexadecimal em vez do tipoObjectId da biblioteca BSON :
1 2 struct Item { 3 oid: String, 4 // rest of fields 5 }
Introduzimos um método para serializar uma string hexadecimal em um
ObjectId
no móduloserde_helpers
chamado serialize_hex_string_as_object_id
. Posso anotar meu campooid
com esta função usando serialize_with
:1 2 struct Item { 3 4 oid: String, 5 // rest of fields 6 }
Agora, se eu serializar uma instância da estrutura
Item
em BSON, o campooid
será representado por um ObjectId
em vez de um string
.Também introduzimos módulos que lidam com a serialização e a desserialização. Por exemplo, talvez eu queira representar dados binários usando o tipoUuid na caixa uuid do Rust :
1 2 struct Item { 3 uuid: Uuid, 4 // rest of fields 5 }
Como o BSON não tem um tipo de UUID específico, precisarei converter esses dados em binários se quiser serializar no BSON. Também gostaria de converter de volta para Uuid ao desserializar do BSON. O módulo
uuid_as_binary
no módulo serde_helpers
pode lidar com ambas as conversões. Adicionarei o seguinte atributo para usar este módulo:1 2 struct Item { 3 4 uuid: Uuid, 5 // rest of fields 6 }
Agora posso trabalhar diretamente com o tipo Uuid sem precisar me preocupar em convertê-lo de e para BSON!
O módulo
serde_helpers
apresenta funções para diversas outras estratégias comuns; você pode conferir a documentação aqui.A especificação BSON define dois tipos de inteiros: um inteiro de 32 bits com sinal e um inteiro de 64 bits com sinal. Isso pode evitar desafios quando você tenta inserir dados com números inteiros sem sinal em suas coleções.
Minha
Student
estrutura do exemplo anterior contém números inteiros sem sinal grade
nos test_score
campos e . As versões anteriores da biblioteca BSON retornariam um erro se eu tentasse serializar uma instância dessa estrutura em Document
, pois nem sempre há um mapeamento claro entre os tipos inteiros não assinados e assinados. No entanto, muitos números inteiros não assinados podem caber em tipos assinados! Por exemplo, talvez eu queira criar o seguinte aluno:1 let student = Student { 2 name: "Alyson".to_string(), 3 grade: 11, 4 test_scores: vec![89, 92, 99], 5 };
Embora os números nos campos
grade
e test_scores
sejam técnicamente inteiros 32 bits sem sinal, eles podem ser convertidos em inteiros 32 bits com assinatura sem perder nenhum dado.O 1.2. A versão 0 da biblioteca BSON não retorna mais um erro ao tentar serializar números inteiros não assinados em BSON e, em vez disso, tenta realizar uma conversão sem perdas em um dos BSON types assinados. Se o inteiro sem sinal não puder ser convertido em um inteiro de 32 ou 64 bit assinado, a serialização ainda retornará um erro.
Agora, posso converter meu struct de aluno em um
Document
:1 let doc = to_document(&student)?;
Posso usar
println!
para ver que o seguinte Document
é retornado:1 Document({ 2 "name": String("Alyson"), 3 "grade": Int32(11), 4 "test_scores": Array([Int32(89), Int32(92), Int32(99)]) 5 })
O Serde é uma ferramenta poderosa que oferece muitas funcionalidades para personalizar a maneira como você converte entre diferentes formatos de dados. No 1, .2.0 versões do driver Rust e da biblioteca BSON, ficou ainda mais fácil trabalhar diretamente com seus tipos de dados do Rust. Se você estiver interessado em recursos de mapeamento mais complexos, vale a pena ler a documentação do Serde sobre atributos. Para obter mais detalhes sobre como trabalhar com o MongoDB no Rust, você pode conferir a documentação do driver Rust e dabiblioteca BSON. Também aceitamos contribuições na forma de solicitações de pull do Github - consulte a seção em nosso README para obter informações sobre como executar nossos testes.
Se tiver dúvidas, acesse o site da nossa comunidade de desenvolvedores, no qual os engenheiros e a comunidade do MongoDB ajudarão você a desenvolver sua próxima grande ideia com o MongoDB.