Docs Menu
Docs Home
/ /

Formato de datos BSON

En esta guía, puedes aprender sobre el formato de datos BSON, cómo MongoDB utiliza BSON para organizar y almacenar datos, y cómo instalar la librería BSON de forma independiente del driver de Ruby.

BSON, o JSON binario, es el formato de datos que MongoDB usa para organizar y almacenar datos. Este formato de datos incluye todos los tipos de estructuras de datos JSON y añade soporte para tipos como fechas, números enteros de diferente tamaño (32bits y 64bits), ObjectId y datos binarios. Para ver una lista completa de los tipos compatibles, consulta la BSON types en la documentación del MongoDB Server.

BSON no es legible por humanos, pero puedes usar la librería BSON de Ruby para convertirlo en la representación JSON legible por humanos. Puedes leer más sobre la relación entre estos formatos en la guía JSON y BSON en el sitio web de MongoDB.

Puedes instalar la librería BSON (bson) de Rubygems manualmente o utilizando el bundler.

Ejecuta el siguiente comando para instalar la gema bson:

gem install bson

Para instalar la gem usando bundler, incluye la siguiente línea en tu Gemfile de la aplicación:

gem 'bson'

La librería BSON es compatible con MRI v2.5 y posteriores y JRuby v9.2 y posteriores.

La serialización para clases definidas en Active Support, como TimeWithZone, no se carga de forma predeterminada para evitar una dependencia fija de BSON en Active Support. Cuando utilices BSON en una aplicación que también utiliza Active Support, debes requerir el soporte de código Active Support:

require 'bson'
require 'bson/active_support'

Puede recuperar la representación BSON sin procesar de un objeto Ruby llamando a to_bson en el objeto. El método to_bson devuelve un BSON::ByteBuffer.

El siguiente código demuestra cómo llamar al método to_bson en objetos Ruby:

"Shall I compare thee to a summer's day".to_bson
1024.to_bson

Puedes generar un objeto Ruby a partir de BSON llamando a from_bson en la clase que deseas instanciar y pasándole una instancia de BSON::ByteBuffer:

String.from_bson(byte_buffer)
BSON::Int32.from_bson(byte_buffer)

bson v4.0 introduce el uso de buffers de bytes nativos en MRI y JRuby en lugar de utilizar StringIO para mejorar el rendimiento.

Para crear un ByteBuffer para la escritura, instancia un BSON::ByteBuffer sin argumentos:

buffer = BSON::ByteBuffer.new

Para guardar bytes en bruto en el búfer de bytes sin transformaciones, utilice los métodos put_byte y put_bytes. Cada método toma una byte string como argumento y copia esta string en el búfer. El método put_byte aplica que el argumento sea un string de longitud 1. put_bytes acepta cualquier longitud de cadenas. Las cadenas pueden contener bytes nulos.

El siguiente código demuestra cómo guardar bytes sin procesar en un búfer de bytes:

buffer.put_byte("\x00")
buffer.put_bytes("\xff\xfe\x00\xfd")

Nota

put_byte y put_bytes no guardes un byte de tipo BSON en el búfer antes de guardar la string de bytes. Esto significa que el buffer no tiene información sobre el tipo de datos que la string de bytes sin procesar codifica.

Los métodos de guardar descritos en las siguientes secciones guardan objetos de tipos particulares en la especificación BSON. El tipo indicado por el nombre del método tiene prioridad sobre el tipo del argumento. Por ejemplo, si se pasa un valor de punto flotante a put_int32, se convierte en un entero y el driver escribe el entero resultante en el búfer de bytes.

Para escribir una string UTF-8 (tipo BSON 0x02) en el búfer de bytes, utiliza el método put_string:

buffer.put_string("hello, world")

Las cadenas BSON siempre se codifican en UTF-8. Esto significa que el argumento de put_string debe estar en UTF-8 o en una codificación convertible a UTF-8 (no binaria). Si el argumento está en una codificación distinta de UTF-8, la string se convierte primero a UTF-8 y luego, la versión codificada en UTF-8 se escribe en el búfer. La string debe ser válida en la codificación que se indica. La string puede contener bytes nulos.

La especificación BSON también define un tipo CString, que se utiliza, por ejemplo, para claves de documentos. Para guardar CStrings en el buffer, utiliza put_cstring:

buffer.put_cstring("hello, world")

Al igual que con las cadenas regulares, las CStrings en BSON deben estar codificadas en UTF-8. Si el argumento no se encuentra en UTF-8, será convertido a UTF-8 y la string resultante se escribirá en el buffer. A diferencia de put_string, la codificación UTF-8 del argumento proporcionado a put_cstring no puede contener ningún byte nulo, ya que el formato de serialización CString en BSON está terminado en nulo.

A diferencia de put_string, put_cstring también acepta símbolos y enteros. En todos los casos, el argumento se convierte en cadena antes de ser escrito en el búfer:

buffer.put_cstring(:hello)
buffer.put_cstring(42)

Para guardar un entero de 32bits o de 64bits en el búfer de bytes, usa los métodos put_int32 y put_int64, respectivamente. Ten en cuenta que los enteros Ruby pueden ser arbitrariamente grandes; si el valor que se escribe excede el rango de un entero de 32bits o de 64bits, put_int32 y put_int64 lanzan un error RangeError.

El siguiente código demuestra cómo se pueden guardar valores enteros en un búfer de bytes:

buffer.put_int32(12345)
buffer.put_int64(123456789012345)

Nota

Si a put_int32 o put_int64 se les proporcionan argumentos de punto flotante, primero se fuerzan los argumentos a enteros y los enteros se escriben en el búfer de bytes.

Para guardar un valor de punto flotante de 64 bits en el búfer de bytes, utiliza put_double:

buffer.put_double(3.14159)

Para recuperar los datos serializados como una cadena de bytes, llama a to_s en el buffer:

buffer = BSON::ByteBuffer.new
buffer.put_string('testing')
socket.write(buffer.to_s)

Nota

ByteBuffer rastrea las posiciones de lectura y escritura por separado. No existe una manera de rebobinar el búfer para escribir. El método rewind solo afecta a la posición de lectura.

Para crear un ByteBuffer para lectura o deserialización desde BSON, instancia BSON::ByteBuffer con una string de bytes como argumento:

buffer = BSON::ByteBuffer.new(string)

Se pueden leer del búfer usando los siguientes métodos que corresponden a diferentes tipos de datos:

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 la lectura desde el comienzo de un búfer, se usa rewind:

buffer.rewind

Nota

ByteBuffer rastrea las posiciones de lectura y escritura por separado. El método rewind afecta solo la posición de lectura.

La siguiente lista proporciona las clases de Ruby que tienen representaciones en la especificación BSON y que tienen un método to_bson definido:

  • Object

  • Array

  • FalseClass

  • Float

  • Hash

  • Integer

  • BigDecimal

  • NilClass

  • Regexp

  • String

  • Symbol (deprecated)

  • Time

  • TrueClass

Además de los objetos principales de Ruby, BSON también proporciona algunos tipos especiales específicos de la especificación. Las siguientes secciones describen otros tipos que son compatibles con el driver.

Utiliza objetos BSON::Binary para almacenar datos binarios arbitrarios. Puedes construir objetos Binary a partir de cadenas binarias, como se muestra en el siguiente código:

BSON::Binary.new("binary_string")
# => <BSON::Binary:0x47113101192900 type=generic data=0x62696e6172795f73...>

Por defecto, los objetos Binary se crean con el subtipo binario BSON 0 (:generic). Puedes especificar explícitamente el subtipo para indicar que los bytes codifican un tipo particular de datos:

BSON::Binary.new("binary_string", :user)
# => <BSON::Binary:0x47113101225420 type=user data=0x62696e6172795f73...>

La siguiente lista proporciona las especificaciones válidas de subtipo:

  • :generic

  • :function

  • :old

  • :uuid_old

  • :uuid

  • :md5

  • :ciphertext

  • :column

  • :sensitive

  • :vector

  • :user

Puede usar los atributos data y type para recuperar los datos de un objeto Binary y su subtipo, como se muestra en el siguiente código:

binary = BSON::Binary.new("binary_string", :user)
binary.data
# => "binary_string"
binary.type
# => :user

Puedes comparar Binary objetos usando el operador <=>, que permite ordenar objetos que tengan el mismo subtipo binario. Para comparar Binary objetos, asegúrese de instalar la versión5.0.2 o posterior de la librería BSON.

Nota

Codificación BINARY

BSON::Binary Los objetos siempre almacenan los datos en codificación BINARY, independientemente de la codificación de la string pasada al constructor:

str = "binary_string"
str.encoding
# => #<Encoding:US-ASCII>
binary = BSON::Binary.new(str)
binary.data
# => "binary_string"
binary.data.encoding
# => #<Encoding:ASCII-8BIT>

Para crear un UUID BSON::Binary (subtipo binario 4) a partir de su representación string compatible con RFC 4122, utiliza el 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 convertir un UUID BSON::Binary en una representación compatible con RFC 4122, utiliza el 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"

Puedes especificar explícitamente la representación estándar de UUID en los métodos from_uuid y to_uuid:

binary = BSON::Binary.from_uuid(uuid_str, :standard)
binary.to_uuid(:standard)

Puede usar la representación :standard solo con un valor Binary de subtipo :uuid, no :uuid_old.

Los datos almacenados en objetos BSON::Binary de subtipo 3 (:uuid_old) pueden persistirse en alguno de los tres diferentes órdenes de bytes, dependiendo del controlador que los creó. Los órdenes de bytes son heredado de CSharp, heredado de Java y heredado de Python. El orden de bytes heredado de Python es el mismo que el orden de bytes estándar de la RFC 4122. Los órdenes de bytes heredados de CSharp y Java tienen algunos bytes en ubicaciones diferentes.

El objeto Binary que contiene un UUID heredado no codifica en qué formato se almacena el UUID. Por lo tanto, los métodos que convierten hacia y desde el formato UUID heredado toman el formato deseado, o representación, como su argumento. Una aplicación puede copiar objetos UUID heredados Binary sin saber en qué orden de bytes almacenan sus datos.

Se proporcionan los siguientes métodos para trabajar con UUIDs antiguos para garantizar la interoperabilidad con implementaciones existentes que almacenan datos en formatos antiguos de UUIDs. En nuevas aplicaciones, utiliza solo el formato :uuid (subtipo 4), que cumple con RFC 4122.

Para serializar un UUID heredado BSON::Binary, utiliza el método to_uuid y especifica la representación deseada. Las representaciones aceptadas son :csharp_legacy, :java_legacy y :python_legacy. Un UUID heredado BSON::Binary no puede convertirse a texto sin especificar una representación.

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 crear un UUID heredado BSON::Binary a partir de la representación en string del UUID, utiliza el método from_uuid y especifica la representación deseada:

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...>

Puede utilizar estos métodos para convertir de una representación a otra:

BSON::Binary.from_uuid('77665544-3322-1100-ffeeddccbbaa9988',:java_legacy).to_uuid(:csharp_legacy)
# => "33221100-5544-7766-8899aabbccddeeff"

A partir de bson v5.1, puede utilizar el tipo BSON::Vector para representar vectores de valores numéricos.

Puede crear un objeto BSON::Vector para almacenar valores de los siguientes tipos:

  • int8

  • float32

  • packed_bit

Puedes utilizar los atributos opcionales dtype y padding para especificar el tipo de datos del vector y el relleno de bits, respectivamente. BSON::Vector en sí mismo es un contenedor para almacenar los valores de tu arreglo, la información de tipo y la especificación de relleno. BSON::Vector objetos se serializan como arreglos normales en MongoDB.

El siguiente ejemplo demuestra cómo crear un objeto BSON::Vector:

BSON::Vector.new([ -0.0016261312, -0.028070757, -0.011342932 ], :float32)

Puede convertir BSON::Vector y objetos de arreglo a objetos BSON::Binary para que se serialicen como instancias binarias de vector BSON (subtipo 9). Utiliza el método BSON::Binary.from_vector, como se muestra en el siguiente código:

vector = BSON::Vector.new([ -0.0016261312, -0.028070757, -0.011342932 ], :float32)
BSON::Binary.from_vector(vector)

Usar el tipo BSON::Binary mejora la eficiencia de almacenamiento. Para obtener más información, consulta la especificación BSON.

Puedes convertir un BSON::Binary en un BSON::Vector utilizando el método BSON::Binary.as_vector.

Tip

Búsqueda vectorial de MongoDB

Para ver un ejemplo que utiliza el tipo BSON::Binary para realizar eficientes MongoDB Vector Search, consulte el Ejemplos de MongoDB Search.

Este tipo representa una string de código JavaScript:

BSON::Code.new("this.value = 5;")

Esta es una subclase de BSON::Document que proporciona accesores para la colección, el identificador y la base de datos de DBRef.

BSON::DBRef.new({"$ref" => "collection", "$id" => "id"})
BSON::DBRef.new({"$ref" => "collection", "$id" => "id", "database" => "db"})

Nota

El constructor BSON::DBRef valida el hash proporcionado y genera un ArgumentError si no es un DBRef válido. Los métodos BSON::ExtJSON.parse_obj y Hash.from_bson no generan un error si se pasa un DBRef no válido, y en su lugar analizan un Hash o deserializan un BSON::Document.

Nota

Todos los documentos BSON se deserializan en instancias de BSON::DBRef si son instancias válidas de DBRef, de lo contrario se deserializan en instancias de BSON::Document. Esto es cierto incluso cuando la invocación se realiza desde la clase 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

BSON::Document es una subclase de Hash que almacena todas las claves como strings, pero permite acceder a ellas utilizando claves tipo símbolo.

BSON::Document[:key, "value"]
BSON::Document.new

Nota

Todos los documentos BSON son deserializados en instancias de BSON::Document, o BSON::DBRef, si son instancias válidas de DBRef, incluso cuando la invocación se realiza desde la clase 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 un valor en BSON que siempre se compara como superior a cualquier otro valor:

BSON::MaxKey.new

BSON::MinKey representa un valor en BSON que siempre se compara como más bajo que cualquier otro valor:

BSON::MinKey.new

BSON::ObjectId representa un identificador único de objeto de 12 bytes:

BSON::ObjectId.new

BSON::Timestamp representa un tiempo con un valor de inicio e incremento:

BSON::Timestamp.new(5, 30)

BSON::Undefined representa un marcador de posición para un valor que no está definido:

BSON::Undefined.new

BSON::Decimal128 representa un valor de punto flotante basado en decimales de 128bits que puede emular el redondeo decimal con precisión exacta:

# Instantiate with a String
BSON::Decimal128.new("1.28")
# Instantiate with a BigDecimal
d = BigDecimal(1.28, 3)
BSON::Decimal128.new(d)

Los métodos BigDecimal#from_bson y BigDecimal#to_bson utilizan internamente los métodos equivalentes BSON::Decimal128. Esto lleva a algunas limitaciones en los valores de BigDecimal que se pueden serializar a BSON y en aquellos que se pueden deserializar a partir de valores de BSON decimal128 existentes.

Serializar instancias de BigDecimal como instancias de BSON::Decimal128 permite una mayor flexibilidad al realizar consultas y agregaciones en MongoDB. La siguiente lista describe las limitaciones en BigDecimal:

  • Decimal128 tiene un rango y precisión limitados, mientras que BigDecimal no tiene restricciones en cuanto a rango y precisión. Decimal128 tiene un valor máximo de aproximadamente 10^6145 y un valor mínimo de aproximadamente -10^6145, y tiene un máximo de 34 bits de precisión.

  • Decimal128 puede aceptar valores firmados de NaN, mientras que BigDecimal no. Todos los valores NaN firmados que se deserialicen en instancias BigDecimal serán sin signo.

  • Decimal128 mantiene los ceros finales al serializar a BSON y deserializar desde BSON. BigDecimal, sin embargo, no mantiene ceros finales y por lo tanto usar BigDecimal puede resultar en una falta de precisión.

Nota

En la librería BSON v5.0, Decimal128 se deserializa en BigDecimal por defecto. Para que los valores de Decimal128 en los documentos BSON se deserialicen en BSON::Decimal128, se puede establecer la opción mode: :bson al llamar a from_bson.

Algunos BSON types tienen representaciones especiales en JSON. La siguiente tabla describe el comportamiento de serialización para los tipos especificados cuando llamas a to_json sobre ellos.

Objeto BSON de Ruby
Representación JSON

BSON::Binary

{ "$binary" : "\x01", "$type" : "md5" }

BSON::Code

{ "$code" : "this.v = 5" }

BSON::CodeWithScope

{ "$code" : "this.v = value", "$scope" : { v => 5 }}

BSON::DBRef

{ "$ref" : "collection", "$id" : { "$oid" : "id" }, "$db" : "database" }

BSON::MaxKey

{ "$maxKey" : 1 }

BSON::MinKey

{ "$minKey" : 1 }

BSON::ObjectId

{ "$oid" : "4e4d66343b39b68407000001" }

BSON::Timestamp

{ "t" : 5, "i" : 30 }

Regexp

{ "$regex" : "[abc]", "$options" : "i" }

Times en Ruby tienen precisión en nanosegundos. Los tiempos en BSON tienen una precisión de milisegundos. Cuando se serializan instancias de Ruby Time a BSON o Extended JSON, los horarios se redondean al milisegundo más próximo.

Nota

Los valores de tiempo se redondean hacia abajo. Si el tiempo precede a la Unix epoch (enero 1, 1970 00:00:00 UTC), el valor absoluto del tiempo aumenta:

time = Time.utc(1960, 1, 1, 0, 0, 0, 999_999)
time.to_f
# => -315619199.000001
time.floor(3).to_f
# => -315619199.001

Debido a este comportamiento de redondeo, recomendamos que todos los cálculos de tiempo se realicen utilizando matemáticas de enteros, ya que la inexactitud de los cálculos de punto flotante podría producir resultados inesperados.

Nota

JRuby 9.2.11.0 redondea los tiempos anteriores a la Unix epoch hacia arriba en vez de hacia abajo. Para saber más sobre este comportamiento, consulta el problema relacionado en GitHub. La librería BSON corrige este comportamiento y reduce al mínimo los tiempos al serializar en JRuby.

BSON admite el almacenamiento de valores de tiempo como el número de segundos desde la Unix epoch. Las instancias Ruby DateTime pueden serializarse a BSON, pero cuando el BSON se deserializa, estos momentos se retornarán como instancias de Time.

La clase DateTime en Ruby soporta calendarios no gregorianos. Cuando las instancias DateTime no-gregorianas son serializadas, primero se convierten al calendario gregoriano, y la fecha respectiva en el calendario gregoriano se almacena en la base de datos.

BSON admite el almacenamiento de valores de tiempo como el número de segundos desde la Unix epoch. Las instancias Ruby Date pueden serializarse a BSON, pero cuando el BSON se deserializa, estos momentos se retornarán como instancias de Time.

Cuando las instancias de Date se serializan, el valor de tiempo empleado es la medianoche del Date en UTC.

Tanto MongoDB como Ruby ofrecen soporte para trabajar con expresiones regulares, aunque utilizan motores de expresiones regulares diferentes. A continuación se detallan las diferencias entre las expresiones regulares de Ruby y las expresiones regulares de MongoDB.

MongoDB utiliza expresiones regulares compatibles con Perl implementadas mediante el uso de la librería PCRE. Expresiones regulares de Ruby se implementan mediante el uso del motor de expresiones regulares Onigmo, que es una bifurcación de la librería Oniguruma.

Las dos implementaciones de expresiones regulares, generalmente, proporcionan una funcionalidad equivalente, pero tienen varias diferencias de sintaxis importantes, que se describen en las siguientes secciones.

No existe una forma sencilla de convertir programáticamente una expresión regular de PCRE en la expresión regular equivalente de Ruby, ya que actualmente no hay un enlace de Ruby para PCRE.

Tanto las expresiones regulares de Ruby como las de PCRE admiten modificadores. Estos también se llaman "opciones" en contextos de Ruby y "banderas" en contextos de PCRE. El significado de los modificadores s y m difiere en Ruby y PCRE de las siguientes maneras:

  • Ruby no tiene el modificador s. En cambio, el modificador m de Ruby cumple la misma función que el modificador s de PCRE, que es hacer que el punto (.) coincida con cualquier carácter, incluidas las nuevas líneas. La documentación de Ruby se refiere al modificador m como habilitador del modo multilínea.

  • Ruby siempre opera en el equivalente del modo multinea de PCRE, activado por el modificador m en expresiones regulares PCRE. En Ruby, el ancla ^ siempre se refiere al comienzo de línea y el ancla $ siempre se refiere al final de línea.

Cuando escriba expresiones regulares destinadas a utilizarse tanto en entornos Ruby como en entornos PCRE, incluidos el Servidor MongoDB y la mayoría de los demás controladores de MongoDB, evite utilizar los anclajes ^ y $. Las siguientes secciones proporcionan soluciones alternativas y recomendaciones para elaborar expresiones regulares portátiles que puedan utilizarse en múltiples contextos.

En las expresiones regulares de Ruby, el ancla ^ siempre se refiere al principio de la línea. En las expresiones regulares de PCRE, el anclaje ^ se refiere al principio de la entrada por defecto, y la bandera m cambia su significado al principio de la línea.

Tanto las expresiones regulares de Ruby como las de PCRE admiten el ancla \A para referirse al principio de la entrada, independientemente de los modificadores. Las siguientes sugerencias permiten escribir expresiones regulares portátiles:

  • Usa el ancla \A para referirse al inicio de la entrada.

  • Utiliza el anclaje ^ para hacer referencia al inicio de la línea si estableces el indicador m en las expresiones regulares de PCRE. Alternativamente, utiliza uno de los siguientes constructos que funcionan independientemente de los modificadores:

    • (?:\A|(?<=\n)): gestiona LF y CR+LF finales de línea

    • (?:\A|(?<=[\r\n]))Gestiona los finales de línea CR, LF y CR+LF

En las expresiones regulares de Ruby, el ancla $ siempre se refiere al final de la línea. En expresiones regulares PCRE, el ancla $ se refiere al final de la entrada por defecto y la bandera m cambia su significado al final de la línea.

Tanto las expresiones regulares de Ruby como las de PCRE admiten el ancla \z para referirse al final de la entrada, independientemente de los modificadores.

Las siguientes sugerencias le permiten escribir expresiones regulares portátiles:

  • Utilice el \z ancla para referirse al final de la entrada.

  • Utiliza el anclaje $ para hacer referencia al inicio de la línea si estableces el indicador m en las expresiones regulares de PCRE. Alternativamente, utiliza uno de los siguientes constructos que funcionan independientemente de los modificadores:

    • (?:\z|(?=\n)): gestiona LF y CR+LF finales de línea

    • (?:\z|(?=[\n\n]))Gestiona los finales de línea CR, LF y CR+LF

Dado que no existe una forma sencilla de convertir programáticamente una expresión regular PCRE en la expresión regular Ruby equivalente, la librería BSON proporciona la clase BSON::Regexp::Raw para almacenar expresiones regulares PCRE.

Se pueden crear instancias BSON::Regexp::Raw utilizando el texto de expresión regular como string y modificadores opcionales PCRE:

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">

El módulo BSON::Regexp está incluido en la clase Ruby Regexp, de manera que se puede omitir el prefijo BSON:::

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">

El siguiente código convierte una expresión regular de Ruby en una instancia de BSON::Regexp::Raw:

regexp = /^Hello.world/
bson_regexp = BSON::Regexp::Raw.new(regexp.source, regexp.options)
# => #<BSON::Regexp::Raw:0x000055df62e42d60 @pattern="^Hello.world", @options=0>

El constructor BSON::Regexp::Raw acepta tanto las opciones numéricas de Ruby como las cadenas de modificadores PCRE.

Para convertir una expresión regular BSON a una expresión regular Ruby, llama el método compile en la expresión regular BSON:

bson_regexp = BSON::Regexp::Raw.new("^hello.world", "s")
bson_regexp.compile
# => /^hello.world/m
bson_regexp = BSON::Regexp::Raw.new("^hello.world", "")
bson_regexp.compile
# => /^hello.world/
bson_regexp = BSON::Regexp::Raw.new("^hello.world", "m")
bson_regexp.compile
# => /^hello.world/

El modificador PCRE s se convirtió en el modificador Ruby m en el primer ejemplo del código anterior, y los dos últimos ejemplos se convirtieron en la misma expresión regular a pesar de que las expresiones regulares BSON originales tenían significados diferentes.

Cuando una expresión regular BSON utiliza los anclajes no portátiles ^ y $, su conversión a una expresión regular de Ruby puede cambiar su significado:

BSON::Regexp::Raw.new("^hello.world", "").compile =~ "42\nhello world"
# => 3

Cuando una expresión regular de Ruby se convierte en una expresión regular de BSON, por ejemplo, como parte de una query, la expresión regular de BSON siempre tiene el modificador m configurado, reflejando el comportamiento de los anclajes ^ y $ en las expresiones regulares de Ruby.

Tanto las expresiones regulares de Ruby como las de BSON implementan el método to_bson para serializar a 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"

Tanto las clases Regexp como BSON::Regexp::Raw implementan el método de clase from_bson que deserializa una expresión regular desde un búfer de bytes BSON. Los métodos de ambas clases devuelven una instancia BSON::Regexp::Raw que debe convertirse en una expresión regular de Ruby usando el método compile como se describe en el código anterior.

El siguiente código demuestra cómo utilizar el método from_bson para deserializar una expresión regular:

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/

Los documentos BSON preservan el orden de las claves porque los documentos se almacenan como listas de pares clave-valor. Los hashes en Ruby también preservan el orden de las claves, por lo que el orden de las claves especificado en tu aplicación se mantiene cuando serializas un hash a un documento BSON, y cuando deserializas un documento BSON en un hash.

La especificación BSON permite que los documentos BSON tengan claves duplicadas, porque los documentos se almacenan como listas de pares clave-valor. Evita crear documentos que contengan claves duplicadas, porque el comportamiento del MongoDB Server no está definido cuando un documento BSON contiene claves duplicadas.

En Ruby, los hash no pueden tener claves duplicadas. Al serializar hashes de Ruby en documentos BSON, no se generan claves duplicadas.

Debido a que las claves en los documentos BSON siempre se almacenan como strings, especificar la misma clave como string y como símbolo en Ruby solo conserva la especificación más reciente:

BSON::Document.new(test: 1, 'test' => 2)
# => {"test"=>2}

Al cargar un documento BSON con claves duplicadas, el último valor para una clave duplicada sobrescribe los valores anteriores para la misma clave.

Volver

Agregación