Join us at MongoDB.local London on 7 May to unlock new possibilities for your data. Use WEB50 to save 50%.
Register now >
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.

Puede instalar la biblioteca 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 biblioteca BSON es compatible con MRI v2.5 y posteriores y JRuby v9.2 y posteriores.

La serialización de las clases definidas en Soporte Activo, como TimeWithZone, no se carga de forma predeterminada para evitar una fuerte dependencia de BSON con Soporte Activo. Al usar BSON en una aplicación que también usa Soporte Activo, debe requerir la compatibilidad del código de Soporte Activo:

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 escribir bytes sin procesar en el búfer de bytes sin transformaciones, utilice los métodos put_byte y put_bytes. Cada método toma una cadena de bytes como argumento y la copia en el búfer. El método put_byte exige que el argumento sea una cadena de longitud 1. put_bytes acepta cadenas de cualquier longitud. 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 escritura descritos en las siguientes secciones escriben objetos de tipos específicos en la especificación BSON. El tipo indicado por el nombre del método prevalece sobre el tipo del argumento. Por ejemplo, si se pasa un valor de punto put_int32 flotante a, se convierte a un entero y el controlador 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)

Puede leer desde el búfer utilizando 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 Ruby que tienen representaciones en la especificación BSON y 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 compatibles con el controlador.

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 de subtipos válidas:

  • :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, utilice 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 utilizar la representación :standard solo con un valor Binary del 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.

Los siguientes métodos para trabajar con UUID heredados se proporcionan para la interoperabilidad con implementaciones existentes que almacenan datos en formatos UUID heredados. En aplicaciones nuevas, utilice únicamente el formato :uuid (subtipo 4), que cumple con la 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 de cadena del UUID, utilice el método from_uuid y especifique 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)

El uso del BSON::Binary tipo mejora la eficiencia del almacenamiento. Para obtener más información, consulte 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 consultas de búsqueda de MongoDB.

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 dado 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 cadenas, pero permite el acceso a ellas mediante claves de símbolos.

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

Nota

Todos los documentos BSON se deserializan 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 y de 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" }

Los tiempos en Ruby tienen una precisión de nanosegundos. Los tiempos en BSON tienen una precisión de milisegundos. Al serializar instancias de Ruby Time en BSON o JSON extendido, los tiempos se redondean al milisegundo más cercano.

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 de época pre-Unix hacia arriba en lugar de hacia abajo. Para obtener más información sobre este comportamiento, consulte el problema de GitHub relacionado. La biblioteca BSON corrige este comportamiento y reduce 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 usando la librería PCRE. Expresiones regulares de Ruby que se implementan mediante el motor de expresiones regulares Onigmo, que es una bifurcación de la biblioteca 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 las expresiones regulares de PCRE, el ancla $ se refiere al final de la entrada por defecto y el indicador m cambia su significado a fin de 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 en una expresión regular Ruby, llame al 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 Ruby como BSON implementan el método to_bson para serializar en 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, ya que se almacenan como listas de pares clave-valor. Evite crear documentos que contengan claves duplicadas, ya que el comportamiento de 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