Tutorial do BSON
Nesta página
- Instalação
- Usar com ActiveSupport
- BSON serialização
- Buffers de bytes
- Escrevendo
- Leitura
- Classes suportadas
BSON::Binary
BSON::Code
BSON::CodeWithScope
BSON::DBRef
BSON::Document
BSON::MaxKey
BSON::MinKey
BSON::ObjectId
BSON::Timestamp
BSON::Undefined
BSON::Decimal128
Symbol
- serialização JSON
- Instâncias de tempo
- Instâncias de DateTime
- Instâncias de datas
- Expressões regulares
- Expressões regulares Ruby versus MongoDB
BSON::Regexp::Raw
classe- Conversão de expressões regulares
- Leitura e escrita
- Ordem das chaves
- Chaves duplicadas
Este tutorial discute o uso da biblioteca Ruby BSON.
Instalação
A biblioteca BSON pode ser instalada a partir do Rubygems manualmente ou com empacotador.
Para instalar a joia manualmente:
gem install bson
Para instalar o gem com o empacotador, inclua o seguinte em seu Gemfile
:
gem 'bson'
A biblioteca BSON é compatível com MRI >= 2.5 e JRuby >= 9.2.
Usar com ActiveSupport
A serialização para classes definidas pelo ActiveSupport, como TimeWithZone, não é carregada por padrão para evitar uma dependência rígida do BSON no ActiveSupport. Ao usar o BSON em um aplicativo que também usa o ActiveSupport, o código relacionado ao ActiveSupport deve ser explicitamente exigido:
require 'bson' require 'bson/active_support'
BSON serialização
A obtenção da representação BSON bruta de um objeto Ruby é feita chamando to_bson
no objeto Ruby, que retornará um BSON::ByteBuffer
. Por exemplo:
"Shall I compare thee to a summer's day".to_bson 1024.to_bson
A geração de um objeto a partir do BSON é feita chamando from_bson
na classe que você deseja instanciar e passando uma instância BSON::ByteBuffer
.
String.from_bson(byte_buffer) BSON::Int32.from_bson(byte_buffer)
Buffers de bytes
A biblioteca BSON 4.0 introduz o uso de buffers de bytes nativos no MRI e JRuby em vez de usar o StringIO
, para melhorar o desempenho.
Escrevendo
Para criar um ByteBuffer
para escrever (ou seja, serializando para BSON), instanciam BSON::ByteBuffer
sem argumentos:
buffer = BSON::ByteBuffer.new
Para escrever bytes brutos no buffer de bytes sem transformações, use os métodos put_byte
e put_bytes
. Eles usam uma cadeia de bytes como argumento e copiam essa cadeia de caracteres no buffer. put_byte
força que o argumento seja uma string de comprimento 1; put_bytes
aceita strings de qualquer comprimento. As strings podem conter bytes nulos.
buffer.put_byte("\x00") buffer.put_bytes("\xff\xfe\x00\xfd")
Observação
put_byte
e put_bytes
não gravam um byte do tipo BSON antes de gravar o argumento no buffer de bytes.
Métodos de escrita subsequentes escrevem objetos de tipos específicos na especificação BSON. Observe que o tipo indicado pelo nome do método tem precedência sobre o tipo do argumento - por exemplo, se um valor de ponto flutuante for fornecido a put_int32
, ele será forçado em um número inteiro e o número inteiro resultante será gravado no buffer de bytes .
Para escrever uma cadeia UTF-8 (BSON tipo 0x02) no buffer de bytes, use put_string
:
buffer.put_string("hello, world")
Observe que as strings BSON são sempre codificadas em UTF-8. Portanto, o argumento deve estar em UTF-8 ou em uma codificação convertível para UTF-8 (ou seja, não binário). Se o argumento estiver em uma codificação diferente de UTF-8, a string será primeiro convertida em UTF-8 e a versão codificada em UTF-8 será gravada no buffer. A string deve ser válida na codificação reivindicação, inclusive sendo UTF-8 válida se a codificação for UTF-8. A string pode conter bytes nulos.
A especificação BSON também define um tipo CString, que é usado, por exemplo, para chaves de documentos. Para escrever CStrings no buffer, use put_cstring
:
buffer.put_cstring("hello, world")
Assim como nas strings regulares, as CStrings no BSON devem ser codificadas em UTF-8. Se o argumento não estiver em UTF-8, ele será convertido para UTF-8 e a string resultante será gravada no buffer. Ao contrário de put_string
, a codificação UTF-8 do argumento fornecido a put_cstring
não pode ter bytes nulos, uma vez que o formato de serialização CString em BSON tem encerramento nulo.
Ao contrário de put_string
, put_cstring
também aceita símbolos e inteiros. Em todos os casos, o argumento é definido como uma string antes de ser escrito:
buffer.put_cstring(:hello) buffer.put_cstring(42)
Para escrever um número inteiro de 32 bits ou 64 bits no buffer de bytes, use os métodos put_int32
e put_int64
, respectivamente. Observe que os números inteiros Ruby podem ser arbitrariamente grandes; se o valor que está sendo gravado exceder o intervalo de um número inteiro de 32 ou 64 bits, put_int32
e put_int64
aumentem RangeError
.
buffer.put_int32(12345) buffer.put_int64(123456789012345)
Observação
Se put_int32
ou put_int64
receberem argumentos de ponto flutuante, os argumentos serão primeiro convertidos em inteiros e os inteiros serão gravados no buffer de bytes.
Para escrever um valor de ponto flutuante de 64 bits no buffer de bytes, use put_double
:
buffer.put_double(3.14159)
Para obter os dados serializados como uma cadeia de bytes (por exemplo, para enviar os dados por um soquete), chame to_s
no buffer:
buffer = BSON::ByteBuffer.new buffer.put_string('testing') socket.write(buffer.to_s)
Observação
ByteBuffer
mantém o controle das posições de leitura e escrita separadamente. Não há como retroceder o buffer para gravação - rewind
afeta apenas a posição de leitura.
Leitura
Para criar um ByteBuffer
para leitura (ou seja, desserializando do BSON), instancie BSON::ByteBuffer
com uma cadeia de bytes como argumento:
buffer = BSON::ByteBuffer.new(string) # a read mode buffer.
A leitura do buffer é feita por meio da seguinte API:
buffer.get_byte # Pulls a single byte from the buffer. buffer.get_bytes(value) # Pulls n number of bytes from the buffer. buffer.get_cstring # Pulls a null-terminated string from the buffer. buffer.get_double # Pulls a 64-bit floating point from the buffer. buffer.get_int32 # Pulls a 32-bit integer (4 bytes) from the buffer. buffer.get_int64 # Pulls a 64-bit integer (8 bytes) from the buffer. buffer.get_string # Pulls a UTF-8 string from the buffer.
Para reiniciar a leitura a partir do início de um buffer, use rewind
:
buffer.rewind
Observação
ByteBuffer
mantém o controle das posições de leitura e escrita separadamente. rewind
afeta apenas a posição de leitura.
Classes suportadas
As principais classes Ruby que têm representações na especificação BSON e terão um método to_bson
definido para elas são: Object
, Array
, FalseClass
, Float
, Hash
, Integer
, BigDecimal
, NilClass
, Regexp
, String
, Symbol
(descontinuado), Time
, TrueClass
.
Além dos principais objetos Ruby, o BSON também fornece alguns tipos especiais específicos para a especificação:
BSON::Binary
Utilize objetos BSON::Binary
para armazenar dados binários arbitrários. Os objetos Binary
podem ser construídos a partir de strings binárias da seguinte forma:
BSON::Binary.new("binary_string") # => <BSON::Binary:0x47113101192900 type=generic data=0x62696e6172795f73...>
Por padrão, objetos Binary
são criados com BSON subtipo binário 0 (:generic
). O subtipo pode ser explicitamente especificado para indicar que os bytes codificam um tipo específico de dados:
BSON::Binary.new("binary_string", :user) # => <BSON::Binary:0x47113101225420 type=user data=0x62696e6172795f73...>
Os subtipos válidos são :generic
, :function
, :old
, :uuid_old
, :uuid
, :md5
e :user
.
Os dados e o subtipo podem ser recuperados de instâncias do Binary
utilizando atributos do data
e type
, como segue:
binary = BSON::Binary.new("binary_string", :user) binary.data => "binary_string" binary.type => :user
Observação
BSON::Binary
os objetos sempre armazenam os dados na codificação BINARY
, independentemente da codificação em que a string passada para o construtor estava:
str = "binary_string" str.encoding # => #<Encoding:US-ASCII> binary = BSON::Binary.new(str) binary.data # => "binary_string" binary.data.encoding # => #<Encoding:ASCII-8BIT>
Métodos de UUID
Para criar um UUID BSON::Binary (subtipo binário 4) a partir de sua representação de string compatível com RFC 4122, use o método from_uuid
:
uuid_str = "00112233-4455-6677-8899-aabbccddeeff" BSON::Binary.from_uuid(uuid_str) # => <BSON::Binary:0x46986653612880 type=uuid data=0x0011223344556677...>
Para stringificar um UUID BSON::Binary para uma representação compatível com RFC 4122, use o método to_uuid
:
binary = BSON::Binary.new("\x00\x11\x22\x33\x44\x55\x66\x77\x88\x99\xAA\xBB\xCC\xDD\xEE\xFF".force_encoding('BINARY'), :uuid) => <BSON::Binary:0x46942046606480 type=uuid data=0x0011223344556677...> binary.to_uuid => "00112233-4455-6677-8899aabbccddeeff"
A representação padrão pode ser especificada explicitamente ao invocar os métodos from_uuid
e to_uuid
:
binary = BSON::Binary.from_uuid(uuid_str, :standard) binary.to_uuid(:standard)
Observe que a representação :standard
só pode ser usada com um binário do subtipo :uuid
(não :uuid_old
).
UUIDs legados
Os dados armazenados em objetos BSON::Binary do subtipo 3 (:uuid_old
) podem ser mantidos em uma de três ordens de bytes diferentes, dependendo de qual driver criou os dados. As ordens de bytes são legado CSharp, legado Java e legado Python. A ordem de bytes legado do Python é a mesma que a ordem de bytes padrão RFC 4122; As ordens de bytes legados do C# e do legado Java têm alguns dos bytes trocados.
O objeto Binary contendo um UUID legado não codifica em qual formato o UUID está armazenado. Portanto, os métodos que convertem de e para o formato UUID legado usam o formato, ou representação, como argumento. Um aplicativo pode copiar objetos binários UUID legados sem saber em qual ordem de bytes eles armazenam seus dados.
Os métodos a seguir para trabalhar com UUIDs legados são fornecidos para interoperabilidade com sistemas existentes que armazenam dados em formatos UUID legados. É recomendável que novos aplicativos usem somente o formato :uuid
(subtipo 4), que é compatível com a RFC 4122.
Para stringificar um UUID legado BSON::Binary, utilize o método to_uuid
especificando a representação desejada. As representações aceitas são :csharp_legacy
, :java_legacy
e :python_legacy
. Observe que um UUID legado BSON::Binary não pode ser stringified sem especificar uma representação.
binary = BSON::Binary.new("\x00\x11\x22\x33\x44\x55\x66\x77\x88\x99\xAA\xBB\xCC\xDD\xEE\xFF".force_encoding('BINARY'), :uuid_old) => <BSON::Binary:0x46942046606480 type=uuid data=0x0011223344556677...> binary.to_uuid # => ArgumentError (Representation must be specified for BSON::Binary objects of type :uuid_old) binary.to_uuid(:csharp_legacy) # => "33221100-5544-7766-8899aabbccddeeff" binary.to_uuid(:java_legacy) # => "77665544-3322-1100-ffeeddccbbaa9988" binary.to_uuid(:python_legacy) # => "00112233-4455-6677-8899aabbccddeeff"
Para criar um UUID legado BSON::Binary a partir da representação de string do UUID, utilize o método from_uuid
especificando a representação desejada:
uuid_str = "00112233-4455-6677-8899-aabbccddeeff" BSON::Binary.from_uuid(uuid_str, :csharp_legacy) # => <BSON::Binary:0x46986653650480 type=uuid_old data=0x3322110055447766...> BSON::Binary.from_uuid(uuid_str, :java_legacy) # => <BSON::Binary:0x46986653663960 type=uuid_old data=0x7766554433221100...> BSON::Binary.from_uuid(uuid_str, :python_legacy) # => <BSON::Binary:0x46986653686300 type=uuid_old data=0x0011223344556677...>
Estes métodos podem ser utilizados para converter de uma representação para outra:
BSON::Binary.from_uuid('77665544-3322-1100-ffeeddccbbaa9988',:java_legacy).to_uuid(:csharp_legacy) # => "33221100-5544-7766-8899aabbccddeeff"
BSON::Code
Representa uma string de código JavaScript.
BSON::Code.new("this.value = 5;")
BSON::CodeWithScope
Observação
O tipo CodeWithScope
está obsoleto a partir do MongoDB 4.2.1. A partir do MongoDB 4.4, o suporte do CodeWithScope
está sendo removido de vários comandos e operadores do servidor, como $where
. Use outros BSON types ao trabalhar com o MongoDB 4.4 e versões mais recentes.
Representa uma string de código JavaScript com um hash de valores.
BSON::CodeWithScope.new("this.value = age;", age: 5)
BSON::DBRef
Esta é uma subclasse de BSON::Document
que fornece acessadores para a coleção, id e banco de dados do DBRef.
BSON::DBRef.new({"$ref" => "collection", "$id" => "id"}) BSON::DBRef.new({"$ref" => "collection", "$id" => "id", "database" => "db"})
Observação
O construtor BSON::DBRef validará o hash fornecido e gerará um ArgumentError se ele não for um DBRef válido. BSON::ExtJSON.parse_obj
e Hash.from_bson
não gerarão um erro se receberem um DBRef inválido e, em vez disso, analisarão um Hash ou desserializarão um BSON::documento.
Observação
Todos os documentos BSON são desserializados em instâncias de BSON::DBRef se forem DBRefs válidos, caso contrário, são desserializados em instâncias de BSON::Document. Isso é verdadeiro mesmo quando a invocação é feita a partir da classe Hash
:
bson = {"$ref" => "collection", "$id" => "id"}.to_bson.to_s loaded = Hash.from_bson(BSON::ByteBuffer.new(bson)) => {"$ref"=>"collection", "$id"=>"id"} loaded.class => BSON::DBRef
Para compatibilidade com versões 2.17 e anteriores do driver Ruby do MongoDB, BSON::DBRef
também pode ser construído usando a API de driver legada. Esta API está obsoleta e será removida em uma versão futura do bson-ruby
:
BSON::DBRef.new("collection", BSON::ObjectId('61eeb760a15d5d0f9f1e401d')) BSON::DBRef.new("collection", BSON::ObjectId('61eeb760a15d5d0f9f1e401d'), "db")
BSON::Document
Esta é uma subclasse de Hash
que armazena todas as chaves como strings, mas permite acesso a elas com chaves de símbolo.
BSON::Document[:key, "value"] BSON::Document.new
Observação
Todos os documentos BSON são desserializados em instâncias de BSON::Document (ou BSON::DBRef, se por acaso forem um DBRef válido), mesmo quando a invocação é feita a partir da classe Hash
:
bson = {test: 1}.to_bson.to_s loaded = Hash.from_bson(BSON::ByteBuffer.new(bson)) => {"test"=>1} loaded.class => BSON::Document
BSON::MaxKey
Representa um valor em BSON que sempre será comparado com outro valor.
BSON::MaxKey.new
BSON::MinKey
Representa um valor em BSON que sempre será comparado como inferior a outro valor.
BSON::MinKey.new
BSON::ObjectId
Representa um identificador exclusivo de 12 bytes para um objeto em uma determinada máquina.
BSON::ObjectId.new
BSON::Timestamp
Representa um horário especial com um valor inicial e incremental.
BSON::Timestamp.new(5, 30)
BSON::Undefined
Representa um espaço reservado para um valor que não foi fornecido.
BSON::Undefined.new
BSON::Decimal128
Representa um valor de ponto flutuante baseado em decimal de 128 bits capaz de emular arredondamentos decimais com precisão exata.
# Instantiate with a String BSON::Decimal128.new("1.28") # Instantiate with a BigDecimal d = BigDecimal(1.28, 3) BSON::Decimal128.new(d)
BSON::Decimal128 vs BigDecimal
Os métodos BigDecimal
from_bson
e to_bson
utilizam os mesmos métodos BSON::Decimal128
subjacentes. Isso leva a algumas limitações que são impostas aos valores BigDecimal
que podem ser serializados para BSON e àqueles que podem ser desserializados a partir de valores decimal128
BSON existentes. Essa alteração foi feita porque a serialização de instâncias BigDecimal
como instâncias BSON::Decimal128
permite mais flexibilidade em termos de query e agregação no MongoDB. As limitações impostas ao BigDecimal
são as seguintes:
decimal128
tem alcance e precisão limitados, enquantoBigDecimal
não tem restrições em termos de alcance e precisão.decimal128
tem um valor máximo de aproximadamente10^6145
e um valor mínimo de aproximadamente-10^6145
, e tem um máximo de 34 bits de precisão.decimal128
é capaz de aceitar valores deNaN
assinados, enquantoBigDecimal
não é. Todos os valoresNaN
assinados que são desserializados em instânciasBigDecimal
não serão assinados.decimal128
mantém os zeros finais ao serializar e desserializar do BSON.BigDecimal
, no entanto, não mantém zeros à direita e, portanto, usarBigDecimal
pode resultar em falta de precisão.
Observação
No BSON 5.0, decimal128
é desserializado em BigDecimal
por padrão. Para ter decimal128
valores em documentos BSON desserializados em BSON::Decimal128
, a opção mode: :bson
pode ser definida em from_bson
.
Symbol
A especificação BSON define um tipo de símbolo que permite percorrer valores Ruby Symbol
(ou seja, um Ruby Symbol``is encoded into a BSON symbol
and a BSON symbol is decoded into a Ruby ``Symbol
). No entanto, como a maioria das linguagens de programação não tem um tipo de símbolo nativo, para promover a interoperabilidade, o MongoDB depreciou o tipo de símbolo BSON e incentiva o uso de strings.
Observação
No BSON, as chaves de hash são sempre strings. Os valores que não são strings se tornarão strings quando usados como chaves de hash:
Hash.from_bson({foo: 'bar'}.to_bson) # => {"foo"=>"bar"} Hash.from_bson({1 => 2}.to_bson) # => {"1"=>2}
Por padrão, a biblioteca BSON codifica valores de hash Symbol
como strings e decodifica símbolos BSON em valores Ruby Symbol
:
{foo: :bar}.to_bson.to_s # => "\x12\x00\x00\x00\x02foo\x00\x04\x00\x00\x00bar\x00\x00" # 0x02 is the string type Hash.from_bson(BSON::ByteBuffer.new("\x12\x00\x00\x00\x02foo\x00\x04\x00\x00\x00bar\x00\x00".force_encoding('BINARY'))) # => {"foo"=>"bar"} # 0x0E is the symbol type Hash.from_bson(BSON::ByteBuffer.new("\x12\x00\x00\x00\x0Efoo\x00\x04\x00\x00\x00bar\x00\x00".force_encoding('BINARY'))) # => {"foo"=>:bar}
Para forçar a codificação de símbolos Ruby para símbolos BSON, envolva os símbolos Ruby em BSON::Symbol::Raw
:
{foo: BSON::Symbol::Raw.new(:bar)}.to_bson.to_s # => "\x12\x00\x00\x00\x0Efoo\x00\x04\x00\x00\x00bar\x00\x00"
serialização JSON
Alguns tipos de BSON possuem representações especiais em JSON. Eles são os seguintes e serão serializados automaticamente no formulário ao chamar to_json
neles.
Objeto | JSON |
---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Instâncias de tempo
Os tempos em Ruby podem ter precisão de nanossegundos. Os horários em BSON (e MongoDB) só podem ter precisão de milissegundos. Quando as instâncias Ruby Time
são serializadas para BSON ou Extended JSON, os horários são construídos para o milissegundo mais próximo.
Observação
O tempo como sempre arredondado para baixo. Se a hora preceder a Era UNIX (1º de janeiro de 1970 00:00:00 UTC), o valor absoluto da hora aumentaria:
time = Time.utc(1960, 1, 1, 0, 0, 0, 999_999) time.to_f # => -315619199.000001 time.floor(3).to_f # => -315619199.001
Observação
JRuby a partir da versão 9.2.11.0 arredonda os tempos pré-Unix para cima em vez de para baixo. bson-Ruby funciona em torno disso e funciona corretamente os horários ao serializar no JRuby.
Devido a esse sombreamento, é altamente recomendável que os aplicativos executem todos os cálculos de tempo usando matemática numérica inteira, pois a inexatidão dos cálculos de ponto flutuante pode produzir resultados inesperados.
Instâncias de DateTime
O BSON suporta apenas o armazenamento do tempo como o número de segundos desde a Era UNIX. As instâncias DateTime
de Ruby podem ser serializadas para BSON, mas quando o BSON é desserializado, os horários serão retornados como instâncias Time
.
DateTime
class in Ruby suporta calendários não-greganos. Quando instâncias DateTime
não-gregorianos são serializadas, elas são primeiro convertidas para o calendário Gregoriano, e a respectiva data no calendário Gregoriano é armazenada no reconhecimento de data center.
Instâncias de datas
O BSON suporta apenas o armazenamento do tempo como o número de segundos desde a Era UNIX. As instâncias Date
de Ruby podem ser serializadas para BSON, mas quando o BSON é desserializado, os horários serão retornados como instâncias Time
.
Quando Date
instâncias são serializadas, o valor de tempo utilizado é meia-noite do dia a que o Date
se refere em UTC.
Expressões regulares
Tanto o MongoDB quanto o Ruby fornecem instalações para trabalhar com expressões regulares, mas usam mecanismos de expressões regulares. As subseções a seguir detalham as diferenças entre expressões regulares Ruby e expressões regulares MongoDB e descrevem como trabalhar com ambas.
Expressões regulares Ruby versus MongoDB
O servidor MongoDB usa expressões regulares compatíveis com Perl implementadas usando a biblioteca PCRE e expressões regulares Ruby são implementadas usando o mecanismo de expressão regular Onigmo, que é uma bifurcação do Oniguruma. As duas implementações de expressão regular geralmente fornecem funcionalidade equivalente, mas têm várias diferenças de sintaxe importantes, conforme descrito abaixo.
Infelizmente, não há uma maneira simples de converter programaticamente uma expressão regular PCRE na expressão regular Ruby equivalente, e atualmente não há vinculações Ruby para PCRE.
Opções/Sinalizadores/Modificadores
As expressões regulares Ruby e PCRE suportam modificadores. Eles também são chamados de "opções" na linguagem Ruby e "sinalizadores" na linguagem PCRE. O significado dos modificadores s
e m
difere em Ruby e PCRE:
Ruby não tem o modificador
s
, em vez disso, o modificador Rubym
executa a mesma função que o modificador PCREs
, que é fazer com que o ponto (.
) corresponda a qualquer caractere, incluindo novas linhas. De forma desconcertante, a documentação Ruby se refere ao modificadorm
como "habilitando o modo de várias linhas".Ruby sempre opera no equivalente do modo multilinha do PCRE, habilitado pelo modificador
m
em expressões regulares PCRE. Em Ruby, a âncora^
sempre se refere ao início da linha e a âncora$
sempre se refere ao final da linha.
Ao escrever expressão destinadas a serem usadas em ambientes Ruby e PCRE (incluindo o MongoDB Server e a maioria dos outros drivers MongoDB), a partir de agora denominadas "expressão portáteis", evite usar as âncoras ^
e $
. As seções a seguir fornecem soluções alternativas e recomendações para criar expressões regulares portáteis.
^
âncora
Em expressões regulares Ruby, a âncora ^
sempre se refere ao início da linha. Nas expressões regulares PCRE, a âncora ^
refere-se ao início da entrada por padrão e o sinalizador m
altera seu significado para o início da linha.
As expressões regulares Ruby e PCRE suportam a âncora \A
para se referir ao início da entrada, independentemente dos modificadores.
Ao escrever expressões regulares portáteis:
Use a âncora
\A
para referir-se ao início da entrada.Use a âncora
^
para se referir ao início da linha (isso requer a definição do sinalizadorm
em expressões regulares PCRE). Como alternativa, use uma das seguintes construções que funcionam independentemente dos modificadores: -(?:\A|(?<=\n))
(lida com extremidades de linha LF e CR+LF) -(?:\A|(?<=[\r\n]))
(lida com extremidades de linha CR, LF e CR+LF)
$
âncora
Em expressões regulares Ruby, a âncora $
sempre se refere ao final da linha. Nas expressões regulares PCRE, a âncora $
refere-se ao final da entrada por padrão e o sinalizador m
altera seu significado para o final da linha.
As expressões regulares Ruby e PCRE suportam a âncora \z
para se referir ao final da entrada, independentemente dos modificadores.
Ao escrever expressões regulares portáteis:
Use a âncora
\z
para referir-se ao final da entrada.Use a âncora
$
para se referir ao início da linha (isso requer a definição do sinalizadorm
em expressões regulares PCRE). Como alternativa, use uma das seguintes construções que funcionam independentemente dos modificadores: -(?:\z|(?=\n))
(lida com extremidades de linha LF e CR+LF) -(?:\z|(?=[\n\n]))
(lida com extremidades de linha CR, LF e CR+LF)
BSON::Regexp::Raw
classe
Como não há uma maneira simples de converter programaticamente uma expressão PCRE na expressão Ruby equivalente, o BSON fornece a classe BSON::Regexp::Raw
para manter as expressão do MongoDB/PCRE. As instâncias dessa classe são chamadas de "expressões regulares BSON" nesta documentação.
As instâncias desta classe podem ser criadas utilizando o texto de expressão regular como uma string e modificadores PCRE opcionais:
BSON::Regexp::Raw.new("^b403158") # => #<BSON::Regexp::Raw:0x000055df63186d78 @pattern="^b403158", @options=""> BSON::Regexp::Raw.new("^Hello.world$", "s") # => #<BSON::Regexp::Raw:0x000055df6317f028 @pattern="^Hello.world$", @options="s">
O módulo BSON::Regexp
está incluído na classe Ruby Regexp
, de modo que o prefixo BSON::
pode ser omitido:
Regexp::Raw.new("^b403158") # => #<BSON::Regexp::Raw:0x000055df63186d78 @pattern="^b403158", @options=""> Regexp::Raw.new("^Hello.world$", "s") # => #<BSON::Regexp::Raw:0x000055df6317f028 @pattern="^Hello.world$", @options="s">
Conversão de expressões regulares
Para converter uma expressão regular Ruby em uma expressão regular BSON, instancie um objeto BSON::Regexp::Raw
da seguinte forma:
regexp = /^Hello.world/ bson_regexp = BSON::Regexp::Raw.new(regexp.source, regexp.options) # => #<BSON::Regexp::Raw:0x000055df62e42d60 @pattern="^Hello.world", @options=0>
Observe que o construtor BSON::Regexp::Raw
aceita as opções numéricas Ruby e as strings de modificadores PCRE.
Para converter uma expressão regular BSON em uma expressão regular Ruby, chame o método compile
na expressão regular BSON:
bson_regexp = BSON::Regexp::Raw.new("^hello.world", "s") bson_regexp.compile # => /^hello.world/m bson_regexp = BSON::Regexp::Raw.new("^hello", "") bson_regexp.compile # => /^hello.world/ bson_regexp = BSON::Regexp::Raw.new("^hello.world", "m") bson_regexp.compile # => /^hello.world/
Observe que o modificador PCRE s
foi convertido no modificador Ruby m
no primeiro exemplo e os dois últimos exemplos foram convertidos na mesma expressão regular, embora as expressões regulares BSON originais tenham significados diferentes.
Quando uma expressão regular BSON utiliza as âncoras não portáteis ^
e $
, sua conversão para uma expressão regular Ruby pode alterar seu significado:
BSON::Regexp::Raw.new("^hello.world", "").compile =~ "42\nhello world" # => 3
Quando uma expressão regular Ruby é convertida em uma expressão regular BSON (por exemplo, para enviar ao servidor como parte de uma query), a expressão regular BSON sempre tem o modificador m
definido refletindo o comportamento de ^
e $
âncoras em expressões regulares Ruby.
Leitura e escrita
As expressões regulares Ruby e BSON implementam o método to_bson
para serialização para BSON:
regexp_ruby = /^b403158/ # => /^b403158/ regexp_ruby.to_bson # => #<BSON::ByteBuffer:0x007fcf20ab8028> _.to_s # => "^b403158\x00m\x00" regexp_raw = Regexp::Raw.new("^b403158") # => #<BSON::Regexp::Raw:0x007fcf21808f98 @pattern="^b403158", @options=""> regexp_raw.to_bson # => #<BSON::ByteBuffer:0x007fcf213622f0> _.to_s # => "^b403158\x00\x00"
As classes Regexp
e BSON::Regexp::Raw
implementam o método de classe from_bson
que desserializa uma expressão regular de um buffer de bytes BSON. Os métodos de ambas as classes retornam uma instância BSON::Regexp::Raw
que deve ser convertida em uma expressão regular Ruby usando o método compile
conforme descrito acima.
byte_buffer = BSON::ByteBuffer.new("^b403158\x00\x00") regex = Regexp.from_bson(byte_buffer) # => #<BSON::Regexp::Raw:0x000055df63100d40 @pattern="^b403158", @options=""> regex.pattern # => "^b403158" regex.options # => "" regex.compile # => /^b403158/
Ordem das chaves
Os documentos BSON preservam a ordem das chaves, pois os documentos são armazenados como listas de pares chave-valor. Os hashes em Ruby também preservam a ordem das chaves; portanto, a ordem das chaves especificada em Ruby será respeitada ao serializar um hash para um documento BSON e, ao desserializar um documento BSON em um hash, a ordem das chaves no documento corresponderá à ordem das chaves no hash.
Chaves duplicadas
A especificação BSON permite que documentos BSON tenham chaves duplicadas, porque os documentos são armazenados como listas de pares chave-valor. A aplicação deve abster-se de gerar esse documento, porque o comportamento do MongoDB Server é indefinido quando um documento BSON contém chaves duplicadas.
Como os hashes Ruby não podem ter chaves duplicadas, ao serializar hashes Ruby para documentos BSON nenhuma chave duplicada será gerada. (Ainda é possível criar manualmente um documento BSON que teria chaves duplicadas no Ruby, e algumas das outras bibliotecas BSON do MongoDB podem permitir a criação de documentos BSON com chaves duplicadas.)
Observe que, como as chaves nos documentos BSON são sempre armazenadas como strings, especificar a mesma chave como string e um símbolo em Ruby apenas mantém a especificação mais recente:
BSON::Document.new(test: 1, 'test' => 2) => {"test"=>2}
Ao carregar um documento BSON com chaves duplicadas, o último valor para uma chave duplicada substitui os valores anteriores para a mesma chave.