From ae7ea9ca9066735018dd8a42495fa0062efc4173 Mon Sep 17 00:00:00 2001 From: Jordan Smith Date: Tue, 19 Nov 2024 07:52:35 -0800 Subject: [PATCH 1/8] Add field types and behaviors --- source/data-modeling.txt | 14 +- source/data-modeling/field-behaviors.txt | 289 +++++++++++ source/data-modeling/field-types.txt | 557 +++++++++++++++++++++ source/includes/field-types/field-types.rb | 390 +++++++++++++++ 4 files changed, 1245 insertions(+), 5 deletions(-) create mode 100644 source/data-modeling/field-behaviors.txt create mode 100644 source/data-modeling/field-types.txt create mode 100644 source/includes/field-types/field-types.rb diff --git a/source/data-modeling.txt b/source/data-modeling.txt index 45c78994..676eed48 100644 --- a/source/data-modeling.txt +++ b/source/data-modeling.txt @@ -11,11 +11,15 @@ Model Your Data .. meta:: :keywords: ruby framework, odm, model, class, query -.. .. toctree:: -.. :caption: Data Modeling -.. -.. Documents +.. toctree:: + :caption: Data Modeling + + Field Types + Field Behaviors In this section, you can learn how to model data in {+odm+}. -.. - :ref:``: Learn how to ... +- :ref:`mongoid-field-types`: Learn about the field types that you can use in + {+odm+} to define the schema for your MongoDB documents. +- :ref:`mongoid-field-behaviors`: Learn how to customize the behaviors of fields + in {+odm+} to meet your application requirements. \ No newline at end of file diff --git a/source/data-modeling/field-behaviors.txt b/source/data-modeling/field-behaviors.txt new file mode 100644 index 00000000..198ac050 --- /dev/null +++ b/source/data-modeling/field-behaviors.txt @@ -0,0 +1,289 @@ +.. _mongoid-field-behaviors: + +========================= +Customize Field Behaviors +========================= + +.. facet:: + :name: genre + :values: reference + +.. meta:: + :keywords: customize, attributes, optimize, model, configure, code example + +.. contents:: On this page + :local: + :backlinks: none + :depth: 1 + :class: singlecol + +In this guide, you can learn how to customize the behavior of fields in {+odm+} models. + +Specify Default Values +---------------------- + +You can configure fields to have default values by using the ``default`` option. +Default field values can be either fixed or processed (``Proc``) values. + +The following example specifies a fixed default value for the ``fulfill_by`` +field: + +.. literalinclude:: /includes/field-types/field-types.rb + :language: ruby + :start-after: # start-field-default + :end-before: # end-field-default + +The following example specifies a ``Proc`` default value for the ``fulfill_by`` +field: + +.. literalinclude:: /includes/field-types/field-types.rb + :language: ruby + :start-after: # start-field-default-processed + :end-before: # end-field-default-processed + +.. note:: + + The driver evaluates default values that are not ``Proc`` instances when the + class loads. The driver evaluates ``Proc`` default values when the document is + instantiated. The following default field values do not produce equivalent outcomes: + + .. code-block:: ruby + + # Time.now is set to the time the class is loaded + field :submitted_at, type: Time, default: Time.now + + # Time.now is set to the time the document is instantiated + field :submitted_at, type: Time, default: ->{ Time.now } + +You can set a default value that depends on the document's state by using the +``self`` keyword inside of a ``Proc`` instance. The following example sets the +``fulfill_by`` default value to depend on the state of the ``submitted_at`` +field: + +.. literalinclude:: /includes/field-types/field-types.rb + :language: ruby + :start-after: # start-field-default-self + :end-before: # end-field-default-self + +By default, {+odm+} applies ``Proc`` default values after setting and +initializing all other attributes. To apply the default before setting the other +attributes, set the ``pre_processed`` option to ``true``, as shown in the +following example: + +.. literalinclude:: /includes/field-types/field-types.rb + :language: ruby + :start-after: # start-field-default-pre-processed + :end-before: # end-field-default-pre-processed + +.. tip:: + + You should always set the ``pre-processed`` option to ``true`` if you want to set a + default ``Proc`` value for the ``_id`` field. + +Specify Storage Names +--------------------- + +You can specify a separate field name to store in the database, while still +referring to the field by its original name in your application. This can be +useful to save storage space, because MongoDB stores all field information along +with every document. + +You can set an alternate storage name by using the ``as:`` keyword. The +following example creates a field called ``name`` that {+odm+} stores in the database as +``n``: + +.. literalinclude:: /includes/field-types/field-types.rb + :language: ruby + :start-after: # start-field-as + :end-before: # end-field-as + +{+odm+} stores the ``name`` field as ``"n"``, but you can still refer to the field as +``name`` in your application. + +Field Aliases +------------- + +You can create an alias for your field by using the ``alias_attribute`` option. +Specifying an alias does not change how {+odm+} stores the field in the +database, but it allows you to refer to the field by a different name in your +application. + +The following example specifies an alias for the ``name`` field: + +.. literalinclude:: /includes/field-types/field-types.rb + :language: ruby + :start-after: # start-field-alias + :end-before: # end-field-alias + +To remove a field alias, you can use the ``unalias_attribute`` option. The +following example removes the alias for the ``name`` field: + +.. literalinclude:: /includes/field-types/field-types.rb + :language: ruby + :start-after: # start-field-unalias + :end-before: # end-field-unalias + +You can also use ``unalias_attribute`` to remove the predefined ``id`` alias from the +``_id`` field. This can be used to store different values in the ``_id`` field +and an ``id`` field. + +Field Redefinition +------------------ + +By default, {+odm+} allows you to redefine fields on a model. To raise an error +when a field is redefined, set the ``duplicate_fields_exception`` configuration +option in your ``mongoid.yml`` file to ``true``. + +If the ``duplicate_fields_exception`` option is set to ``true``, you can still +redefine a specific field by setting the ``overwrite`` option to ``true`` when +you define the field. The following example defines the ``name`` field, and then +redefines the field by using the ``overwrite`` option: + +.. literalinclude:: /includes/field-types/field-types.rb + :language: ruby + :start-after: # start-field-overwrite + :end-before: # end-field-overwrite + +Custom ID Field +--------------- + +By default, {+odm+} defines the ``_id`` field on documents to contain a +``BSON::ObjectId`` value that {+odm+} generates automatically. You can customize +the type or the specify the default value of the ``_id`` field by specifying it in your +model. + +The following example creates a ``Band`` class with a custom ``_id`` field: + +.. literalinclude:: /includes/field-types/field-types.rb + :language: ruby + :start-after: # start-custom-id + :end-before: # end-custom-id + +You can omit the default value for the ``_id`` field. If you don't specify a +default value for the field, {+odm+} persists the value without an ``_id`` +value. If the document is a top-level document, the MongoDB server automatically +assigns an ``_id`` value to the document. However, if the document is an +embedded document, the server does not assign an ``_id`` value. + +When you don't specify a value for the ``_id`` field, {+odm+} does not retrieve +the automatically assigned value from the server. Because of this, you cannot +retrieve the document from the database by using the ``_id`` value. + +Uncastable Values +----------------- + +A value is considered **uncastable** if it cannot be converted to the specified +field type. For example, an array is considered uncastable when assigned to an +``Integer`` field. + +In v8.0 and later, {+odm+} assigns ``nil`` to values that are uncastable. The +original uncastable value is stored in the ``attributes_before_type_cast`` hash +with their field names. + +Custom Getters and Setters +-------------------------- + +You can override the default getter and setter methods for a field by specifying +a method with the same name as the field and calling the ``read_attribute`` or +``write_attribute`` method to operate on the raw attribute value. + +The following example creates a custom getter and setter for the ``name`` field +of a ``Person`` class: + +.. literalinclude:: /includes/field-types/field-types.rb + :language: ruby + :start-after: # start-custom-getter-setter + :end-before: # end-custom-getter-setter + +Read-Only Attributes +-------------------- + +You can specify a field to read-only by specifying the ``attr_readonly`` option. +This allows you to create documents with the attributes, but not update them. + +The following example creates a ``Band`` class and specifies the ``name`` field +as read-only: + +.. literalinclude:: /includes/field-types/field-types.rb + :language: ruby + :start-after: # start-read-only + :end-before: # end-read-only + +If you call a mass-update method, such as ``update_attributes``, and pass in a +read-only field, {+odm+} ignores the read-only field and updates all others. If +you attempt to explicitly update a read-only field, {+odm+} raises a +``ReadonlyAttribute`` exception. + +.. note:: + + Calls to atomic persistence operators, such as ``bit`` and ``inc``, still + persist changes to the read-only field. + +Localize Fields +--------------- + +{+odm+} supports localized fields by using the `i18n gem +`__. When you localize a field, {+odm+} +stores the field as a hash of locale keys and values. Accessing the fields +behaves in the same way as a string value. You can localize fields of any field +type. + +The following example creates a ``Product`` class with a localized ``review`` +field: + +.. literalinclude:: /includes/field-types/field-types.rb + :language: ruby + :start-after: # start-localized-field + :end-before: # end-localized-field + +You can get and set all translations at once by calling the ``_translations`` +method: + +.. literalinclude:: /includes/field-types/field-types.rb + :language: ruby + :start-after: # start-localized-translations + :end-before: # end-localized-translations + +You can specify fallbacks for localized fields by enabling the `i18n fallbacks +`__ feature. + +Enable fallbacks in a Rails application by setting the ``config.i18n.fallbacks`` +configuration setting in your environment and setting the fallback languages: + +.. literalinclude:: /includes/field-types/field-types.rb + :language: ruby + :start-after: # start-localized-fallbacks + :end-before: # end-localized-fallbacks + +Enable fallbacks in non-Rails applications by including the module into the i18n +backend and setting the fallback languages: + +.. literalinclude:: /includes/field-types/field-types.rb + :language: ruby + :start-after: # start-localized-fallbacks-non-rails + :end-before: # end-localized-fallbacks-non-rails + +After enabling fallbacks, if an active language does not have have a +translation, it is looked up in the specified fallback language. + +You can disable fallback languages for a specified field by setting the +``fallbacks`` option to false when defining the field: + +.. literalinclude:: /includes/field-types/field-types.rb + :language: ruby + :start-after: # start-localized-no-fallbacks + :end-before: # end-localized-no-fallbacks + +When querying localized fields, {+odm+} automatically alters the query criteria +to match the current locale. The following example queries the ``Product`` class +for a review in the ``en`` locale: + +.. literalinclude:: /includes/field-types/field-types.rb + :language: ruby + :start-after: # start-localized-query + :end-before: # end-localized-query + +.. note:: + + If you want to query extensively on localized fields, we recommend indexing + each locale that you want to query on. diff --git a/source/data-modeling/field-types.txt b/source/data-modeling/field-types.txt new file mode 100644 index 00000000..f551fc1f --- /dev/null +++ b/source/data-modeling/field-types.txt @@ -0,0 +1,557 @@ +.. _mongoid-field-types: + +=========== +Field Types +=========== + +.. facet:: + :name: genre + :values: reference + +.. meta:: + :keywords: fields, data types, type conversion, code example + +.. contents:: On this page + :local: + :backlinks: none + :depth: 2 + :class: singlecol + +Overview +-------- + +In this guide, you can learn about the field types that you can use in +{+odm+} to define the schema for your MongoDB documents. + +MongoDB uses :manual:`BSON types ` to represent the type of +data stored in document fields. To use that data in a {+odm+} application, +{+odm+} must convert the BSON types to Ruby types at runtime. For example, +when retrieving a document from the database, {+odm+} translates a BSON field +with the type ``double`` to use the Ruby ``Float`` type. It then translates that +field back to a BSON ``double`` when saving the document to the database. + +.. note:: + + Changing the field definition in a model class does not automatically alter + the data stored in the database. To change the data type of a field in the + database, you must re-save the data after the change. + +Field Types +----------- + +You can define the field type for fields in a model class by using the ``field`` +and ``type`` macros. The following example defines the fields of a ``Person`` +class: + +.. literalinclude:: /includes/field-types/field-types.rb + :language: ruby + :start-after: # start-define-fields + :end-before: # end-define-fields + +The following list describes the field types that you can use in {+odm+}: + +- ``Array`` +- ``Bson::Binary`` +- ``BigDecimal`` +- ``Mongoid::Boolean`` or ``"Boolean"`` +- ``Date`` +- ``DateTime`` +- ``Float`` +- ``Hash`` +- ``Integer`` +- ``Object`` +- ``Bson::ObjectId`` +- ``Range`` +- ``Regexp`` +- ``Set`` +- ``String`` +- ``Mongoid::StringifiedSymbol`` +- ``Time`` +- ``ActiveSupport::TimeWithZone`` + +.. note:: + + {+odm+} does not support the ``BSON::Int64`` or ``BSON::Int32`` types. + Defining a field as one of these types will save to the database correctly, + but querying the field will return the fields as an ``Integer``. + + Similarly, querying fields of the ``BSON::Decimal128`` type will return the + fields as a ``BigDecimal`` in BSON versions 5 or later. + +Untyped Fields +~~~~~~~~~~~~~~ + +If you don't specify a type for a field, {+odm+} defaults to the ``Object`` +type. An untyped field can store values of any type that is directly +serializable to BSON. You can leave a field untyped if the field might contain +different types of data, or if the type of the field's value is not known. + +The following example defines a ``Person`` class with an untyped field: + +.. literalinclude:: /includes/field-types/field-types.rb + :language: ruby + :start-after: # start-define-untyped + :end-before: # end-define-untyped + +The type of the ``preferences`` field is ``Object`` and will vary depending on +the type of data stored in that field. The following example saves data into the +``preferences`` field in two different ways: + +.. literalinclude:: /includes/field-types/field-types.rb + :language: ruby + :start-after: # start-untyped + :end-before: # end-untyped + +Because {+odm+} doesn't perform any type conversions on untyped fields when +reading from the database, values that require special handling might now work +correctly in an untyped field. The following BSON data types should not be +stored in untyped fields: + +- ``Date``: Returns as ``Time`` in untyped fields +- ``DateTime``: Returns as ``Time`` in untyped fields +- ``Range``: Returns as ``Hash`` in untyped fields + +StringifiedSymbol +~~~~~~~~~~~~~~~~~ + +Use the ``StringifiedSymbol`` field type to store values that should be exposed +as symbols to Ruby applications. ``StringifiedSymbol`` allows you to use symbols +while ensuring interoperability with other drivers. This type stores all data on +the database as strings, and converts the strings to symbols when read by the +application. Values that cannot be directly converted to symbols, such as +integers and arrays, are converted into strings and then into symbols. + +The following example stores a ``status`` field as a ``StringifiedSymbol`` and +demonstrates how the field is stored and returned: + +.. literalinclude:: /includes/field-types/field-types.rb + :language: ruby + :start-after: # start-stringified-symbol + :end-before: # end-stringified-symbol + +Hash +~~~~ + +You can store Hash data in a field by using the ``Hash`` type. When you specify +a field as a ``Hash``, adhere to the MongoDB :manual:`Naming +Restrictions ` to ensure that the values +store properly in the database. + +The following example creates a ``Person`` class and specifies the ``url`` field +as a ``Hash``. + +.. literalinclude:: /includes/field-types/field-types.rb + :language: ruby + :start-after: # start-hash + :end-before: # end-hash + +Time +~~~~ + +You can store values as ``Time`` instances by using the ``Time`` field value. +``Time`` fields are stored in the time zone configured for your application. To +learn more about configuring time zones, see the :ref:`Time Zones ` +guide. +.. TODO: Update Time Zones guide ref if it's changed during standardization + +The following example creates a ``Voter`` class and specifies the +``registered_at`` property as a ``Time`` field: + +.. literalinclude:: /includes/field-types/field-types.rb + :language: ruby + :start-after: # start-time + :end-before: # end-time + +.. note:: + + Storing a ``Date`` or ``DateTime`` value in a field specified as ``Time`` + converts the value to ``Time`` when assigned. If you store a string in a + ``Time`` field, {+odm+} parses the string by using the ``Time.pars`` method. + +Date +~~~~ + +You can store the following value types in a field specified as a ``Date``: + +- ``Date``: Stores the value as provided. +- ``Time``: Stores the date portion of the value in the value's time zone. +- ``DateTime``: Stores the date portion of the value in the value's time zone. +- ``ActiveSupport::TimeWithZone``: Stores the date portion of the value in the + value's time zone. +- ``String``: Stores the date specified in the string. +- ``Integer``: Takes the value as if it is a UTC timestamp and + converts it into your application's configured time zone. {+odm+} then stores + the date taken from that timestamp. +- ``Float``: Takes the value as if it is a UTC timestamp and + converts it into your application's configured time zone. {+odm+} then stores + the date taken from that timestamp. + +Because converting a ``Time`` or ``DateTime`` discards the time portion, we +recommend explicitly converting ``String``, ``Time``, and ``DateTime``, objects +to ``Date`` before assigning them to the field. + +.. note:: + + When a database contains a string value for a ``Date`` field, the driver + parses the value by using the ``Time.parse`` method, then discards the time + portion. ``Time.parse`` considers values without time zones to be in local + time. + +DateTime +~~~~~~~~ + +When you assign a value to, or query on a ``DateTime`` field, {+odm+} converts +the value to a UTC ``Time`` value before sending it to the MongoDB server. +{+odm+} saves the value with the time zone embedded in the ``DateTime`` object. When +you retrieve the value, {+odm+} converts the UTC time to the time zone +configured for your application. + +The following example creates a ``Ticket`` class and specifies the ``opened_at`` +field as a ``DateTime`` field: + +.. literalinclude:: /includes/field-types/field-types.rb + :language: ruby + :start-after: # start-datetime + :end-before: # end-datetime + +If you save an integer or float value to a ``DateTime`` field, the value is treated as +a Unix timestamp in UTC. The following example saves an integer value to the +``opened_at`` field: + +.. literalinclude:: /includes/field-types/field-types.rb + :language: ruby + :start-after: # start-datetime-int + :end-before: # end-datetime-int + +If you save a string value to a ``DateTime`` field, {+odm+} saves the ticket +with the time zone specified. If a time zone is not specified, {+odm+} saves the +value by using the default {+odm+} timezone: + +.. literalinclude:: /includes/field-types/field-types.rb + :language: ruby + :start-after: # start-datetime-string + :end-before: # end-datetime-string + +.. note:: + + {+odm+} parses string values into ``DateTime`` by using the ``Time.parse`` + method, which considers values without time zones to be in local time. + +Timestamps +~~~~~~~~~~ + +You can include timestamp fields by including the ``Mongoid::Timestamps`` +module when you create your class. Timestamp fields include the following +fields: + +- ``created_at``: Stores the time the document was created. +- ``updated_at``: Stores the time the document was last updated. + +The following example creates a ``Person`` class with timestamp fields: + +.. literalinclude:: /includes/field-types/field-types.rb + :language: ruby + :start-after: # start-timestamps + :end-before: # end-timestamps + +You can also choose to include only the ``created_at`` or ``updated_at`` fields +by including only the ``Created`` or ``Updated`` modules. The following example +creates a ``Person`` class with only the ``created_at`` field, and a ``Post`` +class with only the ``updated_at`` field: + +.. literalinclude:: /includes/field-types/field-types.rb + :language: ruby + :start-after: # start-timestamps-specific + :end-before: # end-timestamps-specific + +You can shorten the timestamp field names to ``c_at`` and ``u_at`` by setting +the ``::Short`` option when including the module: + +.. literalinclude:: /includes/field-types/field-types.rb + :language: ruby + :start-after: # start-timestamps-short + :end-before: # end-timestamps-short + +You can disable creating the timestamp field for specific operations by calling +the ``timeless`` method on the method call. The following example disables the +timestamps for the ``save`` operation: + +.. literalinclude:: /includes/field-types/field-types.rb + :language: ruby + :start-after: # start-timestamps-disable + :end-before: # end-timestamps-disable + +Regexp +~~~~~~ + +You can store regular expressions in a field by using the ``Regexp`` type. + +While MongoDB uses `Perl Compatible Regular Expressions (PCRE) `__, +{+odm+} uses Ruby's `Onigmo `__. PCRE and +Onigmo provide generally similar functionality, but there are several syntax +differences. For example, Onigmo uses ``\A`` and ``\z`` to match the beginning and +end of a string, while PCRE uses ``^`` and ``$``. + +When you declare a field as a ``Regexp``, {+odm+} converts Ruby regular +expressions to BSON regular expressions when storing the result into your +database. The database returns the field as a ``Bson::Regexp::Raw`` instance. +You can use the ``compile`` method on ``BSON::Regexp::Raw`` instances to convert +the data back to a Ruby regular expression. + +The following example creates a ``Token`` class and specifies the ``pattern`` +field as a ``Regexp``: + +.. literalinclude:: /includes/field-types/field-types.rb + :language: ruby + :start-after: # start-regexp + :end-before: # end-regexp + +.. important:: + + Converting a BSON regular expression to a Ruby regular expression might + produce a different regular expression than the original. This difference is + due to the differences between the syntax Onigmo and PCRE. + +BigDecimal +~~~~~~~~~~ + +You can use the ``BigDecimal`` type to store numbers with increased precision. +{+odm+} stores ``BigDecimal`` values in two different ways, depending on the +value of ``Mongoid.map_big_decimal_to_decimal128``: + +- If set to ``True``, {+odm+} stores ``BigDecimal`` values as BSON ``Decimal128`` + values. +- If set to ``False`` (default), {+odm+} stores ``BigDecimal`` values as a string. + +Consider the following limitations when setting the +``Mongoid.map_big_decimal_to_decimal128`` option to true: + +- ``Decimal128`` has a limited range and precision. + ``Decimal128`` has a maximum value of approximately ``10^6145`` and a minimum + of approximately ``-10^6145``, with a maximum of 34 bits of precision. If you + are storing values that are outside of these limits, we recommend storing them + as a string instead. +- ``Decimal128`` accepts signed ``NaN`` values, but ``BigDecimal`` does not. + Retrieving signed ``NaN`` ``Decimal128`` values from the database as + ``BigDecimal`` returns the value unsigned. +- ``Decimal128`` maintains trailing zeroes, but ``BidDecimal`` does not. + Because of this, retrieving ``Decimal128`` values from the database as + ``BigDecimal`` might result in a loss of precision. + +.. note:: + + When you set the ``Mongoid.map_big_decimal_to_decimal128`` option to ``false`` + and store a ``BigDecimal`` into an untyped field, you cannot query the field + as a ``BigDecimal``. Because the value is stored as a string, querying + the untyped field for a ``BigDecimal`` value does not find the value in the + database. To find the value, you must first convert the query value to a string. + + You can avoid this issue by specifying the field as a ``BigDecimal`` type, + rather than untyped. + +Specify Fields with Strings or Symbols +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +You can use strings or symbols to specify certain field types in {+odm+}, instead of +using their class names. The following example specifies the ``state`` field by +using the class name, a string, and a symbol: + +.. code-block:: ruby + + class Order + include Mongoid::Document + + # Class Name + field :state, type: Integer + + # Symbol + field :state, type: :integer + + # String + field :state, type: "integer" + end + +The following field types can be specified as a string or a symbol: + +.. list-table:: + :header-rows: 1 + + * - Class Name + - Symbol + - String + + * - ``Array`` + - ``:array`` + - ``"Array"`` + + * - ``BigDecimal`` + - ``:big_decimal`` + - ``"BigDecimal"`` + + * - ``BSON::Binary`` + - ``:binary`` + - ``"BSON::Binary"`` + + * - ``Mongoid::Boolean`` + - ``:boolean`` + - ``"Mongoid::Boolean"`` + + * - ``Date`` + - ``:date`` + - ``"Date"`` + + * - ``DateTime`` + - ``:date_time`` + - ``"DateTime"`` + + * - ``Float`` + - ``:float`` + - ``"Float"`` + + * - ``Hash`` + - ``:hash`` + - ``"Hash"`` + + * - ``Integer`` + - ``:integer`` + - ``"Integer"`` + + * - ``BSON::ObjectId`` + - ``:object_id`` + - ``"BSON::ObjectId"`` + + * - ``Range`` + - ``:range`` + - ``"Range"`` + + * - ``Regexp`` + - ``:regexp`` + - ``"Regexp"`` + + * - ``Set`` + - ``:set`` + - ``"Set"`` + + * - ``String`` + - ``:string`` + - ``"String"`` + + * - ``StringifiedSymbol`` + - ``:stringified_symbol`` + - ``"StringifiedSymbol"`` + + * - ``Symbol`` + - ``:symbol`` + - ``"Symbol"`` + + * - ``Time`` + - ``:time`` + - ``"Time"`` + +Custom Field Types +~~~~~~~~~~~~~~~~~~ + +You can create custom field types and define how {+odm+} serializes and +deserializes them. To create a custom field type, define a class that +implements the following methods: + +- ``mongoize``, which takes an instance of your custom type and converts it to + an object that MongoDB can store. +- ``demongoize``, which takes an object from MongoDB and converts it to an + instance of your custom type. +- ``evolve``, which takes an instance of your custom type and converts is to a + criteria that MongoDB can use to query the database. + +The following example creates a custom field type called ``Point`` and +implements the preceding methods: + +.. literalinclude:: /includes/field-types/field-types.rb + :language: ruby + :start-after: # start-custom-field-type + :end-before: # end-custom-field-type + +In the preceding example, the ``mongoize`` *instance method* accepts an instance +of your custom type object and converts it to an ``Array`` to store in the +database. The ``mongoize`` *class method* accepts objects of all types and +converts them to similar types that can be stored in the database. {+odm+} uses +the ``mongoize`` class method when it calls the getter and setter methods. + +The ``demongoize`` method converts the stored ``Array`` value into the custom +``Point`` type. The {+odm+} uses this method when it calls the getter. + +The ``evolve`` method converts the custom ``Point`` type into a queryable +``Array`` type, and converts all other types to ``object``. {+odm+} uses this +method when it calls a method that queries the database. + +Phantom Custom Field Types +`````````````````````````` + +You can create a custom field type that saves a different value to the database +than the value assigned in the application. This can be useful to have +descriptive values in the application while storing more compact values in the +database. + +The following example creates a ``ColorMapping`` type that uses the name of the +color in the application, but stores the color as an integer in the database: + +.. literalinclude:: /includes/field-types/field-types.rb + :language: ruby + :start-after: # start-phantom-field-type + :end-before: # end-phantom-field-type + +Dynamic Fields +~~~~~~~~~~~~~~ + +You can instruct {+odm+} to create fields dynamically by setting the +``Mongoid::Attributes::Dynamic`` module in your model. This allows {+odm+} to +create fields based on an arbitrary hash, or by the documents stored in the +database. + +The following example creates a ``Person`` class with dynamic fields: + +.. literalinclude:: /includes/field-types/field-types.rb + :language: ruby + :start-after: # start-dynamic-field + :end-before: # end-dynamic-field + +You can also specify both fixed fields and dynamic fields within the same class. +When doing so, {+odm+} treats all attributes for properties with field definitions +according to their field type, and all other attributes as dynamic. + +When using dynamic fields in your application, you must initially set the value in one of the +following ways: + +- Pass the attribute hash to the constructor. +- Assign values by using the ``attributes=`` method. +- Assign values by using the ``[]=`` method. +- Assign values by using the ``write_attribute`` method. +- Work with values that are already present in the database. + +If you don't initially set the value by using one of the preceding options, +invoking the attribute returns a ``NoMethodError``. + +Reserved Characters +~~~~~~~~~~~~~~~~~~~ + +Both {+odm+} and the MongoDB Query API use the ``.`` character to separate field +names in nested documents, and use the ``$`` character at the beginning of a +word to indicate a query operator. Because of this, you should avoid using these +characters in your field names. + +If your application requires the use of these characters, you can access the +fields by calling the ``send`` method. The following example creates a ``User`` +class with fields that contain reserved characters. It then accesses the fields +with the ``send`` method: + +.. literalinclude:: /includes/field-types/field-types.rb + :language: ruby + :start-after: # start-reserved-characters + :end-before: # end-reserved-characters + +You can also access these fields by calling the ``read_attribute`` method. + +.. important:: + + Because updating and replacing fields containing these reserved characters + requires special operators, calling getters and setters on these fields + raises an ``InvalidDotDollarAssignment`` exception. diff --git a/source/includes/field-types/field-types.rb b/source/includes/field-types/field-types.rb new file mode 100644 index 00000000..9589e963 --- /dev/null +++ b/source/includes/field-types/field-types.rb @@ -0,0 +1,390 @@ +# start-define-fields +class Person + include Mongoid::Document + field :name, type: String + field :date_of_birth, type: Date + field :weight, type: Float +end +# end-define-fields + +# start-define-untyped +class Person + include Mongoid::Document + + field :name, type: String + field :preferences +end +# end-define-untyped + +# start-untyped +product = Product.new(properties: "color=white,size=large") +# properties field saved as String: "color=white,size=large" + +product = Product.new(properties: {color: "white", size: "large"}) +# properties field saved as Object: {:color=>"white", :size=>"large"} +# end-untyped + +# start-stringified-symbol +class Post + include Mongoid::Document + + field :status, type: StringifiedSymbol +end + +# Save status as a symbol +post = Post.new(status: :hello) +# status is stored as "hello" on the database, but returned as a Symbol +post.status +# Outputs: :hello + +# Save status as a string +post = Post.new(status: "hello") +# status is stored as "hello" in the database, but returned as a Symbol +post.status +# Outputs: :hello +# end-stringified-symbol + +# start-hash +class Person + include Mongoid::Document + field :first_name + field :url, type: Hash + + def set_vals + self.first_name = 'Daniel' + self.url = {'home_page' => 'http://www.homepage.com'} + save +end +# end-hash + +# start-time +class Voter + include Mongoid::Document + + field :registered_at, type: Time +end + + Voter.new(registered_at: Date.today) +# end-time + +# start-datetime +class Ticket + include Mongoid::Document + field :opened_at, type: DateTime +end +# end-datetime + +# start-datetime-int +ticket.opened_at = 1544803974 +ticket.opened_at +# Outputs: Fri, 14 Dec 2018 16:12:54 +0000 +# end-datetime-int + +# start-datetime-string +ticket.opened_at = 'Mar 4, 2018 10:00:00 +01:00' +ticket.opened_at +# Outputs: Sun, 04 Mar 2018 09:00:00 +0000 +# end-datetime-string + +# start-timestamps +class Person + include Mongoid::Document + include Mongoid::Timestamps +end +# end-timestamps + +# start-timestamps-specific +class Person + include Mongoid::Document + include Mongoid::Timestamps::Created +end + +class Post + include Mongoid::Document + include Mongoid::Timestamps::Updated +end +# end-timestamps-specific + +# start-timestamps-disable +person.timeless.save +# end-timestamps-disable + +# start-timestamps-short +class Band + include Mongoid::Document + include Mongoid::Timestamps::Short # For c_at and u_at. +end + +class Band + include Mongoid::Document + include Mongoid::Timestamps::Created::Short # For c_at only. +end + +class Band + include Mongoid::Document + include Mongoid::Timestamps::Updated::Short # For u_at only. +end +# end-timestamps-short + +# start-regexp +class Token + include Mongoid::Document + + field :pattern, type: Regexp +end + +token = Token.create!(pattern: /hello.world/m) +token.pattern +# Outputs: /hello.world/m + +# Reload the token from the database +token.reload +token.pattern +# Outputs: # +# end-regexp + +# start-field-default +class Order + include Mongoid::Document + + field :state, type: String, default: 'created' +end +# end-field-default + +# start-field-default-processed +class Order + include Mongoid::Document + + field :fulfill_by, type: Time, default: ->{ Time.now + 3.days } +end +# end-field-default-processed + +# start-field-default-self +field :fulfill_by, type: Time, default: ->{ + self.submitted_at + 4.hours +} +# end-field-default-self + +# start-field-default-pre-processed +field :fulfill_by, type: Time, default: ->{ Time.now + 3.days }, + pre_processed: true +# end-field-default-pre-processed + +# start-field-as +class Band + include Mongoid::Document + field :n, as: :name, type: String +end +# end-field-as + +# start-field-alias +class Band + include Mongoid::Document + field :name, type: String + alias_attribute :n, :name +end +# end-field-alias + +# start-field-unalias +class Band + unalias_attribute :n +end +# end-field-unalias + +# start-field-overwrite +class Person + include Mongoid::Document + field :name + field :name, type: String, overwrite: true +end +# end-field-overwrite + +# start-custom-id +class Band + include Mongoid::Document + field :name, type: String + field :_id, type: String, default: ->{ name } +end +# end-custom-id + +# start-custom-getter-setter +class Person + include Mongoid::Document + field :name, type: String + + # Custom getter for 'name' to return the name in uppercase + def name + read_attribute(:name).upcase if read_attribute(:name) + end + + # Custom setter for 'name' to store the name in lowercase + def name=(value) + write_attribute(:name, value.downcase) + end + end +# end-custom-getter-setter + +# start-custom-field-type +class Point + + attr_reader :x, :y + + def initialize(x, y) + @x, @y = x, y + end + + # Converts an object of this instance into an array + def mongoize + [ x, y ] + end + + class << self + + # Takes any possible object and converts it to how it is + # stored in the database. + def mongoize(object) + case object + when Point then object.mongoize + when Hash then Point.new(object[:x], object[:y]).mongoize + else object + end + end + + # Gets the object as it's stored in the database and instantiates + # this custom class from it. + def demongoize(object) + if object.is_a?(Array) && object.length == 2 + Point.new(object[0], object[1]) + end + end + + # Converts the object supplied to a criteria and converts it + # into a queryable form. + def evolve(object) + case object + when Point then object.mongoize + else object + end + end + end +end +# end-custom-field-type + +# start-phantom-field-type +class ColorMapping + + MAPPING = { + 'black' => 0, + 'white' => 1, + }.freeze + + INVERSE_MAPPING = MAPPING.invert.freeze + + class << self + + def mongoize(object) + MAPPING[object] + end + + def demongoize(object) + INVERSE_MAPPING[object] + end + + def evolve(object) + MAPPING.fetch(object, object) + end + end +end + +class Profile + include Mongoid::Document + field :color, type: ColorMapping +end + +profile = Profile.new(color: 'white') +profile.color +# Outputs: "white" + +# Writes 0 to the database +profile.save! +# end-phantom-field-type + +# start-dynamic-field +class Person + include Mongoid::Document + include Mongoid::Attributes::Dynamic +end +# end-dynamic-field + +# start-reserved-characters +class User + include Mongoid::Document + field :"first.last", type: String + field :"$_amount", type: Integer +end + +user = User.first +user.send(:"first.last") +# Outputs: Mike.Trout +user.send(:"$_amount") +# Outputs: 42650000 +# end-reserved-characters + +# start-localized-field +class Product + include Mongoid::Document + field :review, type: String, localize: true +end + +I18n.default_locale = :en +product = Product.new +product.review = "Marvelous!" +I18n.locale = :de +product.review = "Fantastisch!" + +product.attributes +# Outputs: { "review" => { "en" => "Marvelous!", "de" => "Fantastisch!" } +# end-localized-field + +# start-localized-translations +product.review_translations +# Outputs: { "en" => "Marvelous!", "de" => "Fantastisch!" } +product.review_translations = + { "en" => "Marvelous!", "de" => "Wunderbar!" } +# end-localized-translations + +# start-localized-fallbacks +config.i18n.fallbacks = true +config.after_initialize do + I18n.fallbacks[:de] = [ :en, :es ] +end +# end-localized-fallbacks + +# start-localized-fallbacks-non-rails +require "i18n/backend/fallbacks" +I18n::Backend::Simple.send(:include, I18n::Backend::Fallbacks) +I18n.fallbacks[:de] = [ :en, :es ] +# end-localized-fallbacks-non-rails + +# start-localized-no-fallbacks +class Product + include Mongoid::Document + field :review, type: String, localize: true, fallbacks: false +end +# end-localized-no-fallbacks + +# start-localized-query +# Match all products with Marvelous as the review. The current locale is :en. +Product.where(review: "Marvelous!") +# The resulting MongoDB query filter: { "review.en" : "Marvelous!" } +# end-localized-query + +# start-read-only +class Band + include Mongoid::Document + field :name, type: String + field :origin, type: String + + attr_readonly :name +end +# end-read-only \ No newline at end of file From b457d9e065ec1a43a08438b134f9839bdbc537a0 Mon Sep 17 00:00:00 2001 From: Jordan Smith Date: Tue, 19 Nov 2024 08:32:37 -0800 Subject: [PATCH 2/8] remove behaviors page to shorten PR --- source/data-modeling.txt | 5 +- source/data-modeling/field-behaviors.txt | 289 --------------------- source/includes/field-types/field-types.rb | 144 +--------- 3 files changed, 3 insertions(+), 435 deletions(-) delete mode 100644 source/data-modeling/field-behaviors.txt diff --git a/source/data-modeling.txt b/source/data-modeling.txt index 676eed48..1e32e4cc 100644 --- a/source/data-modeling.txt +++ b/source/data-modeling.txt @@ -15,11 +15,8 @@ Model Your Data :caption: Data Modeling Field Types - Field Behaviors In this section, you can learn how to model data in {+odm+}. - :ref:`mongoid-field-types`: Learn about the field types that you can use in - {+odm+} to define the schema for your MongoDB documents. -- :ref:`mongoid-field-behaviors`: Learn how to customize the behaviors of fields - in {+odm+} to meet your application requirements. \ No newline at end of file + {+odm+} to define the schema for your MongoDB documents. \ No newline at end of file diff --git a/source/data-modeling/field-behaviors.txt b/source/data-modeling/field-behaviors.txt deleted file mode 100644 index 198ac050..00000000 --- a/source/data-modeling/field-behaviors.txt +++ /dev/null @@ -1,289 +0,0 @@ -.. _mongoid-field-behaviors: - -========================= -Customize Field Behaviors -========================= - -.. facet:: - :name: genre - :values: reference - -.. meta:: - :keywords: customize, attributes, optimize, model, configure, code example - -.. contents:: On this page - :local: - :backlinks: none - :depth: 1 - :class: singlecol - -In this guide, you can learn how to customize the behavior of fields in {+odm+} models. - -Specify Default Values ----------------------- - -You can configure fields to have default values by using the ``default`` option. -Default field values can be either fixed or processed (``Proc``) values. - -The following example specifies a fixed default value for the ``fulfill_by`` -field: - -.. literalinclude:: /includes/field-types/field-types.rb - :language: ruby - :start-after: # start-field-default - :end-before: # end-field-default - -The following example specifies a ``Proc`` default value for the ``fulfill_by`` -field: - -.. literalinclude:: /includes/field-types/field-types.rb - :language: ruby - :start-after: # start-field-default-processed - :end-before: # end-field-default-processed - -.. note:: - - The driver evaluates default values that are not ``Proc`` instances when the - class loads. The driver evaluates ``Proc`` default values when the document is - instantiated. The following default field values do not produce equivalent outcomes: - - .. code-block:: ruby - - # Time.now is set to the time the class is loaded - field :submitted_at, type: Time, default: Time.now - - # Time.now is set to the time the document is instantiated - field :submitted_at, type: Time, default: ->{ Time.now } - -You can set a default value that depends on the document's state by using the -``self`` keyword inside of a ``Proc`` instance. The following example sets the -``fulfill_by`` default value to depend on the state of the ``submitted_at`` -field: - -.. literalinclude:: /includes/field-types/field-types.rb - :language: ruby - :start-after: # start-field-default-self - :end-before: # end-field-default-self - -By default, {+odm+} applies ``Proc`` default values after setting and -initializing all other attributes. To apply the default before setting the other -attributes, set the ``pre_processed`` option to ``true``, as shown in the -following example: - -.. literalinclude:: /includes/field-types/field-types.rb - :language: ruby - :start-after: # start-field-default-pre-processed - :end-before: # end-field-default-pre-processed - -.. tip:: - - You should always set the ``pre-processed`` option to ``true`` if you want to set a - default ``Proc`` value for the ``_id`` field. - -Specify Storage Names ---------------------- - -You can specify a separate field name to store in the database, while still -referring to the field by its original name in your application. This can be -useful to save storage space, because MongoDB stores all field information along -with every document. - -You can set an alternate storage name by using the ``as:`` keyword. The -following example creates a field called ``name`` that {+odm+} stores in the database as -``n``: - -.. literalinclude:: /includes/field-types/field-types.rb - :language: ruby - :start-after: # start-field-as - :end-before: # end-field-as - -{+odm+} stores the ``name`` field as ``"n"``, but you can still refer to the field as -``name`` in your application. - -Field Aliases -------------- - -You can create an alias for your field by using the ``alias_attribute`` option. -Specifying an alias does not change how {+odm+} stores the field in the -database, but it allows you to refer to the field by a different name in your -application. - -The following example specifies an alias for the ``name`` field: - -.. literalinclude:: /includes/field-types/field-types.rb - :language: ruby - :start-after: # start-field-alias - :end-before: # end-field-alias - -To remove a field alias, you can use the ``unalias_attribute`` option. The -following example removes the alias for the ``name`` field: - -.. literalinclude:: /includes/field-types/field-types.rb - :language: ruby - :start-after: # start-field-unalias - :end-before: # end-field-unalias - -You can also use ``unalias_attribute`` to remove the predefined ``id`` alias from the -``_id`` field. This can be used to store different values in the ``_id`` field -and an ``id`` field. - -Field Redefinition ------------------- - -By default, {+odm+} allows you to redefine fields on a model. To raise an error -when a field is redefined, set the ``duplicate_fields_exception`` configuration -option in your ``mongoid.yml`` file to ``true``. - -If the ``duplicate_fields_exception`` option is set to ``true``, you can still -redefine a specific field by setting the ``overwrite`` option to ``true`` when -you define the field. The following example defines the ``name`` field, and then -redefines the field by using the ``overwrite`` option: - -.. literalinclude:: /includes/field-types/field-types.rb - :language: ruby - :start-after: # start-field-overwrite - :end-before: # end-field-overwrite - -Custom ID Field ---------------- - -By default, {+odm+} defines the ``_id`` field on documents to contain a -``BSON::ObjectId`` value that {+odm+} generates automatically. You can customize -the type or the specify the default value of the ``_id`` field by specifying it in your -model. - -The following example creates a ``Band`` class with a custom ``_id`` field: - -.. literalinclude:: /includes/field-types/field-types.rb - :language: ruby - :start-after: # start-custom-id - :end-before: # end-custom-id - -You can omit the default value for the ``_id`` field. If you don't specify a -default value for the field, {+odm+} persists the value without an ``_id`` -value. If the document is a top-level document, the MongoDB server automatically -assigns an ``_id`` value to the document. However, if the document is an -embedded document, the server does not assign an ``_id`` value. - -When you don't specify a value for the ``_id`` field, {+odm+} does not retrieve -the automatically assigned value from the server. Because of this, you cannot -retrieve the document from the database by using the ``_id`` value. - -Uncastable Values ------------------ - -A value is considered **uncastable** if it cannot be converted to the specified -field type. For example, an array is considered uncastable when assigned to an -``Integer`` field. - -In v8.0 and later, {+odm+} assigns ``nil`` to values that are uncastable. The -original uncastable value is stored in the ``attributes_before_type_cast`` hash -with their field names. - -Custom Getters and Setters --------------------------- - -You can override the default getter and setter methods for a field by specifying -a method with the same name as the field and calling the ``read_attribute`` or -``write_attribute`` method to operate on the raw attribute value. - -The following example creates a custom getter and setter for the ``name`` field -of a ``Person`` class: - -.. literalinclude:: /includes/field-types/field-types.rb - :language: ruby - :start-after: # start-custom-getter-setter - :end-before: # end-custom-getter-setter - -Read-Only Attributes --------------------- - -You can specify a field to read-only by specifying the ``attr_readonly`` option. -This allows you to create documents with the attributes, but not update them. - -The following example creates a ``Band`` class and specifies the ``name`` field -as read-only: - -.. literalinclude:: /includes/field-types/field-types.rb - :language: ruby - :start-after: # start-read-only - :end-before: # end-read-only - -If you call a mass-update method, such as ``update_attributes``, and pass in a -read-only field, {+odm+} ignores the read-only field and updates all others. If -you attempt to explicitly update a read-only field, {+odm+} raises a -``ReadonlyAttribute`` exception. - -.. note:: - - Calls to atomic persistence operators, such as ``bit`` and ``inc``, still - persist changes to the read-only field. - -Localize Fields ---------------- - -{+odm+} supports localized fields by using the `i18n gem -`__. When you localize a field, {+odm+} -stores the field as a hash of locale keys and values. Accessing the fields -behaves in the same way as a string value. You can localize fields of any field -type. - -The following example creates a ``Product`` class with a localized ``review`` -field: - -.. literalinclude:: /includes/field-types/field-types.rb - :language: ruby - :start-after: # start-localized-field - :end-before: # end-localized-field - -You can get and set all translations at once by calling the ``_translations`` -method: - -.. literalinclude:: /includes/field-types/field-types.rb - :language: ruby - :start-after: # start-localized-translations - :end-before: # end-localized-translations - -You can specify fallbacks for localized fields by enabling the `i18n fallbacks -`__ feature. - -Enable fallbacks in a Rails application by setting the ``config.i18n.fallbacks`` -configuration setting in your environment and setting the fallback languages: - -.. literalinclude:: /includes/field-types/field-types.rb - :language: ruby - :start-after: # start-localized-fallbacks - :end-before: # end-localized-fallbacks - -Enable fallbacks in non-Rails applications by including the module into the i18n -backend and setting the fallback languages: - -.. literalinclude:: /includes/field-types/field-types.rb - :language: ruby - :start-after: # start-localized-fallbacks-non-rails - :end-before: # end-localized-fallbacks-non-rails - -After enabling fallbacks, if an active language does not have have a -translation, it is looked up in the specified fallback language. - -You can disable fallback languages for a specified field by setting the -``fallbacks`` option to false when defining the field: - -.. literalinclude:: /includes/field-types/field-types.rb - :language: ruby - :start-after: # start-localized-no-fallbacks - :end-before: # end-localized-no-fallbacks - -When querying localized fields, {+odm+} automatically alters the query criteria -to match the current locale. The following example queries the ``Product`` class -for a review in the ``en`` locale: - -.. literalinclude:: /includes/field-types/field-types.rb - :language: ruby - :start-after: # start-localized-query - :end-before: # end-localized-query - -.. note:: - - If you want to query extensively on localized fields, we recommend indexing - each locale that you want to query on. diff --git a/source/includes/field-types/field-types.rb b/source/includes/field-types/field-types.rb index 9589e963..cb1c3862 100644 --- a/source/includes/field-types/field-types.rb +++ b/source/includes/field-types/field-types.rb @@ -10,7 +10,7 @@ class Person # start-define-untyped class Person include Mongoid::Document - + field :name, type: String field :preferences end @@ -143,87 +143,6 @@ class Token # Outputs: # # end-regexp -# start-field-default -class Order - include Mongoid::Document - - field :state, type: String, default: 'created' -end -# end-field-default - -# start-field-default-processed -class Order - include Mongoid::Document - - field :fulfill_by, type: Time, default: ->{ Time.now + 3.days } -end -# end-field-default-processed - -# start-field-default-self -field :fulfill_by, type: Time, default: ->{ - self.submitted_at + 4.hours -} -# end-field-default-self - -# start-field-default-pre-processed -field :fulfill_by, type: Time, default: ->{ Time.now + 3.days }, - pre_processed: true -# end-field-default-pre-processed - -# start-field-as -class Band - include Mongoid::Document - field :n, as: :name, type: String -end -# end-field-as - -# start-field-alias -class Band - include Mongoid::Document - field :name, type: String - alias_attribute :n, :name -end -# end-field-alias - -# start-field-unalias -class Band - unalias_attribute :n -end -# end-field-unalias - -# start-field-overwrite -class Person - include Mongoid::Document - field :name - field :name, type: String, overwrite: true -end -# end-field-overwrite - -# start-custom-id -class Band - include Mongoid::Document - field :name, type: String - field :_id, type: String, default: ->{ name } -end -# end-custom-id - -# start-custom-getter-setter -class Person - include Mongoid::Document - field :name, type: String - - # Custom getter for 'name' to return the name in uppercase - def name - read_attribute(:name).upcase if read_attribute(:name) - end - - # Custom setter for 'name' to store the name in lowercase - def name=(value) - write_attribute(:name, value.downcase) - end - end -# end-custom-getter-setter - # start-custom-field-type class Point @@ -328,63 +247,4 @@ class User # Outputs: Mike.Trout user.send(:"$_amount") # Outputs: 42650000 -# end-reserved-characters - -# start-localized-field -class Product - include Mongoid::Document - field :review, type: String, localize: true -end - -I18n.default_locale = :en -product = Product.new -product.review = "Marvelous!" -I18n.locale = :de -product.review = "Fantastisch!" - -product.attributes -# Outputs: { "review" => { "en" => "Marvelous!", "de" => "Fantastisch!" } -# end-localized-field - -# start-localized-translations -product.review_translations -# Outputs: { "en" => "Marvelous!", "de" => "Fantastisch!" } -product.review_translations = - { "en" => "Marvelous!", "de" => "Wunderbar!" } -# end-localized-translations - -# start-localized-fallbacks -config.i18n.fallbacks = true -config.after_initialize do - I18n.fallbacks[:de] = [ :en, :es ] -end -# end-localized-fallbacks - -# start-localized-fallbacks-non-rails -require "i18n/backend/fallbacks" -I18n::Backend::Simple.send(:include, I18n::Backend::Fallbacks) -I18n.fallbacks[:de] = [ :en, :es ] -# end-localized-fallbacks-non-rails - -# start-localized-no-fallbacks -class Product - include Mongoid::Document - field :review, type: String, localize: true, fallbacks: false -end -# end-localized-no-fallbacks - -# start-localized-query -# Match all products with Marvelous as the review. The current locale is :en. -Product.where(review: "Marvelous!") -# The resulting MongoDB query filter: { "review.en" : "Marvelous!" } -# end-localized-query - -# start-read-only -class Band - include Mongoid::Document - field :name, type: String - field :origin, type: String - - attr_readonly :name -end -# end-read-only \ No newline at end of file +# end-reserved-characters \ No newline at end of file From 7cc2ae607571377794295f3cf13de023a86fbc4c Mon Sep 17 00:00:00 2001 From: Jordan Smith Date: Tue, 19 Nov 2024 08:44:47 -0800 Subject: [PATCH 3/8] update include link --- source/data-modeling/field-types.txt | 36 +++++++++---------- .../field-types.rb | 0 2 files changed, 18 insertions(+), 18 deletions(-) rename source/includes/{field-types => data-modeling}/field-types.rb (100%) diff --git a/source/data-modeling/field-types.txt b/source/data-modeling/field-types.txt index f551fc1f..388c7743 100644 --- a/source/data-modeling/field-types.txt +++ b/source/data-modeling/field-types.txt @@ -43,7 +43,7 @@ You can define the field type for fields in a model class by using the ``field`` and ``type`` macros. The following example defines the fields of a ``Person`` class: -.. literalinclude:: /includes/field-types/field-types.rb +.. literalinclude:: /includes/data-modeling/field-types.rb :language: ruby :start-after: # start-define-fields :end-before: # end-define-fields @@ -88,7 +88,7 @@ different types of data, or if the type of the field's value is not known. The following example defines a ``Person`` class with an untyped field: -.. literalinclude:: /includes/field-types/field-types.rb +.. literalinclude:: /includes/data-modeling/field-types.rb :language: ruby :start-after: # start-define-untyped :end-before: # end-define-untyped @@ -97,7 +97,7 @@ The type of the ``preferences`` field is ``Object`` and will vary depending on the type of data stored in that field. The following example saves data into the ``preferences`` field in two different ways: -.. literalinclude:: /includes/field-types/field-types.rb +.. literalinclude:: /includes/data-modeling/field-types.rb :language: ruby :start-after: # start-untyped :end-before: # end-untyped @@ -124,7 +124,7 @@ integers and arrays, are converted into strings and then into symbols. The following example stores a ``status`` field as a ``StringifiedSymbol`` and demonstrates how the field is stored and returned: -.. literalinclude:: /includes/field-types/field-types.rb +.. literalinclude:: /includes/data-modeling/field-types.rb :language: ruby :start-after: # start-stringified-symbol :end-before: # end-stringified-symbol @@ -140,7 +140,7 @@ store properly in the database. The following example creates a ``Person`` class and specifies the ``url`` field as a ``Hash``. -.. literalinclude:: /includes/field-types/field-types.rb +.. literalinclude:: /includes/data-modeling/field-types.rb :language: ruby :start-after: # start-hash :end-before: # end-hash @@ -157,7 +157,7 @@ guide. The following example creates a ``Voter`` class and specifies the ``registered_at`` property as a ``Time`` field: -.. literalinclude:: /includes/field-types/field-types.rb +.. literalinclude:: /includes/data-modeling/field-types.rb :language: ruby :start-after: # start-time :end-before: # end-time @@ -209,7 +209,7 @@ configured for your application. The following example creates a ``Ticket`` class and specifies the ``opened_at`` field as a ``DateTime`` field: -.. literalinclude:: /includes/field-types/field-types.rb +.. literalinclude:: /includes/data-modeling/field-types.rb :language: ruby :start-after: # start-datetime :end-before: # end-datetime @@ -218,7 +218,7 @@ If you save an integer or float value to a ``DateTime`` field, the value is trea a Unix timestamp in UTC. The following example saves an integer value to the ``opened_at`` field: -.. literalinclude:: /includes/field-types/field-types.rb +.. literalinclude:: /includes/data-modeling/field-types.rb :language: ruby :start-after: # start-datetime-int :end-before: # end-datetime-int @@ -227,7 +227,7 @@ If you save a string value to a ``DateTime`` field, {+odm+} saves the ticket with the time zone specified. If a time zone is not specified, {+odm+} saves the value by using the default {+odm+} timezone: -.. literalinclude:: /includes/field-types/field-types.rb +.. literalinclude:: /includes/data-modeling/field-types.rb :language: ruby :start-after: # start-datetime-string :end-before: # end-datetime-string @@ -249,7 +249,7 @@ fields: The following example creates a ``Person`` class with timestamp fields: -.. literalinclude:: /includes/field-types/field-types.rb +.. literalinclude:: /includes/data-modeling/field-types.rb :language: ruby :start-after: # start-timestamps :end-before: # end-timestamps @@ -259,7 +259,7 @@ by including only the ``Created`` or ``Updated`` modules. The following example creates a ``Person`` class with only the ``created_at`` field, and a ``Post`` class with only the ``updated_at`` field: -.. literalinclude:: /includes/field-types/field-types.rb +.. literalinclude:: /includes/data-modeling/field-types.rb :language: ruby :start-after: # start-timestamps-specific :end-before: # end-timestamps-specific @@ -267,7 +267,7 @@ class with only the ``updated_at`` field: You can shorten the timestamp field names to ``c_at`` and ``u_at`` by setting the ``::Short`` option when including the module: -.. literalinclude:: /includes/field-types/field-types.rb +.. literalinclude:: /includes/data-modeling/field-types.rb :language: ruby :start-after: # start-timestamps-short :end-before: # end-timestamps-short @@ -276,7 +276,7 @@ You can disable creating the timestamp field for specific operations by calling the ``timeless`` method on the method call. The following example disables the timestamps for the ``save`` operation: -.. literalinclude:: /includes/field-types/field-types.rb +.. literalinclude:: /includes/data-modeling/field-types.rb :language: ruby :start-after: # start-timestamps-disable :end-before: # end-timestamps-disable @@ -301,7 +301,7 @@ the data back to a Ruby regular expression. The following example creates a ``Token`` class and specifies the ``pattern`` field as a ``Regexp``: -.. literalinclude:: /includes/field-types/field-types.rb +.. literalinclude:: /includes/data-modeling/field-types.rb :language: ruby :start-after: # start-regexp :end-before: # end-regexp @@ -465,7 +465,7 @@ implements the following methods: The following example creates a custom field type called ``Point`` and implements the preceding methods: -.. literalinclude:: /includes/field-types/field-types.rb +.. literalinclude:: /includes/data-modeling/field-types.rb :language: ruby :start-after: # start-custom-field-type :end-before: # end-custom-field-type @@ -494,7 +494,7 @@ database. The following example creates a ``ColorMapping`` type that uses the name of the color in the application, but stores the color as an integer in the database: -.. literalinclude:: /includes/field-types/field-types.rb +.. literalinclude:: /includes/data-modeling/field-types.rb :language: ruby :start-after: # start-phantom-field-type :end-before: # end-phantom-field-type @@ -509,7 +509,7 @@ database. The following example creates a ``Person`` class with dynamic fields: -.. literalinclude:: /includes/field-types/field-types.rb +.. literalinclude:: /includes/data-modeling/field-types.rb :language: ruby :start-after: # start-dynamic-field :end-before: # end-dynamic-field @@ -543,7 +543,7 @@ fields by calling the ``send`` method. The following example creates a ``User`` class with fields that contain reserved characters. It then accesses the fields with the ``send`` method: -.. literalinclude:: /includes/field-types/field-types.rb +.. literalinclude:: /includes/data-modeling/field-types.rb :language: ruby :start-after: # start-reserved-characters :end-before: # end-reserved-characters diff --git a/source/includes/field-types/field-types.rb b/source/includes/data-modeling/field-types.rb similarity index 100% rename from source/includes/field-types/field-types.rb rename to source/includes/data-modeling/field-types.rb From 7ac7ab9117cc344b9aaa10065144ec49fb9dbfc3 Mon Sep 17 00:00:00 2001 From: Jordan Smith Date: Tue, 19 Nov 2024 09:11:21 -0800 Subject: [PATCH 4/8] typos and edits --- snooty.toml | 1 + source/data-modeling/field-types.txt | 25 ++++++++------- source/includes/data-modeling/field-types.rb | 33 ++++++++++---------- 3 files changed, 31 insertions(+), 28 deletions(-) diff --git a/snooty.toml b/snooty.toml index 5b4584c2..7d3af820 100644 --- a/snooty.toml +++ b/snooty.toml @@ -27,3 +27,4 @@ feedback-widget-title = "Feedback" server-manual = "Server manual" api-root = "https://www.mongodb.com/docs/mongoid/master/api/Mongoid" api = "https://www.mongodb.com/docs/mongoid/master/api" +mdb-server = "MongoDB Server" \ No newline at end of file diff --git a/source/data-modeling/field-types.txt b/source/data-modeling/field-types.txt index 388c7743..707a7df4 100644 --- a/source/data-modeling/field-types.txt +++ b/source/data-modeling/field-types.txt @@ -23,11 +23,11 @@ Overview In this guide, you can learn about the field types that you can use in {+odm+} to define the schema for your MongoDB documents. -MongoDB uses :manual:`BSON types ` to represent the type of +MongoDB uses :manual:`BSON types ` to represent the type of data stored in document fields. To use that data in a {+odm+} application, -{+odm+} must convert the BSON types to Ruby types at runtime. For example, +{+odm+} must convert the BSON types to {+language+} types at runtime. For example, when retrieving a document from the database, {+odm+} translates a BSON field -with the type ``double`` to use the Ruby ``Float`` type. It then translates that +with the type ``double`` to use the {+language+} ``Float`` type. It then translates that field back to a BSON ``double`` when saving the document to the database. .. note:: @@ -103,7 +103,7 @@ the type of data stored in that field. The following example saves data into the :end-before: # end-untyped Because {+odm+} doesn't perform any type conversions on untyped fields when -reading from the database, values that require special handling might now work +reading from the database, values that require special handling might not work correctly in an untyped field. The following BSON data types should not be stored in untyped fields: @@ -115,7 +115,7 @@ StringifiedSymbol ~~~~~~~~~~~~~~~~~ Use the ``StringifiedSymbol`` field type to store values that should be exposed -as symbols to Ruby applications. ``StringifiedSymbol`` allows you to use symbols +as symbols to {+language+} applications. ``StringifiedSymbol`` allows you to use symbols while ensuring interoperability with other drivers. This type stores all data on the database as strings, and converts the strings to symbols when read by the application. Values that cannot be directly converted to symbols, such as @@ -152,6 +152,7 @@ You can store values as ``Time`` instances by using the ``Time`` field value. ``Time`` fields are stored in the time zone configured for your application. To learn more about configuring time zones, see the :ref:`Time Zones ` guide. + .. TODO: Update Time Zones guide ref if it's changed during standardization The following example creates a ``Voter`` class and specifies the @@ -166,7 +167,7 @@ The following example creates a ``Voter`` class and specifies the Storing a ``Date`` or ``DateTime`` value in a field specified as ``Time`` converts the value to ``Time`` when assigned. If you store a string in a - ``Time`` field, {+odm+} parses the string by using the ``Time.pars`` method. + ``Time`` field, {+odm+} parses the string by using the ``Time.parse`` method. Date ~~~~ @@ -201,7 +202,7 @@ DateTime ~~~~~~~~ When you assign a value to, or query on a ``DateTime`` field, {+odm+} converts -the value to a UTC ``Time`` value before sending it to the MongoDB server. +the value to a UTC ``Time`` value before sending it to the {+mdb-server+}. {+odm+} saves the value with the time zone embedded in the ``DateTime`` object. When you retrieve the value, {+odm+} converts the UTC time to the time zone configured for your application. @@ -350,7 +351,7 @@ Consider the following limitations when setting the rather than untyped. Specify Fields with Strings or Symbols -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +--------------------------------------- You can use strings or symbols to specify certain field types in {+odm+}, instead of using their class names. The following example specifies the ``state`` field by @@ -449,7 +450,7 @@ The following field types can be specified as a string or a symbol: - ``"Time"`` Custom Field Types -~~~~~~~~~~~~~~~~~~ +------------------ You can create custom field types and define how {+odm+} serializes and deserializes them. To create a custom field type, define a class that @@ -484,7 +485,7 @@ The ``evolve`` method converts the custom ``Point`` type into a queryable method when it calls a method that queries the database. Phantom Custom Field Types -`````````````````````````` +~~~~~~~~~~~~~~~~~~~~~~~~~~ You can create a custom field type that saves a different value to the database than the value assigned in the application. This can be useful to have @@ -500,7 +501,7 @@ color in the application, but stores the color as an integer in the database: :end-before: # end-phantom-field-type Dynamic Fields -~~~~~~~~~~~~~~ +-------------- You can instruct {+odm+} to create fields dynamically by setting the ``Mongoid::Attributes::Dynamic`` module in your model. This allows {+odm+} to @@ -531,7 +532,7 @@ If you don't initially set the value by using one of the preceding options, invoking the attribute returns a ``NoMethodError``. Reserved Characters -~~~~~~~~~~~~~~~~~~~ +------------------- Both {+odm+} and the MongoDB Query API use the ``.`` character to separate field names in nested documents, and use the ``$`` character at the beginning of a diff --git a/source/includes/data-modeling/field-types.rb b/source/includes/data-modeling/field-types.rb index cb1c3862..e9d2d102 100644 --- a/source/includes/data-modeling/field-types.rb +++ b/source/includes/data-modeling/field-types.rb @@ -26,9 +26,9 @@ class Person # start-stringified-symbol class Post - include Mongoid::Document + include Mongoid::Document - field :status, type: StringifiedSymbol + field :status, type: StringifiedSymbol end # Save status as a symbol @@ -46,31 +46,32 @@ class Post # start-hash class Person - include Mongoid::Document - field :first_name - field :url, type: Hash + include Mongoid::Document + field :first_name + field :url, type: Hash - def set_vals - self.first_name = 'Daniel' - self.url = {'home_page' => 'http://www.homepage.com'} - save + def set_vals + self.first_name = 'Daniel' + self.url = {'home_page' => 'http://www.homepage.com'} + save + end end # end-hash # start-time class Voter - include Mongoid::Document + include Mongoid::Document - field :registered_at, type: Time + field :registered_at, type: Time end - Voter.new(registered_at: Date.today) +Voter.new(registered_at: Date.today) # end-time # start-datetime class Ticket - include Mongoid::Document - field :opened_at, type: DateTime + include Mongoid::Document + field :opened_at, type: DateTime end # end-datetime @@ -128,9 +129,9 @@ class Band # start-regexp class Token - include Mongoid::Document + include Mongoid::Document - field :pattern, type: Regexp + field :pattern, type: Regexp end token = Token.create!(pattern: /hello.world/m) From 2ada28bda59ac8abf12dd2c1fa16029e0b99e249 Mon Sep 17 00:00:00 2001 From: Jordan Smith Date: Tue, 19 Nov 2024 09:22:22 -0800 Subject: [PATCH 5/8] remove unused constant --- snooty.toml | 3 +-- source/data-modeling/field-types.txt | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/snooty.toml b/snooty.toml index 7d3af820..f16a171d 100644 --- a/snooty.toml +++ b/snooty.toml @@ -26,5 +26,4 @@ quickstart-rails-app-name = "my-rails-app" feedback-widget-title = "Feedback" server-manual = "Server manual" api-root = "https://www.mongodb.com/docs/mongoid/master/api/Mongoid" -api = "https://www.mongodb.com/docs/mongoid/master/api" -mdb-server = "MongoDB Server" \ No newline at end of file +api = "https://www.mongodb.com/docs/mongoid/master/api" \ No newline at end of file diff --git a/source/data-modeling/field-types.txt b/source/data-modeling/field-types.txt index 707a7df4..78954c12 100644 --- a/source/data-modeling/field-types.txt +++ b/source/data-modeling/field-types.txt @@ -202,7 +202,7 @@ DateTime ~~~~~~~~ When you assign a value to, or query on a ``DateTime`` field, {+odm+} converts -the value to a UTC ``Time`` value before sending it to the {+mdb-server+}. +the value to a UTC ``Time`` value before sending it to the MongoDB server. {+odm+} saves the value with the time zone embedded in the ``DateTime`` object. When you retrieve the value, {+odm+} converts the UTC time to the time zone configured for your application. From b4315fedd5494fb8299a1b4dcbf937a7a3ae0032 Mon Sep 17 00:00:00 2001 From: Jordan Smith Date: Mon, 25 Nov 2024 15:18:32 -0800 Subject: [PATCH 6/8] feedback changes pt 1 --- source/data-modeling/field-types.txt | 156 ++++++++++--------- source/includes/data-modeling/field-types.rb | 30 ++-- 2 files changed, 98 insertions(+), 88 deletions(-) diff --git a/source/data-modeling/field-types.txt b/source/data-modeling/field-types.txt index 78954c12..a43de1c7 100644 --- a/source/data-modeling/field-types.txt +++ b/source/data-modeling/field-types.txt @@ -20,26 +20,28 @@ Field Types Overview -------- -In this guide, you can learn about the field types that you can use in -{+odm+} to define the schema for your MongoDB documents. +In this guide, you can learn about the field types supported in +{+odm+} that you can use to define the schema for your MongoDB documents. -MongoDB uses :manual:`BSON types ` to represent the type of -data stored in document fields. To use that data in a {+odm+} application, +MongoDB uses :manual:`BSON types ` to represent the data +types stored in document fields. To use BSON data in a {+odm+} application, {+odm+} must convert the BSON types to {+language+} types at runtime. For example, -when retrieving a document from the database, {+odm+} translates a BSON field -with the type ``double`` to use the {+language+} ``Float`` type. It then translates that -field back to a BSON ``double`` when saving the document to the database. +when retrieving a document from the database, {+odm+} translates a BSON +``double`` type to use the {+language+} ``Float`` type. When you save the +document again, {+odm+} converts the field back to a BSON ``double``. + +.. TODO: To learn more about modeling documents in {+odm+}, see :ref:`mongoid-modeling-documents`. .. note:: - Changing the field definition in a model class does not automatically alter - the data stored in the database. To change the data type of a field in the - database, you must re-save the data after the change. + Modifying the field definition in a model class does not change any data + stored in the database. To change the data type of a field in the + database, you must re-save the data again. Field Types ----------- -You can define the field type for fields in a model class by using the ``field`` +You can define field names and types in model classes by using the ``field`` and ``type`` macros. The following example defines the fields of a ``Person`` class: @@ -48,12 +50,12 @@ class: :start-after: # start-define-fields :end-before: # end-define-fields -The following list describes the field types that you can use in {+odm+}: +The following list provides the field types that you can use in {+odm+}: - ``Array`` - ``Bson::Binary`` - ``BigDecimal`` -- ``Mongoid::Boolean`` or ``"Boolean"`` +- ``Mongoid::Boolean`` or ``Boolean`` - ``Date`` - ``DateTime`` - ``Float`` @@ -71,31 +73,31 @@ The following list describes the field types that you can use in {+odm+}: .. note:: - {+odm+} does not support the ``BSON::Int64`` or ``BSON::Int32`` types. - Defining a field as one of these types will save to the database correctly, - but querying the field will return the fields as an ``Integer``. + {+odm+} does not support ``BSON::Int64`` or ``BSON::Int32`` as field types. + {+odm+} saves these values to the database correctly, but when you retrieve + the documents, the fields are returned as ``Integer`` types. - Similarly, querying fields of the ``BSON::Decimal128`` type will return the - fields as a ``BigDecimal`` in BSON versions 5 or later. + Similarly, when querying fields with the ``BSON::Decimal128`` type, {+odm+} + returns the fields as ``BigDecimal`` types. Untyped Fields ~~~~~~~~~~~~~~ -If you don't specify a type for a field, {+odm+} defaults to the ``Object`` +If you don't specify a type for a field, {+odm+} interprets it as the default ``Object`` type. An untyped field can store values of any type that is directly serializable to BSON. You can leave a field untyped if the field might contain different types of data, or if the type of the field's value is not known. -The following example defines a ``Person`` class with an untyped field: +The following example defines a ``Product`` class with an untyped field: .. literalinclude:: /includes/data-modeling/field-types.rb :language: ruby :start-after: # start-define-untyped :end-before: # end-define-untyped -The type of the ``preferences`` field is ``Object`` and will vary depending on +The type of the ``properties`` field is ``Object`` but varies depending on the type of data stored in that field. The following example saves data into the -``preferences`` field in two different ways: +``properties`` field in two different ways: .. literalinclude:: /includes/data-modeling/field-types.rb :language: ruby @@ -103,9 +105,9 @@ the type of data stored in that field. The following example saves data into the :end-before: # end-untyped Because {+odm+} doesn't perform any type conversions on untyped fields when -reading from the database, values that require special handling might not work -correctly in an untyped field. The following BSON data types should not be -stored in untyped fields: +reading from the database, values that require special handling might not be retrieved +correctly in as the value of an untyped field. Do not store the following BSON data types +in untyped fields: - ``Date``: Returns as ``Time`` in untyped fields - ``DateTime``: Returns as ``Time`` in untyped fields @@ -132,8 +134,8 @@ demonstrates how the field is stored and returned: Hash ~~~~ -You can store Hash data in a field by using the ``Hash`` type. When you specify -a field as a ``Hash``, adhere to the MongoDB :manual:`Naming +You can store ``Hash`` data in a field by using the ``Hash`` type. When you specify +a field as a ``Hash``, ensure that you follow the MongoDB :manual:`Naming Restrictions ` to ensure that the values store properly in the database. @@ -148,15 +150,15 @@ as a ``Hash``. Time ~~~~ -You can store values as ``Time`` instances by using the ``Time`` field value. +You can store values as BSON ``Time`` instances by using the ``Time`` field value. ``Time`` fields are stored in the time zone configured for your application. To learn more about configuring time zones, see the :ref:`Time Zones ` guide. .. TODO: Update Time Zones guide ref if it's changed during standardization -The following example creates a ``Voter`` class and specifies the -``registered_at`` property as a ``Time`` field: +The following example creates a ``Voter`` class and specifies that the value of the +``registered_at`` field is a ``Time`` type: .. literalinclude:: /includes/data-modeling/field-types.rb :language: ruby @@ -167,7 +169,7 @@ The following example creates a ``Voter`` class and specifies the Storing a ``Date`` or ``DateTime`` value in a field specified as ``Time`` converts the value to ``Time`` when assigned. If you store a string in a - ``Time`` field, {+odm+} parses the string by using the ``Time.parse`` method. + ``Time`` field, {+odm+} parses the string by using the ``Time.parse()`` method. Date ~~~~ @@ -194,20 +196,20 @@ to ``Date`` before assigning them to the field. .. note:: When a database contains a string value for a ``Date`` field, the driver - parses the value by using the ``Time.parse`` method, then discards the time - portion. ``Time.parse`` considers values without time zones to be in local + parses the value by using the ``Time.parse()`` method, then discards the time + portion. ``Time.parse()`` considers values without time zones to be in local time. DateTime ~~~~~~~~ -When you assign a value to, or query on a ``DateTime`` field, {+odm+} converts +When you assign a value to a field defined as a ``DateTime`` or query on these fields, {+odm+} converts the value to a UTC ``Time`` value before sending it to the MongoDB server. {+odm+} saves the value with the time zone embedded in the ``DateTime`` object. When you retrieve the value, {+odm+} converts the UTC time to the time zone configured for your application. -The following example creates a ``Ticket`` class and specifies the ``opened_at`` +The following example creates a ``Ticket`` class and specifies the ``purchased_at`` field as a ``DateTime`` field: .. literalinclude:: /includes/data-modeling/field-types.rb @@ -217,7 +219,7 @@ field as a ``DateTime`` field: If you save an integer or float value to a ``DateTime`` field, the value is treated as a Unix timestamp in UTC. The following example saves an integer value to the -``opened_at`` field: +``purchased_at`` field: .. literalinclude:: /includes/data-modeling/field-types.rb :language: ruby @@ -226,29 +228,34 @@ a Unix timestamp in UTC. The following example saves an integer value to the If you save a string value to a ``DateTime`` field, {+odm+} saves the ticket with the time zone specified. If a time zone is not specified, {+odm+} saves the -value by using the default {+odm+} timezone: +value by using the timezone configured as the default for your application: .. literalinclude:: /includes/data-modeling/field-types.rb :language: ruby :start-after: # start-datetime-string :end-before: # end-datetime-string +To learn more about configuring time zones, see the :ref:`Time Zones ` +guide. + +.. TODO: Update Time Zones guide ref if it's changed during standardization + .. note:: - {+odm+} parses string values into ``DateTime`` by using the ``Time.parse`` + {+odm+} parses string values into ``DateTime`` by using the ``Time.parse()`` method, which considers values without time zones to be in local time. Timestamps ~~~~~~~~~~ -You can include timestamp fields by including the ``Mongoid::Timestamps`` -module when you create your class. Timestamp fields include the following -fields: +You can include timestamp fields in a class by including the ``Mongoid::Timestamps`` +module when you create your class. When you include the ``Mongoid::Timestamps``, +{+odm+} creates the following fields in your class: - ``created_at``: Stores the time the document was created. - ``updated_at``: Stores the time the document was last updated. -The following example creates a ``Person`` class with timestamp fields: +The following example creates a ``Post`` class with timestamp fields: .. literalinclude:: /includes/data-modeling/field-types.rb :language: ruby @@ -257,7 +264,7 @@ The following example creates a ``Person`` class with timestamp fields: You can also choose to include only the ``created_at`` or ``updated_at`` fields by including only the ``Created`` or ``Updated`` modules. The following example -creates a ``Person`` class with only the ``created_at`` field, and a ``Post`` +creates a ``Post`` class with only the ``created_at`` field, and a ``Post`` class with only the ``updated_at`` field: .. literalinclude:: /includes/data-modeling/field-types.rb @@ -287,17 +294,17 @@ Regexp You can store regular expressions in a field by using the ``Regexp`` type. -While MongoDB uses `Perl Compatible Regular Expressions (PCRE) `__, -{+odm+} uses Ruby's `Onigmo `__. PCRE and +While MongoDB implements `Perl Compatible Regular Expressions (PCRE) `__, +{+odm+} uses {+language+}'s `Onigmo `__ library. PCRE and Onigmo provide generally similar functionality, but there are several syntax differences. For example, Onigmo uses ``\A`` and ``\z`` to match the beginning and end of a string, while PCRE uses ``^`` and ``$``. -When you declare a field as a ``Regexp``, {+odm+} converts Ruby regular +When you declare a field as a ``Regexp``, {+odm+} converts {+language+} regular expressions to BSON regular expressions when storing the result into your database. The database returns the field as a ``Bson::Regexp::Raw`` instance. You can use the ``compile`` method on ``BSON::Regexp::Raw`` instances to convert -the data back to a Ruby regular expression. +the data back to a {+language+} regular expression. The following example creates a ``Token`` class and specifies the ``pattern`` field as a ``Regexp``: @@ -309,23 +316,23 @@ field as a ``Regexp``: .. important:: - Converting a BSON regular expression to a Ruby regular expression might + Converting a BSON regular expression to a {+language+} regular expression might produce a different regular expression than the original. This difference is - due to the differences between the syntax Onigmo and PCRE. + due to the differences between the Onigmo and PCRE syntaxes. BigDecimal ~~~~~~~~~~ You can use the ``BigDecimal`` type to store numbers with increased precision. {+odm+} stores ``BigDecimal`` values in two different ways, depending on the -value of ``Mongoid.map_big_decimal_to_decimal128``: +value you set for the ``Mongoid.map_big_decimal_to_decimal128`` configuration property: -- If set to ``True``, {+odm+} stores ``BigDecimal`` values as BSON ``Decimal128`` +- If set to ``true``, {+odm+} stores ``BigDecimal`` values as BSON ``Decimal128`` values. -- If set to ``False`` (default), {+odm+} stores ``BigDecimal`` values as a string. +- If set to ``false`` (default), {+odm+} stores ``BigDecimal`` values as a string. Consider the following limitations when setting the -``Mongoid.map_big_decimal_to_decimal128`` option to true: +``Mongoid.map_big_decimal_to_decimal128`` option to ``true``: - ``Decimal128`` has a limited range and precision. ``Decimal128`` has a maximum value of approximately ``10^6145`` and a minimum @@ -335,7 +342,7 @@ Consider the following limitations when setting the - ``Decimal128`` accepts signed ``NaN`` values, but ``BigDecimal`` does not. Retrieving signed ``NaN`` ``Decimal128`` values from the database as ``BigDecimal`` returns the value unsigned. -- ``Decimal128`` maintains trailing zeroes, but ``BidDecimal`` does not. +- ``Decimal128`` maintains trailing zeroes, but ``BigDecimal`` does not. Because of this, retrieving ``Decimal128`` values from the database as ``BigDecimal`` might result in a loss of precision. @@ -350,8 +357,8 @@ Consider the following limitations when setting the You can avoid this issue by specifying the field as a ``BigDecimal`` type, rather than untyped. -Specify Fields with Strings or Symbols ---------------------------------------- +Specify Field Types as Strings or Symbols +----------------------------------------- You can use strings or symbols to specify certain field types in {+odm+}, instead of using their class names. The following example specifies the ``state`` field by @@ -363,16 +370,16 @@ using the class name, a string, and a symbol: include Mongoid::Document # Class Name - field :state, type: Integer + field :order_num, type: Integer # Symbol - field :state, type: :integer + field :order_num, type: :integer # String - field :state, type: "integer" + field :order_num, type: "integer" end -The following field types can be specified as a string or a symbol: +The following table provides the field types that can you can specify as strings or symbols: .. list-table:: :header-rows: 1 @@ -460,7 +467,7 @@ implements the following methods: an object that MongoDB can store. - ``demongoize``, which takes an object from MongoDB and converts it to an instance of your custom type. -- ``evolve``, which takes an instance of your custom type and converts is to a +- ``evolve``, which takes an instance of your custom type and converts it to a criteria that MongoDB can use to query the database. The following example creates a custom field type called ``Point`` and @@ -503,10 +510,10 @@ color in the application, but stores the color as an integer in the database: Dynamic Fields -------------- -You can instruct {+odm+} to create fields dynamically by setting the +You can instruct {+odm+} to create fields dynamically by inluding the ``Mongoid::Attributes::Dynamic`` module in your model. This allows {+odm+} to -create fields based on an arbitrary hash, or by the documents stored in the -database. +create fields based on an arbitrary hash, or based on the documents already +stored in the database. The following example creates a ``Person`` class with dynamic fields: @@ -515,9 +522,11 @@ The following example creates a ``Person`` class with dynamic fields: :start-after: # start-dynamic-field :end-before: # end-dynamic-field -You can also specify both fixed fields and dynamic fields within the same class. -When doing so, {+odm+} treats all attributes for properties with field definitions -according to their field type, and all other attributes as dynamic. +.. tip:: + + You can specify both fixed fields and dynamic fields within the same class. + In this case, {+odm+} treats all attributes for properties with field definitions + according to their field type, and all other attributes as dynamic. When using dynamic fields in your application, you must initially set the value in one of the following ways: @@ -534,15 +543,15 @@ invoking the attribute returns a ``NoMethodError``. Reserved Characters ------------------- -Both {+odm+} and the MongoDB Query API use the ``.`` character to separate field -names in nested documents, and use the ``$`` character at the beginning of a -word to indicate a query operator. Because of this, you should avoid using these +Both {+odm+} and the MongoDB Query API reserve the ``.`` character to separate field +names in nested documents and the ``$`` character at the beginning of a +string to indicate a query operator. Because of this, you should avoid using these characters in your field names. If your application requires the use of these characters, you can access the fields by calling the ``send`` method. The following example creates a ``User`` class with fields that contain reserved characters. It then accesses the fields -with the ``send`` method: +by using the ``send`` method: .. literalinclude:: /includes/data-modeling/field-types.rb :language: ruby @@ -556,3 +565,8 @@ You can also access these fields by calling the ``read_attribute`` method. Because updating and replacing fields containing these reserved characters requires special operators, calling getters and setters on these fields raises an ``InvalidDotDollarAssignment`` exception. + +.. Additional Information +.. ---------------------- + +.. TODO: Add Additional Information section with links to relevant pages once they are standardized diff --git a/source/includes/data-modeling/field-types.rb b/source/includes/data-modeling/field-types.rb index e9d2d102..eac9e962 100644 --- a/source/includes/data-modeling/field-types.rb +++ b/source/includes/data-modeling/field-types.rb @@ -8,11 +8,11 @@ class Person # end-define-fields # start-define-untyped -class Person +class Product include Mongoid::Document field :name, type: String - field :preferences + field :properties end # end-define-untyped @@ -49,13 +49,9 @@ class Person include Mongoid::Document field :first_name field :url, type: Hash - - def set_vals - self.first_name = 'Daniel' - self.url = {'home_page' => 'http://www.homepage.com'} - save - end end + +person = Person.new(url: {'home_page' => 'http://www.homepage.com'}) # end-hash # start-time @@ -71,31 +67,31 @@ class Voter # start-datetime class Ticket include Mongoid::Document - field :opened_at, type: DateTime + field :purchased_at, type: DateTime end # end-datetime # start-datetime-int -ticket.opened_at = 1544803974 -ticket.opened_at +ticket.purchased_at = 1544803974 +ticket.purchased_at # Outputs: Fri, 14 Dec 2018 16:12:54 +0000 # end-datetime-int # start-datetime-string -ticket.opened_at = 'Mar 4, 2018 10:00:00 +01:00' -ticket.opened_at +ticket.purchased_at = 'Mar 4, 2018 10:00:00 +01:00' +ticket.purchased_at # Outputs: Sun, 04 Mar 2018 09:00:00 +0000 # end-datetime-string # start-timestamps -class Person +class Post include Mongoid::Document include Mongoid::Timestamps end # end-timestamps # start-timestamps-specific -class Person +class Post include Mongoid::Document include Mongoid::Timestamps::Created end @@ -107,7 +103,7 @@ class Post # end-timestamps-specific # start-timestamps-disable -person.timeless.save +post.timeless.save # end-timestamps-disable # start-timestamps-short @@ -225,7 +221,7 @@ class Profile profile.color # Outputs: "white" -# Writes 0 to the database +# Sets "color" field to 0 in profile.save! # end-phantom-field-type From cca558e2d6724ee6dc7dc51bbc7ac0a4a0820cdf Mon Sep 17 00:00:00 2001 From: Jordan Smith Date: Tue, 26 Nov 2024 10:05:57 -0800 Subject: [PATCH 7/8] feedback changes pt 2 --- source/data-modeling/field-types.txt | 67 ++++++++++++++++------------ 1 file changed, 38 insertions(+), 29 deletions(-) diff --git a/source/data-modeling/field-types.txt b/source/data-modeling/field-types.txt index a43de1c7..ee154122 100644 --- a/source/data-modeling/field-types.txt +++ b/source/data-modeling/field-types.txt @@ -113,24 +113,6 @@ in untyped fields: - ``DateTime``: Returns as ``Time`` in untyped fields - ``Range``: Returns as ``Hash`` in untyped fields -StringifiedSymbol -~~~~~~~~~~~~~~~~~ - -Use the ``StringifiedSymbol`` field type to store values that should be exposed -as symbols to {+language+} applications. ``StringifiedSymbol`` allows you to use symbols -while ensuring interoperability with other drivers. This type stores all data on -the database as strings, and converts the strings to symbols when read by the -application. Values that cannot be directly converted to symbols, such as -integers and arrays, are converted into strings and then into symbols. - -The following example stores a ``status`` field as a ``StringifiedSymbol`` and -demonstrates how the field is stored and returned: - -.. literalinclude:: /includes/data-modeling/field-types.rb - :language: ruby - :start-after: # start-stringified-symbol - :end-before: # end-stringified-symbol - Hash ~~~~ @@ -169,7 +151,11 @@ The following example creates a ``Voter`` class and specifies that the value of Storing a ``Date`` or ``DateTime`` value in a field specified as ``Time`` converts the value to ``Time`` when assigned. If you store a string in a - ``Time`` field, {+odm+} parses the string by using the ``Time.parse()`` method. + ``Time`` field, {+odm+} parses the string by using the ``Time.parse()`` + method. + +.. TODO: Add this to the note: +.. To learn more about how {+odm+} converts queries, see :ref:`` Date ~~~~ @@ -200,6 +186,9 @@ to ``Date`` before assigning them to the field. portion. ``Time.parse()`` considers values without time zones to be in local time. +.. TODO: Add this to the note: +.. To learn more about how {+odm+} converts queries, see :ref:`` + DateTime ~~~~~~~~ @@ -281,7 +270,7 @@ the ``::Short`` option when including the module: :end-before: # end-timestamps-short You can disable creating the timestamp field for specific operations by calling -the ``timeless`` method on the method call. The following example disables the +the ``timeless()`` method on the method call. The following example disables the timestamps for the ``save`` operation: .. literalinclude:: /includes/data-modeling/field-types.rb @@ -303,7 +292,7 @@ end of a string, while PCRE uses ``^`` and ``$``. When you declare a field as a ``Regexp``, {+odm+} converts {+language+} regular expressions to BSON regular expressions when storing the result into your database. The database returns the field as a ``Bson::Regexp::Raw`` instance. -You can use the ``compile`` method on ``BSON::Regexp::Raw`` instances to convert +You can use the ``compile()`` method on ``BSON::Regexp::Raw`` instances to convert the data back to a {+language+} regular expression. The following example creates a ``Token`` class and specifies the ``pattern`` @@ -320,6 +309,8 @@ field as a ``Regexp``: produce a different regular expression than the original. This difference is due to the differences between the Onigmo and PCRE syntaxes. +.. TODO: To learn more about regular expressions in {+odm+}, see :ref:`mongoid-regular-expressions`. + BigDecimal ~~~~~~~~~~ @@ -357,6 +348,24 @@ Consider the following limitations when setting the You can avoid this issue by specifying the field as a ``BigDecimal`` type, rather than untyped. +StringifiedSymbol +~~~~~~~~~~~~~~~~~ + +Use the ``StringifiedSymbol`` field type to store values that should be exposed +as symbols to {+language+} applications. ``StringifiedSymbol`` allows you to use symbols +while ensuring interoperability with other drivers. This type stores all data on +the database as strings, and converts the strings to symbols when read by the +application. Values that cannot be directly converted to symbols, such as +integers and arrays, are converted into strings and then into symbols. + +The following example stores a ``status`` field as a ``StringifiedSymbol`` and +demonstrates how the field is stored and returned: + +.. literalinclude:: /includes/data-modeling/field-types.rb + :language: ruby + :start-after: # start-stringified-symbol + :end-before: # end-stringified-symbol + Specify Field Types as Strings or Symbols ----------------------------------------- @@ -478,16 +487,16 @@ implements the preceding methods: :start-after: # start-custom-field-type :end-before: # end-custom-field-type -In the preceding example, the ``mongoize`` *instance method* accepts an instance +In the preceding example, the ``mongoize()`` *instance method* accepts an instance of your custom type object and converts it to an ``Array`` to store in the -database. The ``mongoize`` *class method* accepts objects of all types and +database. The ``mongoize()`` *class method* accepts objects of all types and converts them to similar types that can be stored in the database. {+odm+} uses -the ``mongoize`` class method when it calls the getter and setter methods. +the ``mongoize()`` class method when it calls the getter and setter methods. -The ``demongoize`` method converts the stored ``Array`` value into the custom +The ``demongoize()`` method converts the stored ``Array`` value into the custom ``Point`` type. The {+odm+} uses this method when it calls the getter. -The ``evolve`` method converts the custom ``Point`` type into a queryable +The ``evolve()`` method converts the custom ``Point`` type into a queryable ``Array`` type, and converts all other types to ``object``. {+odm+} uses this method when it calls a method that queries the database. @@ -549,16 +558,16 @@ string to indicate a query operator. Because of this, you should avoid using the characters in your field names. If your application requires the use of these characters, you can access the -fields by calling the ``send`` method. The following example creates a ``User`` +fields by calling the ``send()`` method. The following example creates a ``User`` class with fields that contain reserved characters. It then accesses the fields -by using the ``send`` method: +by using the ``send()`` method: .. literalinclude:: /includes/data-modeling/field-types.rb :language: ruby :start-after: # start-reserved-characters :end-before: # end-reserved-characters -You can also access these fields by calling the ``read_attribute`` method. +You can also access these fields by calling the ``read_attribute()`` method. .. important:: From bed6feb7c14fce14ee8451288f5a010dc3a6e1ab Mon Sep 17 00:00:00 2001 From: Jordan Smith Date: Mon, 2 Dec 2024 12:18:32 -0800 Subject: [PATCH 8/8] final feedback: --- source/data-modeling/field-types.txt | 16 ++++++++-------- source/includes/data-modeling/field-types.rb | 8 ++++---- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/source/data-modeling/field-types.txt b/source/data-modeling/field-types.txt index ee154122..1d4df189 100644 --- a/source/data-modeling/field-types.txt +++ b/source/data-modeling/field-types.txt @@ -320,7 +320,7 @@ value you set for the ``Mongoid.map_big_decimal_to_decimal128`` configuration pr - If set to ``true``, {+odm+} stores ``BigDecimal`` values as BSON ``Decimal128`` values. -- If set to ``false`` (default), {+odm+} stores ``BigDecimal`` values as a string. +- If set to ``false`` (default), {+odm+} stores ``BigDecimal`` values as strings. Consider the following limitations when setting the ``Mongoid.map_big_decimal_to_decimal128`` option to ``true``: @@ -329,7 +329,7 @@ Consider the following limitations when setting the ``Decimal128`` has a maximum value of approximately ``10^6145`` and a minimum of approximately ``-10^6145``, with a maximum of 34 bits of precision. If you are storing values that are outside of these limits, we recommend storing them - as a string instead. + as strings instead. - ``Decimal128`` accepts signed ``NaN`` values, but ``BigDecimal`` does not. Retrieving signed ``NaN`` ``Decimal128`` values from the database as ``BigDecimal`` returns the value unsigned. @@ -346,7 +346,7 @@ Consider the following limitations when setting the database. To find the value, you must first convert the query value to a string. You can avoid this issue by specifying the field as a ``BigDecimal`` type, - rather than untyped. + instead of as untyped. StringifiedSymbol ~~~~~~~~~~~~~~~~~ @@ -358,7 +358,7 @@ the database as strings, and converts the strings to symbols when read by the application. Values that cannot be directly converted to symbols, such as integers and arrays, are converted into strings and then into symbols. -The following example stores a ``status`` field as a ``StringifiedSymbol`` and +The following example defines the ``status`` field as a ``StringifiedSymbol`` and demonstrates how the field is stored and returned: .. literalinclude:: /includes/data-modeling/field-types.rb @@ -370,7 +370,7 @@ Specify Field Types as Strings or Symbols ----------------------------------------- You can use strings or symbols to specify certain field types in {+odm+}, instead of -using their class names. The following example specifies the ``state`` field by +using their class names. The following example specifies the ``order_num`` field by using the class name, a string, and a symbol: .. code-block:: ruby @@ -472,11 +472,11 @@ You can create custom field types and define how {+odm+} serializes and deserializes them. To create a custom field type, define a class that implements the following methods: -- ``mongoize``, which takes an instance of your custom type and converts it to +- ``mongoize()``: Takes an instance of your custom type and converts it to an object that MongoDB can store. -- ``demongoize``, which takes an object from MongoDB and converts it to an +- ``demongoize()``: Takes an object from MongoDB and converts it to an instance of your custom type. -- ``evolve``, which takes an instance of your custom type and converts it to a +- ``evolve()``: Takes an instance of your custom type and converts it to a criteria that MongoDB can use to query the database. The following example creates a custom field type called ``Point`` and diff --git a/source/includes/data-modeling/field-types.rb b/source/includes/data-modeling/field-types.rb index eac9e962..f740815a 100644 --- a/source/includes/data-modeling/field-types.rb +++ b/source/includes/data-modeling/field-types.rb @@ -107,17 +107,17 @@ class Post # end-timestamps-disable # start-timestamps-short -class Band +class Post include Mongoid::Document include Mongoid::Timestamps::Short # For c_at and u_at. end -class Band +class Post include Mongoid::Document include Mongoid::Timestamps::Created::Short # For c_at only. end -class Band +class Post include Mongoid::Document include Mongoid::Timestamps::Updated::Short # For u_at only. end @@ -221,7 +221,7 @@ class Profile profile.color # Outputs: "white" -# Sets "color" field to 0 in +# Sets "color" field to 0 in MongoDB profile.save! # end-phantom-field-type