diff --git a/.dockerignore b/.dockerignore deleted file mode 100644 index c2c70813..00000000 --- a/.dockerignore +++ /dev/null @@ -1,2 +0,0 @@ -# Cannot dockerignore .git - giza insists on using git on the docs repo -/build diff --git a/.github/workflows/check-autobuilder.yml b/.github/workflows/check-autobuilder.yml deleted file mode 100644 index 7e15fe91..00000000 --- a/.github/workflows/check-autobuilder.yml +++ /dev/null @@ -1,13 +0,0 @@ -name: Check Autobuilder for Errors - -on: - pull_request: - paths: - - "source/**" - -jobs: - check: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - uses: cbush/snooty-autobuilder-check@main \ No newline at end of file diff --git a/.github/workflows/copy-compat-to-docs-shared.yml b/.github/workflows/copy-compat-to-docs-shared.yml new file mode 100644 index 00000000..f04fa143 --- /dev/null +++ b/.github/workflows/copy-compat-to-docs-shared.yml @@ -0,0 +1,80 @@ +name: Copy Files to docs-shared + +on: + workflow_dispatch: {} # use to manually trigger workflow + push: + branches: + - "master" + paths: + - "source/includes/mongodb-compatibility-table-mongoid.rst" + - "source/includes/language-compatibility-table-mongoid.rst" + - "source/includes/rails-compatibility-table-mongoid.rst" + - "source/includes/ror-compatibility-table-mongoid.rst" + - "source/includes/ruby-driver-compatibility-table-mongoid.rst" + +jobs: + copy-file: + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v3 + + - name: Copy mongodb-compat table + uses: dmnemec/copy_file_to_another_repo_action@main + env: + API_TOKEN_GITHUB: ${{ secrets.API_TOKEN_GITHUB }} + with: + source_file: "source/includes/mongodb-compatibility-table-mongoid.rst" + destination_repo: "10gen/docs-shared" + destination_folder: "dbx" + user_email: "docs-builder-bot@mongodb.com" + user_name: "docs-builder-bot" + commit_message: "Auto-import from docs-mongoid" + + - name: Copy language-compat table + uses: dmnemec/copy_file_to_another_repo_action@main + env: + API_TOKEN_GITHUB: ${{ secrets.API_TOKEN_GITHUB }} + with: + source_file: "source/includes/language-compatibility-table-mongoid.rst" + destination_repo: "10gen/docs-shared" + destination_folder: "dbx" + user_email: "docs-builder-bot@mongodb.com" + user_name: "docs-builder-bot" + commit_message: "Auto-import from docs-mongoid" + + - name: Copy ruby-on-rails-compat table + uses: dmnemec/copy_file_to_another_repo_action@main + env: + API_TOKEN_GITHUB: ${{ secrets.API_TOKEN_GITHUB }} + with: + source_file: "source/includes/rails-compatibility-table-mongoid.rst" + destination_repo: "10gen/docs-shared" + destination_folder: "dbx" + user_email: "docs-builder-bot@mongodb.com" + user_name: "docs-builder-bot" + commit_message: "Auto-import from docs-mongoid" + + - name: Copy rails-feature-compat table + uses: dmnemec/copy_file_to_another_repo_action@main + env: + API_TOKEN_GITHUB: ${{ secrets.API_TOKEN_GITHUB }} + with: + source_file: "source/includes/ror-compatibility-table-mongoid.rst" + destination_repo: "10gen/docs-shared" + destination_folder: "dbx" + user_email: "docs-builder-bot@mongodb.com" + user_name: "docs-builder-bot" + commit_message: "Auto-import from docs-mongoid" + + - name: Copy ruby-driver-compat table + uses: dmnemec/copy_file_to_another_repo_action@main + env: + API_TOKEN_GITHUB: ${{ secrets.API_TOKEN_GITHUB }} + with: + source_file: "source/includes/ruby-driver-compatibility-table-mongoid.rst" + destination_repo: "10gen/docs-shared" + destination_folder: "dbx" + user_email: "docs-builder-bot@mongodb.com" + user_name: "docs-builder-bot" + commit_message: "Auto-import from docs-mongoid" diff --git a/.github/workflows/vale-tdbx.yml b/.github/workflows/vale-tdbx.yml index e33882b5..84de594a 100644 --- a/.github/workflows/vale-tdbx.yml +++ b/.github/workflows/vale-tdbx.yml @@ -12,27 +12,30 @@ jobs: - name: checkout uses: actions/checkout@v4 + - name: Install docutils + run: sudo apt-get install -y docutils + - id: files uses: masesgroup/retrieve-changed-files@v2 with: - format: 'csv' + format: "csv" - name: checkout-latest-rules uses: actions/checkout@v4 with: repository: mongodb/mongodb-vale-action - path: './tdbx-vale-rules' + path: "./tdbx-vale-rules" token: ${{secrets.GITHUB_TOKEN}} - name: move-files-for-vale-action run: | - cp tdbx-vale-rules/.vale.ini .vale.ini - mkdir -p .github/styles/ - cp -rf tdbx-vale-rules/.github/styles/ .github/ + cp tdbx-vale-rules/.vale.ini .vale.ini + mkdir -p .github/styles/ + cp -rf tdbx-vale-rules/.github/styles/ .github/ - name: run-vale uses: errata-ai/vale-action@reviewdog with: reporter: github-pr-check files: ${{steps.files.outputs.added_modified}} fail_on_error: true - token: ${{secrets.GITHUB_TOKEN}} \ No newline at end of file + token: ${{secrets.GITHUB_TOKEN}} diff --git a/.gitignore b/.gitignore deleted file mode 100644 index 2e79a6f4..00000000 --- a/.gitignore +++ /dev/null @@ -1,4 +0,0 @@ -.DS_Store -giza.log -build/ -.yardoc diff --git a/Dockerfile b/Dockerfile deleted file mode 100644 index 1fabdde6..00000000 --- a/Dockerfile +++ /dev/null @@ -1,27 +0,0 @@ -# Debian 9 ships with Python 3.5 which is not usable with mut -# (https://jira.mongodb.org/browse/DOP-1301, fixed by -# https://github.com/p-mongo/mut/commit/5beefc1c9ac2a9afabb86d918bfe598477b927e1). -# Debian 10 ships with Python 3.7 which is supposed to work but it doesn't -# per https://jira.mongodb.org/browse/DOP-1303. -# Use Debian 9 with the patch. -FROM debian:9 - -ENV DEBIAN_FRONTEND=noninteractive - -RUN apt-get update && \ - apt-get -y install make git python-pip python3-pip \ - ruby yard pkg-config libxml2-dev && \ - python -m pip install giza && \ - python3 -m pip install mut - -# Apply the fix in -# https://github.com/p-mongo/mut/commit/5beefc1c9ac2a9afabb86d918bfe598477b927e1 -RUN git clone https://github.com/p-mongo/mut && \ - cd mut && \ - python3 setup.py install - -WORKDIR /app - -COPY . . - -ENTRYPOINT ["./entrypoint.sh"] diff --git a/Makefile b/Makefile deleted file mode 100644 index a6b19a5b..00000000 --- a/Makefile +++ /dev/null @@ -1,112 +0,0 @@ -# Makefile for Mongoid docs - -GIT_BRANCH := $(shell git rev-parse --abbrev-ref HEAD) -USER=$(shell whoami) -STAGING_URL="https://docs-mongodborg-staging.corp.mongodb.com" -PRODUCTION_URL="https://docs.mongodb.com" - -STAGING_BUCKET=docs-mongodb-org-prd-staging -PRODUCTION_BUCKET=docs-mongodb-org-prd - -SEARCH_INDEX_BUCKET=docs-search-indexes-test - -PROJECT=mongoid -PREFIX=mongoid -TARGET_DIR=source-${GIT_BRANCH} - -SOURCE_FILE_DIR=build/mongoid-${GIT_BRANCH} -DOTCOM_STAGING_URL="https://mongodbcom-cdn.website.staging.corp.mongodb.com" -DOTCOM_STAGING_BUCKET=docs-mongodb-org-dotcomstg -DOTCOM_PRODUCTION_URL="https://mongodb.com" -DOTCOM_PRODUCTION_BUCKET=docs-mongodb-org-dotcomprd -DOTCOM_PREFIX=docs/mongoid -DOTCOM_STGPREFIX=docs-qa/mongoid - -# Parse our published-branches configuration file to get the name of -# the current "stable" branch. This is weird and dumb, yes. -STABLE_BRANCH=`grep 'manual' build/docs-tools/data/${PROJECT}-published-branches.yaml | cut -d ':' -f 2 | grep -Eo '[0-9a-z.]+'` - -.PHONY: help stage fake-deploy deploy deploy-search-index api-docs get-assets migrate clean - -help: ## Show this help message - @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-20s\033[0m %s\n", $$1, $$2}' - @echo - @echo 'Variables' - @printf " \033[36m%-18s\033[0m %s\n" 'ARGS' 'Arguments to pass to mut-publish' - -html: migrate ## Builds this branch's HTML under build//html - giza make html - -## Migrate the files from the driver repo and build the dirhtml for publishing -## In course of building, will use yard to build API docs (takes a while) -# you must install yard -# generate the api docs from the mongoid project and output to the build dir - -debug: - @echo ${DOTCOM_PRODUCTION_BUCKET} - -publish: migrate ## Builds this branch's publishable HTML and other artifacts under build/public - giza make publish - @echo "Making api directory in /build/public/${GIT_BRANCH}" - if [ -d build/public/${GIT_BRANCH}/api ]; then rm -rf build/public/${GIT_BRANCH}/api ; fi; - mkdir build/public/${GIT_BRANCH}/api - - yard doc ${SOURCE_FILE_DIR} --exclude ${SOURCE_FILE_DIR}/spec --exclude ${SOURCE_FILE_DIR}/perf --readme ${SOURCE_FILE_DIR}/README.md -o build/public/${GIT_BRANCH}/api/ - if [ ${GIT_BRANCH} = master ]; then mut-redirects config/redirects -o build/public/.htaccess; fi - -stage: ## Host online for review - mut-publish build/${GIT_BRANCH}/html ${DOTCOM_STAGING_BUCKET} --prefix=${DOTCOM_STGPREFIX} --stage ${ARGS} - @echo "Hosted at ${DOTCOM_STAGING_URL}/${DOTCOM_STGPREFIX}/${USER}/${GIT_BRANCH}/index.html" - - - -fake-deploy: build/public/${GIT_BRANCH} ## Create a fake deployment in the staging bucket - mut-publish build/public ${DOTCOM_STAGING_BUCKET} --prefix=${DOTCOM_STGPREFIX} --deploy --verbose ${ARGS} - @echo "Hosted at ${DOTCOM_STAGING_URL}/${DOTCOM_STGPREFIX}/${GIT_BRANCH}/index.html" - - - -deploy: build/public/${GIT_BRANCH} ## Deploy to the production bucket - mut-publish build/public/ ${DOTCOM_PRODUCTION_BUCKET} --prefix=${DOTCOM_PREFIX} --deploy --redirects build/public/.htaccess ${ARGS} - @echo "Hosted at ${DOTCOM_PRODUCTION_URL}/${DOTCOM_PREFIX}/${GIT_BRANCH}" - - $(MAKE) deploy-search-index - -deploy-search-index: ## Update the search index for this branch - @echo "Building search index" - if [ ${STABLE_BRANCH} = ${GIT_BRANCH} ]; then \ - mut-index upload build/public/${GIT_BRANCH} -o ${PROJECT}-${GIT_BRANCH}.json -u ${PRODUCTION_URL}/${PROJECT}/${GIT_BRANCH} -b ${SEARCH_INDEX_BUCKET} -p search-indexes/prd -g -s --exclude build/public/${GIT_BRANCH}/api; \ - else \ - mut-index upload build/public/${GIT_BRANCH} -o ${PROJECT}-${GIT_BRANCH}.json -u ${PRODUCTION_URL}/${PROJECT}/${GIT_BRANCH} -b ${SEARCH_INDEX_BUCKET} -p search-indexes/prd -s --exclude build/public/${GIT_BRANCH}/api; \ - fi - -# in case you want to just generate the api-docs -# generate the api docs -# you must install yard -# generate the api docs from the mongoid project and output to the build dir - -api-docs: - @echo "Making api directory in /build/public/${GIT_BRANCH}" - if [ -d build/public/${GIT_BRANCH}/api ]; then rm -rf build/public/${GIT_BRANCH}/api ; fi; - mkdir build/public/${GIT_BRANCH}/api - - yard doc ${SOURCE_FILE_DIR} --exclude ${SOURCE_FILE_DIR}/spec --exclude ${SOURCE_FILE_DIR}/perf --readme ${SOURCE_FILE_DIR}/README.md -o build/public/${GIT_BRANCH}/api/ - - -migrate: get-assets - @echo "Making target source directory" - if [ -d ${TARGET_DIR} ]; then rm -rf ${TARGET_DIR} ; fi; - mkdir ${TARGET_DIR} - - - @echo "Copying over mongoid doc files" - cp -r ${SOURCE_FILE_DIR}/docs/* ${TARGET_DIR}/ - -# This gets the docs-tools and the mongoid docs from the mongoid repo. -# the assets are defined in the config/build_conf.yaml - -get-assets: - giza generate assets - -clean: - rm -rf build giza.log source-master diff --git a/README.rst b/README.rst index d719f98c..d58e8179 100644 --- a/README.rst +++ b/README.rst @@ -1,135 +1,23 @@ -===================== -Mongoid Documentation -===================== +============================= +MongoDB Mongoid Documentation +============================= -This repository contains documentation for Mongoid, the object-document -mapper for MongoDB in Ruby. - -Build Locally -------------- - -To build the documentation locally, - -- Install `giza `_, if not already - installed. Note that giza requires Python 2.7 and does not work with Python 3. - There is an `installation guide - `_ on the MongoDB meta site - to help you get started. - -- Run the following to download and build this documentation locally:: - - git clone git@github.com:mongodb/docs-mongoid - cd docs-mongoid/ - make html - -The generated HTML will be placed in `build/master/html/`. - -*Note*: The build process invokes Sphinx and expects Sphinx's various -binaries to be in PATH. - -Stage ------ - -Install `mut `_ following its installation -instructions. Note that mut requires Python 3 and does not work with Python 2.7. - -Request access to the documentation staging bucket. Obtain AWS S3 -access key id and secret access key. - -Create ``~/.config/giza-aws-authentication.conf`` with the following contents:: - - [authentication] - accesskey= - secretkey= - -(If you run ``make stage`` without configuring authentication, it will -also give you these instructions.) - -Finally to publish the docs to the staging bucket, run:: - - make stage - -The output will include a URL to the published documentation. - -Deploy To Live Site -------------------- - -Install `mut `_ following its installation -instructions. Note that mut requires Python 3 and does not work with Python 2.7. - -Request access to the production documentation bucket. Obtain AWS S3 -access key id and secret access key. - -Create ``~/.config/giza-aws-authentication.conf`` with the following contents:: - - [authentication] - accesskey= - secretkey= - -Publish the docs to the production bucket:: - - make publish deploy - -Using Docker ------------- - -Build the docker image:: - - docker build -t docs-mongoid . - -Build the documentation to ``build`` subdir, clobbering its existing contents:: - - rm -rf build && \ - mkdir build && \ - docker run -itv `pwd`/build:/build \ - docs-mongoid sh -c 'make html && rsync -av build/ /build' - -Note that since Docker container runs everything as root, the built files -will also be owned by root and will require root access to modify or remove. - -Deploy to live site (no need to configure ``~/.config/giza-aws-authentication.conf`` -on the host system):: - - docker run -it \ - -e AWS_ACCESS_KEY_ID= \ - -e AWS_SECRET_ACCESS_KEY= \ - docs-mongoid make publish deploy - -Contribute ----------- - -To contribute to the documentation, please sign the `MongoDB -Contributor Agreement -`_ if you have not -already. - -See the following documents that provide an overview of the -documentation style and process: - -- `Style Guide `_ -- `Documentation Practices `_ -- `Documentation Organization `_ -- `Build Instructions `_ +This repository contains documentation for the MongoDB Mongoid ODM for Ruby. File JIRA Tickets ----------------- -File issue reports or requests at the `Documentation Jira Project -`_. - -Listed Mongoid Branches ------------------------ - -The branch list as well as which is the current one is maintained here: https://github.com/mongodb/docs-tools/blob/master/data/mongoid-published-branches.yaml +Please file issue reports or requests at the `Documentation Jira Project +`__. Licenses -------- All documentation is available under the terms of a `Creative Commons -License `_. +License `__. The MongoDB Documentation Project is governed by the terms of the `MongoDB Contributor Agreement -`_. +`__. -- The MongoDB Docs Team diff --git a/conf-sitemap.xml b/conf-sitemap.xml deleted file mode 100644 index 1be77f4c..00000000 --- a/conf-sitemap.xml +++ /dev/null @@ -1,26 +0,0 @@ - - - - - - - - - - - - - - - - - - diff --git a/conf.py b/conf.py deleted file mode 100644 index 04b71b94..00000000 --- a/conf.py +++ /dev/null @@ -1,115 +0,0 @@ -# -*- coding: utf-8 -*- -# -# MongoDB documentation build configuration file, created by -# sphinx-quickstart on Mon Oct 3 09:58:40 2011. -# -# This file is execfile()d with the current directory set to its containing dir. - -import sys -import os.path -import datetime - -from sphinx.errors import SphinxError - -from giza.config.runtime import RuntimeStateConfig -from giza.config.helper import fetch_config, get_versions, get_manual_path - -conf = fetch_config(RuntimeStateConfig()) -sconf = conf.system.files.data.sphinx_local - -sys.path.append(os.path.join(conf.paths.projectroot, conf.paths.buildsystem, 'sphinxext')) - - -# -- General configuration ---------------------------------------------------- - -needs_sphinx = '1.0' - -extensions = [ - 'sphinx.ext.extlinks', - 'sphinx.ext.todo', - 'mongodb', - 'directives', - 'intermanual', - 'fasthtml' -] - -locale_dirs = [ os.path.join(conf.paths.projectroot, conf.paths.locale) ] -gettext_compact = False - -templates_path = ['.templates'] -exclude_patterns = [] - -source_suffix = '.txt' - -master_doc = sconf.master_doc -language = 'en' -project = sconf.project - -copyright = u'2008-{0}'.format(datetime.date.today().year) - -version = conf.version.branch -release = conf.version.release - - -rst_epilog = '\n'.join([ - '.. |branch| replace:: ``{0}``'.format(conf.git.branches.current), - '.. |copy| unicode:: U+000A9', - '.. |year| replace:: {0}'.format(datetime.date.today().year), - '.. |hardlink| replace:: http://docs.mongodb.com/mongoid', -]) - -pygments_style = 'sphinx' - -extlinks = { - 'manual': ('https://www.mongodb.com/docs/manual%s', ''), -} - -## add `extlinks` for each published version. -for i in conf.git.branches.published: - extlinks[i] = ( ''.join([ conf.project.url, '/', i, '%s' ]), '' ) - -intersphinx_mapping = {} - -intersphinx_mapping = {} -for i in conf.system.files.data.intersphinx: - intersphinx_mapping[i.name] = ( i.url, os.path.join(conf.paths.projectroot, - conf.paths.output, - i.path)) - -languages = [] -# -- Options for HTML output --------------------------------------------------- - -html_theme = sconf.theme.name -html_theme_path = [ os.path.join(conf.paths.buildsystem, 'themes') ] -html_title = conf.project.title -htmlhelp_basename = 'MongoDB' - -html_logo = ".static/logo-mongodb.png" - -html_copy_source = False -html_domain_indices = True -html_use_index = False -html_split_index = False -html_show_sourcelink = False -html_show_sphinx = True -html_show_copyright = True - - -html_theme_options = { - 'branch': conf.git.branches.current, - 'manual_path': get_manual_path(conf), - 'translations': languages, - 'language': language, - 'repo_name': sconf.theme.repo, - 'jira_project': sconf.theme.jira, - 'google_analytics': sconf.theme.google_analytics, - 'project': sconf.theme.project, - 'version': version, - 'version_selector': get_versions(conf), - 'active_branches': conf.version.active, - 'stable': conf.version.stable, - 'sitename': sconf.theme.sitename, - 'nav_excluded': sconf.theme.nav_excluded, -} - -html_sidebars = sconf.sidebars diff --git a/config/build_conf.yaml b/config/build_conf.yaml deleted file mode 100644 index c6673055..00000000 --- a/config/build_conf.yaml +++ /dev/null @@ -1,41 +0,0 @@ -git: - repo: 'mongodb/docs-mongoid' - remote: - upstream: 'mongodb/docs-mongoid' - tools: 'mongodb/docs-tools' -project: - name: 'mongoid' - tag: 'mongoid' - url: 'https://docs.mongodb.com/mongoid' - title: 'Mongoid Manual' - branched: true -version: - release: 'upcoming' - branch: 'master' -system: - runstate: - serial_sphinx: publish - files: - - 'intersphinx.yaml' - - 'integration.yaml' - - 'sphinx_local.yaml' - - 'push.yaml' - - htaccess: ['htaccess.yaml'] -assets: - - branch: master - path: build/docs-tools - repository: https://github.com/mongodb/docs-tools.git - - branch: master - path: build/mongoid-master - repository: https://github.com/mongodb/mongoid.git -paths: - output: 'build' - source: 'source-master' - includes: 'source-master/includes' - images: 'source-master/images' - tools: 'bin' - buildsystem: 'build/docs-tools' - builddata: 'config' - locale: 'locale' - -... \ No newline at end of file diff --git a/config/htaccess.yaml b/config/htaccess.yaml deleted file mode 100644 index b6cb9da8..00000000 --- a/config/htaccess.yaml +++ /dev/null @@ -1,31 +0,0 @@ -# Each YAML document has the following schema: -# -# from: "the path to the redirected URL" -# to: "the path to the new location for the resource" -# type: "currently this script only supports 'redirect', and -# ignores all documents with another type." -# code: "the HTTP redirect code. Raises exception if not 301, 302, 303." -# outputs: "a list of branches/paths as follows:" -# -# - 'all': generates redirects for all published branches, but *not* 'manual'. But giza will take care of manual as part of integration.yaml logic in redirects.py -# -# - '': generates redirects for a specific branch. -# -# - 'before-': generates redirects for all branches that -# reflect releases previous to the specified branch. (Inclusive.) -# -# - 'after-': generates redirects for all branches that -# reflect releases after the specified branch. (Non-inclusive.) -# -# All paths are relative to the ``source/`` directory. -# -######################################################################## -### Giza requires this file, even if no redirects -### More as a safety -- in case, we remove redirects during deploys -from: '/' -to: '/master' -type: 'redirect' -code: 301 -outputs: - - {'/mongoid': 'https://docs.mongodb.com/mongoid'} -... diff --git a/config/integration.yaml b/config/integration.yaml deleted file mode 100644 index 406866dd..00000000 --- a/config/integration.yaml +++ /dev/null @@ -1,10 +0,0 @@ -base: - links: - - { '6.2': 'master' } - targets: - - 'html' - - 'dirhtml' - doc-root: - - 'sitemap.xml' - - '.htaccess' - branch-root: [] diff --git a/config/push.yaml b/config/push.yaml deleted file mode 100644 index a3795069..00000000 --- a/config/push.yaml +++ /dev/null @@ -1,2 +0,0 @@ -### Apparently, giza requires this file, even if empty -### Must include ref to this file in the build_conf.yaml diff --git a/config/redirects b/config/redirects index 2516d155..6be7b119 100644 --- a/config/redirects +++ b/config/redirects @@ -1,10 +1,72 @@ define: prefix docs/mongoid -define: base https://www.mongodb.com/docs/mongoid -define: versions 8.1 upcoming +define: base https://www.mongodb.com/${prefix} +define: versions v8.0 v8.1 v9.0 master -symlink: current -> 8.1 +symlink: current -> v9.0 +symlink: upcoming -> master raw: ${prefix}/ -> ${base}/current/ -raw: ${prefix}/master -> ${base}/upcoming/ +raw: ${prefix}/stable -> ${base}/current/ -raw: ${prefix}/current/release-notes/mongoid-7.0 -> ${base}/current/ +# Redirects for standardized (new) page URLs in old docs +[*-v8.1]: ${prefix}/${version}/compatibility/ -> ${base}/${version}/reference/compatibility/ +[*-v8.1]: ${prefix}/${version}/configuration/ -> ${base}/${version}/reference/configuration/ +[*-v8.1]: ${prefix}/${version}/integrations-tools/rails-integration/ -> ${base}/${version}/reference/rails-integration/ +[*-v8.1]: ${prefix}/${version}/quick-start-sinatra/ -> ${base}/${version}/tutorials/getting-started-sinatra/ +[*-v8.1]: ${prefix}/${version}/quick-start-rails/ -> ${base}/${version}/tutorials/getting-started-rails7/ +[*-v8.1]: ${prefix}/${version}/data-modeling/documents/ -> ${base}/${version}/tutorials/documents/ +[*-v8.1]: ${prefix}/${version}/security/encryption/ -> ${base}/${version}/tutorials/automatic-encryption/ +[*-v8.1]: ${prefix}/${version}/data-modeling/field-types/ -> ${base}/${version}/reference/fields/ +[*-v8.1]: ${prefix}/${version}/data-modeling/inheritance/ -> ${base}/${version}/reference/inheritance/ +[*-v8.1]: ${prefix}/${version}/data-modeling/associations/ -> ${base}/${version}/reference/associations/ +[*-v8.1]: ${prefix}/${version}/data-modeling/validation/ -> ${base}/${version}/reference/validation/ +[*-v8.1]: ${prefix}/${version}/configuration/collection-config/ -> ${base}/${version}/reference/collection-configuration/ +[*-v8.1]: ${prefix}/${version}/data-modeling/indexes/ -> ${base}/${version}/reference/indexes/ +[*-v8.1]: ${prefix}/${version}/configuration/sharding/ -> ${base}/${version}/reference/sharding/ +[*-v8.1]: ${prefix}/${version}/interact-data/crud/ -> ${base}/${version}/reference/crud/ +[*-v8.1]: ${prefix}/${version}/interact-data/specify-query/ -> ${base}/${version}/reference/queries/ +[*-v8.1]: ${prefix}/${version}/interact-data/text-search/ -> ${base}/${version}/reference/text-search/ +[*-v8.1]: ${prefix}/${version}/interact-data/aggregation/ -> ${base}/${version}/reference/aggregation/ +[*-v8.1]: ${prefix}/${version}/configuration/persistence-config/ -> ${base}/${version}/reference/persistence-configuration/ +[*-v8.1]: ${prefix}/${version}/interact-data/nested-attributes/ -> ${base}/${version}/reference/nested-attributes/ +[*-v8.1]: ${prefix}/${version}/data-modeling/callbacks/ -> ${base}/${version}/reference/callbacks/ +[*-v8.1]: ${prefix}/${version}/interact-data/transaction/ -> ${base}/${version}/reference/transactions/ +[*-v8.1]: ${prefix}/${version}/whats-new/ -> ${base}/${version}/release-notes/mongoid-9.0/ +[*-v8.1]: ${prefix}/${version}/code-documentation/ -> ${base}/${version}/contributing/code-documentation/ +[*-v8.1]: ${prefix}/${version}/issues-and-help/ -> ${base}/${version}/contributing/contributing-guidelines/ +[*-v8.1]: ${prefix}/${version}/integrations-tools/external-resources/ -> ${base}/${version}/ecosystem/ + +# Redirects for old page URLs in standardized (new) docs +[v9.0-*]: ${prefix}/${version}/installation/ -> ${base}/${version}/#quick-start +[v9.0-*]: ${prefix}/${version}/reference/compatibility/ -> ${base}/${version}/compatibility/ +[v9.0-*]: ${prefix}/${version}/reference/configuration/ -> ${base}/${version}/configuration/ +[v9.0-*]: ${prefix}/${version}/reference/rails-integration/ -> ${base}/${version}/integrations-tools/rails-integration/ +[v9.0-*]: ${prefix}/${version}/tutorials/getting-started-sinatra/ -> ${base}/${version}/quick-start-sinatra/ +[v9.0-*]: ${prefix}/${version}/tutorials/getting-started-rails7/ -> ${base}/${version}/quick-start-rails/ +[v9.0-*]: ${prefix}/${version}/tutorials/getting-started-rails6/ -> ${base}/${version}/quick-start-rails/ +[v9.0-*]: ${prefix}/${version}/tutorials/documents/ -> ${base}/${version}/data-modeling/documents/ +[v9.0-*]: ${prefix}/${version}/tutorials/common-errors/ -> ${base}/${version}/ +[v9.0-*]: ${prefix}/${version}/tutorials/automatic-encryption/ -> ${base}/${version}/security/encryption/ +[v9.0-*]: ${prefix}/${version}/reference/fields/ -> ${base}/${version}/data-modeling/field-types/ +[v9.0-*]: ${prefix}/${version}/reference/inheritance/ -> ${base}/${version}/data-modeling/inheritance/ +[v9.0-*]: ${prefix}/${version}/reference/associations/ -> ${base}/${version}/data-modeling/associations/ +[v9.0-*]: ${prefix}/${version}/reference/validation/ -> ${base}/${version}/data-modeling/validation/ +[v9.0-*]: ${prefix}/${version}/reference/collection-configuration/ -> ${base}/${version}/configuration/collection-config/ +[v9.0-*]: ${prefix}/${version}/reference/indexes/ -> ${base}/${version}/data-modeling/indexes/ +[v9.0-*]: ${prefix}/${version}/reference/sharding/ -> ${base}/${version}/configuration/sharding/ +[v9.0-*]: ${prefix}/${version}/reference/crud/ -> ${base}/${version}/interact-data/crud/ +[v9.0-*]: ${prefix}/${version}/reference/queries/ -> ${base}/${version}/interact-data/specify-query/ +[v9.0-*]: ${prefix}/${version}/reference/text-search/ -> ${base}/${version}/interact-data/text-search/ +[v9.0-*]: ${prefix}/${version}/reference/aggregation/ -> ${base}/${version}/interact-data/aggregation/ +[v9.0-*]: ${prefix}/${version}/reference/map-reduce/ -> ${base}/${version}/interact-data/aggregation/ +[v9.0-*]: ${prefix}/${version}/reference/persistence-configuration/ -> ${base}/${version}/configuration/persistence-config/ +[v9.0-*]: ${prefix}/${version}/reference/nested-attributes/ -> ${base}/${version}/interact-data/nested-attributes/ +[v9.0-*]: ${prefix}/${version}/reference/callbacks/ -> ${base}/${version}/data-modeling/callbacks/ +[v9.0-*]: ${prefix}/${version}/reference/sessions/ -> ${base}/${version}/interact-data/transaction/ +[v9.0-*]: ${prefix}/${version}/reference/transactions/ -> ${base}/${version}/interact-data/transaction/ +[v9.0-*]: ${prefix}/${version}/release-notes/upgrading/ -> ${base}/${version}/whats-new/ +[v9.0-*]: ${prefix}/${version}/release-notes/mongoid-9.0/ -> ${base}/${version}/whats-new/ +[v9.0-*]: ${prefix}/${version}/contributing/code-documentation/ -> ${base}/${version}/code-documentation/ +[v9.0-*]: ${prefix}/${version}/contributing/contributing-guidelines/ -> ${base}/${version}/issues-and-help/ +[v9.0-*]: ${prefix}/${version}/additional-resources/ -> ${base}/${version}/integrations-tools/external-resources/ +[v9.0-*]: ${prefix}/${version}/ecosystem/ -> ${base}/${version}/integrations-tools/external-resources/ diff --git a/config/sphinx.yaml b/config/sphinx.yaml deleted file mode 100644 index 9272a19b..00000000 --- a/config/sphinx.yaml +++ /dev/null @@ -1,31 +0,0 @@ -# The items in the ``builders`` list are the name of Sphinx builders -# supported by ``sphinx-build``. -# -# The ``prerequsites`` list stores all targets that must build before -# sphinx can begin. -# -# The ``generated-source`` list stores all the targets that generate rst. - - -prerequisites: - - setup - - intersphinx - - generate-source -generated-source: - - tables - - api - - toc - - intersphinx - - images -### --- #### -web-base: - tags: - - web -### --- #### -dirhtml: - builder: dirhtml - inherit: web-base -html: - builder: html - inherit: web-base -... \ No newline at end of file diff --git a/config/sphinx_local.yaml b/config/sphinx_local.yaml deleted file mode 100644 index 3bd81124..00000000 --- a/config/sphinx_local.yaml +++ /dev/null @@ -1,18 +0,0 @@ -project: 'mongoid-manual' -master_doc: 'index' -logo: ".static/logo.png" -paths: - static: ['source/.static'] -theme: - name: 'mongoid' - project: 'mongoid' - google_analytics: 'UA-7301842-8' - book_path_base: 'mongoid' - repo: 'docs-mongoid' - jira: 'DOCS' - sitename: 'Mongoid' - nav_excluded: [] -sidebars: - '**': - - 'pagenav.html' -... diff --git a/entrypoint.sh b/entrypoint.sh deleted file mode 100755 index 023892ed..00000000 --- a/entrypoint.sh +++ /dev/null @@ -1,9 +0,0 @@ -#!/bin/sh - -set -ex - -if test -n "$1"; then - exec "$@" -else - make publish -fi diff --git a/snooty.toml b/snooty.toml index 9eb93a00..35af2fcd 100644 --- a/snooty.toml +++ b/snooty.toml @@ -1,8 +1,35 @@ name = "mongoid" title = "Mongoid" -intersphinx = ["https://www.mongodb.com/docs/manual/objects.inv"] +intersphinx = [ + "https://www.mongodb.com/docs/manual/objects.inv", + "https://www.mongodb.com/docs/drivers/objects.inv", + "https://www.mongodb.com/docs/atlas/objects.inv", +] + +sharedinclude_root = "https://raw.githubusercontent.com/10gen/docs-shared/main/" + +toc_landing_pages = [ + "/quick-start-rails", + "/quick-start-sinatra", + "/interact-data/specify-query", + "/issues-and-help" +] [constants] -rails-6-version = 6.0 -rails-7-version = 7.1 +rails-8-version-docs = "v8.0" +odm = "Mongoid" +version = "9.0" +full-version = "{+version+}.4" +ruby-driver = "Ruby driver" +language = "Ruby" +ror = "{+language+} on Rails" +quickstart-sinatra-app-name = "my-sinatra-app" +quickstart-rails-app-name = "my-rails-app" +feedback-widget-title = "Feedback" +server-manual = "Server manual" +api = "https://www.mongodb.com/docs/mongoid/current/api" +ruby-api = "https://www.mongodb.com/docs/ruby-driver/current/api" +active-record-docs = "https://guides.rubyonrails.org" +shared-library = "Automatic Encryption Shared Library" +mdb-server = "MongoDB Server" diff --git a/source/.gitignore b/source/.gitignore deleted file mode 100644 index e35d8850..00000000 --- a/source/.gitignore +++ /dev/null @@ -1 +0,0 @@ -_build diff --git a/source/Makefile b/source/Makefile deleted file mode 100644 index d4bb2cbb..00000000 --- a/source/Makefile +++ /dev/null @@ -1,20 +0,0 @@ -# Minimal makefile for Sphinx documentation -# - -# You can set these variables from the command line, and also -# from the environment for the first two. -SPHINXOPTS ?= -SPHINXBUILD ?= sphinx-build -SOURCEDIR = . -BUILDDIR = _build - -# Put it first so that "make" without argument is like "make help". -help: - @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) - -.PHONY: help Makefile - -# Catch-all target: route all unknown targets to Sphinx using the new -# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). -%: Makefile - @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) diff --git a/source/README.md b/source/README.md deleted file mode 100644 index a3e415fa..00000000 --- a/source/README.md +++ /dev/null @@ -1,14 +0,0 @@ -Mongoid Documentation -================================= - -This subdirectory contains the high-level driver documentation, including -tutorials and the reference. - -To build the documentation locally for review, install `sphinx` and -`sphinx-book-theme`, then execute `make html` in this directory: - - pip install 'sphinx<4.3' sphinx-book-theme - make html - -Note: sphinx 4.3 is currently breaking when trying to render Mongoid -documentation. diff --git a/source/additional-resources.txt b/source/additional-resources.txt deleted file mode 100644 index 8de82da5..00000000 --- a/source/additional-resources.txt +++ /dev/null @@ -1,61 +0,0 @@ -******************** -Additional Resources -******************** - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 2 - :class: singlecol - -This page lists some of the third-party guides and blog posts about Mongoid, -as well as sample Mongoid applications. Additional resources for the driver -are listed on the `respective driver page -`_. - - -Screencasts -=========== - -- `RailsCasts: Mongoid (revised) - `_ - - An overview of Mongoid, by Ryan Bates including the basics - of setting up an app, querying for documents, adding embedded - associations, overriding the id, and more. - -- `Ruby on Rails Web Services and Integration with MongoDB, Week 3: Mongoid - `_ - - A detailed introduction to Mongoid and Ruby on Rails web services. - -- `Create a search bar in Rails with Mongoid - `_ - - A Tutorial explaining how to implement text search with Mongoid. - - -Articles -======== - -- `A Simple Content Management System in Sinatra `_ - - Building a content management application with Sinatra and Mongoid. - -- `How To Create A Ruby API With Sinatra `_ - - Creating a Sinatra API with Mongoid. - -- `Converting an existing Ruby on Rails application to MongoDB `_ - - How to Convert an existing Ruby on Rails application to use MongoDB and Mongoid. - - -Sample Applications -=================== - -- `Mongoid Demo `_ - - A repository containing sample applications using Mongoid. diff --git a/source/api.txt b/source/api.txt new file mode 100644 index 00000000..8a1121db --- /dev/null +++ b/source/api.txt @@ -0,0 +1,15 @@ +.. _mongoid-api-landing: + +================= +API Documentation +================= + +.. meta:: + :description: Read the API documentation for Mongoid and the Ruby Driver. + +.. toctree:: + :titlesonly: + :maxdepth: 1 + + {+odm+} <{+api+}> + {+language+} Driver <{+ruby-api+}> diff --git a/source/code-documentation.txt b/source/code-documentation.txt new file mode 100644 index 00000000..5f10ea21 --- /dev/null +++ b/source/code-documentation.txt @@ -0,0 +1,353 @@ +.. _mongoid-code-documentation: + +================== +Code Documentation +================== + +.. contents:: On this page + :local: + :backlinks: none + :depth: 2 + :class: singlecol + +Overview +-------- + +In this guide, you can learn about {+odm+}'s code documentation +conventions. If you have a suggestion to improve the API documentation +or introduce a new feature, you can use this guide to help you format +your documentation. + +{+odm+} uses a variation of :github:`YARD `, a {+language+} +documentation tool, for its code documentation. + +.. _mongoid-code-documentation-structure: + +Structure +--------- + +- **Modules:** All class and module definitions must be preceded by + a documentation comment. + + .. code-block:: ruby + + # This is the documentation for the class. It's amazing + # what they do with corrugated cardboard these days. + class CardboardBox + +- **Methods:** All method definitions must be preceded by a + documentation comment. Use ``@param``, ``@yield``, and ``@return`` to + specify inputs and output. To learn more, see the + :ref:`mongoid-code-documentation-type-declaration` section. + + .. code-block:: ruby + + # Turn a person into whatever they'd like to be. + # + # @param [ Person ] person The human to transmogrify. + # + # @return [ Tiger ] The transmogrified result. + def transmogrify(person) + +- **Errors:** Use ``@raise`` to describe errors specific to the method. + + .. code-block:: ruby + + # @raise [ Errors::Validations ] If validation failed. + def validate! + +- **Private Methods:** Private methods must be documented unless they are + so brief and straightforward that it is obvious what they do. Note that, + for example, a method might be brief and straightforward but the type of + its parameter may not be obvious, in which case the parameter must be + appropriately documented. + + .. code-block:: ruby + + private + + # Documentation is optional here. + def do_something_obvious + +- **API Private:** Classes and public methods which are not intended for + external usage must be marked ``@api private``. This macro does not + require a comment. + + Note that, because {+odm+}'s modules are mixed into application classes, + ``private`` visibility of a method does not necessarily indicate its + status as an API private method. + + .. code-block:: ruby + + # This is an internal-only method. + # + # @api private + def dont_call_me_from_outside + +- **Notes and TODOs:** Use ``@note`` to explain caveats, edge cases, + and behavior which may surprise users. Use ``@todo`` to record + follow-ups and suggestions for future improvement. + + .. code-block:: ruby + + # Clear all stored data. + # + # @note This operation deletes data in the database. + # @todo Refactor this method for performance. + def erase_data! + +- **Deprecation:** Use the ``@deprecated`` macro to indicate deprecated + functionality. This macro does not require a comment. + + .. code-block:: ruby + + # This is how we did things back in the day. + # + # @deprecated + def the_old_way + +.. _mongoid-code-documentation-formatting: + +Formatting +---------- + +- **Line Wrapping:** Use double-space indent when wrapping lines of macros. + Do not indent line wraps in the description. + + .. code-block:: ruby + + # This is the description of the method. Line wraps in the description + # must not be indented. + # + # @return [ Symbol ] For macros, wraps must be double-space indented + # on the second, third (and so on) lines. + +- **Whitespace:** Do not use leading/trailing empty comment lines, + or more than one consecutive empty comment line. + + .. code-block:: ruby + + # DO THIS: + # @return [ Symbol ] The return value + def my_method + + # NOT THIS: + # @return [ Symbol ] The return value + # + def my_method + + # NOT THIS: + # @param [ Symbol ] foo The input value + # + # + # @return [ Symbol ] The return value + def my_method(foo) + +.. _mongoid-code-documentation-type-declaration: + +Type Declaration +---------------- + +- **Type Unions:** Use pipe ``|`` to denote a union of allowed types. + + .. code-block:: ruby + + # @param [ Symbol | String ] name Either a Symbol or a String. + +- **Nested Types:** Use angle brackets ``< >`` to denote type nesting. + + .. code-block:: ruby + + # @param [ Array ] array An Array of symbols. + +- **Hash:** Use comma ``,`` to denote the key and value types. + + .. code-block:: ruby + + # @param [ Hash ] hash A Hash whose keys are Symbols, + # and whose values are Integers. + +- **Array:** Use pipe ``|`` to denote a union of allowed types. + + .. code-block:: ruby + + # @param [ Array ] array An Array whose members must + # be either Symbols or Strings. + +- **Array:** Use comma ``,`` to denote the types of each position in a tuple. + + .. code-block:: ruby + + # @return [ Array ] A 3-member Array whose first + # element is a Symbol, and whose second and third elements are Integers. + +- **Array:** Use pipe ``|`` on the top level if the inner types cannot be + mixed within the Array. + + .. code-block:: ruby + + # @param [ Array | Array ] array An Array containing only + # Symbols, or an Array containing only Hashes. The Array may not contain + # a mix of Symbols and Hashes. + +- **Nested Types:** For clarity, use square brackets ``[ ]`` to denote nested unions + when commas are also used. + + .. code-block:: ruby + + # @param [ Hash ] hash A Hash whose keys are Symbols, + # and whose values are boolean values. + +- **{+language+} Values:** Specific values may be denoted in the type + using {+language+} syntax. + + .. code-block:: ruby + + # @param [ :before | :after ] timing One of the Symbol values :before or :after. + +- **True, False, and Nil:** Use ``true``, ``false``, and ``nil`` rather than + ``TrueClass``, ``FalseClass``, and ``NilClass``. Do not use ``Boolean`` as a type + since it does not exist in {+language+}. + + .. code-block:: ruby + + # DO THIS: + # @param [ true | false | nil ] bool A boolean or nil value. + + # NOT THIS: + # @param [ TrueClass | FalseClass | NilClass ] bool A boolean or nil value. + # @param [ Boolean ] bool A boolean value. + +- **Return Self:** Specify return value ``self`` where a method returns ``self``. + + .. code-block:: ruby + + # @return [ self ] Returns the object itself. + +- **Splat Args:** Use three-dot ellipses ``...`` in the type declaration and + star ``*`` in the parameter name to denote a splat. + + .. code-block:: ruby + + # @param [ String... ] *items The list of items' names as Strings. + def buy_groceries(*items) + +- **Splat Args:** Do not use ``Array`` as the type unless each arg is actually an Array. + + .. code-block:: ruby + + # DO NOT DO THIS: + # @param [ Array ] *items The list of items' names as Strings. + def buy_groceries(*items) + + buy_groceries("Cheese", "Crackers", "Wine") + + # DO THIS: + # @param [ Array... ] *arrays One or more arrays containing name parts. + def set_people_names(*arrays) + + set_people_names(["Harlan", "Sanders"], ["Jane", "K", ""Doe"], ["Jim", "Beam"]) + +- **Splat Args:** Use comma ``,`` to denote positionality in a splat. + + .. code-block:: ruby + + # @param [ Symbol..., Hash ] *args A list of names, followed by a hash + # as the optional last arg. + def say_hello(*args) + +- **Splat Args:** Specify type unions with square brackets ``[ ]``. + + .. code-block:: ruby + + # @param [ [ String | Symbol ]... ] *fields A splat of mixed Symbols and Strings. + +- **Keyword Arguments:** Following YARD conventions, use ``@param`` for keyword + arguments, and specify keyword argument names as symbols. + + .. code-block:: ruby + + # @param [ String ] query The search string + # @param [ Boolean ] :exact_match Whether to do an exact match + # @param [ Integer ] :results_per_page Number of results + def search(query, exact_match: false, results_per_page: 10) + +- **Hash Options:** Define hash key-value options with ``@option`` macro + immediately following the Hash ``@param``. Note ``@option`` parameter names + are symbols. + + .. code-block:: ruby + + # @param opts [ Hash ] The optional hash argument or arguments. + # @option opts [ String | Array ] :items The items as Strings to include. + # @option opts [ Integer ] :limit An Integer denoting the limit. + def buy_groceries(opts = {}) + +- **Double Splats:** Use double-star ``**`` in the parameter name to denote a + keyword arg splat (double splat). Note that type does not need declared on + the double-splat element, as it is implicitly . Instead, + define value types with ``@option`` macro below. Note ``@option`` parameter + names are symbols. + + .. code-block:: ruby + + # @param **kwargs The optional keyword argument or arguments. + # @option **kwargs [ String | Array ] :items The items as Strings to include. + # @option **kwargs [ Integer ] :limit An Integer denoting the limit. + def buy_groceries(**kwargs) + +- **Blocks:** Use ``@yield`` to specify when the method yields to a block. + + .. code-block:: ruby + + # @yield [ Symbol, Symbol, Symbol ] Evaluate the guess of who did the crime. + # Must take the person, location, and weapon used. Must return true or false. + def whodunit + yield(:mustard, :ballroom, :candlestick) + end + +- **Blocks:** If the method explicitly specifies a block argument, specify the block + argument using ``@param`` preceded by an ampersand ``&``, and also specify ``@yield``. + Note ``@yield`` must be used even when method calls ``block.call`` rather than + ``yield`` internally. + + .. code-block:: ruby + + # @param &block The block. + # @yield [ Symbol, Symbol, Symbol ] Evaluate the guess of who did the crime. + # Must take the person, location, and weapon used. Must return true or false. + def whodunit(&block) + yield(:scarlet, :library, :rope) + end + + # @param &block The block. + # @yield [ Symbol, Symbol, Symbol ] Evaluate the guess of who did the crime. + # Must take the person, location, and weapon used. Must return true or false. + def whodunit(&block) + block.call(:plum, :kitchen, :pipe) + end + +- **Blocks:** Use ``@yieldparam`` and ``@yieldreturn`` instead of ``@yield`` where + beneficial for clarity. + + .. code-block:: ruby + + # @param &block The block. + # @yieldparam [ Symbol ] The person. + # @yieldparam [ Symbol ] The location. + # @yieldparam [ Symbol ] The weapon used. + # @yieldreturn [ true | false ] Whether the guess is correct. + def whodunit(&block) + yield(:peacock, :conservatory, :wrench) + end + +- **Proc Args:** Use ``@param`` (not ``@yield``) for proc arguments. The + inputs to the proc may be specified as subtypes. + + .. code-block:: ruby + + # @param [ Proc ] my_proc Proc argument which must + # take 3 integers and must return true or false whether the guess is valid. + def guess_three(my_proc) + my_proc.call(42, 7, 88) + end diff --git a/source/compatibility.txt b/source/compatibility.txt new file mode 100644 index 00000000..9ce29e41 --- /dev/null +++ b/source/compatibility.txt @@ -0,0 +1,108 @@ +.. _mongoid-compatibility: + +============= +Compatibility +============= + +.. contents:: On this page + :local: + :backlinks: none + :depth: 2 + :class: singlecol + +.. facet:: + :name: genre + :values: reference + +.. meta:: + :keywords: backwards compatibility, versions, upgrade + +{+language+} Driver Compatibility +------------------------- + +The following compatibility table specifies the versions of the +:ruby:`{+ruby-driver+} ` that are compatible with each +{+odm+} version. + +.. note:: Patch Version Compatibility + + Older patch versions of {+odm+} within the same minor release might + support older {+ruby-driver+} versions. For example, {+odm+} v7.0.5 + supports {+ruby-driver+} v2.5 and later, but {+odm+} v7.0.6 requires + driver v2.7 or later. + +.. include:: /includes/ruby-driver-compatibility-table-mongoid.rst + +MongoDB Compatibility +--------------------- + +The following compatibility table specifies the recommended version or versions +of {+odm+} that you can use with a specific version of MongoDB. To use +features of a particular {+mdb-server+} version, both the +{+ruby-driver+} and {+odm+} must be compatible with that MongoDB +version. To learn about the driver's MongoDB compatibility details, +see :ruby:`Compatibility ` +in the {+ruby-driver+} documentation. + +The first column lists the version of {+odm+}. + +.. sharedinclude:: dbx/lifecycle-schedule-callout.rst + +.. sharedinclude:: dbx/compatibility-table-legend.rst + +.. include:: /includes/mongodb-compatibility-table-mongoid.rst + +To learn more about how to read the compatibility tables, see the guide +on :ref:`MongoDB Compatibility Tables `. + +Language Compatibility +---------------------- + +The following compatibility table specifies the recommended version or +versions of {+odm+} that you can use with specific {+language+} +interpreter versions. + +The first column lists the version of {+odm+}. + +.. include:: /includes/language-compatibility-table-mongoid.rst + +To learn more about how to read the compatibility tables, see the guide +on :ref:`MongoDB Compatibility Tables `. + +{+ror+} Compatibility +--------------------------- + +The following compatibility table specifies which versions of {+ror+} +are supported by {+odm+}. + +.. include:: /includes/rails-compatibility-table-mongoid.rst + +Rails Frameworks Support +~~~~~~~~~~~~~~~~~~~~~~~~ + +{+odm+} is compatible with many of the frameworks that comprise {+ror+}. +In this section, you can learn about which frameworks you can use with {+odm+}. + +{+odm+} attempts to offer API compatibility with `Active Record +<{+active-record-docs+}/active_record_basics.html>`__, but libraries +that depend directly on Active Record might not work as expected if you +use {+odm+} as a direct replacement. + +.. note:: + + You can use {+odm+} alongside Active Record within the same + application. + +.. include:: /includes/ror-compatibility-table-mongoid.rst + +How to Get Help +--------------- + +If you have questions about compatibility, visit the following resources +for further guidance: + +- Ask questions on our :community-forum:`MongoDB Community Forums <>`. +- Visit the :technical-support:`Support Channels `. +- File an issue or feature request in Jira, our issue tracker. You can + find instructions on filing a ticket on the + :ref:`mongoid-issues-and-help` page. diff --git a/source/conf.py b/source/conf.py deleted file mode 100644 index b634583a..00000000 --- a/source/conf.py +++ /dev/null @@ -1,57 +0,0 @@ -# Configuration file for the Sphinx documentation builder. -# -# This file only contains a selection of the most common options. For a full -# list see the documentation: -# https://www.sphinx-doc.org/en/master/usage/configuration.html - -# -- Path setup -------------------------------------------------------------- - -# If extensions (or modules to document with autodoc) are in another directory, -# add these directories to sys.path here. If the directory is relative to the -# documentation root, use os.path.abspath to make it absolute, like shown here. -# -# import os -# import sys -# sys.path.insert(0, os.path.abspath('.')) - - -# -- Project information ----------------------------------------------------- - -project = 'Mongoid' -copyright = '2021, MongoDB' - - -# -- General configuration --------------------------------------------------- - -# Add any Sphinx extension module names here, as strings. They can be -# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom -# ones. -extensions = [ -] - -# Add any paths that contain templates here, relative to this directory. -templates_path = ['_templates'] - -# List of patterns, relative to source directory, that match files and -# directories to ignore when looking for source files. -# This pattern also affects html_static_path and html_extra_path. -exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] - - -# -- Options for HTML output ------------------------------------------------- - -# The theme to use for HTML and HTML Help pages. See the documentation for -# a list of builtin themes. -# -html_theme = 'alabaster' - -# Add any paths that contain custom static files (such as style sheets) here, -# relative to this directory. They are copied after the builtin static files, -# so a file named "default.css" will overwrite the builtin "default.css". -html_static_path = ['_static'] - -source_suffix = { - '.txt': 'restructuredtext', -} - -html_theme = 'sphinx_book_theme' diff --git a/source/configuration.txt b/source/configuration.txt new file mode 100644 index 00000000..aa20b4a1 --- /dev/null +++ b/source/configuration.txt @@ -0,0 +1,46 @@ +.. _mongoid-configuration: + +============= +Configuration +============= + +.. facet:: + :name: genre + :values: reference + +.. meta:: + :keywords: ruby framework, odm, setup, config + +.. toctree:: + :caption: Configuration + + Application Configuration + Persistence Targets + Sharding + Logging + Query Cache Middleware + Forking Servers + Collection Configuration + +In this section, you can learn how to configure different options with {+odm+}. + +- :ref:`mongoid-app-config`: Learn about settings you can use to + customize how your application works with MongoDB. + +- :ref:`mongoid-persistence`: Learn how to use {+odm+} to view and + customize your document storage. + +- :ref:`mongoid-sharding-configuration`: Learn how to configure + sharding in your application. + +- :ref:`mongoid-logging-config`: Learn how to configure loggers in your + application. + +- :ref:`mongoid-query-cache-config`: Learn how to implement query cache + middleware in your application. + +- :ref:`mongoid-forking-server-config`: Learn how to configure your + application to use a forking web server. + +- :ref:`mongoid-collection-config`: Learn how to specify configuration options + for a MongoDB collection in your application. diff --git a/source/configuration/app-config.txt b/source/configuration/app-config.txt new file mode 100644 index 00000000..013ecf56 --- /dev/null +++ b/source/configuration/app-config.txt @@ -0,0 +1,362 @@ +.. _mongoid-app-config: + +========================= +Application Configuration +========================= + +.. facet:: + :name: genre + :values: reference + +.. meta:: + :keywords: code example, customize, behavior + +.. contents:: On this page + :local: + :backlinks: none + :depth: 2 + :class: singlecol + +Overview +-------- + +In this guide, you can learn how to configure how your application +connects to MongoDB and how it processes your data. When you set up your +application, you are required to supply a **connection string**, or +connection URI, which contains a set of instructions that {+odm+} uses to +connect to a MongoDB. To learn more about connection strings, see +:manual:`Connection Strings ` in the +{+server-manual+}. + +You primarily configure {+odm+} by using a ``mongoid.yml`` file that +specifies your connection options and clients. To learn more about +creating a ``mongoid.yml`` file when setting up an application, see one +of the following guides: + +- :ref:`mongoid-quick-start-rails` +- :ref:`mongoid-quick-start-sinatra` + +Structure of mongoid.yml +------------------------ + +The simplest configuration for an application configures {+odm+} to +connect to a MongoDB server at ``localhost:27017`` and access the database +named ``myDB``. The ``mongoid.yml`` file for this configuration +resembles the following file: + +.. code-block:: yaml + :caption: mongoid.yml + + development: + clients: + default: + database: myDB + hosts: + - localhost:27017 + +The top level key in the preceding file, ``development``, refers to the +environment name which the application is running. Possible values include +``development``, ``test`` or ``production``. The third level key, +``default``, refers to the MongoDB client name. Most applications use a +single client named ``default``. + +Generate Default Configuration +------------------------------ + +If you are using {+ror+} as your application framework, you can generate +a default configuration file in your application by running the +following shell command: + +.. code-block:: bash + + rails g mongoid:config + +This command creates the configuration file at ``config/mongoid.yml``. +An initializer is also created at ``config/initializers/mongoid.rb``. We +recommended that you specify all settings in ``mongoid.yml``, but you can +also use the ``mongoid.rb`` initializer to set configuration options. +Settings in ``mongoid.yml`` always override settings in the initializer. + +If you are not using {+ror+}, you can create the ``config/mongoid.yml`` +file, then copy and paste the configuration file shown in the preceding +section. + +Load {+odm+} Configuration +-------------------------- + +.. important:: + + You must configure {+odm+} before using any {+odm+} component. After you + use or reference a component, changing the configuration might not apply + changes to already instantiated components. + +If you are using {+ror+}, Rails automatically loads your {+odm+} +configuration for the current environment as stored in ``Rails.env``. + +You can also specify {+odm+} as the ORM for your application by +adding the following lines to your ``application.rb`` file: + +.. code-block:: ruby + + config.generators do |g| + g.orm :mongoid + end + +If you are not using {+ror+}, you must load your {+odm+} configuration +manually. Call the ``Mongoid.load!`` method, which takes the +configuration file path as its argument: + +.. code-block:: ruby + + # Uses automatically detected environment name + Mongoid.load!("config/mongoid.yml") + + # Specifies environment name manually + Mongoid.load!("config/mongoid.yml", :development) + +{+odm+} detects the environment name by examining the following sources, +in order: + +1. If ``Rails`` top-level constant is defined: ``Rails.env`` +#. If ``Sinatra`` top-level constant is defined: ``Sinatra::Base.environment`` +#. ``RACK_ENV`` environment variable +#. ``MONGOID_ENV`` environment variable. + +.. note:: + + You can also configure {+odm+} directly in {+language+} without using + a configuration file. This configuration style does not support the + concept of environments. Any configuration that you provide is + applied to the current environment. + +.. _mongoid-config-options-all: + +Configuration Options +--------------------- + +The following annotated example ``mongoid.yml`` file describes where to +set each type of configuration option and provides information about +each option and its parameters. + +.. literalinclude:: /includes/configuration/sample-config-options.yml + :language: yaml + +Version Based Defaults +---------------------- + +{+odm+} supports setting configuration options to their default values +for specific versions. This might be useful for when you update to a new +{+odm+} version. When upgrading your {+odm+} version, set the following +options on ``Mongoid::Config``: + +.. code-block:: ruby + + Mongoid.configure do |config| + config.load_defaults + end + +This allows you to upgrade to a new version of {+odm+} while using +the configuration options from the previous installed version. +This feature gives you more time to implement tests for each changed +behavior to make sure your code doesn't break or behave in unexpected +ways. After you verify that your application works as expected, you can +remove this code to use the current version's default configuration. + +ERb Pre-processing +------------------ + +When loading a configuration file, {+odm+} processes it by using +:github:`ERb ` before parsing it as YAML. This allows {+odm+} +to construct the contents of the configuration file at runtime +based on environment variables. + +Because {+odm+} performs ERb rendering before YAML parsing, all ERb +directives in the configuration file are evaluated, including those +occurring in YAML comments. + +The following sample ``mongoid.yml`` file demonstrates how you can +reference an environment variable that stores your connection string: + +.. code-block:: yaml + + development: + clients: + default: + uri: "<%= ENV['MONGODB_URI'] %>" + +.. tip:: + + When outputting values from ERb, ensure the values are valid YAML + and escape them as needed. + +.. _mongoid-config-time-zones: + +Time Zone Configuration +----------------------- + +{+odm+} uses Active Support's time zone functionality, which provides +more functionality than {+language+}'s standard library. Active Support +allows configuration of the ``Time.zone`` variable, a thread-global +variable which provides context for working with date and time values. + +You can implement correct time zone handling in your application by +performing the following steps: + +1. Set the operating system's time zone to UTC. + +#. Set your application default time zone to UTC, as shown in the + following code: + + .. code-block:: ruby + + # If using Rails, in `application.rb`: + class Application < Rails::Application + config.time_zone = 'UTC' + end + + # If not using Rails: + Time.zone = 'UTC' + +#. In each controller and job class, set the appropriate time zone in a + ``before_filter`` callback: + + .. code-block:: ruby + + class ApplicationController < ActionController::Base + before_filter :fetch_user, :set_time_zone + + def set_time_zone + Time.zone = @user.time_zone + end + end + +#. Then, you can work with times in the local time zone. + +#. Use Active Support methods instead of the {+language+} standard library. + + - Use the ``Time.zone.now`` or ``Time.current`` methods instead of + ``Time.now`` + - Use the ``Date.current`` method instead of ``Date.today`` + + The {+language+} standard library methods such as ``Time.now`` and + ``Date.today`` reference your system time zone and not the value of + the ``Time.zone`` variable. + + You might mistake these similarly named methods, so we recommend + using the `Rails/TimeZone + `__ + feature from RuboCop in your tests. + +Set Time Zone on MongoDB Data +----------------------------- + +MongoDB stores all times in UTC without time zone information. +{+odm+} models load and return time values as instances of +``ActiveSupport::TimeWithZone``. You can set the ``use_utc`` option +to control how {+odm+} sets the time zone when loading values from the +database: + +- ``false`` (*default*): {+odm+} uses the value of ``Time.zone`` to set + the time zone of values from the database. +- ``true``: {+odm+} always sets the time zone as UTC on loaded + time values. + +The ``use_utc`` option affects only how data is loaded and does not affect +how data is persisted. For example, if you assign a ``Time`` or +``ActiveSupport::TimeWithZone`` instance to a time field, the time +zone information of the assigned instance is used +regardless of the ``use_utc`` value. + +Alternatively, if you assign a string value to a time field, any time +zone information in the string is parsed. If the string does not include +time zone information it is parsed according to the ``Time.zone`` value. + +The following code sets a ``Time.zone`` value and demonstrates how +{+odm+} processes different time strings: + +.. code-block:: ruby + + Time.use_zone("Asia/Kolkata") do + + # String does not include time zone, so "Asia/Kolkata" is used + ghandi.born_at = "1869-10-02 7:10 PM" + + # Time zone in string (-0600) is used + amelia.born_at = "1897-07-24 11:30 -0600" + end + +SSL/TLS Configuration +--------------------- + +You can configure advanced TLS options in your application, such as +enabling or disabling certain ciphers. To learn about the main SSL/TLS +options, see the :ref:`mongoid-config-options-all` section of this guide. + +You can set TLS context hooks on the {+ruby-driver+}. TLS context +hooks are user-provided ``Proc`` objects that are invoked before any TLS +socket connection is created in the driver. These hooks can be used to +modify the underlying ``OpenSSL::SSL::SSLContext`` object used by the +socket. + +To set TLS context hooks, add a ``Proc`` to the ``Mongo.tls_context_hooks`` +array. This task can be done in an initializer. The following example adds a hook +that only enables the ``"AES256-SHA"`` cipher: + +.. code-block:: ruby + + Mongo.tls_context_hooks.push( + Proc.new { |context| + context.ciphers = ["AES256-SHA"] + } + ) + +Every ``Proc`` in ``Mongo.tls_context_hooks`` is passed an +``OpenSSL::SSL::SSLContext`` object as its sole argument. These ``Proc`` +objects are run sequentially during socket creation. + +.. warning:: + + TLS context hooks are global and affect all ``Mongo::Client`` + instances in an application. + +To learn more about TLS context hooks, see :ruby:`Modifying SSLContext +` in the {+ruby-driver+} +documentation. + +Network Compression +------------------- + +{+odm+} supports compression of messages to and from MongoDB. This +functionality is provided by the {+ruby-driver+}, which implements the +following supported algorithms: + +- `Zstandard `__ (*Recommended*): To use ``zstandard`` + compression, you must install the :rubygems:`zstd-ruby + ` library. This compressor + produces the highest compression at the same CPU consumption compared + to the other compressors. +- :github:`Snappy `: To use ``snappy`` compression, you + must install the :rubygems:`snappy ` library. +- `Zlib `__: To use ``zlib`` compression, you + must install the :rubygems:`zlib ` library. + +To use wire protocol compression, configure the driver options +in your ``mongoid.yml`` file: + +.. code-block:: yaml + + development: + clients: + default: + ... + options: + # Specify compresses to use. (default is no compression) + # Accepted values are zstd, zlib, snappy, or a combination + compressors: ["zstd", "snappy"] + +If you do not explicitly request any compressors, the driver does not +use compression, even if the required dependencies for one or more +compressors are installed. + +The driver chooses the first compressor, if you specify multiple, that +is supported by your MongoDB deployment. diff --git a/source/configuration/collection-config.txt b/source/configuration/collection-config.txt new file mode 100644 index 00000000..2cf28c06 --- /dev/null +++ b/source/configuration/collection-config.txt @@ -0,0 +1,115 @@ +.. _mongoid-collection-config: + +======================== +Collection Configuration +======================== + +.. facet:: + :name: genre + :values: reference + +.. meta:: + :keywords: code example, collections, time series, capped collection + +.. contents:: On this page + :local: + :backlinks: none + :depth: 2 + :class: singlecol + +Overview +-------- + +In this guide, you can learn how to specify configuration options for a +collection in your {+odm+} application. + +Configure Collection Options +---------------------------- + +You can specify configuration options for a collection by using the +``:collection_options`` argument with the ``store_in`` +macro. The ``:collection_options`` argument accepts any collection option that +your {+ruby-driver+} and {+mdb-server+} versions support. + +.. note:: + + You must explicitly create a collection to apply any specified collection + options. Create your collection by running the collection management Rake task, as + shown in :ref:`mongoid-create-collection-rake` section of this guide. + +To learn more about collection options available in the {+ruby-driver+}, see the +:ruby:`Collections ` guide in the {+ruby-driver+} +documentation. + +The following sections show examples of how to configure collection options when +using {+odm+}. + +Time Series Collection +~~~~~~~~~~~~~~~~~~~~~~ + +Time series collections efficiently store sequences of measurements over a +period of time. The following example shows how to configure a time series +collection: + +.. literalinclude:: /includes/configuration/collection-config.rb + :language: ruby + :start-after: # start-time-series-config + :end-before: # end-time-series-config + :emphasize-lines: 7-13 + +To learn more about time series collections, see the :manual:`Time Series Collections +` guide in the MongoDB {+server-manual+}. + +Capped Collection +~~~~~~~~~~~~~~~~~ + +Capped collections have maximum size or document counts that prevent them from +growing beyond a specified threshold. The following example shows how to configure +a capped collection: + +.. literalinclude:: /includes/configuration/collection-config.rb + :language: ruby + :start-after: # start-capped-collection-config + :end-before: # end-capped-collection-config + :emphasize-lines: 4-7 + +To learn more about capped collections, see the :manual:`Capped Collections +` guide in the MongoDB {+server-manual+}. + +Default Collation +~~~~~~~~~~~~~~~~~ + +Collations are sets of rules for how to compare strings, typically in a +particular natural language. The following example shows how to specify a +default collation to use on a +collection: + +.. literalinclude:: /includes/configuration/collection-config.rb + :language: ruby + :start-after: # start-default-collation-config + :end-before: # end-default-collation-config + :emphasize-lines: 4-8 + +To learn more about collations, see the :manual:`Collation +` guide in the MongoDB {+server-manual+}. + +.. _mongoid-create-collection-rake: + +Collection Management Rake Task +------------------------------- + +To apply the collection options you specify in your {+odm+} application, you +must explicitly create the corresponding collection. To do so, use the +``db:mongoid:create_collections`` Rake task by running the following command in +your shell: + +.. code-block:: bash + + rake db:mongoid:create_collections + +You can also run the ``create_collection`` command on a single model in the +Rails console, as shown in the following example: + +.. code-block:: ruby + + Model.create_collection diff --git a/source/configuration/forking-server-config.txt b/source/configuration/forking-server-config.txt new file mode 100644 index 00000000..ea896857 --- /dev/null +++ b/source/configuration/forking-server-config.txt @@ -0,0 +1,132 @@ +.. _mongoid-forking-server-config: + +============================ +Forking Server Configuration +============================ + +.. facet:: + :name: genre + :values: reference + +.. meta:: + :keywords: code example, parent process, child process, behavior + +.. contents:: On this page + :local: + :backlinks: none + :depth: 2 + :class: singlecol + +Overview +-------- + +In this guide, you can learn about configuring your application to use a +forking web server. + +When using {+odm+} with a forking web server, adhere to the following +guidelines: + +- If possible, do not perform any MongoDB operations in the parent + process before forking. To learn more about how the {+ruby-driver+} + handles forking, see :ruby:`Usage with Forking Servers + ` in the driver + documentation. + +- You can avoid connection errors such as ``Mongo::Error::SocketError`` + and ``Mongo::Error::NoServerAvailable`` by performing the following actions: + + 1. Disconnect MongoDB clients in the parent {+language+} process + immediately *before* forking by using the + ``Mongoid.disconnect_clients`` method. This ensures that the parent and + child processes do not accidentally reuse the same sockets and have + I/O conflicts. ``Mongoid.disconnect_clients`` does not disrupt any + in-flight MongoDB operations, and automatically reconnects when you + perform new operations. + + #. Reconnect your MongoDB clients in the child {+language+} process + immediately *after* forking by using the + ``Mongoid.reconnect_clients`` method. This is required to respawn + the driver's monitoring threads in the child process. + +Most web servers provide hooks that your application can use to +perform actions when the worker processes are forked. The following +sections provide configuration examples for some common {+language+} web +servers. + +Puma +---- + +Use the ``on_worker_boot`` hook to reconnect clients in the workers. Use +the ``before_fork`` and ``on_refork`` hooks to close clients in the +parent process. To learn more about these hooks, see `Clustered mode +hooks `__ in the Puma +API documentation. + +The following code uses the ``on_worker_boot``, ``before_fork``, and +``on_refork`` hooks in a sample Puma configuration file: + +.. code-block:: ruby + :caption: config/puma.rb + + # Runs in the Puma master process before it forks a child worker. + before_fork do + Mongoid.disconnect_clients + end + + # Required when using Puma's fork_worker option. Runs in the + # child worker 0 process before it forks grandchild workers. + on_refork do + Mongoid.disconnect_clients + end + + # Runs in each Puma child process after it forks from its parent. + on_worker_boot do + Mongoid.reconnect_clients + end + +Unicorn +------- + +Use the ``before_fork`` hook to close clients in the parent process. Use +the ``after_fork`` hook to reconnect clients in the workers. To +learn more about these hooks, see `Configurator +`__ in the Unicorn +API documentation. + +The following code uses the ``before_fork`` and ``after_fork`` +hooks in a sample Unicorn configuration file: + +.. code-block:: ruby + :caption: config/unicorn.rb + + before_fork do |_server, _worker| + Mongoid.disconnect_clients + end + + after_fork do |_server, _worker| + Mongoid.reconnect_clients + end + +Passenger +--------- + +Use the ``starting_worker_process`` hook to reconnect clients in the +workers. To learn more about this hook, see `Smart spawning hooks +`__ +in the Passenger documentation. + +.. note:: + + Passenger does have a hook that is invoked in the + parent process before the workers are forked. + +The following code uses the ``starting_worker_process`` hook to +reconnect clients: + +.. code-block:: ruby + + if defined?(PhusionPassenger) + PhusionPassenger.on_event(:starting_worker_process) do |forked| + Mongoid.reconnect_clients if forked + end + end diff --git a/source/configuration/logging-config.txt b/source/configuration/logging-config.txt new file mode 100644 index 00000000..1a6271f7 --- /dev/null +++ b/source/configuration/logging-config.txt @@ -0,0 +1,165 @@ +.. _mongoid-logging-config: + +===================== +Logging Configuration +===================== + +.. facet:: + :name: genre + :values: reference + +.. meta:: + :keywords: code example, customize, trace + +.. contents:: On this page + :local: + :backlinks: none + :depth: 3 + :class: singlecol + +Overview +-------- + +In this guide, you can learn how to configure logging in your {+odm+} +application. When configuring logging, note that {+odm+} provides a +model layer on top of the {+ruby-driver+}, and the *driver* dispatches +data operations to MongoDB. Therefore, some logging output in an +application that uses {+odm+} comes from {+odm+} itself, and some comes from +the driver. + +Driver Logger +------------- + +The {+odm+} client is a {+ruby-driver+} client instance, so the logger +of a {+odm+} client is the {+ruby-driver+} logger, not the {+odm+} +logger. The following code creates a {+odm+} client logger: + +.. code-block:: ruby + + Mongoid.client(:default).logger + +Depending on your application framework and how you configure {+odm+} +and the {+ruby-driver+}, they may use the same logger +instance or different instances, potentially with different +configurations. + +{+ror+} Configuration +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +When used in a {+ror+} application, {+odm+} by default inherits +the logger and the log level from Rails. {+odm+} sets the driver's +logger to the same logger instance: + +.. code-block:: ruby + + Rails.logger === Mongoid.logger + # => true + + Mongoid.logger === Mongo::Logger.logger + # => true + +To change the log level, use the `standard Rails configuration +<{+active-record-docs+}/debugging_rails_applications.html#log-levels>`__. +Place the following block in one of your environment configuration +files, such as ``config/environments/production.rb``: + +.. code-block:: ruby + + Rails.application.configure do + config.log_level = :debug + end + +.. note:: + + The ``log_level`` {+odm+} configuration option is not used when + {+odm+} operates in a Rails application, because {+odm+} inherits + Rails' log level. + +To configure either the {+odm+} or driver logger differently from the +Rails logger, use an initializer as shown in the following code: + +.. code-block:: ruby + + Rails.application.configure do + config.after_initialize do + # Change Mongoid log destination and level + Mongoid.logger = Logger.new(STDERR).tap do |logger| + logger.level = Logger::DEBUG + end + + # Change driver log destination and level + Mongo::Logger.logger = Logger.new(STDERR).tap do |logger| + logger.level = Logger::DEBUG + end + end + end + +.. note:: + + There is no provision in the {+language+} standard library ``Logger`` + to return the log device, such as the ``IO`` object, that a logger is + using. + + To make, for example, {+odm+} or the {+ruby-driver+} log to the + standard Rails log file (``log/development.log``) with a + different level from standard Rails logger (``Rails.logger``), you + must open the file separately and pass the resulting ``IO`` object to + the ``Logger`` constructor. + +Because {+odm+} sets its own logger and the driver's logger to the +same instance as the Rails logger, modifying any of the instances affects +all the loggers. For example, the following code changes the log level for +all three loggers: + +.. code-block:: ruby + + Mongoid::Logger.logger.level = Logger::DEBUG + +Standalone Configuration +~~~~~~~~~~~~~~~~~~~~~~~~ + +When not loaded in a {+ror+} application, {+odm+} respects the +``log_level`` top-level configuration option: + +.. code-block:: yaml + :emphasize-lines: 6 + + development: + clients: + default: + ... + options: + log_level: :debug + +You can also configure the log level in-line: + +.. code-block:: ruby + + Mongoid.configure do |config| + config.log_level = :debug + end + +The default log destination is standard error. To change the log +destination, create a new logger instance as shown in the following +code: + +.. code-block:: ruby + + Mongoid.logger = Logger.new(STDERR).tap do |logger| + logger.level = Logger::DEBUG + end + +To change the {+ruby-driver+} log level or destination, add the +following block to your application file: + +.. code-block:: ruby + + Mongo::Logger.logger = Logger.new(STDERR).tap do |logger| + logger.level = Logger::DEBUG + end + +.. note:: + + {+odm+} does not change the driver's logger when running in + standalone mode. + diff --git a/source/configuration/persistence-config.txt b/source/configuration/persistence-config.txt new file mode 100644 index 00000000..5452de0b --- /dev/null +++ b/source/configuration/persistence-config.txt @@ -0,0 +1,295 @@ +.. _mongoid-persistence: + +========================= +Persistence Configuration +========================= + +.. facet:: + :name: genre + :values: reference + +.. meta:: + :keywords: code example, customize, config + +.. contents:: On this page + :local: + :backlinks: none + :depth: 3 + :class: singlecol + +Overview +-------- + +In this guide, you can learn about how {+odm+} persists data in your database +and collections. **Persistence configuration** refers to the settings that +control how {+odm+} stores data in MongoDB. This includes the client, +database, and collection where documents for a model class are stored, as +well as other configuration options such as read and write preferences. This guide +provides methods and examples that you can use to access and update +the persistence configuration of a model class. + +.. note:: + + The term "client" refers to a host configuration defined under + ``clients`` in your ``mongoid.yml`` file. Most applications use + a single client named ``default``. + +Default Collection Name +----------------------- + +By default, {+odm+} stores documents in a collection whose name is the pluralized +form of its representative class name. In the following example, for the +``Restaurant`` class, the corresponding collection is named ``restaurants``. For +the ``Person`` class, the corresponding collection is named ``people``. + +.. literalinclude:: /includes/configuration/persistence-configuration.rb + :language: ruby + :start-after: start default modeling + :end-before: end default modeling + +However, the default rules of pluralization don't always work. For +example, suppose your model is named ``Rey``. The plural form of this word in +Spanish is ``reyes``, but the default collection name is ``reys``. + +You can create a new pluralization rule for your model class by calling the +`ActiveSupport::Inflector::Inflections.plural `__ +instance method and passing the singular and plural forms of your class name. +The following example specifies ``reyes`` as the plural of ``rey``: + +.. literalinclude:: /includes/configuration/persistence-configuration.rb + :language: ruby + :start-after: start set pluralization + :end-before: end set pluralization + +As a result, {+odm+} stores ``Rey`` model class documents in the ``reyes`` +collection. + +.. note:: BSON Document Structure + + When {+odm+} stores a document in a database, it serializes the + {+language+} object to a BSON document that has the following + structure: + + .. literalinclude:: /includes/configuration/persistence-configuration.rb + :language: ruby + :start-after: start BSON model + :end-before: end BSON model + +Persistence Context Attributes +------------------------------ + +Every model class contains the following methods, which you can use to retrieve +information about where {+odm+} persists the model: + +- ``client_name``: Retrieves the client name +- ``database_name``: Retrieves the database name +- ``collection_name``: Retrieves the collection name + +The following example shows how to retrieve and print the names of the client, +database, and collection where documents for the ``Band`` class are persisted: + +.. io-code-block:: + + .. input:: /includes/configuration/persistence-configuration.rb + :language: ruby + :start-after: start persistence context attributes + :end-before: end persistence context attributes + + .. output:: + :language: ruby + :visible: false + + default + + my_bands + + bands + +Customize Your Persistence Configuration +---------------------------------------- + +{+odm+} provides both model-level and runtime options for customizing your +persistence configuration. The following sections describe these options. + +Model-Level Persistence Options +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Suppose you want to store your model's documents in a collection with a +different name than the pluralized form of the model class name. +You can use the ``store_in`` macro to change the collection, database, or client +where {+odm+} stores a model's documents. The following example shows how +to use the ``store_in`` macro to store documents from the ``Person`` class in +a collection called ``citizens`` in the ``other`` database within a client +named ``analytics``: + +.. literalinclude:: /includes/configuration/persistence-configuration.rb + :language: ruby + :start-after: start store_in example + :end-before: end store_in example + +The ``store_in`` macro can also accept a lambda function. This is useful if you +want to define a persistence context with values that cannot use a constant string. + +You might want to use this pattern in a multi-tenant application, +where multiple users share common access to an application. By using +a lambda, you can define a persistence context based on information that is local +to the current thread so that users cannot access each others' data. + +The following example stores documents in a database determined by a +thread-local variable: + +.. literalinclude:: /includes/configuration/persistence-configuration.rb + :language: ruby + :start-after: start store_in lambda example + :end-before: end store_in lambda example + +.. _mongoid-set-runtime-persistence-options: + +Runtime Persistence Options +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +You can use the ``with`` method on a model class or instance to change +a model's persistence configuration for a group of operations during runtime. + +Call the ``with`` method on a model class or instance and pass options +that define a persistence context. You can call the ``with`` method in two ways: + +- ``with(context, options)``: ``context`` is an instance of + ``Mongoid::PersistenceContext`` and ``options`` is a Hash that represents a + customizable set of options. + +- ``with(options)``: ``options`` is a Hash that represents a + customizable set of options. + +Then, use a block to define the operations that you want to execute in the +specified context. The context that you define only exists while the code +in the block runs. + +By default, {+odm+} stores documents for the ``Band`` class in a collection called +``bands``. The following code example uses the ``with`` method to temporarily +use a different client, database, and collection to perform operations on the +``Band`` class's documents: + +.. literalinclude:: /includes/configuration/persistence-configuration.rb + :language: ruby + :start-after: start with example + :end-before: end with example + +.. note:: Define Clients + + In the preceding example, you must define the ``tertiary`` cluster under + ``clients`` in your ``mongoid.yml`` file. + +.. important:: Block Scope + + You must call the ``with`` method with a block. + This is because {+odm+} uses the options you pass to the method to create + a new client in the background. A block defines the scope of this client + so it can be closed and its resources freed. + +You can also pass the ``with`` method configuration options for read or write +operations. The configurations apply only to the specified type of operation. + +The following example uses the ``with`` method to specify the use of the +secondary node for all read operations within the block. + +.. literalinclude:: /includes/configuration/persistence-configuration.rb + :language: ruby + :start-after: start read configuration + :end-before: end read configuration + +.. note:: Ensuring Consistency in Contexts + + When you perform an operation in one context, {+odm+} doesn't automatically + perform the same operation in different contexts. + For example, if you insert a ``Band`` model document into + the ``artists`` collection, the same document will not be inserted into the + ``bands`` collection. + +Global Persistence Contexts ++++++++++++++++++++++++++++ + +In previous examples in this section, you changed persistence context only in +the scope of a block. You can use {+odm+} to globally define a custom persistence +context that all operations in your program use. +This lets you change the persistence context for all operations at runtime +without repeatedly calling the ``with`` method. + +You can use the following methods to globally define the persistence context +in your program: + +- ``{+odm+}.override_client``: {+odm+} performs all operations on the specified client. + +- ``{+odm+}.override_database``: {+odm+} performs all operations on the specified + database. + +In the following code example, the application stores information for different +locales in different databases. The code shows how to use the +``{+odm+}.override_database`` method to globally set the persistence +context based on the locale: + +.. literalinclude:: /includes/configuration/persistence-configuration.rb + :language: ruby + :start-after: start global configuration example + :end-before: end global configuration example + +In the preceding example, {+odm+} performs all other operations on this thread +on an alternative database determined by the locale. Because the ``after_action`` +macro sets the override option to ``nil``, subsequent requests with no +changes in persistence configuration use the default configuration. + +Client and Collection Access +---------------------------- + +You can access the client or collection of a model or document instance by using +the ``mongo_client`` and ``collection`` class methods: + +.. literalinclude:: /includes/configuration/persistence-configuration.rb + :language: ruby + :start-after: start access client collection + :end-before: end access client collection + +When using these methods, you can also set runtime persistence options by calling +the ``with`` method, similar to examples in the :ref:`mongoid-set-runtime-persistence-options` +section. + +``mongo_client.with`` +~~~~~~~~~~~~~~~~~~~~~ + +The following code example accesses the client used by the ``Band`` model class. +It then uses the ``with`` method on the client to write to the ``music`` +database, setting the ``w`` write option to ``0`` to not require write acknowledgement. + +.. literalinclude:: /includes/configuration/persistence-configuration.rb + :language: ruby + :start-after: start client with example + :end-before: end client with example + +``collection.with`` +~~~~~~~~~~~~~~~~~~~ + +You can override the ``:read`` and ``:write`` options on a collection by using the +``with`` method. The following example shows how to use +the ``with`` method to set the ``w`` write option to ``0``: + +.. literalinclude:: /includes/configuration/persistence-configuration.rb + :language: ruby + :start-after: start collection with example + :end-before: end collection with example + +API Documentation +----------------- + +For more information about the methods mentioned in this guide, see the following +API documentation: + +- `#client_name <{+api+}/Mongoid/PersistenceContext.html#client_name-instance_method>`__ +- `#database_name <{+api+}/Mongoid/Clients/Options/ClassMethods.html#database_name-instance_method>`__ +- `#collection_name <{+api+}/Mongoid/Clients/Options/ClassMethods.html#collection_name-instance_method>`__ +- `#store_in <{+api+}/Mongoid/Clients/StorageOptions/ClassMethods.html#store_in-instance_method>`__ +- `Model.with <{+api+}/Mongoid/Clients/Options.html#with-instance_method>`__ +- `Mongoid::PersistenceContext <{+api+}/Mongoid/PersistenceContext.html>`__ +- `Mongoid.override_client <{+api+}/Mongoid/Config.html#override_client-instance_method>`__ +- `Mongoid.override_database <{+api+}/Mongoid/Config.html#override_database-instance_method>`__ +- `Model.mongo_client <{+api+}/Mongoid/Clients/Options/ClassMethods.html#mongo_client-instance_method>`__ +- `Model.collection <{+api+}/Mongoid/Clients/Options/ClassMethods.html#collection-instance_method>`__ \ No newline at end of file diff --git a/source/configuration/query-cache-config.txt b/source/configuration/query-cache-config.txt new file mode 100644 index 00000000..5f8aaefd --- /dev/null +++ b/source/configuration/query-cache-config.txt @@ -0,0 +1,74 @@ +.. _mongoid-query-cache-config: + +==================================== +Query Cache Middleware Configuration +==================================== + +.. facet:: + :name: genre + :values: reference + +.. meta:: + :keywords: code example, storage, memory + +.. contents:: On this page + :local: + :backlinks: none + :depth: 2 + :class: singlecol + +Overview +-------- + +In this guide, you can learn how to configure your application to use +query cache middleware. Query cache middleware allows you to +activate the :ref:`mongoid-query-cache` for each request to store your +query results. This can improve your application speed and efficiency by +reducing the number of calls your application must make to the database. + +.. _mongoid-query-cache-rack: + +Enable Query Cache for Rack Web Requests +---------------------------------------- + +The {+ruby-driver+} provides a Rack middleware which enables the +query cache during each web request. The +following code demonstrates how to enable the Query Cache Middleware in +a {+ror+} application: + +.. code-block:: ruby + :caption: config/application.rb + + # Add Mongo::QueryCache::Middleware at the bottom of the middleware + # stack or before other middleware that queries MongoDB. + config.middleware.use Mongo::QueryCache::Middleware + +To learn more about using Rack middleware in Rails applications, see +`Configuring Middleware Stack +<{+active-record-docs+}/rails_on_rack.html#configuring-middleware-stack>`__ +in the Rails documentation. + +Enable Query Cache for Active Job +--------------------------------- + +The {+ruby-driver+} provides Query Cache Middleware for `Active Job +<{+active-record-docs+}/active_job_basics.html>`__. +You can enable it for all jobs in an initializer, as shown in the +following code: + +.. code-block:: ruby + :caption: config/initializers/active_job.rb + + # Enable Mongo driver query cache for Active Job + ActiveSupport.on_load(:active_job) do + include Mongo::QueryCache::Middleware::ActiveJob + end + +You can also enable it for a specific job class, as shown in the +following code: + +.. code-block:: ruby + + class MyJob < ActiveJob::Base + include Mongo::QueryCache::Middleware::ActiveJob + end diff --git a/source/configuration/sharding.txt b/source/configuration/sharding.txt new file mode 100644 index 00000000..140ea967 --- /dev/null +++ b/source/configuration/sharding.txt @@ -0,0 +1,168 @@ +.. _mongoid-sharding-configuration: + +====================== +Sharding Configuration +====================== + +.. facet:: + :name: genre + :values: reference + +.. meta:: + :keywords: ruby framework, odm, code example + +.. contents:: On this page + :local: + :backlinks: none + :depth: 2 + :class: singlecol + +Overview +-------- + +Sharding is a way to distribute your data across multiple machines. MongoDB +uses sharding to support deployments with large data sets and high +throughput operations. In this guide, you can learn how to configure sharding in +your {+odm+} application. + +Declare Shard keys +------------------ + +MongoDB uses shard keys to distribute a collection's documents across +shards. A shard key is an indexed field, or multiple fields covered by a +compound index, that determines the distribution of the collection's +documents among the cluster's shards. In your +{+odm+} application, you can declare a shard key by +using the ``shard_key`` macro when you create a model. + +The following example creates a ``Person`` class with a shard key on the +``ssn`` field: + +.. literalinclude:: /includes/configuration/sharding.rb + :language: ruby + :start-after: # start-shard-key + :end-before: # end-shard-key + :emphasize-lines: 6 + +.. note:: + + To shard a collection, the collection must have an index that starts with the + shard key. The index can be on only the shard key, or it can be a compound index + where the shard key is the prefix. You can use {+odm+}'s index-management + functionality to create the index. To learn more about index management with + {+odm+}, see the :ref:`Index Management ` guide. + +If a model declares a shard key, {+odm+} expects the sharded collection to +use the declared key for sharding. When {+odm+} reloads models, it provides +the shard key along with the ``_id`` field to the ``find`` command to improve query +performance. If the collection is not sharded with the specified shard key, +queries might not return the expected results. + +Syntax +~~~~~~ + +You can declare shard keys by using either the full MongoDB +syntax or by using a shorthand syntax. + +The full syntax follows the format +of the ``mongosh`` :manual:`shardCollection() +` method, and allows you to specify the +following types of shard keys: + +- Ranged keys +- Hashed keys +- Compound keys + +The full syntax also allows you to specify collection and sharding options. + +The following example creates each of the preceding type of shard key on the +``sson`` field: + +.. literalinclude:: /includes/configuration/sharding.rb + :language: ruby + :start-after: # start-shard-key-formats + :end-before: # end-shard-key-formats + +The shorthand syntax allows you to declare a shard key by specifying only the +field name. This syntax supports only ranged and compound shard keys, and does not allow you +to specify collection or sharding options. + +The following example creates a ranged and a compound shard key: + +.. literalinclude:: /includes/configuration/sharding.rb + :language: ruby + :start-after: # start-shard-key-shorthand + :end-before: # end-shard-key-shorthand + +Specify Associated and Embedded Fields +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +You can specify a shard key on a ``belongs_to`` association in place of a field +name. When doing so, {+odm+} creates the shard key on the primary key of the +associated collection. + +The following example creates a shard key on the ``belongs_to`` association in a +``Person`` model. Because the associated ``country`` collection has a primary +key called ``country_id``, {+odm+} shards by that field: + +.. literalinclude:: /includes/configuration/sharding.rb + :language: ruby + :start-after: # start-shard-key-association + :end-before: # end-shard-key-association + +You can specify a shard key on an embedded document by using dot notation to +delimit the field names. The following example creates a shard key on the +``address.city`` field: + +.. literalinclude:: /includes/configuration/sharding.rb + :language: ruby + :start-after: # start-shard-key-embedded + :end-before: # end-shard-key-embedded + +.. note:: + + Because the period (``.``) character is used to delimit embedded fields, {+odm+} does + not support creating shard keys on fields with names that contain a period + character. + +Sharding Management Rake Tasks +------------------------------ + +You can shard collections in your database according to the shard keys defined +in your {+odm+} models by running the ``db:mongoid:shard_collections`` rake +task. To ensure that the collections contain indexes that start with the shard +key, you can first run the ``db:mongoid:create_indexes`` rake task. + +Run the following rake commands to create the indexes and shard the collections +based on your model's shard keys: + +.. code-block:: bash + + rake db:mongoid:create_indexes + rake db:mongoid:shard_collections + +Index management and sharding rake tasks do not stop when they encounter an +error with a particular model class. Instead, they log the error and continue +processing the next model. To ensure the rake tasks did not encounter any +errors, check the output of the {+odm+} logger configured for your application. + +.. note:: + + When performing schema-related operations in a sharded cluster, nodes might + contain out-of-date local configuration-related cache data. To clear the caches, + run the :manual:`flushRouterConfig ` + command on each ``mongos`` node. + +Additional Information +---------------------- + +To learn more about sharding with MongoDB, see the :manual:`Sharding +` guide in the MongoDB {+server-manual+}. + +API Documentation +~~~~~~~~~~~~~~~~~ + +To learn more about the ``shard_key`` macro discussed in this +guide, see the `shard_key +<{+api+}/Mongoid/Shardable/ClassMethods.html#shard_key-instance_method>`__ API +documentation. diff --git a/source/contributing.txt b/source/contributing.txt deleted file mode 100644 index ec3e097f..00000000 --- a/source/contributing.txt +++ /dev/null @@ -1,21 +0,0 @@ -.. _contributing: - -************ -Contributing -************ - -.. default-domain:: mongodb - -.. toctree:: - :titlesonly: - - Code Documentation - Contributing Guidelines - -Overview --------- - -Learn how to contribute to the Mongoid repository in the following sections: - -- :ref:`Code Documentation ` -- :ref:`Contributing Guidelines ` \ No newline at end of file diff --git a/source/contributing/code-documentation.txt b/source/contributing/code-documentation.txt deleted file mode 100644 index 9713ce27..00000000 --- a/source/contributing/code-documentation.txt +++ /dev/null @@ -1,353 +0,0 @@ -.. _code-documentation: - -****************** -Code Documentation -****************** - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 2 - :class: singlecol - - -Code Documentation -================== - -Mongoid uses its own flavor of `YARD `_ -for code documentation. Please note the conventions outlined in this document. - - -.. _code-documentation-structure: - -Structure ---------- - -- **Modules:** All class and module definitions should be preceded by - a documentation comment. - - .. code-block:: ruby - - # This is the documentation for the class. It's amazing - # what they do with corrugated cardboard these days. - class CardboardBox - -- **Methods:** All method definitions should be preceded by a documentation comment. - Use ``@param``, ``@yield``, and ``@return`` to specify inputs and output. - For further details, refer to - :ref:`Type Declaration ` below. - - .. code-block:: ruby - - # Turn a person into whatever they'd like to be. - # - # @param [ Person ] person The human to transmogrify. - # - # @return [ Tiger ] The transmogrified result. - def transmogrify(person) - -- **Errors:** Use ``@raise`` to explain errors specific to the method. - - .. code-block:: ruby - - # @raise [ Errors::Validations ] If validation failed. - def validate! - -- **Private Methods:** Private methods should be documented unless they are - so brief and straightforward that it is obvious what they do. Note that, - for example, a method may be brief and straightforward but the type of - its parameter may not be obvious, in which case the parameter must be - appropriately documented. - - .. code-block:: ruby - - private - - # Documentation is optional here. - def do_something_obvious - -- **API Private:** Classes and public methods which are not intended for - external usage should be marked ``@api private``. This macro does not - require a comment. - - Note that, because Mongoid's modules are mixed into application classes, - ``private`` visibility of a method does not necessarily indicate its - status as an API private method. - - .. code-block:: ruby - - # This is an internal-only method. - # - # @api private - def dont_call_me_from_outside - -- **Notes and TODOs:** Use ``@note`` to explain caveats, edge cases, - and behavior which may surprise users. Use ``@todo`` to record - follow-ups and suggestions for future improvement. - - .. code-block:: ruby - - # Clear all stored data. - # - # @note This operation deletes data in the database. - # @todo Refactor this method for performance. - def erase_data! - -- **Deprecation:** Use the ``@deprecated`` macro to indicate deprecated - functionality. This macro does not require a comment. - - .. code-block:: ruby - - # This is how we did things back in the day. - # - # @deprecated - def the_old_way - - -.. _code-documentation-formatting: - -Formatting ----------- - -- **Line Wrapping:** Use double-space indent when wrapping lines of macros. - Do not indent line wraps in the description. - - .. code-block:: ruby - - # This is the description of the method. Line wraps in the description - # should not be indented. - # - # @return [ Symbol ] For macros, wraps must be double-space indented - # on the second, third, etc. lines. - -- **Whitespace:** Do not use leading/trailing empty comment lines, - or more than one consecutive empty comment line. - - .. code-block:: ruby - - # GOOD: - # @return [ Symbol ] The return value - def my_method - - # BAD: - # @return [ Symbol ] The return value - # - def my_method - - # BAD: - # @param [ Symbol ] foo The input value - # - # - # @return [ Symbol ] The return value - def my_method(foo) - - -.. _code-documentation-type-declaration: - -Type Declaration ----------------- - -- **Type Unions:** Use pipe ``|`` to denote a union of allowed types. - - .. code-block:: ruby - - # @param [ Symbol | String ] name Either a Symbol or a String. - -- **Nested Types:** Use angle brackets ``< >`` to denote type nesting. - - .. code-block:: ruby - - # @param [ Array ] array An Array of symbols. - -- **Hash:** Use comma ``,`` to denote the key and value types. - - .. code-block:: ruby - - # @param [ Hash ] hash A Hash whose keys are Symbols, - # and whose values are Integers. - -- **Array:** Use pipe ``|`` to denote a union of allowed types. - - .. code-block:: ruby - - # @param [ Array ] array An Array whose members must - # be either Symbols or Strings. - -- **Array:** Use comma ``,`` to denote the types of each position in a tuple. - - .. code-block:: ruby - - # @return [ Array ] A 3-member Array whose first - # element is a Symbol, and whose second and third elements are Integers. - -- **Array:** Use pipe ``|`` on the top level if the inner types cannot be - mixed within the Array. - - .. code-block:: ruby - - # @param [ Array | Array ] array An Array containing only - # Symbols, or an Array containing only Hashes. The Array may not contain - # a mix of Symbols and Hashes. - -- **Nested Types:** For clarity, use square brackets ``[ ]`` to denote nested unions - when commas are also used. - - .. code-block:: ruby - - # @param [ Hash ] hash A Hash whose keys are Symbols, - # and whose values are boolean values. - -- **Ruby Values:** Specific values may be denoted in the type using Ruby syntax. - - .. code-block:: ruby - - # @param [ :before | :after ] timing One of the Symbol values :before or :after. - -- **True, False, and Nil:** Use ``true``, ``false``, and ``nil`` rather than - ``TrueClass``, ``FalseClass``, and ``NilClass``. Do not use ``Boolean`` as a type - since it does not exist in Ruby. - - .. code-block:: ruby - - # GOOD: - # @param [ true | false | nil ] bool A boolean or nil value. - - # BAD: - # @param [ TrueClass | FalseClass | NilClass ] bool A boolean or nil value. - # @param [ Boolean ] bool A boolean value. - -- **Return Self:** Specify return value ``self`` where a method returns ``self``. - - .. code-block:: ruby - - # @return [ self ] Returns the object itself. - -- **Splat Args:** Use three-dot ellipses ``...`` in the type declaration and - star ``*`` in the parameter name to denote a splat. - - .. code-block:: ruby - - # @param [ String... ] *items The list of items name(s) as Strings. - def buy_groceries(*items) - -- **Splat Args:** Do not use ``Array`` as the type unless each arg is actually an Array. - - .. code-block:: ruby - - # BAD: - # @param [ Array ] *items The list of items name(s) as Strings. - def buy_groceries(*items) - - buy_groceries("Cheese", "Crackers", "Wine") - - # OK: - # @param [ Array... ] *arrays One or more arrays containing name parts. - def set_people_names(*arrays) - - set_people_names(["Harlan", "Sanders"], ["Jane", "K", ""Doe"], ["Jim", "Beam"]) - -- **Splat Args:** Use comma ``,`` to denote positionality in a splat. - - .. code-block:: ruby - - # @param [ Symbol..., Hash ] *args A list of names, followed by a hash - # as the optional last arg. - def say_hello(*args) - -- **Splat Args:** Specify type unions with square brackets ``[ ]``. - - .. code-block:: ruby - - # @param [ [ String | Symbol ]... ] *fields A splat of mixed Symbols and Strings. - -- **Keyword Arguments:** Following YARD conventions, use ``@param`` for keyword - arguments, and specify keyword argument names as symbols. - - .. code-block:: ruby - - # @param [ String ] query The search string - # @param [ Boolean ] :exact_match Whether to do an exact match - # @param [ Integer ] :results_per_page Number of results - def search(query, exact_match: false, results_per_page: 10) - -- **Hash Options:** Define hash key-value options with ``@option`` macro - immediately following the Hash ``@param``. Note ``@option`` parameter names - are symbols. - - .. code-block:: ruby - - # @param opts [ Hash ] The optional hash argument(s). - # @option opts [ String | Array ] :items The items(s) as Strings to include. - # @option opts [ Integer ] :limit An Integer denoting the limit. - def buy_groceries(opts = {}) - -- **Double Splats:** Use double-star ``**`` in the parameter name to denote a - keyword arg splat (double splat). Note that type does not need declared on - the double-splat element, as it is implicitly . Instead, - define value types with ``@option`` macro below. Note ``@option`` parameter - names are symbols. - - .. code-block:: ruby - - # @param **kwargs The optional keyword argument(s). - # @option **kwargs [ String | Array ] :items The items(s) as Strings to include. - # @option **kwargs [ Integer ] :limit An Integer denoting the limit. - def buy_groceries(**kwargs) - -- **Blocks:** Use ``@yield`` to specify when the method yields to a block. - - .. code-block:: ruby - - # @yield [ Symbol, Symbol, Symbol ] Evaluate the guess of who did the crime. - # Must take the person, location, and weapon used. Must return true or false. - def whodunit - yield(:mustard, :ballroom, :candlestick) - end - -- **Blocks:** If the method explicitly specifies a block argument, specify the block - argument using ``@param`` preceded by an ampersand ``&``, and also specify ``@yield``. - Note ``@yield`` should be used even when method calls ``block.call`` rather than - ``yield`` internally. - - .. code-block:: ruby - - # @param &block The block. - # @yield [ Symbol, Symbol, Symbol ] Evaluate the guess of who did the crime. - # Must take the person, location, and weapon used. Must return true or false. - def whodunit(&block) - yield(:scarlet, :library, :rope) - end - - # @param &block The block. - # @yield [ Symbol, Symbol, Symbol ] Evaluate the guess of who did the crime. - # Must take the person, location, and weapon used. Must return true or false. - def whodunit(&block) - block.call(:plum, :kitchen, :pipe) - end - -- **Blocks:** Use ``@yieldparam`` and ``@yieldreturn`` instead of ``@yield`` where - beneficial for clarity. - - .. code-block:: ruby - - # @param &block The block. - # @yieldparam [ Symbol ] The person. - # @yieldparam [ Symbol ] The location. - # @yieldparam [ Symbol ] The weapon used. - # @yieldreturn [ true | false ] Whether the guess is correct. - def whodunit(&block) - yield(:peacock, :conservatory, :wrench) - end - -- **Proc Args:** Proc arguments should use ``@param`` (not ``@yield``). The - inputs to the proc may be specified as subtype(s). - - .. code-block:: ruby - - # @param [ Proc ] my_proc Proc argument which must - # take 3 integers and must return true or false whether the guess is valid. - def guess_three(my_proc) - my_proc.call(42, 7, 88) - end diff --git a/source/contributing/contributing-guidelines.txt b/source/contributing/contributing-guidelines.txt deleted file mode 100644 index 68491bd5..00000000 --- a/source/contributing/contributing-guidelines.txt +++ /dev/null @@ -1,43 +0,0 @@ -.. _contributing-guidelines: - -*********************** -Contributing Guidelines -*********************** - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 2 - :class: singlecol - - -Contributing Guidelines -======================= - -If you wish to propose an enhancement to Mongoid, please create a Jira ticket -describing the enhancement and what it would enable you to achieve in your -application that is not already possible. If you believe Mongoid is not -behaving correctly, please create a Jira ticket describing how you use Mongoid, -what the existing behavior is that you consider incorrect or problematic, and -what your desired behavior is. If you wish to make changes yourself, the -following guildelines should be followed: - -#. Create a fork of Mongoid. -#. Create a new branch in that fork. -#. Make your changes. -#. Ensure that the proposed changes have adequate test coverage. -#. Raise a PR against Mongoid master. If these changes correspond to a specific - Jira ticket, title the PR: "MONGOID- Description of Changes". -#. The Mongoid team will review the PR and make comments/suggest changes. -#. Once all of the changes and fixes are made, and the Mongoid team determine - the PR fit for merging, we will merge the PR into master and determine - whether it needs to be backported. -#. Backports to previous stable versions are done if the change is a bug fix, - is not backwards breaking, and if the commit is applicable to the - corresponding stable branch. Presently backport candidates would include - versions 7.3-8.0. -#. Changes to 6.0-7.2 are generally not made unless it fixes a security - vulnerability. -#. 5.x and earlier is generally not supported. \ No newline at end of file diff --git a/source/data-modeling.txt b/source/data-modeling.txt new file mode 100644 index 00000000..7a026f86 --- /dev/null +++ b/source/data-modeling.txt @@ -0,0 +1,50 @@ +.. _mongoid-data-modeling: + +=============== +Model Your Data +=============== + +.. facet:: + :name: genre + :values: reference + +.. meta:: + :keywords: ruby framework, odm, model, class, query + +.. toctree:: + :caption: Data Modeling + + Documents + Field Types + Field Behaviors + Inheritance + Document Validation + Callbacks + Data Associations + Indexes + +In this section, you can learn how to model data in {+odm+}. + +- :ref:`mongoid-modeling-documents`: Learn about the ``Document`` + module. + +- :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. + +- :ref:`mongoid-modeling-inheritance`: Learn how to implement + inheritance in your model classes. + +- :ref:`mongoid-modeling-validation`: Learn how to create document + validation rules for your model classes. + +- :ref:`mongoid-modeling-callbacks`: Learn how to implement callbacks to + customize the life cycle of your models. + +- :ref:`mongoid-associations`: Learn how to create and manage data + associations between your model classes. + +- :ref:`mongoid-optimize-queries-with-indexes`: Learn how to create and manage + indexes for your model classes. diff --git a/source/data-modeling/associations.txt b/source/data-modeling/associations.txt new file mode 100644 index 00000000..80afd719 --- /dev/null +++ b/source/data-modeling/associations.txt @@ -0,0 +1,930 @@ +.. _mongoid-associations: + +============ +Associations +============ + +.. facet:: + :name: genre + :values: reference + +.. meta:: + :keywords: ruby framework, odm, code example + +.. contents:: On this page + :local: + :backlinks: none + :depth: 2 + :class: singlecol + +Overview +-------- + +Associations in {+odm+} allow you to create relationships between models. In +this guide, you can learn about the different types of associations that +{+odm+} supports and how to use them in your application. + +Referenced Associations +----------------------- + +Referenced associations allow you to create a relationship between two models +where one model references the other. {+odm+} supports the following referenced +association types: + +- ``has_one`` +- ``has_many`` +- ``belongs_to`` +- ``has_and_belongs_to_many`` + +The following sections describe how to use each of these association types. + +Has One +~~~~~~~ + +You can use the ``has_one`` macro to declare that documents represented by one class also contain a +document represented by a separate child class. The +following example creates a ``Band`` class with a ``has_one`` relationship to a +``Studio`` class: + +.. literalinclude:: /includes/data-modeling/associations.rb + :language: ruby + :start-after: # start-has-one + :end-before: # end-has-one + +When you declare a ``has_one`` association, the child class must also use the +``belongs_to`` association that references the parent class. The following +example shows the ``Studio`` class referenced in the preceding ``Band`` class: + +.. literalinclude:: /includes/data-modeling/associations.rb + :language: ruby + :start-after: # start-has-one-child + :end-before: # end-has-one-child + +To learn more about the ``belongs_to`` macro, see the :ref:`Belongs To +` section. + +You can use validations to ensure that the child +class is present in your parent class, as shown in the following example: + +.. literalinclude:: /includes/data-modeling/associations.rb + :language: ruby + :start-after: # start-has-one-validation + :end-before: # end-has-one-validation + :emphasize-lines: 6 + +To learn more about validations in {+odm+}, see the :ref:`Validations +` guide. + +Has Many +~~~~~~~~ + +You can use the ``has_many`` macro to declare that documents represented by a class contain +multiple child documents represented by another class. The following +example creates a ``Band`` class with a ``has_many`` relationship to a +``Members`` class: + +.. literalinclude:: /includes/data-modeling/associations.rb + :language: ruby + :start-after: # start-has-many + :end-before: # end-has-many + +When you declare a ``has_many`` association, the child class must also use the +``belongs_to`` association that references the parent class. The following +example shows the ``Member`` class referenced in the preceding ``Band`` class: + +.. literalinclude:: /includes/data-modeling/associations.rb + :language: ruby + :start-after: # start-has-many-child + :end-before: # end-has-many-child + +To learn more about the ``belongs_to`` macro, see the :ref:`Belongs To +` section. + +You can use validations to ensure that the child +class is present in your parent class, as shown in the following example: + +.. literalinclude:: /includes/data-modeling/associations.rb + :language: ruby + :start-after: # start-has-many-validation + :end-before: # end-has-many-validation + :emphasize-lines: 6 + +To learn more about validations in {+odm+}, see the :ref:`Validations +` guide. + +Retrieve Association Information +```````````````````````````````` + +You can use the ``any?`` method on a ``has_many`` association to determine if the +association contains any documents without retrieving the entire set of +documents from the database. + +The following example uses the ``any?`` method to determine if documents in the +``Band`` class contain any ``Members`` documents: + +.. literalinclude:: /includes/data-modeling/associations.rb + :language: ruby + :start-after: # start-has-many-any + :end-before: # end-has-many-any + +You can also use the ``any?`` method with a filter to find documents that match +a specified criteria, as shown in the following example: + +.. literalinclude:: /includes/data-modeling/associations.rb + :language: ruby + :start-after: # start-has-many-any-filter + :end-before: # end-has-many-any-filter + +You can supply a class name to the ``any?`` method to filter the results by the +name of the class. This is useful for polymorphic associations: + +.. literalinclude:: /includes/data-modeling/associations.rb + :language: ruby + :start-after: # start-has-many-any-class + :end-before: # end-has-many-any-class + +.. note:: + + After the data of the associated class is loaded to {+odm+}, subsequent calls + to the ``any?`` method do not query the database. Instead, {+odm+} uses the + data that is already loaded in memory. + +You can also call the ``exists?`` method to determine if there are any persisted +documents in the association. The ``exists?`` method always queries the +database and checks only for documents that have been saved to the database. +The ``exists?`` method does not allow for filtering and does not accept any +arguments. + +The following example uses the ``exists?`` method to determine if there are any persisted +``Members`` documents in the ``Band`` class: + +.. literalinclude:: /includes/data-modeling/associations.rb + :language: ruby + :start-after: # start-has-many-exists + :end-before: # end-has-many-exists + +.. _mongoid-belongs-to: + +Belongs To +~~~~~~~~~~ + +Use the ``belongs_to`` macro to declare that a document represented by one class +is a child of a document represented by another +class. By default, the ``_id`` field of the parent class is stored in the child +class. The following example creates a ``Members`` class with a ``belongs_to`` +association to a ``Band`` class: + +.. literalinclude:: /includes/data-modeling/associations.rb + :language: ruby + :start-after: # start-belongs-to + :end-before: # end-belongs-to + +You can allow {+odm+} to persist documents to the database without storing the +``_id`` of the associated parent class by setting the ``optional`` option to +``true``, as shown in the following example: + +.. literalinclude:: /includes/data-modeling/associations.rb + :language: ruby + :start-after: # start-belongs-to-optional + :end-before: # end-belongs-to-optional + +.. tip:: + + You can globally change the default behavior of the ``belongs_to`` + association to not require their parent class by setting the + ``belongs_to_required_by_default`` configuration option to ``false`` in your + application's configuration settings. + +You can specify a ``belongs_to`` association in a child class without specifying a matching +``has_one`` or ``has_many`` association in the parent class. When doing so, you +can't access the fields of the child document from the parent class, but you can +access the parent fields that are stored in the child class, such as the +parent's ``_id`` field. In the following example, the ``Band`` +class cannot access the ``Members`` class, but the ``Members`` class can access the +``Band`` class: + +.. literalinclude:: /includes/data-modeling/associations.rb + :language: ruby + :start-after: # start-belongs-to-one-way + :end-before: # end-belongs-to-one-way + +For clarity, you can optionally set the ``inverse_of`` option to ``nil`` to +indicate that the parent class does not contain a ``has_one`` or ``has_many`` +association to the child class, as shown in the following example: + +.. literalinclude:: /includes/data-modeling/associations.rb + :language: ruby + :start-after: # start-belongs-to-inverse + :end-before: # end-belongs-to-inverse + +Has and Belongs To Many +~~~~~~~~~~~~~~~~~~~~~~~ + +Use the ``has_and_belongs_to_many`` macro to declare that a class model contains +a many-to-many relationship with another class. In a many-to-many relationship, +each document in one class can be associated with multiple documents in another +class. The following example creates a ``Band`` class with a +``has_and_belongs_to_many`` relationship to a ``Members`` class. A ``Band`` document can +reference multiple ``Members`` documents, and a ``Members`` document can +reference multiple ``Band`` documents. + +.. literalinclude:: /includes/data-modeling/associations.rb + :language: ruby + :start-after: # start-has-and-belongs-to-many + :end-before: # end-has-and-belongs-to-many + +When you declare a ``has_and_belongs_to_many`` association, both model instances +store a list of the associated document's ``_id`` values. You can set the +``inverse_of`` option to ``nil`` to store the associated document's ``_id`` values in +only one of the model instances. The following example prompts {+odm+} to store +the associated document's ``_id`` values in only the ``Band`` class: + +.. literalinclude:: /includes/data-modeling/associations.rb + :language: ruby + :start-after: # start-has-and-belongs-to-many-inverse + :end-before: # end-has-and-belongs-to-many-inverse + +.. tip:: + + When you update a document that has a ``has_and_belongs_to_many`` association, + {+odm+} sets the ``updated_at`` field of updated document but does not set the + ``updated_at`` field of the associated documents. + +Query Referenced Associations +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +You can use an aggregation pipeline to query for documents across referenced +associations. The aggregation pipeline allows you to create queries across +multiple collections and manipulate data into a specified format. To learn more +about using the aggregation pipeline, see the :ref:`Aggregation +` guide. + +For simple queries, you can query the association directly. When you directly +query on a collection, you can query only on fields and values that are in the +collection itself. You cannot directly query on collections +associated to the one you are querying. + +For example, consider the following ``Band`` and ``Tour`` classes: + +.. literalinclude:: /includes/data-modeling/associations.rb + :language: ruby + :start-after: # start-query-models + :end-before: # end-query-models + +The following example queries the ``Tour`` class for documents that have a +``year`` value of ``2000`` or greater and saves the ``band_id`` of those +documents. It then queries the ``Band`` class for documents that have those +``band_id`` values. + +.. literalinclude:: /includes/data-modeling/associations.rb + :language: ruby + :start-after: # start-association-query + :end-before: # end-association-query + +Embedded Associations +--------------------- + +You can use embedded associations to store different types of documents in the +same collection. {+odm+} supports embedded associations with the following +macros: + +- ``embeds_one`` +- ``embeds_many`` +- ``embedded_in`` +- ``recursively_embeds_one`` +- ``recursively_embeds_many`` + +The following sections describe how to use these association types. + +Embeds One +~~~~~~~~~~ + +To specify that a class model contains an embedded document of a different +class type, use the ``embeds_one`` macro in the parent class and the ``embedded_in`` +macro in the embedded class. The following example creates a ``Band`` class with +an embedded ``Label`` class: + +.. literalinclude:: /includes/data-modeling/associations.rb + :language: ruby + :start-after: # start-embed-one + :end-before: # end-embed-one + +{+odm+} stores documents embedded with the ``embeds_one`` macro in the +parent document as a field with the same name as the embedded class. The +preceding ``Label`` documents are stored in the ``Band`` document, as shown in +the following example: + +.. literalinclude:: /includes/data-modeling/associations.rb + :language: ruby + :start-after: # start-embed-one-stored + :end-before: # end-embed-one-stored + +You can store the embedded document with a different name by using the +``store_as`` option, as shown in the following example: + +.. literalinclude:: /includes/data-modeling/associations.rb + :language: ruby + :start-after: # start-embed-store-as + :end-before: # end-embed-store-as + +Embeds Many +~~~~~~~~~~~ + +To specify that a class model contains multiple embedded documents of a +different class type, use the ``embeds_many`` macro in the parent class and the +``embedded_in`` macro in the embedded class. The following example creates a +``Band`` class with multiple embedded ``Album`` type documents: + +.. literalinclude:: /includes/data-modeling/associations.rb + :language: ruby + :start-after: # start-embed-many + :end-before: # end-embed-many + +{+odm+} stores documents embedded with the ``embeds_many`` macro in the +parent document as an array field with the same name as the embedded class. The +preceding ``Album`` documents are stored in the ``Band`` document, as shown in +the following example: + +.. literalinclude:: /includes/data-modeling/associations.rb + :language: ruby + :start-after: # start-embed-many-stored + :end-before: # end-embed-many-stored + +You can store the embedded document with a different name by using the +``store_as`` option, as shown in the following example: + +.. literalinclude:: /includes/data-modeling/associations.rb + :language: ruby + :start-after: # start-embed-many-store-as + :end-before: # end-embed-many-store-as + +Recursive Embedding +~~~~~~~~~~~~~~~~~~~ + +You can embed one or more documents of the same type into a parent +class by using the ``recursively_embeds_one`` and ``recursively_embeds_many`` +macros. Both macros provide accessors for the parent and child documents through +a ``parent_*`` method and a ``child_*`` method, where ``*`` represents the name +of the class. The following example creates a ``Band`` class that recursively +embeds multiple other ``Band`` documents to represent multiple band names: + +.. literalinclude:: /includes/data-modeling/associations.rb + :language: ruby + :start-after: # start-recursive-embed + :end-before: # end-recursive-embed + +You can access the parent and child documents through the ``parent_band`` and +``child_band`` methods, as shown in the following example: + +.. literalinclude:: /includes/data-modeling/associations.rb + :language: ruby + :start-after: # start-recursive-embed-access + :end-before: # end-recursive-embed-access + +Query Embedded Associations +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +You can access embedded documents when querying the collection of the parent +class by using dot notation. + +The following example uses dot notation to query ``Tour`` type documents that +are embedded in a ``Band`` class. The query returns documents that have a +``tours.year`` value of ``2000`` or greater: + +.. literalinclude:: /includes/data-modeling/associations.rb + :language: ruby + :start-after: # start-embedded-query + :end-before: # end-embedded-query + +You can use the ``pluck`` projection method to retrieve embedded documents +without retrieving their associated parent documents, as shown in the following +example: + +.. literalinclude:: /includes/data-modeling/associations.rb + :language: ruby + :start-after: # start-embedded-query-pluck + :end-before: # end-embedded-query-pluck + +You can use {+odm+} query methods to perform **embedded matching**, which allows +you to query on embedded associations of documents that are already loaded in +the application. {+odm+} implements embedded matching without sending queries to +the server. + +The following query operators are supported with embedded matching: + +- :manual:`Comparison operators ` +- :manual:`Logical operators ` +- :manual:`Array query operators ` +- :manual:`$exists ` +- :manual:`$mod ` +- :manual:`$type ` +- :manual:`$regex ` +- :manual:`Bitwise operators ` +- :manual:`$comment ` + +The following example queries the embedded ``tours`` field of a loaded ``Band`` +document by using the ``$gte`` comparison operator: + +.. literalinclude:: /includes/data-modeling/associations.rb + :language: ruby + :start-after: # start-embedded-matching + :end-before: # end-embedded-matching + +Embedded matching on loaded documents has the following known limitations: + +- Embedded matching is not implemented for the following features: + + - :ref:`Text search ` + - :manual:`Geospatial queries ` + - Operators that execute JavaScript code, such as :manual:`$where + ` + - Operators that are implemented through other server functionality, such as + :manual:`$expr ` + and :manual:`$jsonSchema + ` + +- {+odm+} expands ``Range`` arguments to hashes with ``$gte`` and ``$lte`` + conditions. This can lead to invalid queries in some cases and raises a an + ``InvalidQuery`` exception. + +- With the ``$regex`` operator, you cannot specify a regular expression object + as a pattern while also providing options to the ``$options`` field. You can + only provide options if the regular expression pattern is a string. + +- {+mdb-server+} versions 4.0 and earlier do not strictly validate ``$type`` + arguments. + +Omit _id Fields +~~~~~~~~~~~~~~~ + +By default, {+odm+} adds an ``_id`` field to embedded documents. You can omit +this field from embedded documents by explicitly specifying the ``_id`` field in +your model and omitting the default value. The following example instructs {+odm+} +not to add an ``_id`` field to the ``Albums`` class: + +.. literalinclude:: /includes/data-modeling/associations.rb + :language: ruby + :start-after: # start-embedded-omit-id + :end-before: # end-embedded-omit-id + :emphasize-lines: 4 + +In the preceding ``Albums`` class, the ``_id`` field is not automatically added. +Without a default value, {+odm+} does not store the value in the database +unless you provide one in your model. + +Delete Embedded Associations +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +You can delete child documents from ``embeds_many`` associations by using one of +the following methods: + +- ``clear`` +- ``delete_all`` +- ``destroy_all`` + +The ``clear`` method uses the :manual:`$unset operator +` operator to remove an entire embedded +association from the parent document. The ``clear`` method does not run any +``destroy`` callbacks. The following example uses the ``clear`` +method to remove all embedded associations from the ``Band`` class: + +.. literalinclude:: /includes/data-modeling/associations.rb + :language: ruby + :start-after: # start-embedded-clear + :end-before: # end-embedded-clear + +The ``delete_all`` method uses the :manual:`$pullAll operator +` operator to remove documents in an +embedded association. ``delete_all`` loads the association if it has not +yet been loaded, then only removes the documents that exist in the application. +The ``delete_all`` method does not run any ``destroy`` callbacks. +The following example uses the ``delete_all`` method to remove all embedded +``Album`` documents from the ``Band`` class: + +.. literalinclude:: /includes/data-modeling/associations.rb + :language: ruby + :start-after: # start-embedded-delete-all + :end-before: # end-embedded-delete-all + +The ``destroy_all`` method also uses the :manual:`$pullAll operator +` operator to remove documents in an +embedded association. It also runs any ``destroy`` callbacks that are defined on +the associated documents. The following example uses the ``destroy_all`` +method to remove all embedded ``Album`` documents from the ``Band`` class: + +.. literalinclude:: /includes/data-modeling/associations.rb + :language: ruby + :start-after: # start-embedded-destroy-all + :end-before: # end-embedded-destroy-all + +Customize Association Behavior +------------------------------ + +You can use {+odm+} to customize how associations behave in your application. +The following sections describe ways to customize association behaviors. + +Extensions +~~~~~~~~~~ + +Extensions allow you to add custom functionality to an association. You can +define an extension on an association by specifying a block in the association +definition, as shown in the following example: + +.. literalinclude:: /includes/data-modeling/association-behaviors.rb + :language: ruby + :start-after: # start-extensions + :end-before: # end-extensions + :emphasize-lines: 4-6 + +Custom Association Names +~~~~~~~~~~~~~~~~~~~~~~~~ + +You can use the ``class_name`` macro to specify a custom class name for an +association. This is useful when you want to name the association something other +than the name of the class. The following example uses the +``class_name`` macro to specify that an embedded association called ``records`` +represents the ``Album`` class: + +.. literalinclude:: /includes/data-modeling/association-behaviors.rb + :language: ruby + :start-after: # start-custom-name + :end-before: # end-custom-name + +Custom Keys +~~~~~~~~~~~ + +By default, {+odm+} uses the ``_id`` field of the parent class when looking up +associations. You can specify different fields to use by using the +``primary_key`` and ``foreign_key`` macros. The following example specifies a new +primary and foreign key for the ``albums`` association on a ``Band`` class: + +.. literalinclude:: /includes/data-modeling/association-behaviors.rb + :language: ruby + :start-after: # start-custom-keys + :end-before: # end-custom-keys + +If you are specifying a ``has_and_belongs_to_many`` association, you can also +use the ``inverse_primary_key`` and ``inverse_foreign_key`` macros. The +``inverse_primary_key`` macro specifies the field on the local model that the +remote model uses to look up the documents. +The ``inverse_foreign_key`` macro specifies the field on the remote model +that stores the values found in ``inverse_primary_key``. + +The following example specifies a new primary and foreign key for the +``Band`` and ``Members`` classes in a ``has_and_belongs_to_many`` association: + +.. literalinclude:: /includes/data-modeling/association-behaviors.rb + :language: ruby + :start-after: # start-custom-inverse-keys + :end-before: # end-custom-inverse-keys + :emphasize-lines: 9, 20 + +Custom Scopes +~~~~~~~~~~~~~ + +You can specify the scope of an association by using the ``scope`` parameter. +The ``scope`` parameter determines the documents that {+odm+} considers part +of an association. A scoped association returns only documents that match the +scope conditions when queried. You can set the ``scope`` to either a ``Proc`` with an arity +of zero or a ``Symbol`` that references a named scope on the associated model. +The following example sets custom scopes on associations in a ``Band`` class: + +.. literalinclude:: /includes/data-modeling/association-behaviors.rb + :language: ruby + :start-after: # start-custom-scope + :end-before: # end-custom-scope + +.. note:: + + You can add documents that do not match the scope conditions to an + association. {+odm+} saves the documents to the database and they will appear + in associated memory. However, you won't see the documents when querying the + association. + +Validations +~~~~~~~~~~~ + +When {+odm+} loads an association into memory, by default, it uses the +``validates_associated`` macro to validate that any children are also present. +{+odm+} validates children for the following association types: + +- ``embeds_many`` +- ``embeds_one`` +- ``has_many`` +- ``has_one`` +- ``has_and_belongs_to_many`` + +You can turn off this validation behavior by setting the ``validate`` macro to +``false`` when defining the association, as shown in the following example: + +.. literalinclude:: /includes/data-modeling/association-behaviors.rb + :language: ruby + :start-after: # start-validation-false + :end-before: # end-validation-false + +Polymorphism +~~~~~~~~~~~~ + +{+odm+} supports polymorphism on the child classes of one-to-one and one-to-many associations. +Polymorphic associations allows a single association to contain objects of different class +types. You can define a polymorphic association by setting the ``polymorphic`` +option to ``true`` in a child association and adding the ``as`` option to the +parent association. The following example creates a polymorphic association in a +``Band`` class: + +.. literalinclude:: /includes/data-modeling/association-behaviors.rb + :language: ruby + :start-after: # start-polymorphic + :end-before: # end-polymorphic + +In the preceding example, the ``:featured`` association in the ``Band`` class can contain either a +``Label`` or ``Album`` document. + +.. important:: + + {+odm+} supports polymorphism only from child to parent. You cannot specify + a parent ``has_one`` or ``has_many`` association as polymorphic. + +``has_and_belongs_to_many`` associations do not support polymorphism. + +Custom Polymorphic Types +```````````````````````` + +Starting in version 9.0.2, {+odm+} supports custom polymorphic types through +a global registry. You can specify alternative keys to represent different +classes, decoupling your code from the data. The following example specifies +the string ``"artist"`` as an alternate key for the ``Band`` class: + +.. literalinclude:: /includes/data-modeling/association-behaviors.rb + :language: ruby + :start-after: # start-custom-polymorphic + :end-before: # end-custom-polymorphic + +In the preceding example, the ``identify_as`` directive instructs {+odm+} +to store the ``Band`` class in the database as the string ``"artist"``. + +You can also specify multiple aliases, as shown in the following example: + +.. literalinclude:: /includes/data-modeling/association-behaviors.rb + :language: ruby + :start-after: # start-multiple-alias + :end-before: # end-multiple-alias + +In the preceding example, ``artist`` is the default name and the others are used +only for looking up records. This allows you to refactor your +code without breaking the associations in your data. + +Polymorphic type aliases are global. The keys you specify must be unique across your +entire code base. However, you can register alternative resolvers that +can be used for different subsets of your models. In this case, the keys must +be unique only for each resolver. The following example shows how to register +alternate resolvers: + +.. literalinclude:: /includes/data-modeling/association-behaviors.rb + :language: ruby + :start-after: # start-polymorphic-resolvers + :end-before: # end-polymorphic-resolvers + +Both ``Music::Band`` and ``Tools::Band`` are aliased as +``"bnd"``, but each model uses its own resolver to avoid conflicts. + +Dependent Behavior +~~~~~~~~~~~~~~~~~~ + +You can provide ``dependent`` options to referenced associations to specify how +{+odm+} handles associated documents when a document is deleted. You can specify +the following options: + +- ``delete_all``: Deletes all child documents without running any model + callbacks. +- ``destroy``: Deletes the child documents and runs all model callbacks. +- ``nullify``: Sets the foreign key of the child documents to ``nil``. The child + document might become orphaned if it is referenced by only the parent. +- ``restrict_with_exception``: Raises an exception if the child document is not + empty. +- ``restrict_with_error``: Cancels the operation and returns ``false`` if the + child document is not empty. + +If you don't specify any ``dependent`` options, {+odm+} leaves the child +document unchanged when the parent document is deleted. The child document +continues to reference the deleted parent document, and if it is +referenced through only the parent, the child document becomes orphaned. + +The following example specifies ``dependent`` options on the ``Band`` class: + +.. literalinclude:: /includes/data-modeling/association-behaviors.rb + :language: ruby + :start-after: # start-dependent + :end-before: # end-dependent + +Autosave Referenced Associations +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +By default, {+odm+} does not automatically save associated documents from +non-embedded associations when saving the parent document. This can +result in dangling references to documents that don't exist. + +You can use the ``autosave`` option on a referenced association to +automatically save associated documents when saving the parent document. The +following example creates a ``Band`` class with an associated ``Album`` class +and specifies the ``autosave`` option: + +.. literalinclude:: /includes/data-modeling/association-behaviors.rb + :language: ruby + :start-after: # start-autosave + :end-before: # end-autosave + :emphasize-lines: 10 + +.. note:: + + {+odm+} automatically adds autosave functionality to an association that uses + the ``accepts_nested_attributes_for`` option. + +You do not need to specify the ``autosave`` option for embedded associations +because {+odm+} saves embedded documents in the parent document. + +Autobuild +~~~~~~~~~ + +You can add the ``autobuild`` option to one-to-one associations, such as +``has_one`` and ``embeds_one``, to automatically instantiate a new document when +accessing a ``nil`` association. The following example adds the ``autobuild`` +option to an association on the ``Band`` class: + +.. literalinclude:: /includes/data-modeling/association-behaviors.rb + :language: ruby + :start-after: # start-autobuild + :end-before: # end-autobuild + +Touch +~~~~~ + +When {+odm+} *touches* a document, it updates the document's +``updated_at`` field to the current date and time. You can add the ``touch`` +option to any ``belongs_to`` association to ensure that {+odm+} touches the +parent document whenever the child document is updated. The following example +adds the ``touch`` option to an association on the ``Band`` class: + +.. literalinclude:: /includes/data-modeling/association-behaviors.rb + :language: ruby + :start-after: # start-touch + :end-before: # end-touch + +You can also use the ``touch`` option to specify another field on the parent +association, as a string or a symbol. When {+odm+} touches the parent +association, it sets both the ``updated_at`` field and the specified field +to the current date and time. + +The following example instructs {+odm+} to touch the ``bands_updated_at`` field: + +.. literalinclude:: /includes/data-modeling/association-behaviors.rb + :language: ruby + :start-after: # start-touch-specific + :end-before: # end-touch-specific + +.. note:: + + In embedded associations, when an embedded document is touched, {+odm+} + touches its parents recursively. Because of this, adding a ``touch`` + attribute to an ``embedded_in`` association is unnecessary. + + {+odm+} does not support specifying additional fields to touch in + ``embedded_in`` associations. + +Counter Cache +~~~~~~~~~~~~~ + +You can use the ``counter_cache`` option to store the number of objects +that belong to an associated field. When you specify this option, {+odm+} stores +an extra attribute on the associated models to store the count. Because of this, +you must specify the ``Mongoid::Attributes::Dynamic`` module in the associated +classes. + +The following example adds the ``counter_cache`` option to a ``Band`` +class and specifies the ``Mongoid::Attributes::Dynamic`` in a ``Label`` class: + +.. literalinclude:: /includes/data-modeling/association-behaviors.rb + :language: ruby + :start-after: # start-counter-cache + :end-before: # end-counter-cache + :emphasize-lines: 4, 9 + +Association Metadata +-------------------- + +When you define an association, {+odm+} stores metadata about that association. +You can access the metadata by calling the ``reflect_on_association`` method on +a model class or document, or by directly accessing the metadata on a specific +document. The following example shows how to access metadata by using the +``reflect_on_association`` method and by direct access: + +.. literalinclude:: /includes/data-modeling/association-behaviors.rb + :language: ruby + :start-after: # start-access-metadata + :end-before: # end-access-metadata + +.. note:: + + Replace ```` in the preceding example with the name of your + association. + +Attributes +~~~~~~~~~~ + +All associations contain attributes that store information about the associated +document. Associations contain the following attributes: + +- ``_target``: The proxied document or documents +- ``_base``: The document on which the association is defined +- ``_association``: Information about the association + +The following example accesses each of the preceding attributes: + +.. literalinclude:: /includes/data-modeling/association-behaviors.rb + :language: ruby + :start-after: # start-attributes + :end-before: # end-attributes + +The following table shows the information stored in the ``_association`` +attribute: + +.. list-table:: + :header-rows: 1 + :widths: 30 60 + + * - Method + - Description + * - ``Association#as`` + - The name of the parent to a polymorphic child. + * - ``Association#as?`` + - Returns whether an ``as`` option exists. + * - ``Association#autobuilding?`` + - Returns whether the association is autobuilding. + * - ``Association#autosaving?`` + - Returns whether the association is autosaving. + * - ``Association#cascading_callbacks?`` + - Returns whether the association has callbacks cascaded down from the parent. + * - ``Association#class_name`` + - The class name of the proxied document. + * - ``Association#cyclic?`` + - Returns whether the association is a cyclic association. + * - ``Association#dependent`` + - The association's dependent option. + * - ``Association#destructive?`` + - Returns ``true`` if the association has a dependent delete or destroy method. + * - ``Association#embedded?`` + - Returns whether the association is embedded in another document. + * - ``Association#forced_nil_inverse?`` + - Returns whether the association has a ``nil`` inverse defined. + * - ``Association#foreign_key`` + - The name of the foreign-key field. + * - ``Association#foreign_key_check`` + - The name of the foreign-key field's dirty-check method. + * - ``Association#foreign_key_setter`` + - The name of the foreign-key field's setter. + * - ``Association#indexed?`` + - Returns whether the foreign key is auto indexed. + * - ``Association#inverses`` + - The names of all inverse associations. + * - ``Association#inverse`` + - The name of a single inverse association. + * - ``Association#inverse_class_name`` + - The class name of the association on the inverse side. + * - ``Association#inverse_foreign_key`` + - The name of the foreign-key field on the inverse side. + * - ``Association#inverse_klass`` + - The class of the association on the inverse side. + * - ``Association#inverse_association`` + - The metadata of the association on the inverse side. + * - ``Association#inverse_of`` + - The explicitly defined name of the inverse association. + * - ``Association#inverse_setter`` + - The name of the method used to set the inverse. + * - ``Association#inverse_type`` + - The name of the polymorphic-type field of the inverse. + * - ``Association#inverse_type_setter`` + - The name of the polymorphic-type field's setter of the inverse. + * - ``Association#key`` + - The name of the field in the attribute's hash that is used to get the association. + * - ``Association#klass`` + - The class of the proxied documents in the association. + * - ``Association#name`` + - The association name. + * - ``Association#options`` + - Returns ``self``, for API compatibility with ActiveRecord. + * - ``Association#order`` + - The custom sorting options on the association. + * - ``Association#polymorphic?`` + - Returns whether the association is polymorphic. + * - ``Association#setter`` + - The name of the field to set the association. + * - ``Association#store_as`` + - The name of the attribute in which to store an embedded association. + * - ``Association#touchable?`` + - Returns whether the association has a touch option. + * - ``Association#type`` + - The name of the field to get the polymorphic type. + * - ``Association#type_setter`` + - The name of the field to set the polymorphic type. + * - ``Association#validate?`` + - Returns whether the association has an associated validation. diff --git a/source/data-modeling/callbacks.txt b/source/data-modeling/callbacks.txt new file mode 100644 index 00000000..0eb74517 --- /dev/null +++ b/source/data-modeling/callbacks.txt @@ -0,0 +1,178 @@ +.. _mongoid-modeling-callbacks: + +========= +Callbacks +========= + +.. facet:: + :name: genre + :values: reference + +.. meta:: + :keywords: ruby framework, odm, code example, life cycle + +.. contents:: On this page + :local: + :backlinks: none + :depth: 2 + :class: singlecol + +Overview +-------- + +In this guide, you can learn how to implement **callbacks** in your +{+odm+} models to customize the life cycle of your model instances. + +Callbacks are methods that {+odm+} triggers at specified moments of +an object's life cycle. They allow you to initiate specified actions +before or after changes to an object's state. + +{+odm+} implements many of the callbacks from Active Record. To learn +more, see `Callbacks +<{+active-record-docs+}/active_record_callbacks.html>`__ in the +Active Record documentation. + +Supported Callbacks +------------------- + +Mongoid supports the following callbacks on model classes that implement +the :ref:`Document ` module: + +- ``after_initialize`` +- ``after_build`` +- ``before_validation`` +- ``after_validation`` +- ``before_create`` +- ``around_create`` +- ``after_create`` +- ``after_find`` +- ``before_update`` +- ``around_update`` +- ``after_update`` +- ``before_upsert`` +- ``around_upsert`` +- ``after_upsert`` +- ``before_save`` +- ``around_save`` +- ``after_save`` +- ``before_destroy`` +- ``around_destroy`` +- ``after_destroy`` + +To learn more about any of the preceding callback types, see the +`ActiveRecord::Callbacks +`__ +reference in the Rails API documentation. + +You can implement callbacks in both top-level and embedded document +models. + +.. note:: Callback Invocation Behavior + + For efficiency, {+odm+} invokes the callback only on the document + that you performed the persistence action on. This behavior enables + {+odm+} to support large hierarchies and handle optimized atomic + updates efficiently by not invoking callbacks throughout the document + hierarchy. + +Take precautions and ensure testability when implementing callbacks for +domain logic, because these designs can lead to unexpected errors when +callbacks in the chain halt execution. We recommend using callbacks for +cross-cutting concerns outside of your program's core functionality, +such as queueing up background jobs. + +Document Callbacks +------------------ + +You must implement and register callbacks on your model classes. +You can register a callback by using ordinary methods, blocks and +``Proc`` objects, or by defining custom callback objects that use +classes or modules. + +This example demonstrates how to register callbacks on the ``Contact`` +model class in the following ways: + +- Includes the ``before_save`` class method, which triggers the + ``process_phone`` method before a ``Contact`` instance is saved to + MongoDB. The ``process_phone`` method is defined separately in the class. + +- Includes the ``after_destroy`` class method and uses a block to print a + message when a ``Contact`` instance is deleted. + +.. literalinclude:: /includes/data-modeling/callbacks.rb + :start-after: start-doc-callback + :end-before: end-doc-callback + :language: ruby + :emphasize-lines: 8, 11-13, 16-18 + :dedent: + +The following code performs data operations that demonstrate the +callback actions: + +.. literalinclude:: /includes/data-modeling/callbacks.rb + :start-after: start-doc-ops + :end-before: end-doc-ops + :language: ruby + :dedent: + +Because callback functionality comes from Active Support, you can +alternatively use the ``set_callback`` class method syntax to register +callbacks. The following code demonstrates how to use this syntax to +create a callback that stores original values of the ``name`` field in +the ``aliases`` array: + +.. literalinclude:: /includes/data-modeling/callbacks.rb + :start-after: start-doc-set-syntax + :end-before: end-doc-set-syntax + :language: ruby + :emphasize-lines: 8-12 + :dedent: + +Association Callbacks +--------------------- + +{+odm+} provides the following association callbacks: + +- ``after_add`` +- ``after_remove`` +- ``before_add`` +- ``before_remove`` + +If you register an association callback on your model class, it is +invoked whenever you add or remove a document from any of the following +associations: + +- ``embeds_many`` +- ``has_many`` +- ``has_and_belongs_to_many`` + +Specify association callbacks as options on the respective association. +You must pass the added or removed document as the parameter to the +specified callback. + +The following code demonstrates how to register an association callback +on a ``User`` model class that embeds multiple ``SavedArticle`` +instances to limit the number of embedded documents for a single +instance: + +.. literalinclude:: /includes/data-modeling/callbacks.rb + :start-after: start-association-callback + :end-before: end-association-callback + :language: ruby + :emphasize-lines: 6, 10-15 + :dedent: + +Additional Information +---------------------- + +To learn how to prevent {+odm+} from running callbacks, see the +following references in the Active Record documentation: + +- `Skipping Callbacks <{+active-record-docs+}/active_record_callbacks.html#skipping-callbacks>`__ +- `Suppressing Saving <{+active-record-docs+}/active_record_callbacks.html#suppressing-saving>`__ + +To learn about how {+odm+} manages callbacks in transactions, see the +:ref:`mongoid-data-txn` guide. + +To learn how to access and change your MongoDB data, see the +:ref:`mongoid-interact-data` guides. diff --git a/source/data-modeling/documents.txt b/source/data-modeling/documents.txt new file mode 100644 index 00000000..f1e06630 --- /dev/null +++ b/source/data-modeling/documents.txt @@ -0,0 +1,96 @@ +.. _mongoid-modeling-documents: + +========= +Documents +========= + +.. facet:: + :name: genre + :values: reference + +.. meta:: + :keywords: ruby framework, odm, code example, bson + +.. contents:: On this page + :local: + :backlinks: none + :depth: 2 + :class: singlecol + +Overview +-------- + +In this guide, you can learn about the ``Mongoid::Document`` module in +{+odm+}. The ``Document`` module is a {+language+} implementation of a +MongoDB document, which stores data in field-and-value pairs. To learn +more about the terminology, structure, and limitations of +MongoDB documents, see :manual:`Documents ` in the +{+server-manual+}. + +You must include the ``Mongoid::Document`` module in any class that you +want to persist to MongoDB. By including the ``Document`` module in your +model class, you can use its methods on instances of your model class. + +The following code demonstrates how to include the ``Document`` module +in a sample ``Person`` model class: + +.. code-block:: ruby + :emphasize-lines: 2 + + class Person + include Mongoid::Document + + field :name, type: String + end + +You can find more information about the ``Document`` module in the `API +documentation <{+api+}/Mongoid/Document.html>`__. + +Work with Documents +------------------- + +You can store instances of your models directly in a collection, or you +can embed them in other classes that use the ``Document`` module. +When you save a ``Document`` instance to MongoDB, it is converted +to a BSON object that is similar to a {+language+} hash or JSON +object. + +The following code creates an instance of the ``Person`` model defined +in the preceding section: + +.. code-block:: ruby + + Person.create(name: 'Meena Kumar') + +The document appears in MongoDB as follows: + +.. code-block:: json + :copyable: false + + { + "_id": { + "$oid": "673b6dce61700598c24a72b0" + }, + "name": "Meena Kumar" + } + +.. note:: _id Field + + When you persist an instance of a model to the database, MongoDB + automatically adds an ``_id`` field that has a unique value even if you + do not explicitly define this field in your model. + + To learn more about this field, see the :manual:`ObjectId reference + ` in the {+server-manual+}. + +Additional Information +---------------------- + +To learn more about the field types that you can use in {+odm+} models, see +the :ref:`mongoid-field-types` guide. + +To learn how to access and change your MongoDB data, see the +:ref:`mongoid-interact-data` guides. + +To learn more about how to model your data by using {+odm+} models, +see the :ref:`mongoid-data-modeling` guides. diff --git a/source/data-modeling/field-behaviors.txt b/source/data-modeling/field-behaviors.txt new file mode 100644 index 00000000..5600af31 --- /dev/null +++ b/source/data-modeling/field-behaviors.txt @@ -0,0 +1,300 @@ +.. _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 + +Overview +-------- + +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 ``Proc`` values. + +The following example specifies a fixed default value for the ``state`` +field: + +.. literalinclude:: /includes/data-modeling/field-behaviors.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/data-modeling/field-behaviors.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`` 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 in a ``Proc`` instance. The following example sets the +``fulfill_by`` default value to depend on the state of the ``submitted_at`` +field: + +.. literalinclude:: /includes/data-modeling/field-behaviors.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/data-modeling/field-behaviors.rb + :language: ruby + :start-after: # start-field-default-pre-processed + :end-before: # end-field-default-pre-processed + +.. tip:: + + Always set the ``pre-processed`` option to ``true`` to set a + default ``Proc`` value for the ``_id`` field. + +.. _mongoid-field-behaviors-storage-names: + +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 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/data-modeling/field-behaviors.rb + :language: ruby + :start-after: # start-field-as + :end-before: # end-field-as + +{+odm+} stores the ``name`` field as ``"n"``, but you can still access the field as +``name`` in your application. + +.. _mongoid-field-behaviors-aliases: + +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 access the field by a different name in your +application. + +The following example specifies an alias for the ``name`` field: + +.. literalinclude:: /includes/data-modeling/field-behaviors.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/data-modeling/field-behaviors.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/data-modeling/field-behaviors.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 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/data-modeling/field-behaviors.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 document without an ``_id`` +value. For top-level documents, the MongoDB server automatically +assigns an ``_id`` value. However, for embedded documents, 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/data-modeling/field-behaviors.rb + :language: ruby + :start-after: # start-custom-getter-setter + :end-before: # end-custom-getter-setter + +Read-Only Attributes +-------------------- + +You can specify a field to be 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/data-modeling/field-behaviors.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. + +To learn about specifying entire models as read-only, see the +:ref:`mongoid-crud-read-only` section of the Perform Data Operations +guide. + +Localize Fields +--------------- + +{+odm+} supports localized fields by using the :github:`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/data-modeling/field-behaviors.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/data-modeling/field-behaviors.rb + :language: ruby + :start-after: # start-localized-translations + :end-before: # end-localized-translations + +You can specify fallbacks for localized fields by enabling the +:github:`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/data-modeling/field-behaviors.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/data-modeling/field-behaviors.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/data-modeling/field-behaviors.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/data-modeling/field-behaviors.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..5967162e --- /dev/null +++ b/source/data-modeling/field-types.txt @@ -0,0 +1,573 @@ +.. _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 supported in +{+odm+} that you can use to define the schema for your MongoDB documents. + +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 +``double`` type to use the {+language+} ``Float`` type. When you save the +document again, {+odm+} converts the field back to a BSON ``double``. + +To learn more about modeling documents in {+odm+}, see the +:ref:`mongoid-modeling-documents` guide. + +.. note:: + + 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 field names and types in model classes by using the ``field`` +and ``type`` macros. The following example defines the fields of a ``Person`` +class: + +.. literalinclude:: /includes/data-modeling/field-types.rb + :language: ruby + :start-after: # start-define-fields + :end-before: # end-define-fields + +The following list provides 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 ``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, 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+} 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 ``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 ``properties`` field is ``Object`` but varies depending on +the type of data stored in that field. The following example saves data into the +``properties`` field in two different ways: + +.. literalinclude:: /includes/data-modeling/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 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 +- ``Range``: Returns as ``Hash`` in untyped fields + +Hash +~~~~ + +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. + +The following example creates a ``Person`` class and specifies the ``url`` field +as a ``Hash``. + +.. literalinclude:: /includes/data-modeling/field-types.rb + :language: ruby + :start-after: # start-hash + :end-before: # end-hash + +Time +~~~~ + +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:`mongoid-config-time-zones` +section of the Application Configuration guide. + +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 + :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.parse`` + method. To learn more about how {+odm+} converts queries, see the + :ref:`mongoid-query-field-type-conversions` section of the Specify a + Query guide. + +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. To learn more about how {+odm+} converts queries, see the + :ref:`mongoid-query-field-type-conversions` section of the Specify a + Query guide. + +DateTime +~~~~~~~~ + +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 ``purchased_at`` +field as a ``DateTime`` field: + +.. literalinclude:: /includes/data-modeling/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 +``purchased_at`` field: + +.. literalinclude:: /includes/data-modeling/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 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:`mongoid-config-time-zones` +section of the Application Configuration guide. + +.. 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 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 ``Post`` class with timestamp fields: + +.. literalinclude:: /includes/data-modeling/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 ``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 + :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/data-modeling/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/data-modeling/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 implements `Perl Compatible Regular Expressions (PCRE) `__, +{+odm+} uses {+language+}'s :github:`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 {+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 {+language+} regular expression. + +The following example creates a ``Token`` class and specifies the ``pattern`` +field as a ``Regexp``: + +.. literalinclude:: /includes/data-modeling/field-types.rb + :language: ruby + :start-after: # start-regexp + :end-before: # end-regexp + +.. important:: + + 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 Onigmo and PCRE syntaxes. To learn + more about regular expressions in {+odm+}, see the + :ref:`mongoid-query-regex` section of the Specify a Query guide. + +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 you set for the ``Mongoid.map_big_decimal_to_decimal128`` configuration property: + +- If set to ``true``, {+odm+} stores ``BigDecimal`` values as BSON ``Decimal128`` + values. +- 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``: + +- ``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 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. +- ``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. + +.. 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, + instead of as 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 defines the ``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 +----------------------------------------- + +You can use strings or symbols to specify certain field types in {+odm+}, instead of +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 + + class Order + include Mongoid::Document + + # Class Name + field :order_num, type: Integer + + # Symbol + field :order_num, type: :integer + + # String + field :order_num, type: "integer" + end + +The following table provides the field types that can you can specify as strings or symbols: + +.. 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``: Takes an instance of your custom type and converts it to + an object that MongoDB can store. +- ``demongoize``: Takes an object from MongoDB and converts it to an + instance of your custom type. +- ``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 +implements the preceding methods: + +.. literalinclude:: /includes/data-modeling/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/data-modeling/field-types.rb + :language: ruby + :start-after: # start-phantom-field-type + :end-before: # end-phantom-field-type + +.. _mongoid-field-types-dynamic: + +Dynamic Fields +-------------- + +You can instruct {+odm+} to create fields dynamically by including the +``Mongoid::Attributes::Dynamic`` module in your model. This allows {+odm+} to +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: + +.. literalinclude:: /includes/data-modeling/field-types.rb + :language: ruby + :start-after: # start-dynamic-field + :end-before: # end-dynamic-field + +.. 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: + +- 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 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 +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. + +.. 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/data-modeling/indexes.txt b/source/data-modeling/indexes.txt new file mode 100644 index 00000000..dcf39899 --- /dev/null +++ b/source/data-modeling/indexes.txt @@ -0,0 +1,220 @@ +.. _mongoid-optimize-queries-with-indexes: +.. _mongoid-indexes: + +============================= +Optimize Queries With Indexes +============================= + +.. default-domain:: mongodb + +.. facet:: + :name: genre + :values: reference + +.. meta:: + :keywords: code example, odm, optimization, efficiency, Atlas search + +.. contents:: On this page + :local: + :backlinks: none + :depth: 3 + :class: singlecol + +Overview +-------- + +In this guide, you can learn how to use **indexes** with {+odm+}. Indexes can +improve the efficiency of queries by limiting the number of documents MongoDB +needs to scan. If your application is repeatedly running queries +on certain fields, you can create an index on those fields to improve query +performance. + +The following sections in this guide describe how to declare and create different +types of indexes using {+odm+}. The examples use the ``Restaurant`` model, which +maps to the ``restaurants`` collection in the ``sample_restaurants`` database. +To learn how to connect to this database +and collection using {+odm+}, see the :ref:`mongoid-quick-start-rails` or +:ref:`mongoid-quick-start-sinatra` guides. + +Declare and Create an Index +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +When using {+odm+}, you can declare your index using the ``index`` macro and +then create it using the ``create_indexes`` command. + +The following code example shows how to declare and create an ascending index +named ``cuisine_index`` on the ``cuisine`` field in the ``Restaurant`` class: + +.. literalinclude:: /includes/data-modeling/indexes.rb + :language: ruby + :emphasize-lines: 8, 11 + :start-after: start create index + :end-before: end create index + +The ``index`` macro defines the index you want to create and the ``create_indexes`` +command creates it in the ``restaurants`` collection. + +When defining an index, the first hash object contains the field you want to +index and its direction. ``1`` represents an ascending index, and ``-1`` represents a +descending index. The second hash object contains index options. To learn more +about index options, see the :ref:`mongoid-indexes-api-documentation` section. + +Aliases and Declaring Indexes ++++++++++++++++++++++++++++++ + +You can use aliased field names in index definitions. For example, the following +code creates an index on the ``b`` field, which is an alias of the ``borough`` +field: + +.. literalinclude:: /includes/data-modeling/indexes.rb + :language: ruby + :start-after: start create alias index + :end-before: end create alias index + +Create an Index on Embedded Document Fields +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +You can define an index on embedded document fields. The following code example +shows how to declare an ascending index on the ``street`` field, which is embedded +within the ``address`` field in the ``Restaurant`` model. + +.. literalinclude:: /includes/data-modeling/indexes.rb + :language: ruby + :start-after: start create embedded index + :end-before: end create embedded index + +Create a Compound Index +~~~~~~~~~~~~~~~~~~~~~~~ + +You can define a compound index on multiple fields. The following code example +shows how to declare a compound index that is ascending on the ``borough`` +field and descending on the ``name`` field. + +.. literalinclude:: /includes/data-modeling/indexes.rb + :language: ruby + :start-after: start create compound index + :end-before: end create compound index + +Create a Geospatial Index +~~~~~~~~~~~~~~~~~~~~~~~~~ + +You can define a 2dsphere index on fields that contain GeoJSON objects or +coordinate pairs. +The following example defines a 2dsphere index on a field that contains GeoJSON +objects: + +.. literalinclude:: /includes/data-modeling/indexes.rb + :language: ruby + :start-after: start create 2dsphere index + :end-before: end create 2dsphere index + +For more information on 2dsphere indexes, see the :manual:`2dsphere ` +guide in the MongoDB {+server-manual+}. + +For more information on the GeoJSON type, see the :manual:`GeoJSON Objects ` +guide in the MongoDB {+server-manual+}. + +Create a Sparse Index +~~~~~~~~~~~~~~~~~~~~~ + +You can define a sparse index on fields that are not present in all documents. +The following code example defines a sparse index on the ``borough`` field: + +.. literalinclude:: /includes/data-modeling/indexes.rb + :language: ruby + :start-after: start create sparse index + :end-before: end create sparse index + +For more information on sparse indexes, see the :manual:`Sparse Indexes ` +guide in the MongoDB {+server-manual+}. + +Create Multiple Indexes +~~~~~~~~~~~~~~~~~~~~~~~ + +You can define multiple indexes within your model and create them using a single +``create_indexes`` call. The following example shows how to create multiple +indexes at the same time: + +.. literalinclude:: /includes/data-modeling/indexes.rb + :language: ruby + :start-after: start create multiple indexes + :end-before: end create multiple indexes + +Drop Indexes +~~~~~~~~~~~~ + +You can drop all indexes in your collection. The following example drops all +indexes in the ``Restaurant`` model: + +.. literalinclude:: /includes/data-modeling/indexes.rb + :language: ruby + :start-after: start drop indexes + :end-before: end drop indexes + +.. note:: Default Index + + MongoDB creates a default index on the ``_id`` field during the + creation of a collection. This index prevents clients from inserting + two documents with the same values for the ``_id`` field. You cannot + drop this index. + +Atlas Search Indexes +~~~~~~~~~~~~~~~~~~~~ + +You can declare and manage Atlas Search indexes using {+odm+}. + +To declare a search index, use the ``search_index`` macro within your model. To +create the search indexes declared within a model, use the ``create_search_indexes`` +command. The following code example shows how to declare and create an Atlas +Search index named ``my_search_index``. +The index is on the ``name`` and ``cuisine`` fields and is dynamic. + +.. literalinclude:: /includes/data-modeling/indexes.rb + :language: ruby + :start-after: start create atlas search index + :end-before: end create atlas search index + +To learn more about the syntax for creating an Atlas Search index, see +the :atlas:`Create an Atlas Search Index ` +guide in the MongoDB Atlas documentation. + +Remove an Atlas Search Index +++++++++++++++++++++++++++++ + +To remove an Atlas Search index, use the ``remove_search_indexes`` command. The +following code example shows how to remove an Atlas Search index from the +``restaurants`` collection: + +.. literalinclude:: /includes/data-modeling/indexes.rb + :language: ruby + :start-after: start remove atlas search index + :end-before: end remove atlas search index + +List Atlas Search Indexes ++++++++++++++++++++++++++ + +You can enumerate through all Atlas Search indexes in your collection +by using the ``search_indexes`` command. The following example enumerates through +all Atlas Search indexes in the ``restaurants`` collection and prints out their +information: + +.. literalinclude:: /includes/data-modeling/indexes.rb + :language: ruby + :start-after: start list atlas search index + :end-before: end list atlas search index + +.. _mongoid-indexes-api-documentation: + +API Documentation +----------------- + +To learn more about using indexes in {+odm+}, see the +`Mongoid::Indexable::ClassMethods <{+api+}/Mongoid/Indexable/ClassMethods.html>`__ +documentation. + +To learn more about index options, see the `Mongoid::Indexable::Validators::Options +<{+api+}/Mongoid/Indexable/Validators/Options.html>`__ documentation. + +To learn more about using Atlas Search indexes in {+odm+}, see the +`Mongoid::SearchIndexable::ClassMethods <{+api+}/Mongoid/SearchIndexable/ClassMethods.html>`__ +documentation. \ No newline at end of file diff --git a/source/data-modeling/inheritance.txt b/source/data-modeling/inheritance.txt new file mode 100644 index 00000000..08441ffd --- /dev/null +++ b/source/data-modeling/inheritance.txt @@ -0,0 +1,308 @@ +.. _mongoid-modeling-inheritance: + +=========== +Inheritance +=========== + +.. facet:: + :name: genre + :values: reference + +.. meta:: + :keywords: ruby framework, odm, relationship, code example, polymorphic + +.. contents:: On this page + :local: + :backlinks: none + :depth: 2 + :class: singlecol + +Overview +-------- + +In this guide, you can learn how to implement **inheritance** into your +{+odm+} models. Inheritance allows you to apply the characteristics of +one "parent" class to one or more "child" classes. + +{+odm+} supports inheritance in top-level and embedded documents. +When a child model class inherits from a parent class, {+odm+} copies +the parent class's fields, associations, validations, and scopes to +the child class. + +Assign Inheritance +------------------ + +When creating a child model class, use the ``<`` character to implement +inheritance from a specified parent class. The following model classes +demonstrate how to create parent and child classes between the +``Person``, ``Employee``, and ``Manager`` models: + +.. literalinclude:: /includes/data-modeling/inheritance.rb + :start-after: start-simple-inheritance + :end-before: end-simple-inheritance + :language: ruby + :emphasize-lines: 7, 14 + :dedent: + +{+odm+} saves instances of ``Person``, ``Employee``, and ``Manager`` in +the ``people`` collection. {+odm+} sets the ``_type`` discriminator +field to the model class name in documents to ensure that documents are +returned as the expected types when you perform read operations. + +Embedded Documents +~~~~~~~~~~~~~~~~~~ + +You can also implement an inheritance pattern in embedded associations. +Similar to the behavior of top-level model classes, {+odm+} sets the +``_type`` discriminator field in embedded documents depending on the +model class used to create them. + +The following example adds an embedded association to the ``Person`` +model and creates parent and child models for the embedded ``Info`` +class: + +.. literalinclude:: /includes/data-modeling/inheritance.rb + :start-after: start-embedded-inheritance + :end-before: end-embedded-inheritance + :language: ruby + :emphasize-lines: 5, 14, 17, 22 + :dedent: + +Query Behavior +~~~~~~~~~~~~~~ + +When you query on a child model class, the query returns only documents +in which the value of the ``_type`` field matches the queried class or +further child classes. For example, if you query on the ``Employee`` +class, the query returns documents from the ``people`` collection in +which the ``_type`` value is either ``"Employee"`` or ``"Manager"``. All +other discriminator values are considered as instances of the parent +``Person`` class. + +When querying on a parent class such as ``Person``, {+odm+} returns +documents that meet any of the following criteria: + +- Discriminator value is the name of the parent class or any of the + child classes. For example, ``"Person"``, ``"Employee"``, or ``"Manager"``. + +- Lacks a discriminator value. + +- Discriminator value does not map to either the parent or any of its + child classes. For example, ``"Director"`` or ``"Specialist"``. + +Change the Discriminator Key +---------------------------- + +You might change the discriminator key from the default field name +``_type`` for any of the following reasons: + +- Optimization: You can select a shorter key such as ``_t``. + +- Consistency with an existing system: You might be using an existing + system or dataset that has predefined keys. + +You can change the discriminator key on the class level +or on the global level. To change the discriminator key on the class +level, you can set the custom key name on the parent class by using the +``discriminator_key`` method. + +The following example demonstrates how to set a custom discriminator key +when defining a model class: + +.. code-block:: ruby + :emphasize-lines: 6 + + class Person + include Mongoid::Document + + field :name, type: String + + self.discriminator_key = "sub_type" + end + +When you create an instance of ``Person`` or any of its child classes, +{+odm+} adds the ``sub_type`` field to documents in MongoDB. + +.. note:: + + You can change the discriminator key only on the parent class. + {+odm+} raises an error if you set a custom key on any child + class. + +If you change the discriminator key after defining a child class, {+odm+} +adds the new key field, but the old field is unchanged. For example, suppose +you add the following code to your application *after* defining your +model classes: + +.. code-block:: ruby + + Person.discriminator_key = "sub_type" + +In this case, when you create an instance of a child class such as +``Employee``, {+odm+} adds both the ``sub_type`` and ``_type`` fields to +the document. + +You can also change the discriminator key at the global level, so that +all classes use the specified key instead of the ``_type`` field. + +You can set a global key by adding the following code to your +application *before* defining any model classes: + +.. code-block:: ruby + + Mongoid.discriminator_key = "sub_type" + +All classes use ``sub_type`` as the discriminator key and do not include +the ``_type`` field. + +.. note:: + + You must set the discriminator key on the global level before defining + any child classes for the classes to use that global value. If you set + the global key after defining child classes, your saved documents + contain the default ``_type`` field. + +Change the Discriminator Value +------------------------------ + +You can customize the value that {+odm+} sets as the discriminator value +in MongoDB. Use the ``discriminator_value`` method when defining a +class to customize the discriminator value, as shown in the following +example: + +.. code-block:: ruby + :emphasize-lines: 6 + + class Employee + include Mongoid::Document + + field :company, type: String + + self.discriminator_value = "Worker" + end + +When you create an instance of ``Employee``, the document's ``_type`` +discriminator field has a value of ``"Worker"`` instead of the class +name. + +.. note:: + + Because the discriminator value customization is declared in child classes, + you must load the child classes retrieved by a query *before* sending + that query. + + In the preceding example, the ``Employee`` class definition must be + loaded before you query on ``Person`` if the returned documents include + instances of ``Employee``. Autoloading cannot resolve the + discriminator value ``"Worker"`` to return documents as instances of + ``Employee``. + +Embedded Associations +--------------------- + +You can create any type of parent class or child class in an embedded +association by assignment or by using the ``build`` and ``create`` +methods. You can pass desired model class as the second parameter to the +``build`` and ``create`` methods to instruct {+odm+} to create that +specific instance as an emdedded document. + +The following code creates an instance of ``Employee``, then +demonstrates how to add embedded documents by using the different +creation methods: + +.. literalinclude:: /includes/data-modeling/inheritance.rb + :start-after: start-association-operations + :end-before: end-association-operations + :language: ruby + :dedent: + +The following document is stored in the ``people`` database: + +.. code-block:: json + :emphasize-lines: 13, 20, 25, 32 + + { + "_id": {...}, + "name": "Lance Huang", + "company": "XYZ Communications", + "tenure": 2, + "_type": "Employee", + "infos": [ + { + "_id": {...}, + "active": true, + "value": "l.huang@company.com", + "category": "work", + "_type": "Email" + }, + { + "_id": {...}, + "active": false, + "value": "lanceh11@mymail.com", + "category": "personal", + "_type": "Email" + }, + { + "_id": {...}, + "active": true, + "_type": "Info" + }, + { + "_id": {...}, + "active": true, + "value": 1239007777, + "country": "USA", + "_type": "Phone" + } + ] + } + +Persistence Contexts +-------------------- + +You can change the persistence context of a child class from +the persistence context of its parent to store the document in a +different location than the default. By using the ``store_in`` method, +you can store an instance of a child class in a different collection, +database, or cluster than an instance of the parent model. + +The following model definitions demonstrate how to use the +``store_in`` method to store instances of ``Employee`` and ``Manager`` +in a different collection than the ``people`` collection: + +.. code-block:: ruby + :emphasize-lines: 7, 12 + + class Person + include Mongoid::Document + end + + class Employee < Person + # Specifies "employees" as target collection + store_in collection: :employees + end + + class Manager < Employee + # Specifies "managers" as target collection + store_in collection: :managers + end + +.. note:: + + {+odm+} still adds the discriminator field to stored documents. + +If you set an alternate target collection on some child classes +and not others, instances of the classes without specified collections +are stored in the collection associated with the parent class. + +.. note:: + + When you change the target collection for a child class, instances of + that class do not appear in the results from queries on the parent + class. + +Additional Information +---------------------- + +To learn more about configuring the target collection for your +operations, see the :ref:`mongoid-persistence` guide. diff --git a/source/data-modeling/validation.txt b/source/data-modeling/validation.txt new file mode 100644 index 00000000..96ac5474 --- /dev/null +++ b/source/data-modeling/validation.txt @@ -0,0 +1,343 @@ +.. _mongoid-modeling-validation: + +=================== +Document Validation +=================== + +.. facet:: + :name: genre + :values: reference + +.. meta:: + :keywords: ruby framework, odm, schema, code example + +.. contents:: On this page + :local: + :backlinks: none + :depth: 2 + :class: singlecol + +Overview +-------- + +In this guide, you can learn how to define **validation rules** in your +{+odm+} models. After you implement validation into your models, {+odm+} +prevents you from running write operations that violate the validation +rules. Use document validation to restrict data types and value ranges +of document fields in your collections. + +{+odm+} includes ``ActiveModel::Validations`` from Active Record to +provide validation functionality, including an associated and uniqueness +validator. To learn more, see the `Active Record Validations +<{+active-record-docs+}/active_record_validations.html>`__ +Rails guide and `ActiveModel::Validations +`__ +Rails API documentation. + +.. note:: Comparing {+odm+} and MongoDB Validation + + Validation in {+odm+} applies only in the context of your + application and differs from creating schema validation rules in + MongoDB. This means that your validation rules do not apply to write + operations that are performed outside of your application. To learn + more about MongoDB schema validation, see :manual:`Schema Validation + ` in the {+server-manual+}. + +Validation Helpers +------------------ + +{+odm+} supports Active Record validation helpers that you can use when defining your +model classes. You can use these helpers to set common validation rules +in your application, such as checking for the presence of a field, +comparing a field value to a specified value, or ensuring that a field +has a unique value. + +Define a Validation Rule +~~~~~~~~~~~~~~~~~~~~~~~~ + +Use the ``validates`` macro to create a validation rule, then include +the validation helper and the required specifications for the rule. + +.. tip:: + + Each validation helper accepts one or more field names, which allows you + to define the same rule for multiple fields. + +The following code demonstrates how to use the ``presence`` validation +helper to require that ``Person`` instances contain a value for the +``name`` field: + +.. literalinclude:: /includes/data-modeling/validation.rb + :start-after: start-simple-val + :end-before: end-simple-val + :language: ruby + :emphasize-lines: 5 + :dedent: + +You can learn about other useful validation helpers in the +:ref:`mongoid-common-validations` section of this guide. + +.. _mongoid-common-validations: + +Common Validations +------------------ + +In this section, you can learn about the following common validation +rules and view examples that use validation helpers: + +- :ref:`mongoid-compare-validation` +- :ref:`mongoid-format-validation` +- :ref:`mongoid-inclusion-exclusion-validation` +- :ref:`mongoid-presence-absence-validation` +- :ref:`mongoid-uniqueness-validation` +- :ref:`mongoid-association-validation` +- :ref:`mongoid-other-validation` + +.. _mongoid-compare-validation: + +Comparison Rule +~~~~~~~~~~~~~~~ + +You can use the ``comparison`` helper to validate a document based on +the value of a specified field. + +The ``comparison`` helper supports the following options: + +- ``greater_than``: The value must be greater than the supplied value. +- ``greater_than_or_equal_to``: The value must be greater than or equal to the supplied value. +- ``equal_to``: The value must be equal to the supplied value. +- ``less_than``: The value must be less than the supplied value. +- ``less_than_or_equal_to``: The value must be less than or equal to the supplied value. +- ``other_than``: The value must be different than the supplied value. + +This example defines the following comparison validation rules on the +``Order`` model: + +- ``delivery_date``: Must be after (greater than) the value of ``order_date`` +- ``quantity``: Must be less than ``5`` + +.. literalinclude:: /includes/data-modeling/validation.rb + :start-after: start-comparison + :end-before: end-comparison + :language: ruby + :emphasize-lines: 8-9 + :dedent: + +.. _mongoid-format-validation: + +Formatting Rule +~~~~~~~~~~~~~~~ + +You can use the ``format`` helper to validate a document based on +whether a field value matches a regular expression. Use the ``with`` +option to specify the regular expression. + +This example defines a format validation rule on the +``User`` model to ensure that the ``username`` field contains only +letters: + +.. literalinclude:: /includes/data-modeling/validation.rb + :start-after: start-fmt + :end-before: end-fmt + :language: ruby + :emphasize-lines: 6 + :dedent: + +.. tip:: Alternative Helper Method + + The ``Mongoid::Document`` module provides macro methods for certain + validations. Instead of using the ``format`` validation helper in the + ``validates`` macro statement, you can use the + ``validates_format_of`` method, as shown in the following code: + + .. code-block:: ruby + + validates_format_of :username, with: /\A[a-zA-Z]+\z/ + +.. _mongoid-inclusion-exclusion-validation: + +Inclusion or Exclusion Rule +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +You can use the ``inclusion`` and ``exclusion`` helpers to validate a +document based on whether a field value is in a specified list +of values. Use the ``in`` option to specify the list of values. + +This example defines an inclusion validation rule on the +``Order`` model to ensure that the ``shipping`` field value is one of +the accepted values: + +.. literalinclude:: /includes/data-modeling/validation.rb + :start-after: start-inclusion + :end-before: end-inclusion + :language: ruby + :emphasize-lines: 6 + :dedent: + +.. _mongoid-presence-absence-validation: + +Presence or Absence Rule +~~~~~~~~~~~~~~~~~~~~~~~~ + +You can use the ``presence`` and ``absence`` helpers to validate a +document based on whether a field value is present or absent (empty). + +This example defines an absence validation rule on the +``Order`` model to ensure that the ``delivery_date`` field value is +either ``nil`` or an empty string: + +.. literalinclude:: /includes/data-modeling/validation.rb + :start-after: start-absence + :end-before: end-absence + :language: ruby + :emphasize-lines: 6 + :dedent: + +.. tip:: Alternative Helper Method + + The ``Mongoid::Document`` module provides macro methods for certain + validations. Instead of using the ``presence`` validation helper in the + ``validates`` macro statement, you can use the + ``validates_presence_of`` method, as shown in the following code: + + .. code-block:: ruby + + validates_presence_of :delivery_date + +.. _mongoid-uniqueness-validation: + +Uniqueness Rule +~~~~~~~~~~~~~~~ + +You can use the ``uniqueness`` helper to validate a +document based on whether a field value is unique from other values in +the collection. You can use the ``scope`` option to specify one or more +field names that {+odm+} uses to limit the uniqueness check. + +This example defines a uniqueness validation rule on the +``Person`` model to ensure that the ``first_name`` field value is +unique within documents that have the same ``last_name`` value: + +.. literalinclude:: /includes/data-modeling/validation.rb + :start-after: start-unique + :end-before: end-unique + :language: ruby + :emphasize-lines: 7 + :dedent: + +.. tip:: Alternative Helper Method + + The ``Mongoid::Document`` module provides macro methods for certain + validations. Instead of using the ``uniqueness`` validation helper in the + ``validates`` macro statement, you can use the + ``validates_uniqueness_of`` method, as shown in the following code: + + .. code-block:: ruby + + validates_uniqueness_of :first_name + + {+odm+} uses a ``primary`` read preference when you use the + ``validates_uniqueness_of`` method on a model, because if it + queries a secondary member of the replica set, it might read stale data. + + This method takes a ``conditions`` option that allows you to specify + conditions to add when {+odm+} checks for uniqueness: + + .. code-block:: ruby + + validates_uniqueness_of :name, conditions: -> { where(:age.gte => 10) } + +.. _mongoid-association-validation: + +Validate Associations +~~~~~~~~~~~~~~~~~~~~~ + +You can use the ``validates_associated`` helper to validate any +associations that your model has. When you include this validation rule, +{+odm+} validates any association documents any time you try to +save an instance. To learn more about associations, see the +:ref:`mongoid-associations` guide. + +This example defines an association validation rule on the +``Author`` model to run the validation rules for the embedded ``Book`` +instances: + +.. literalinclude:: /includes/data-modeling/validation.rb + :start-after: start-assoc + :end-before: end-assoc + :language: ruby + :emphasize-lines: 6 + :dedent: + +.. important:: + + Don't use the ``validates_associated`` helper on both ends of your + associations because this causes {+odm+} to perform validations in an + infinite loop. + +.. _mongoid-other-validation: + +Custom Validation Rules +~~~~~~~~~~~~~~~~~~~~~~~ + +You can use the ``validates_each`` and ``validates_with`` helpers to +create custom validators. To learn more about these helpers and view +examples, see the `validates_each +<{+active-record-docs+}/active_record_validations.html#validates-each>`__ +and `validates_with +<{+active-record-docs+}/active_record_validations.html#validates-with>`__ +references in the Active Record documentation. + +To learn more about custom validators, see `Performing Custom +Validations +<{+active-record-docs+}/active_record_validations.html#performing-custom-validations>`__ +in the Active Record documentation. + +Behavior +-------- + +{+odm+} performs validation when you persist, or save, a document to the +database. The following methods trigger your validation rules, so +{+odm+} saves the object to the database only if it passes validation: + +- ``create`` +- ``save`` +- ``update`` + +When you use the ``!``-suffixed version of the preceding methods, +{+odm+} returns an ``Mongoid::Errors::Validations`` exception if +validation fails for an object. + +Trigger Validation +~~~~~~~~~~~~~~~~~~ + +You can run validations manually by using the ``valid?`` method. This +method returns ``true`` if the object passes validation, and +``false`` otherwise: + +.. literalinclude:: /includes/data-modeling/validation.rb + :start-after: start-valid + :end-before: end-valid + :language: ruby + :emphasize-lines: 7, 11, 14 + :dedent: + +{+odm+} behaves differently from Active Record when running ``valid?`` +on persisted data. Active Record's ``valid?`` runs all +validations, but {+odm+}'s ``valid?`` runs validations only on +documents that are in memory to optimize performance. + +Additional Information +---------------------- + +To learn more about the field types that you can use in {+odm+} models, see +the :ref:`mongoid-field-types` guide. + +To learn more about validation methods and macros in {+odm+}, see the +:mongoid-api:`Mongoid::Validatable ` module +reference in the API documentation. + +To view a full list of validations helpers in Active Record, see the +`ActiveModel::Validations::HelperMethods +`__ +reference in the Rails API documentation. diff --git a/source/ecosystem.txt b/source/ecosystem.txt deleted file mode 100644 index a754c45e..00000000 --- a/source/ecosystem.txt +++ /dev/null @@ -1,84 +0,0 @@ -********* -Ecosystem -********* - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 2 - :class: singlecol - -Mongoid has an extensive ecosystem of libraries integrating with or built -on top of Mongoid, are listed below. - - -Projects -======== - -- `Workarea Commerce `_ - - Workarea is an enterprise-grade Ruby on Rails commerce platform that uses Mongoid. - - -Extension Libraries -=================== - -- `Mongoid Tree `_ - - A tree structure for Mongoid documents using the materialized path pattern. - -- `Mongoid Token `_ - - A little random, unique token generator for Mongoid documents. - -- `Mongoid Collection Snapshot `_ - - Easy maintenance of collections of processed data in MongoDB with the Mongoid ODM. - -- `Mongoid Locker `_ - - Document-level locking for MongoDB via Mongoid. - -- `Mongo Beautiful Logger `_ - - A simple and beautiful logger library for MongoDB in your Ruby/Rails app. - -- `Mongoid Search `_ - - Simple full text search for Mongoid. - -- `Mongoid Fulltext Search `_ - - Full-text search using n-gram matching for the Mongoid ODM. - - -Integration Libraries -===================== - -- `CarrierWave Mongoid `_ - - Mongoid Support for the Carrierwave file uploads library. - -- `Mongoid RSpec `_ - - RSpec matchers and macros for Mongoid applications. - -- `RailsAdmin `_ supports Mongoid out - of the box. - -- `ActiveAdmin Mongoid `_ - - ActiveAdmin hacks to support Mongoid. - -- `Mongoid History `_ - - Multi-user non-linear history tracking, auditing, undo, redo for mongoid. -- `Delayed Job Mongoid `_ - - Mongoid backend for delayed_job. - -- `Mongo Session Store `_ - - A Rails-compatible session store for Mongoid. diff --git a/source/img/rails-blog-new-comment.png b/source/img/rails-blog-new-comment.png deleted file mode 100644 index f82a56e9..00000000 Binary files a/source/img/rails-blog-new-comment.png and /dev/null differ diff --git a/source/img/rails-new-blog.png b/source/img/rails-new-blog.png deleted file mode 100644 index 210355a2..00000000 Binary files a/source/img/rails-new-blog.png and /dev/null differ diff --git a/source/includes/aggregation/builder-dsl.rb b/source/includes/aggregation/builder-dsl.rb new file mode 100644 index 00000000..9e884ba7 --- /dev/null +++ b/source/includes/aggregation/builder-dsl.rb @@ -0,0 +1,6 @@ +criteria = Tour.where('participant.name' => 'Serenity'). + unwind(:states). + group(_id: 'states', :states.add_to_set => '$states'). + project(_id: 0, states: 1) + +@states = Tour.collection.aggregate(criteria.pipeline).to_json \ No newline at end of file diff --git a/source/includes/aggregation/ruby-aggregation.rb b/source/includes/aggregation/ruby-aggregation.rb new file mode 100644 index 00000000..91181f90 --- /dev/null +++ b/source/includes/aggregation/ruby-aggregation.rb @@ -0,0 +1,21 @@ +band_ids = Band.collection.aggregate([ + { '$lookup' => { + from: 'tours', + localField: '_id', + foreignField: 'band_id', + as: 'tours', + } }, + { '$lookup' => { + from: 'awards', + localField: '_id', + foreignField: 'band_id', + as: 'awards', + } }, + { '$match' => { + 'tours.year' => {'$gte' => 2000}, + 'awards._id' => {'$exists' => true}, + } }, + {'$project' => {_id: 1}}, +]) + +bands = Band.find(band_ids.to_a) \ No newline at end of file diff --git a/source/includes/configuration/collection-config.rb b/source/includes/configuration/collection-config.rb new file mode 100644 index 00000000..fc687d66 --- /dev/null +++ b/source/includes/configuration/collection-config.rb @@ -0,0 +1,39 @@ +# start-time-series-config +class Measurement + include Mongoid::Document + + field :temperature, type: Integer + field :timestamp, type: Time + + store_in collection_options: { + time_series: { + timeField: "timestamp", + granularity: "minutes" + }, + expire_after: 604800 + } +end +# end-time-series-config + +# start-capped-collection-config +class Blog + include Mongoid::Document + + store_in collection_options: { + capped: true, + size: 1024 + } +end +# end-capped-collection-config + +# start-default-collation-config +class Title + include Mongoid::Document + + store_in collection_options: { + collation: { + locale: 'fr' + } + } +end +# end-default-collation-config \ No newline at end of file diff --git a/source/includes/configuration/persistence-configuration.rb b/source/includes/configuration/persistence-configuration.rb new file mode 100644 index 00000000..e6c1ae3f --- /dev/null +++ b/source/includes/configuration/persistence-configuration.rb @@ -0,0 +1,133 @@ +# start default modeling +class Restaurant + include Mongoid::Document +end + +class Person + include Mongoid::Document +end +# end default modeling + +# start set pluralization +ActiveSupport::Inflector.inflections do |inflect| + inflect.plural("rey", "reyes") +end +# end set pluralization + +# start BSON model +{ + "_id" : ObjectId("4d3ed089fb60ab534684b7e9"), + "title" : "Sir", + "name" : { + "_id" : ObjectId("4d3ed089fb60ab534684b7ff"), + "first_name" : "Durran" + }, + "addresses" : [ + { + "_id" : ObjectId("4d3ed089fb60ab534684b7e0"), + "city" : "Berlin", + "country" : "Deutschland" + } + ] +} +# end BSON model + +# start store_in example +class Person + include Mongoid::Document + store_in collection: "citizens", database: "other", client: "analytics" +end +# end store_in example + +# start store_in lambda example +class Band + include Mongoid::Document + store_in database: ->{ Thread.current[:database] } +end +# end store_in lambda example + +# start persistence context attributes +puts Band.client_name + +puts Band.database_name + +puts Band.collection_name +# end persistence context attributes + +# start with example +class Band + include Mongoid::Document + + field :name, type: String + field :likes, type: Integer +end + +# Creates document in 'bands' collection in 'music-non-stop' database within +# default cluster +Band.with(database: "music-non-stop") do |band_class| + band_class.create(name: "Medusa and the Polyps") +end + +# Deletes all documents in 'artists' collection within default database +Band.with(collection: "artists") do |band_class| + band_class.delete_all +end + +band = Band.new(name: "Japanese Breakfast") + +# Perform operations on tertiary cluster +band.with(client: :tertiary) do |band_object| + band_object.save! + + band.save! +end +# end with example + +# start read configuration +Band.with(read: {mode: :secondary}) do + Band.count + + # This write operation runs in the + # new persistence context, but is + # not affected by the read preference. + Band.create(name: "Metallica") +end +# end read configuration + +# start global configuration example +class BandsController < ApplicationController + before_action :switch_database + after_action :reset_database + + private + + def switch_database + I18n.locale = params[:locale] || I18n.default_locale + Mongoid.override_database("my_db_name_#{I18n.locale}") + end + + def reset_database + Mongoid.override_database(nil) + end +end +# end global configuration example + +# start access client collection +Band.mongo_client +band.mongo_client + +Band.collection +band.collection +# end access client collection + +# start client with example +Band.mongo_client.with(write: { w: 0 }, database: "music") do |client| + client[:artists].find(...) +end +# end client with example + +# start collection with example +Band.collection.with(write: { w: 0 }) do |collection| + collection[:artists].find(...) +end +# end collection with example \ No newline at end of file diff --git a/source/includes/configuration/sample-config-options.yml b/source/includes/configuration/sample-config-options.yml new file mode 100644 index 00000000..9667583a --- /dev/null +++ b/source/includes/configuration/sample-config-options.yml @@ -0,0 +1,235 @@ +development: + # Configures available database clients. (required) + clients: + # Defines the default client. (required) + default: + # Supplies your connection URI, including the database name. + uri: mongodb+srv://user:pass@mongo0.example.com/myDB + + # OR, you can define the parameters separately. + # Defines the name of the default database. (required) + database: my_db_development + # Provides the hosts the client can connect to. + # Must be an array of host:port pairs. (required) + hosts: + - localhost:27017 + options: + # All options in this section are Ruby driver client options. + # To learn more, visit + # https://www.mongodb.com/docs/ruby-driver/current/reference/create-client/ + + # Sets the write concern. (default = { w: 1 }) + write: + w: 1 + + # Sets the read preference. Valid options for mode are: :secondary, + # :secondary_preferred, :primary, :primary_preferred, :nearest + # (default: primary) + read: + mode: :secondary_preferred + tag_sets: # If using tag sets + - use: web + + # Sets name of the user for authentication. + user: "user" + + # Sets password of the user for authentication. + password: "password" + + # Sets user's database roles. + roles: + - "dbOwner" + + # Sets the authentication mechanism. Valid options include: + # :scram, :scram256, :mongodb_cr, :mongodb_x509, :gssapi, :aws, :plain + # MongoDB Server defaults to :scram + auth_mech: :scram + + # Sets the database or source to authenticate the user against. + # (default: the database specified above or admin) + auth_source: admin + + # Specifies type of connection. Can be one of: :direct, + # :replica_set, :sharded + # Set to :direct when connecting to hidden members of a replica set. + connect: :direct + + # Changes the time taken for the server monitors to refresh + # their status via hello commands. (default: 10) + heartbeat_frequency: 10 + + # Sets time in seconds for selecting servers for a :nearest read + # preference. (default: 0.015) + local_threshold: 0.015 + + # Sets timeout in seconds for selecting a server for an + # operation. (default: 30) + server_selection_timeout: 30 + + # Sets maximum number of connections in the connection pool. + # (default: 5) + max_pool_size: 5 + + # Sets minimum number of connections in the connection pool. + # (default: 1) + min_pool_size: 1 + + # Sets time to wait, in seconds, in the connection pool for a + # connection to be checked in before timing out. (default: 5) + wait_queue_timeout: 5 + + # Sets time to wait to establish a connection before timing out, + # in seconds. (default: 10) + connect_timeout: 10 + + # Sets name of the replica set to connect to. Servers provided as + # seeds that do not belong to this replica set are ignored. + replica_set: myRS + + # Sets compressors to use for wire protocol compression. + # (default is to not use compression) + compressors: ["zstd", "snappy", "zlib"] + + # Specified whether to connect to the servers by using ssl. + # (default: false) + ssl: true + + # Sets certificate file used to identify the connection for SSL. + ssl_cert: /path/to/my.cert + + # Sets private keyfile used to identify the connection against MongoDB. + # Even if the key is stored in the same file as the certificate, + # both need to be explicitly specified. + ssl_key: /path/to/my.key + + # Sets passphrase for the private key. + ssl_key_pass_phrase: password123 + + # Specifies whether to do peer certification validation. + # (default: true) + ssl_verify: true + + # Sets file containing concatenated certificate authority + # certificates used to validate certs passed from the other end + # of the connection. + ssl_ca_cert: /path/to/ca.cert + + # Specifies whether to truncate long log lines. (default: true) + truncate_logs: true + + # Optional Mongoid-specific configuration. + options: + # Allows BSON::Decimal128 to be parsed and returned directly in + # field values. Only has effect when BSON 5+ is present. + allow_bson5_decimal128: false + + # Sets app name that is printed to the MongoDB logs upon establishing + # a connection. Value is used as the database name if the database + # name is not provided. (default: nil) + app_name: nil + + # When false, callbacks for embedded documents will not be + # called. This is the default in 9.0. + # Setting this flag to true restores the pre-9.0 behavior, where callbacks + # for embedded documents are called, which might lead to stack overflow errors. + around_callbacks_for_embeds: false + + # Sets the async_query_executor for the application. By default the + # thread pool executor is set to :immediate. Options are: + # - :immediate - Initializes a single +Concurrent::ImmediateExecutor+ + # - :global_thread_pool - Initializes a single +Concurrent::ThreadPoolExecutor+ + # that uses the +async_query_concurrency+ for the +max_threads+ value. + async_query_executor: :immediate + + # Marks belongs_to associations as required by default, so saving a + # model with a missing association triggers a validation error. + belongs_to_required_by_default: true + + # Sets the global discriminator key. + discriminator_key: "_type" + + # Raises an exception when a field is redefined. + duplicate_fields_exception: false + + # Defines how many asynchronous queries can be executed concurrently. + # This option should be set only if `async_query_executor` is set + # to `:global_thread_pool`. + global_executor_concurrency: nil + + # When this flag is true, any attempt to change the _id of a persisted + # document will raise an exception (Errors::ImmutableAttribute). + # This is the default in 9.0. Setting this flag to false restores the + # pre-9.0 behavior, where changing the _id of a persisted + # document might be ignored. + immutable_ids: true + + # Includes the root model name in json serialization. + include_root_in_json: false + + # # Include the _type field in serialization. + include_type_for_serialization: false + + # Specifies whether to join nested persistence contexts for atomic + # operations to parent contexts. + join_contexts: false + + # When this flag is false (the default for 9.0), a document that + # is created or loaded remembers the storage options that were active + # when it was loaded, and will use those same options by default when + # saving or reloading itself. + # + # When this flag is true, a document does not remember the storage + # options from when it was loaded/created, and + # subsequent updates will need to explicitly set up those options + # each time. + legacy_persistence_context_behavior: false + + # Specifies whether to use legacy read-only behavior. To learn more, + # visit https://www.mongodb.com/docs/mongoid/current/interact-data/crud + legacy_readonly: false + + # Sets the log level. This must be set before referencing clients + # or Mongo.logger, because changes to this option are not be + # propagated to any clients and loggers that already exist. + log_level: :info + + # Stores BigDecimals as Decimal128s instead of strings in the db. + map_big_decimal_to_decimal128: true + + # Preloads all models in development, needed when models use + # inheritance. + preload_models: false + + # When this flag is true, callbacks for every embedded document will be + # called only once, even if the embedded document is embedded in multiple + # documents in the root document's dependencies graph. + # This is the default in 9.0. Setting this flag to false restores the + # pre-9.0 behavior, where callbacks are called for every occurrence of an + # embedded document. + prevent_multiple_calls_of_embedded_callbacks: true + + # Raises an error when performing a find operation and no document + # matches. + raise_not_found_error: true + + # Raises an error when defining a scope with the same name as an + # existing method. + scope_overwrite_exception: false + + # Returns stored times as UTC. + use_utc: false + + # Optional driver-specific configuration. + driver_options: + # When this flag is off, an aggregation done on a view is performed on + # the documents included in that view, instead of all documents in the + # collection. When this flag is on, the view filter is ignored. + broken_view_aggregate: true + + # When this flag is set to false, the view options is correctly + # propagated to readable methods. + broken_view_options: true + + # When this flag is set to true, the update and replace methods + # validate the parameters and raise an error if they are invalid. + validate_update_replace: false diff --git a/source/includes/configuration/sharding.rb b/source/includes/configuration/sharding.rb new file mode 100644 index 00000000..878fb5f3 --- /dev/null +++ b/source/includes/configuration/sharding.rb @@ -0,0 +1,58 @@ +# start-shard-key +class Person + include Mongoid::Document + + field :ssn + + shard_key ssn: 1 + + # The collection must also have an index that starts with the shard key. + index ssn: 1 +end +# end-shard-key + +# start-shard-key-formats +# Create a ranged shard key +shard_key ssn: 1 + +# Create a compound shard key +shard_key ssn: 1, country: 1 + +# Create a hashed shard key +shard_key ssn: :hashed + +# Specify a shard key option +shard_key {ssn: :hashed}, unique: true +# end-shard-key-formats + +# start-shard-key-shorthand +# Create a ranged shard key +shard_key :ssn + +# Create a compound shard key +shard_key :ssn, :country +# end-shard-key-shorthand + +# start-shard-key-association +class Person + include Mongoid::Document + + belongs_to :country + + # Shards by country_id + shard_key country: 1 + + # The collection must have an index that starts with the shard key + index country: 1 +end +# end-shard-key-association + +# start-shard-key-embedded +class Person + include Mongoid::Document + + field :address + + shard_key "address.city" +end +# end-shard-key-embedded \ No newline at end of file diff --git a/source/includes/data-modeling/association-behaviors.rb b/source/includes/data-modeling/association-behaviors.rb new file mode 100644 index 00000000..a74d0739 --- /dev/null +++ b/source/includes/data-modeling/association-behaviors.rb @@ -0,0 +1,228 @@ +# start-extensions +class Band + include Mongoid::Document + + embeds_many :albums do + def find_by_name(name) + where(name: name).first + end + end +end + +band.albums.find_by_name("Omega") # returns album "Omega" +# end-extensions + +# start-custom-name +class Band + include Mongoid::Document + + embeds_many :records, class_name: "Album" +end +# end-custom-name + +# start-custom-keys +class Band + include Mongoid::Document + + field :band_id, type: String + has_many :albums, primary_key: 'band_id', foreign_key: 'band_id_ref' +end + +class Album + include Mongoid::Document + + field :band_id_ref, type: String + belongs_to :band, primary_key: 'band_id', foreign_key: 'band_id_ref' +end +# end-custom-keys + +# start-custom-inverse-keys +class Band + include Mongoid::Document + + field :band_id, type: String + field :member_ids, type: Array + + has_many :members, + primary_key: 'member_id', foreign_key: 'member_ids', + inverse_primary_key: 'band_id', inverse_foreign_key: 'band_ids' +end + +class Member + include Mongoid::Document + + field :member_id, type: String + field :band_ids, type: Array + + has_many :bands, + primary_key: 'band_id', foreign_key: 'band_ids', + inverse_primary_key: 'member_id', inverse_foreign_key: 'member_ids' +end +# end-custom-inverse-keys + +# start-custom-scope +class Band + include Mongoid::Document + + has_many :albums, scope: -> { where(published: true) } + + # Uses a scope called "upcoming" on the Tour model + has_many :tours, scope: :upcoming +end +# end-custom-scope + +# start-validation-false +class Band + include Mongoid::Document + + embeds_many :albums, validate: false +end +# end-validation-false + +# start-polymorphic +class Tour + include Mongoid::Document + + has_one :band, as: :featured +end + +class Label + include Mongoid::Document + + has_one :band, as: :featured +end + +class Band + include Mongoid::Document + + belongs_to :featured, polymorphic: true +end +# end-polymorphic + +# start-custom-polymorphic +class Band + include Mongoid::Document + + identify_as 'artist' + has_many :albums, as: :record +end +# end-custom-polymorphic + +# start-multiple-alias +class Band + include Mongoid::Document + + identify_as 'artist', 'group', 'troupe' + has_many :albums, as: :record +end +# end-multiple-alias + +# start-polymorphic-resolvers +Mongoid::ModelResolver.register_resolver Mongoid::ModelResolver.new, :mus +Mongoid::ModelResolver.register_resolver Mongoid::ModelResolver.new, :tool + +module Music + class Band + include Mongoid::Document + + identify_as 'bnd', resolver: :mus + end +end + +module Tools + class Band + include Mongoid::Document + + identify_as 'bnd', resolver: :tool + end +end +# end-polymorphic-resolvers + +# start-dependent +class Band + include Mongoid::Document + + has_many :albums, dependent: :delete_all + belongs_to :label, dependent: :nullify +end +# end-dependent + +# start-autosave +class Band + include Mongoid::Document + + has_many :albums +end + +class Album + include Mongoid::Document + + belongs_to :band, autosave: true +end + +band = Band.new +album = Album.create!(band: band) +# The band is persisted at this point. +# end-autosave + +# start-autobuild +class Band + include Mongoid::Document + + embeds_one :label, autobuild: true + has_one :producer, autobuild: true +end +# end-autobuild + +# start-touch +class Band + include Mongoid::Document + + field :name + belongs_to :label, touch: true +end +# end-touch + +# start-touch-specific +class Band + include Mongoid::Document + + belongs_to :label, touch: :bands_updated_at +end +# end-touch-specific + +# start-counter-cache +class Band + include Mongoid::Document + + belongs_to :label, counter_cache: true +end + +class Label + include Mongoid::Document + include Mongoid::Attributes::Dynamic + + has_many :bands +end +# end-counter-cache + +# start-access-metadata +# Get the metadata for a named association from the class or document +Model.reflect_on_association(:) + +# Directly access metadata on a document +model.associations[:] +# end-access-metadata + +# start-attributes +class Band + include Mongoid::Document + + embeds_many :songs +end + +Band.songs = [ song ] +Band.songs._target # returns [ song ] +Band.songs._base # returns band +Band.songs._association # returns the association metadata +# end-attributes \ No newline at end of file diff --git a/source/includes/data-modeling/associations.rb b/source/includes/data-modeling/associations.rb new file mode 100644 index 00000000..b5580d2b --- /dev/null +++ b/source/includes/data-modeling/associations.rb @@ -0,0 +1,299 @@ +# start-has-one +class Band + include Mongoid::Document + + has_one :studio +end +# end-has-one + +# start-has-one-child +class Studio + include Mongoid::Document + + belongs_to :band +end +# end-has-one-child + +# start-has-one-validation +class Band + include Mongoid::Document + + has_one :studio + + validates_presence_of :studio +end +# end-has-one-validation + +# start-has-many +class Band + include Mongoid::Document + + has_many :members +end +# end-has-many + +# start-has-many-child +class Member + include Mongoid::Document + + belongs_to :band +end +# end-has-many-child + +# start-has-many-validation +class Band + include Mongoid::Document + + has_many :members + + validates_presence_of :members +end +# end-has-many-validation + +# start-has-many-any +band = Band.first +band.members.any? +# end-has-many-any + +# start-has-many-any-filter +band = Band.first +band.members.any? { |member| member.instrument == 'piano' } +# end-has-many-any-filter + +# start-has-many-any-class +class Drummer < Member +end + +band = Band.first +band.members.any?(Drummer) +# end-has-many-any-class + +# start-has-many-exists +band = Band.create! +# Member is not persisted. +band.members.build + +band.members.exists? +# Outputs: false + +# Persist the member +band.members.map(&:save!) + +band.members.exists? +# Outputs: true +# end-has-many-exists + +# start-belongs-to +class Members + include Mongoid::Document + + belongs_to :band +end +# end-belongs-to + +# start-belongs-to-optional +class Members + include Mongoid::Document + + belongs_to :band, optional: true +end +# end-belongs-to-optional + +# start-belongs-to-one-way +class Band + include Mongoid::Document +end + +class Members + include Mongoid::Document + + belongs_to :band +end +# end-belongs-to-one-way + +# start-belongs-to-inverse +class Band + include Mongoid::Document +end + +class Members + include Mongoid::Document + + belongs_to :band, inverse_of: nil +end +# end-belongs-to-inverse + +# start-has-and-belongs-to-many +class Band + include Mongoid::Document + + has_and_belongs_to_many :members +end + +class Members + include Mongoid::Document + + has_and_belongs_to_many :bands +end +# end-has-and-belongs-to-many + +# start-has-and-belongs-to-many-inverse +class Band + include Mongoid::Document + + has_and_belongs_to_many :tags, inverse_of: nil +end + +class Tag + include Mongoid::Document +end +# end-has-and-belongs-to-many-inverse + +# start-query-models +class Band + include Mongoid::Document + has_many :tours + + field :name, type: String +end +class Tour + include Mongoid::Document + belongs_to :band + + field :year, type: Integer +end +# end-query-models + +# start-association-query +band_ids = Tour.where(year: {'$gte' => 2000}).pluck(:band_id) +bands = Band.find(band_ids) +# end-association-query + +# start-embed-one +class Band + include Mongoid::Document + + embeds_one :label +end + +class Label + include Mongoid::Document + field :name, type: String + + embedded_in :band +end +# end-embed-one + +# start-embed-one-stored +# Band document +{ + "_id" : ObjectId("..."), + "label" : { + "_id" : ObjectId("..."), + "name" : "Periphery", + } +} +# end-embed-one-stored + +# start-embed-store-as +class Band + include Mongoid::Document + + embeds_one :label, store_as: "record_label" +end +# end-embed-store-as + +# start-embed-many +class Band + include Mongoid::Document + + embeds_many :albums +end + +class Album + include Mongoid::Document + field :name, type: String + + embedded_in :band +end +# end-embed-many + +# start-embed-many-stored +{ + "_id" : ObjectId("..."), + "albums" : [ + { + "_id" : ObjectId("..."), + "name" : "Omega", + } + ] +} +# end-embed-many-stored + +# start-embed-many-store-as +class Band + include Mongoid::Document + + embeds_many :albums, store_as: "records" +end +# end-embed-many-store-as + +# start-recursive-embed +class Band + include Mongoid::Document + field :name, type: String + + recursively_embeds_many +end +# end-recursive-embed + +# start-recursive-embed-access +root = Band.new(name: "Linkin Park") + +# Add child bands +child_one = root.child_band.build(name: "Lincoln Park") +child_two = root.child_band.build(name: "Xero") + +# Access parent band +child_one.parent_band +# Outputs: root +# end-recursive-embed-access + +# start-embedded-query +Band.where('tours.year' => {'$gte' => 2000}) +# end-embedded-query + +# start-embedded-query-pluck +# Get awards for bands that have toured since 2000 +Band.where('tours.year' => {'$gte' => 2000}).pluck(:awards) +# end-embedded-query-pluck + +# start-embedded-matching +band = Band.where(name: 'Astral Projection').first +tours = band.tours.where(year: {'$gte' => 2000}) +# end-embedded-matching + +# start-embedded-omit-id +class Album + include Mongoid::Document + field :name, type: String + field :_id, type: Object + + embedded_in :band +end +# end-embedded-omit-id + +# start-embedded-clear +band = Band.find(...) +band.tours.clear +# end-embedded-clear + +# start-embedded-delete-all +band = Band.find(...) +band.tours.delete_all +# end-embedded-delete-all + +# start-embedded-destroy-all +band = Band.find(...) +band.tours.destroy_all +# end-embedded-destroy-all \ No newline at end of file diff --git a/source/includes/data-modeling/callbacks.rb b/source/includes/data-modeling/callbacks.rb new file mode 100644 index 00000000..c48e04d5 --- /dev/null +++ b/source/includes/data-modeling/callbacks.rb @@ -0,0 +1,78 @@ +# start-doc-callback +class Contact + include Mongoid::Document + + field :name, type: String + field :phone, type: String + + # Creates a callback to clean phone numbers before saving + before_save :process_phone + + protected + def process_phone + self.phone = phone.gsub(/[^0-9]/, "") if attribute_present?("phone") + end + + # Creates a callback to send a message about object deletion + after_destroy do + p "deleted the contact for #{name}" + end +end +# end-doc-callback + +# start-doc-ops +Contact.create(name: 'Serena Atherton', phone: '999 555-3030') +# => `phone` field saved as '9995553030' +Contact.create(name: 'Zayba Haq', phone: '999 123?5050') +# => `phone` field saved as '9991235050' + +Contact.first.destroy +# => Console message: "deleted the contact for Serena Atherton" +# end-doc-ops + +# start-doc-set-syntax +class Contact + include Mongoid::Document + + field :name, type: String + field :phone, type: String + field :aliases, type: Array, default: [] + + set_callback(:update, :before) do |document| + if document.name_changed? + document.push(aliases: document.name_was) + end + end +end + +Contact.create(name: 'Xavier Bloom', phone: '4447779999') +Contact.first.update(name: 'Xav - coworker') +# Saved document in MongoDB: +# {"aliases":["Xavier Bloom"],"name":"Xav - coworker","phone":"4447779999"} +# end-doc-set-syntax + +# start-association-callback +class User + include Mongoid::Document + + field :username, type: String + # Registers the callback in the association statement + embeds_many :saved_articles, before_add: :send_message + + protected + # Passes the association document as a parameter to the callback + def send_message(saved_article) + if saved_articles.count >= 10 + p "you can't save more than 10 articles at a time" + throw(:abort) + end + end +end + +class SavedArticle + include Mongoid::Document + embedded_in :user + + field :url, type: String +end +# end-association-callback \ No newline at end of file diff --git a/source/includes/data-modeling/field-behaviors.rb b/source/includes/data-modeling/field-behaviors.rb new file mode 100644 index 00000000..f1f59e47 --- /dev/null +++ b/source/includes/data-modeling/field-behaviors.rb @@ -0,0 +1,139 @@ +# 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-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 diff --git a/source/includes/data-modeling/field-types.rb b/source/includes/data-modeling/field-types.rb new file mode 100644 index 00000000..f740815a --- /dev/null +++ b/source/includes/data-modeling/field-types.rb @@ -0,0 +1,247 @@ +# 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 Product + include Mongoid::Document + + field :name, type: String + field :properties +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 +end + +person = Person.new(url: {'home_page' => 'http://www.homepage.com'}) +# 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 :purchased_at, type: DateTime +end +# end-datetime + +# start-datetime-int +ticket.purchased_at = 1544803974 +ticket.purchased_at +# Outputs: Fri, 14 Dec 2018 16:12:54 +0000 +# end-datetime-int + +# start-datetime-string +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 Post + include Mongoid::Document + include Mongoid::Timestamps +end +# end-timestamps + +# start-timestamps-specific +class Post + include Mongoid::Document + include Mongoid::Timestamps::Created +end + +class Post + include Mongoid::Document + include Mongoid::Timestamps::Updated +end +# end-timestamps-specific + +# start-timestamps-disable +post.timeless.save +# end-timestamps-disable + +# start-timestamps-short +class Post + include Mongoid::Document + include Mongoid::Timestamps::Short # For c_at and u_at. +end + +class Post + include Mongoid::Document + include Mongoid::Timestamps::Created::Short # For c_at only. +end + +class Post + 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-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" + +# Sets "color" field to 0 in MongoDB +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 \ No newline at end of file diff --git a/source/includes/data-modeling/indexes.rb b/source/includes/data-modeling/indexes.rb new file mode 100644 index 00000000..ab68b9ca --- /dev/null +++ b/source/includes/data-modeling/indexes.rb @@ -0,0 +1,123 @@ +# start create index +class Restaurant + include Mongoid::Document + + field :name, type: String + field :cuisine, type: String + field :borough, type: String + + index({ cuisine: 1}, { name: "cuisine_index", unique: false }) +end + +Restaurant.create_indexes +# end create index + +# start create alias index +class Restaurant + include Mongoid::Document + + field :borough, as: :b + + index({ b: 1}, { name: "borough_index" }) +end +# end create alias index + +# start create embedded index +class Address + include Mongoid::Document + + field :street, type: String +end + +class Restaurant + include Mongoid::Document + + embeds_many :addresses + index({"addresses.street": 1}) +end +# end create embedded index + +# start create compound index +class Restaurant + include Mongoid::Document + + field :name, type: String + field :borough, type: String + + index({borough: 1, name: -1}, { name: "compound_index"}) +end +# end create compound index + +# start create 2dsphere index +class Restaurant + include Mongoid::Document + + field :location, type: Array + + index({location: "2dsphere"}, { name: "location_index"}) +end +# end create 2dsphere index + +# start create sparse index +class Restaurant + include Mongoid::Document + + field :name, type: String + field :cuisine, type: String + field :borough, type: String + + index({ borough: 1}, { sparse: true }) +end +# end create sparse index + +# start create multiple indexes +class Restaurant + include Mongoid::Document + + field :name, type: String + field :cuisine, type: String + field :borough, type: String + + index({ name: 1}) + index({ cuisine: -1}) +end + +Restaurant.create_indexes +# end create multiple indexes + +# start drop indexes +Restaurant.remove_indexes +# end drop indexes + +# start create atlas search index +class Restaurant + include Mongoid::Document + + field :name, type: String + field :cuisine, type: String + field :borough, type: String + + search_index :my_search_index, + mappings: { + fields: { + name: { + type: "string" + }, + cuisine: { + type: "string" + } + }, + dynamic: true + } +end + +Restaurant.create_search_indexes +# end create atlas search index + +# start remove atlas search index +Restaurant.remove_search_indexes +# end remove atlas search index + +#start list atlas search index +Restaurant.search_indexes.each { |index| puts index } +# end list atlas search index diff --git a/source/includes/data-modeling/inheritance.rb b/source/includes/data-modeling/inheritance.rb new file mode 100644 index 00000000..d54a468b --- /dev/null +++ b/source/includes/data-modeling/inheritance.rb @@ -0,0 +1,76 @@ +# start-simple-inheritance +class Person + include Mongoid::Document + + field :name, type: String +end + +class Employee < Person + field :company, type: String + field :tenure, type: Integer + + scope :new_hire, ->{ where(:tenure.lt => 1) } +end + +class Manager < Employee +end +# end-simple-inheritance + +# start-embedded-inheritance +class Person + include Mongoid::Document + + field :name, type: String + embeds_many :infos +end + +... + +class Info + include Mongoid::Document + + field :active, type: Boolean + embedded_in :person +end + +class Phone < Info + field :value, type: Float + field :country, type: String +end + +class Email < Info + field :value, type: String + field :category, type: String +end +# end-embedded-inheritance + +# start-association-operations +# Creates a new Employee instance +e = Employee.create( + name: "Lance Huang", + company: "XYZ Communications", + tenure: 2 +) + +# Builds an Info object +e.infos.build({ active: true }) + +# Builds a Phone object +e.infos.build( + { active: true, value: 1239007777, country: "USA" }, + Phone +) + +# Creates an Email object +e.infos.create( + { active: true, value: "l.huang@company.com", category: "work" }, + Email +) + +# Creates and assigns an Email object +p = Email.new(active: false, value: "lanceh11@mymail.com", category: "personal" ) +e.infos << p + +# Saves the Employee instance to database +e.save +# end-association-operations diff --git a/source/includes/data-modeling/nested_attr.rb b/source/includes/data-modeling/nested_attr.rb new file mode 100644 index 00000000..d71f613b --- /dev/null +++ b/source/includes/data-modeling/nested_attr.rb @@ -0,0 +1,58 @@ +# start-simple-nested +class Band + include Mongoid::Document + embeds_many :albums + belongs_to :producer + accepts_nested_attributes_for :albums, :producer +end +# end-simple-nested + +#start-use-method +# Retrieves a Band instance +band = Band.where(name: 'Tennis').first +# Updates the "producer" association +band.producer_attributes = { name: 'Alaina Moore' } +#end-use-method + +# start-create-attr +band = Band.create( + name: 'Tennis', + albums_attributes: [ + { name: 'Swimmer', year: 2020 }, + { name: 'Young & Old', year: 2013 }] +) +# end-create-attr + +# start-update-create +band = Band.where(name: 'Vampire Weekend').first +band.update(albums_attributes: [ + { name: 'Contra', year: 2010 } +]) +# end-update-create + +# start-update-id +band = Band.where(name: 'Vampire Weekend').first +# Retrieves the first entry from the albums array +album = band.albums.first +# Updates the entry by passing the _id value +band.update(albums_attributes: [ + { _id: album._id, year: 2011 } ]) +# end-update-id + +# start-delete-id +band = Band.where(name: 'Vampire Weekend').first +# Retrieves the first entry from the albums array +album = band.albums.first +# Deletes the entry by passing the _id value +band.update(albums_attributes: [ + { _id: album._id, _destroy: true } ]) +# end-delete-id + +# start-multiple-ops +band = Band.where(name: 'Yeah Yeah Yeahs').first +# Performs multiple data changes +band.update(albums_attributes: [ + { name: 'Show Your Bones', year: 2006 }, + { _id: 1, name: 'Fever To T3ll' }, + { _id: 2, _destroy: true } ]) +# end-multiple-ops \ No newline at end of file diff --git a/source/includes/data-modeling/validation.rb b/source/includes/data-modeling/validation.rb new file mode 100644 index 00000000..47b51e27 --- /dev/null +++ b/source/includes/data-modeling/validation.rb @@ -0,0 +1,89 @@ +# start-simple-val +class Person + include Mongoid::Document + + field :name, type: String + validates :name, presence: true +end +# end-simple-val + +# start-comparison +class Order + include Mongoid::Document + + field :order_date, type: DateTime + field :delivery_date, type: DateTime + field :quantity, type: Integer + + validates :delivery_date, comparison: { greater_than: :order_date } + validates :quantity, comparison: { less_than: 5 } +end +# end-comparison + +# start-fmt +class User + include Mongoid::Document + + field :username, type: String + + validates :username, format: { with: /\A[a-zA-Z]+\z/ } +end +# end-fmt + +# start-inclusion +class Order + include Mongoid::Document + + field :shipping, type: String + + validates :shipping, inclusion: { in: %w(standard priority overnight) } +end +# end-inclusion + +# start-absence +class Order + include Mongoid::Document + + field :delivery_date, type: String + + validates :delivery_date, absence: true +end +# end-absence + +# start-unique +class Person + include Mongoid::Document + + field :first_name, type: String + field :last_name, type: String + + validates :first_name, uniqueness: { scope: :last_name } +end +# end-unique + +# start-assoc +class Author + include Mongoid::Document + + embeds_many :books + + validates_associated :books +end +# end-assoc + +# start-valid +class Person + include Mongoid::Document + + field :name, type: String + field :age, type: Integer + + validates :age, comparison: { greater_than_or_equal_to: 0 } +end + +# Returns true +Person.new(name: "Berta Odom", age: 4).valid? + +# Returns false +Person.new(name: "Cody Peng", age: -5).valid? +# end-valid \ No newline at end of file diff --git a/source/includes/fact-environments.rst b/source/includes/fact-environments.rst new file mode 100644 index 00000000..737ab901 --- /dev/null +++ b/source/includes/fact-environments.rst @@ -0,0 +1,7 @@ +- `MongoDB Atlas + `__: the fully + managed service for MongoDB deployments in the cloud +- :manual:`MongoDB Enterprise `: the + subscription-based, self-managed version of MongoDB +- :manual:`MongoDB Community `: the + source-available, free-to-use, and self-managed version of MongoDB diff --git a/source/includes/figures/atlas_connection_select_cluster.png b/source/includes/figures/atlas_connection_select_cluster.png new file mode 100644 index 00000000..e072febf Binary files /dev/null and b/source/includes/figures/atlas_connection_select_cluster.png differ diff --git a/source/includes/figures/quickstart-rails-list.png b/source/includes/figures/quickstart-rails-list.png new file mode 100644 index 00000000..e6405847 Binary files /dev/null and b/source/includes/figures/quickstart-rails-list.png differ diff --git a/source/includes/figures/quickstart-sinatra-list.png b/source/includes/figures/quickstart-sinatra-list.png new file mode 100644 index 00000000..8adc6c1c Binary files /dev/null and b/source/includes/figures/quickstart-sinatra-list.png differ diff --git a/source/includes/interact-data/crud.rb b/source/includes/interact-data/crud.rb new file mode 100644 index 00000000..290a51b1 --- /dev/null +++ b/source/includes/interact-data/crud.rb @@ -0,0 +1,336 @@ +# start create! example +Person.create!( + first_name: "Heinrich", + last_name: "Heine" +) + +Person.create!([ + { first_name: "Heinrich", last_name: "Heine" }, + { first_name: "Willy", last_name: "Brandt" } +]) + +Person.create!(first_name: "Heinrich") do |doc| + doc.last_name = "Heine" +end +# end create! example + +# start create example +Person.create( + first_name: "Heinrich", + last_name: "Heine" +) + +class Post + include Mongoid::Document + validates_uniqueness_of :title +end + +posts = Post.create([{title: "test"}, {title: "test"}]) +posts.map { |post| post.persisted? } # => [true, false] +# end create example + +# start save! example +person = Person.new( + first_name: "Esmeralda", + last_name: "Qemal" +) +person.save! + +person.first_name = "Malik" +person.save! +# end save! example + +# start save example +person = Person.new( + first_name: "Tamara", + last_name: "Graham" +) +person.save + +person.first_name = "Aubrey" +person.save(validate: false) +# end save example + +# start attributes example +person = Person.new(first_name: "James", last_name: "Nan") +person.save + +puts person.attributes +# end attributes example + +# start reload example +band = Band.create!(name: 'Sun 1') +# => # + +band.name = 'Moon 2' +# => # + +band.reload +# => # +# end reload example + +# start reload unsaved example +existing = Band.create!(name: 'Photek') + +band = Band.new(id: existing.id) +band.reload + +puts band.name +# end reload unsaved example + +# start update attributes! example +person.update_attributes!( + first_name: "Maximilian", + last_name: "Hjalmar" +) +# end update attributes! example + +# start update attributes example +person.update_attributes( + first_name: "Hasan", + last_name: "Emine" +) +# end update attributes example + +# start update attribute example +person.update_attribute(:first_name, "Jean") +# end update attribute example + +# start upsert example +person = Person.new( + first_name: "Balu", + last_name: "Rama" +) +person.upsert + +person.first_name = "Ananda" +person.upsert(replace: true) +# end upsert example + +# start touch example +person.touch(:audited_at) +# end touch example + +# start delete example +person = Person.create!(name: 'Edna Park') + +unsaved_person = Person.new(id: person.id) +unsaved_person.delete +person.reload +# end delete example + +# start destroy example +person.destroy +# end destroy example + +# start delete all example +Person.delete_all +# end delete all example + +# start destroy all example +Person.destroy_all +# end destroy all example + +# start new record example +person = Person.new( + first_name: "Tunde", + last_name: "Adebayo" +) +puts person.new_record? + +person.save! +puts person.new_record? +# end new record example + +# start persisted example +person = Person.new( + first_name: "Kiana", + last_name: "Kahananui" +) +puts person.persisted? + +person.save! +puts person.persisted? +# end persisted example + +# start field values default +class Person + include Mongoid::Document + field :first_name +end + +person = Person.new + +person.first_name = "Artem" +person.first_name # => "Artem" +# end field values default + +# start field values hash +class Person + include Mongoid::Document + + field :first_name, as: :fn +end + +person = Person.new(first_name: "Artem") + +person["fn"] +# => "Artem" + +person[:first_name] = "Vanya" +# => "Artem" + +person +# => # +# end field values hash + +# start read write attributes +class Person + include Mongoid::Document + + def first_name + read_attribute(:fn) + end + + def first_name=(value) + write_attribute(:fn, value) + end +end + +person = Person.new + +person.first_name = "Artem" +person.first_name +# => "Artem" +# end read write attributes + +# start read write instance +class Person + include Mongoid::Document + field :first_name, as: :fn +end + +person = Person.new(first_name: "Artem") +# => # + +person.read_attribute(:first_name) +# => "Artem" + +person.read_attribute(:fn) +# => "Artem" + +person.write_attribute(:first_name, "Pushkin") + +person +# => # +# end read write instance + +# start attributes= example +person.attributes = { first_name: "Jean-Baptiste", middle_name: "Emmanuel" } +# end attributes= example + +# start write_attributes example +person.write_attributes( + first_name: "Jean-Baptiste", + middle_name: "Emmanuel", +) +# end write_attributes example + +# start atomically example +person.atomically do + person.inc(age: 1) + person.set(name: 'Jake') +end +# end atomically example + +# start default block atomic example +person.atomically do + person.atomically do + person.inc(age: 1) + person.set(name: 'Jake') + end + raise 'An exception' + # Name and age changes are persisted +end +# end default block atomic example + +# start join_contexts atomic +person.atomically do + person.atomically(join_context: true) do + person.inc(age: 1) + person.set(name: 'Jake') + end + raise 'An exception' + # Name and age changes are not persisted +end +# end join_contexts atomic + +# start-dirty-tracking-view +# Retrieves a person instance +person = Person.first +# Sets a new `name` value +person.name = "Sarah Frank" + +# Checks to see if the document is changed +person.changed? # true +# Gets an array of changed fields. +person.changed # [ :name ] +# Gets a hash of the old and changed values for each field +person.changes # { "name" => [ "Sarah Frink", "Sarah Frank" ] } + +# Checks if a specific field is changed +person.name_changed? # true +# Gets the changes for a specific field +person.name_change # [ "Sarah Frink", "Sarah Frank" ] + +# Gets the previous value for a field +person.name_was # "Sarah Frink" +# end-dirty-tracking-view + +# start-dirty-tracking-reset +person = Person.first +person.name = "Sarah Frank" + +# Reset the changed `name` field +person.reset_name! +person.name # "Sarah Frink" +# end-dirty-tracking-reset + +# start-dirty-tracking-prev +person = Person.first +person.name = "Sarah Frank" +person.save # Clears out current changes + +# Lists the previous changes +person.previous_changes +# { "name" => [ "Sarah Frink", "Sarah Frank" ] } +# end-dirty-tracking-prev + +# start-container-save +person = Person.new +interests = person.interests +# => # +interests << 'Hiking' +# => # + +# Assigns the Set to the field +person.interests = interests +# => # +person.interests +# => # +# end-container-save + +# start-override-readonly +class Person + include Mongoid::Document + field :name, type: String + + def readonly? + true + end +end + +person = Person.first +person.readonly? # => true +person.destroy # => raises ReadonlyDocument error +# end-override-readonly diff --git a/source/includes/interact-data/modify-results.rb b/source/includes/interact-data/modify-results.rb new file mode 100644 index 00000000..f2da3cfe --- /dev/null +++ b/source/includes/interact-data/modify-results.rb @@ -0,0 +1,40 @@ +# start-only +Band.where(members: 4).only(:name) +# end-only + +# start-only-embed +bands = Band.only(:name, 'tours.year') +# end-only-embed + +# start-only-embed-association +# Returns null +Band.where(name: 'Astral Projection').only(:name).first.managers + +# Returns the first Manager object +Band.where(name: 'Astral Projection').only(:name, :manager_ids).first.managers +# end-only-embed-association + +# start-without +Band.where(members: 4).without(:year) +# end-without + +# start-limit +Band.limit(5) +# end-limit + +# start-skip-limit +Band.skip(2).limit(5) +# Skips the first two results and returns +# the following five results +# end-skip-limit + +# start-skip +Band.skip(3) + +# Equivalent +Band.offset(3) +# end-skip + +# start-batch +Band.batch_size(500) +# end-batch diff --git a/source/includes/interact-data/query.rb b/source/includes/interact-data/query.rb new file mode 100644 index 00000000..16e6277d --- /dev/null +++ b/source/includes/interact-data/query.rb @@ -0,0 +1,341 @@ +# start-simple-field-query +Band.where(name: 'Depeche Mode') +Band.where('name' => 'Depeche Mode') +# end-simple-field-query + +# start-query-api-query +Band.where(founded: {'$gt' => 1980}) +Band.where('founded' => {'$gt' => 1980}) +# end-query-api-query + +# start-symbol-query +Band.where(:founded.gt => 1980) +# end-symbol-query + +# start-defined-field-query +Band.where(founded: '2020') +# end-defined-field-query + +# start-raw-field-query +Band.where(founded: Mongoid::RawValue('2020')) +# end-raw-field-query + +# start-id-field-query +Band.where(id: '5ebdeddfe1b83265a376a760') +Band.where(_id: '5ebdeddfe1b83265a376a760') +# end-id-field-query + +# start-embedded-query +Band.where('manager.name' => 'Smith') +# end-embedded-query + +# start-embedded-ne-query +Band.where(:'manager.name'.ne => 'Smith') +# end-embedded-ne-query + +# start-logical-ops +# Uses "and" to combine criteria +Band.where(label: 'Trust in Trance').and(name: 'Astral Projection') + +# Uses "or" to specify criteria +Band.where(label: 'Trust in Trance').or(Band.where(name: 'Astral Projection')) + +# Uses "not" to specify criteria +Band.not(label: 'Trust in Trance', name: 'Astral Projection') + +# Uses "not" without arguments +Band.not.where(label: 'Trust in Trance', name: 'Astral Projection') +# end-logical-ops + +# start-logical-and-ops +# Conditions passed to separate "and" calls +Band.and(name: 'Sun Kil Moon').and(member_count: 2) + +# Multiple conditions in the same "and" call +Band.and({name: 'Sun Kil Moon'}, {member_count: 2}) + +# Multiple conditions in an array - Deprecated +Band.and([{name: 'Sun Kil Moon'}, {member_count: 2}]) + +# Condition in "where" and a scope +Band.where(name: 'Sun Kil Moon').and(Band.where(member_count: 2)) + +# Condition in "and" and a scope +Band.and({name: 'Sun Kil Moon'}, Band.where(member_count: 2)) + +# Scope as an array element, nested arrays - Deprecated +Band.and([Band.where(name: 'Sun Kil Moon'), [{member_count: 2}]]) +# end-logical-and-ops + +# start-logical-combination-ops +# Combines as "and" +Band.where(name: 'Swans').where(name: 'Feist') + +# Combines as "or" +Band.where(name: 'Swans').or(name: 'Feist') +# end-logical-combination-ops + +# start-logical-combination-ops-2 +# "or" applies to the first condition, and the second is combined +# as "and" +Band.or(name: 'Sun').where(label: 'Trust') + +# Same as previous example - "where" and "and" are aliases +Band.or(name: 'Sun').and(label: 'Trust') + +# Same operator can be stacked any number of times +Band.or(name: 'Sun').or(label: 'Trust') + +# The last label condition is added to the top level as "and" +Band.where(name: 'Sun').or(label: 'Trust').where(label: 'Feist') +# Interpreted query: +# {"$or"=>[{"name"=>"Sun"}, {"label"=>"Trust"}], "label"=>"Feist"} +# end-logical-combination-ops-2 + +# start-not-logical +# "not" negates "where" +Band.not.where(name: 'Best') + +# The second "where" is added as "$and" +Band.not.where(name: 'Best').where(label: /Records/) + +# "not" negates its argument +Band.not(name: 'Best') +# end-not-logical + +# start-not-logical-note +# String negation - uses "$ne" +Band.not.where(name: 'Best') + +# Regex negation - uses "$not" +Band.not.where(name: /Best/) +# end-not-logical-note + +# start-not-behavior +# Simple condition +Band.not(name: /Best/) + +# Complex conditions +Band.where(name: /Best/).not(name: 'Astral Projection') + +# Symbol operator syntax +Band.not(:name.ne => 'Astral Projection') +# end-not-behavior + +# start-incremental-1 +Band.in(name: ['a']).in(name: ['b']) +# Interpreted query: +# {"name"=>{"$in"=>["a"]}, "$and"=>[{"name"=>{"$in"=>["b"]}}]} +# end-incremental-1 + +# start-in-merge +Band.in(name: ['a']).override.in(name: ['b']) +# Interpreted query: +# {"name"=>{"$in"=>["b"]}} + +Band.in(name: ['a', 'b']).intersect.in(name: ['b', 'c']) +# Interpreted query: +# {"name"=>{"$in"=>["b"]}} + +Band.in(name: ['a']).union.in(name: ['b']) +# Interpreted query: +# {"name"=>{"$in"=>["a", "b"]}} +# end-in-merge + +# start-merge-reset +Band.in(name: ['a']).union.ne(name: 'c').in(name: ['b']) +# Interpreted query: +# {"name"=>{"$in"=>["a"], "$ne"=>"c"}, "$and"=>[{"name"=>{"$in"=>["b"]}}]} +# end-merge-reset + +# start-merge-where +Band.in(name: ['a']).union.where(name: {'$in' => 'b'}) +# Interpreted query: +# {"foo"=>{"$in"=>["a"]}, "$and"=>[{"foo"=>{"$in"=>"b"}}]} +# end-merge-where + +# start-range-query +Band.in(year: 1950..1960) +# Interpreted query: +# {"year"=>{"$in"=>[1950, 1951, 1952, 1953, 1954, 1955, 1956, 1957, 1958, 1959, 1960]}} +# end-range-query + +# start-elem-match-1 +aerosmith = Band.create!(name: 'Aerosmith', tours: [ + {city: 'London', year: 1995}, + {city: 'New York', year: 1999}, +]) + +swans = Band.create!(name: 'Swans', tours: [ + {city: 'Milan', year: 2014}, + {city: 'Montreal', year: 2015}, +]) + +# Returns only "Aerosmith" +Band.elem_match(tours: {city: 'London'}) +# end-elem-match-1 + +# start-elemmatch-embedded-class +class Band + include Mongoid::Document + field :name, type: String + embeds_many :tours +end + +class Tour + include Mongoid::Document + field :city, type: String + field :year, type: Integer + embedded_in :band +end +# end-elemmatch-embedded-class + +# start-elemmatch-embedded-operations +aerosmith = Band.create!(name: 'Aerosmith') + +Tour.create!(band: aerosmith, city: 'London', year: 1995) +Tour.create!(band: aerosmith, city: 'New York', year: 1999) + +# Returns the "Aerosmith" document +Band.elem_match(tours: {city: 'London'}) +# end-elemmatch-embedded-operations + +# start-elemmatch-recursive +class Tag + include Mongoid::Document + + field name:, type: String + recursively_embeds_many +end + +# Creates the root Tag +root = Tag.create!(name: 'root') + +# Adds embedded Tags +sub1 = Tag.new(name: 'sub_tag_1', child_tags: [Tag.new(name: 'sub_sub_tag_1')]) + +root.child_tags << sub1 +root.child_tags << Tag.new(name: 'sub_tag_2') +root.save! + +# Searches for Tag in which one child Tag tame is "sub_tag_1" +Tag.elem_match(child_tags: {name: 'sub_tag_1'}) + +# Searches for a child Tag in which one child Tag tame is "sub_sub_tag_1" +root.child_tags.elem_match(child_tags: {name: 'sub_sub_tag_1'}) +# end-elemmatch-recursive + +# start-id-query-multiple +# Equivalent ways to match multiple documents +Band.find('5f0e41d92c97a64a26aabd10', '5f0e41b02c97a64a26aabd0e') +Band.find(['5f0e41d92c97a64a26aabd10', '5f0e41b02c97a64a26aabd0e']) +# end-id-query-multiple + +# start-ordinal-examples +# Returns the first document in the collection +Band.first + +# Returns the first matching document +Band.where(founded: {'$gt' => 1980}).first + +# Returns the first two matching documents +Band.first(2) + +# Returns the last matching document +Band.where(founded: {'$gt' => 1980}).last + +# Returns the second to last document +Band.second_to_last +# end-ordinal-examples + +# start-field-val-examples +Band.distinct(:name) +# Example output: "Ghost Mountain" "Hello Goodbye" "She Said" + +Band.where(:members.gt => 2).distinct(:name) +# Example output: "Arctic Monkeys" "The Smiths" + +Band.distinct('tours.city') +# Example output: "London" "Sydney" "Amsterdam" + +Band.all.pick(:name) +# Example output: "The Smiths" + +Band.all.pluck(:country) +# Example output: "England" "Spain" "England" "Japan" + +Band.all.tally(:country) +# Example output: ["England",2] ["Italy",3] +# end-field-val-examples + +# start-query-findby +# Simple equality query +Band.find_by(name: "Photek") + +# Performs an action on each returned result +Band.find_by(name: "Tool") do |band| + band.fans += 1 +end +# end-query-findby + +# start-query-find-or-create +# If no matches, creates a Band with just the "name" field +Band.find_or_create_by(name: "Photek") + +# If no matches, creates a Band with just the "name" field because the +# query condition is not a literal +Band.where(:likes.gt => 10).find_or_create_by(name: "Photek") + +# Creates a Band in which the name is Aerosmith because there is no +# document in which "name" is Photek and Aerosmith at the same time +Band.where(name: "Photek").find_or_create_by(name: "Aerosmith") +# end-query-find-or-create + +# start-regex +# Matches "description" values that start exactly with "Impala" +Band.where(description: /\AImpala/) +# => nil + +# Matches "description" values that start exactly with "Impala" +Band.where(description: BSON::Regexp::Raw.new('^Impala')) +# => nil + +# Matches "description" values that start exactly with "Impala" with +# the multiline option +Band.where(description: BSON::Regexp::Raw.new('^Impala', 'm')) +# => Returns sample document +# end-regex + +# start-field-conversion-model +class Album + include Mongoid::Document + + field :release_date, type: Date + field :last_commented, type: Time + field :last_purchased +end +# end-field-conversion-model + +# start-date-queries-1 +Album.where(release_date: Date.today) +# Interpreted query: +# {"release_date"=>2024-11-05 00:00:00 UTC} + +Album.where(last_commented: Time.now) +# Interpreted query: +# {"last_commented"=>2024-11-04 17:20:47.329472 UTC} +# end-date-queries-1 + +# start-date-queries-2 +Album.where(last_commented: Date.today) +# Interpreted query: +# {"last_commented"=>Mon, 04 Nov 2024 00:00:00.000000000 EST -05:00} + +Album.where(last_purchased: Date.today) +# Interpreted query: +# {"last_purchased"=>"2024-11-04"} + +Album.where(last_reviewed: Date.today) +# Interpreted query: +# {"last_reviewed"=>2024-11-04 00:00:00 UTC} +# end-date-queries-2 diff --git a/source/includes/interact-data/scoping.rb b/source/includes/interact-data/scoping.rb new file mode 100644 index 00000000..1a6f7631 --- /dev/null +++ b/source/includes/interact-data/scoping.rb @@ -0,0 +1,170 @@ +# start-named-scope-1 +class Band + include Mongoid::Document + + field :country, type: String + field :genres, type: Array + + scope :japanese, ->{ where(country: "Japan") } + scope :rock, ->{ where(:genres.in => [ "rock" ]) } +end +# end-named-scope-1 + +# start-query-named-scope +Band.japanese.rock +# end-query-named-scope + +# start-named-scope-2 +class Band + include Mongoid::Document + + field :name, type: String + field :country, type: String + + scope :based_in, ->(country){ where(country: country) } +end +# end-named-scope-2 + +# start-query-named-scope-2 +Band.based_in("Spain") +# end-query-named-scope-2 + +# start-named-scope-3 +class Band + include Mongoid::Document + + def self.on_tour + true + end + + scope :on_tour, ->{ where(on_tour: true) } +end +# end-named-scope-3 + +# start-default-scope-1 +class Band + include Mongoid::Document + + field :name, type: String + field :active, type: Boolean + + default_scope -> { where(active: true) } +end +# end-default-scope-1 + +# start-default-scope-2 +class Band + include Mongoid::Document + + field :name, type: String + field :on_tour, type: Boolean, default: true + + default_scope ->{ where(on_tour: false) } +end + +# Creates a new Band instance in which "on_tour" is "false" +Band.new +# end-default-scope-2 + +# start-unscoped +# Inline example +Band.unscoped.where(name: "Depeche Mode") + +# Block example +Band.unscoped do + Band.where(name: "Depeche Mode") +end +# end-unscoped + +# start-scope-association +class Label + include Mongoid::Document + + field :name, type: String + + embeds_many :bands +end + +class Band + include Mongoid::Document + + field :name, type: String + field :active, default: true + + embedded_in :label + default_scope ->{ where(active: true) } +end +# end-scope-association + +# start-scope-association-steps +label = Label.new(name: "Hello World Records") +band = Band.new(name: "Ghost Mountain") +label.bands.push(band) +label.bands # Displays the Band because "active" is "true" +band.update_attribute(:active, false) # Updates "active" to "false" + +# Displays the "Ghost Mountain" band +label.bands # => {"_id":"...","name":"Ghost Mountain",...} + +# Won't display "Ghost Mountain" band after reloading +label.reload.bands # => nil +# end-scope-association-steps + +# start-scope-query-behavior +class Band + include Mongoid::Document + + field :name + field :touring + field :member_count + + default_scope ->{ where(touring: true) } +end + +# Combines the condition to the default scope with "and" +Band.where(name: 'Infected Mushroom') +# Interpreted query: +# {"touring"=>true, "name"=>"Infected Mushroom"} + +# Combines the first condition to the default scope with "and" +Band.where(name: 'Infected Mushroom').or(member_count: 3) +# Interpreted query: +# {"$or"=>[{"touring"=>true, "name"=>"Infected Mushroom"}, {"member_count"=>3}]} + +# Combines the condition to the default scope with "or" +Band.or(member_count: 3) +# Interpreted query: +# {"$or"=>[{"touring"=>true}, {"member_count"=>3}]} +# end-scope-query-behavior + +# start-override-scope +class Band + include Mongoid::Document + + field :country, type: String + field :genres, type: Array + + scope :mexican, ->{ where(country: "Mexico") } +end +# end-override-scope + +# start-override-scope-block +Band.with_scope(Band.mexican) do + Band.all +end +# end-override-scope-block + +# start-class-methods +class Band + include Mongoid::Document + + field :name, type: String + field :touring, type: Boolean, default: true + + def self.touring + where(touring: true) + end +end + +Band.touring +# end-class-methods diff --git a/source/includes/interact-data/text-search.rb b/source/includes/interact-data/text-search.rb new file mode 100644 index 00000000..1d431ceb --- /dev/null +++ b/source/includes/interact-data/text-search.rb @@ -0,0 +1,22 @@ +# start-text-index-model +class Dish + include Mongoid::Document + + field :name, type: String + field :description, type: String + + index description: 'text' +end +# end-text-index-model + +# start-term +Dish.where('$text' => {'$search' => 'herb'}) +# end-term + +# start-phrase +Dish.where('$text' => {'$search' => "\"serves 2\""}) +# end-phrase + +# start-exclude +Dish.where('$text' => {'$search' => 'vegan -tofu'}) +# end-exclude \ No newline at end of file diff --git a/source/includes/interact-data/transaction.rb b/source/includes/interact-data/transaction.rb new file mode 100644 index 00000000..7f2f9354 --- /dev/null +++ b/source/includes/interact-data/transaction.rb @@ -0,0 +1,122 @@ +# start-example-models +class Book + include Mongoid::Document + + field :title, type: String + field :author, type: String + field :length, type: Integer +end + +class Film + include Mongoid::Document + + field :title, type: String + field :year, type: Integer +end +# end-example-models + +# start-txn-operations +# Starts a transaction from the model class +Book.transaction do + # Saves new Book and Film instances to MongoDB + Book.create(title: 'Covert Joy', author: 'Clarice Lispector') + Film.create(title: 'Nostalgia', year: 1983) +end + +# Starts a transaction from an instance of Book +book = Book.create(title: 'Sula', author: 'Toni Morrison') +book.transaction do + # Saves a new field value to the Book instance + book.length = 192 + book.save! +end + +# Starts a transaction from the Mongoid instance +Mongoid.transaction do + # Deletes the Book instance in MongoDB + book.destroy +end +# end-txn-operations + +# start-different-clients +# Defines a class by using the :default client +class Post + include Mongoid::Document +end + +# Defines a class by using the :encrypted_client +class User + include Mongoid::Document + + store_in client: :encrypted_client +end + +# Starts a transaction on the :encrypted_client +User.transaction do + # Uses the same client, so the operation is in the transaction + User.create! + # Uses a different client, so it is *not* in the transaction + Post.create! +end +# end-different-clients + +# start-lower-lvl-api +# Starts a session from the model class +Book.with_session do |session| + session.start_transaction + # Creates a Book + Book.create(title: 'Siddhartha', author: 'Hermann Hesse') + + # Commits the transaction + session.commit_transaction +rescue StandardError + # Ends the transaction if there is an error + session.abort_transaction +end +# end-lower-lvl-api + +# start-commit-retry +begin + session.commit_transaction +rescue Mongo::Error => e + if e.label?(Mongo::Error::UNKNOWN_TRANSACTION_COMMIT_RESULT_LABEL) + retry + else + raise + end +end +# end-commit-retry + +# start-other-client +# Specifies that the operation should use the "other" client instead of +# the default client +User.with(client: :other) do + Post.with(client: :other) do + Post.with_session do |session| + session.start_transaction + Post.create! + Post.create! + User.create! + session.commit_transaction + end + end +end +# end-other-client + +# start-model-session +Book.with_session(causal_consistency: true) do + Book.create! + book = Person.first + book.title = "Swann's Way" + book.save +end +# end-model-session + +# start-instance-session +book = Book.new +book.with_session(causal_consistency: true) do + book.title = 'Catch-22' + book.save + book.sellers << Shop.create! +end +# end-instance-session \ No newline at end of file diff --git a/source/includes/language-compatibility-table-mongoid.rst b/source/includes/language-compatibility-table-mongoid.rst new file mode 100644 index 00000000..e7d3b66a --- /dev/null +++ b/source/includes/language-compatibility-table-mongoid.rst @@ -0,0 +1,48 @@ +.. list-table:: + :header-rows: 1 + :stub-columns: 1 + :class: compatibility-large + + * - {+odm+} Version + - {+language+} 3.2 + - {+language+} 3.1 + - {+language+} 3.0 + - {+language+} 2.7 + - {+language+} 2.6 + - {+language+} 2.5 + - J{+language+} 9.4 + - J{+language+} 9.3 + - J{+language+} 9.2 + + * - 9.0 + - ✓ + - ✓ + - ✓ + - ✓ + - + - + - ✓ + - + - + + * - 8.1 + - ✓ + - ✓ + - ✓ + - ✓ + - ✓ + - + - + - ✓ + - + + * - 8.0 + - + - ✓ + - ✓ + - ✓ + - ✓ + - + - + - ✓ + - diff --git a/source/includes/mongodb-compatibility-table-mongoid.rst b/source/includes/mongodb-compatibility-table-mongoid.rst new file mode 100644 index 00000000..06fbb6af --- /dev/null +++ b/source/includes/mongodb-compatibility-table-mongoid.rst @@ -0,0 +1,34 @@ +.. list-table:: + :header-rows: 1 + :stub-columns: 1 + :class: compatibility-large + + * - {+odm+} Version + - MongoDB 8.0 + - MongoDB 7.0 + - MongoDB 6.0 + - MongoDB 5.0 + - MongoDB 4.4 + - MongoDB 4.2 + - MongoDB 4.0 + - MongoDB 3.6 + + * - 9.0 + - ✓ + - ✓ + - ✓ + - ✓ + - ✓ + - ✓ + - ✓ + - ✓ + + * - 8.0 to 8.1 + - + - ✓ + - ✓ + - ✓ + - ✓ + - ✓ + - ✓ + - ✓ diff --git a/source/includes/quick-start/create-cxn-str.rst b/source/includes/quick-start/create-cxn-str.rst new file mode 100644 index 00000000..2708e1c6 --- /dev/null +++ b/source/includes/quick-start/create-cxn-str.rst @@ -0,0 +1,51 @@ +You can connect to your MongoDB deployment by providing a +**connection URI**, also called a *connection string*, which +tells {+odm+} how to connect to a MongoDB deployment and behave while +connected. + +The connection string includes the hostname or IP address and +port of your deployment, the authentication mechanism, user credentials +when applicable, and connection options. + +To learn about connecting to an instance or deployment not hosted on +Atlas, see :manual:`Connection Strings ` +in the {+server-manual+}. + +.. procedure:: + :style: connected + + .. step:: Find your MongoDB Atlas connection string + + To retrieve your connection string for the deployment that + you created in the previous step, log in to your Atlas account. + Then, navigate to the :guilabel:`Database` section and click the + :guilabel:`Connect` button for your new deployment. + + .. figure:: /includes/figures/atlas_connection_select_cluster.png + :alt: The connect button in the clusters section of the Atlas UI + + Proceed to the :guilabel:`Connect your application` section. Select + **{+language+}** from the :guilabel:`Driver` selection menu and + the most recent {+ruby-driver+} version from the + :guilabel:`Version` selection menu. + + Deselect the :guilabel:`View full code sample` option to view only + the connection string. + + .. step:: Copy your connection string + + Click the button on the right of the connection string to copy it + to your clipboard. + + .. step:: Update the placeholders + + Paste the connection string into a file in your preferred text editor + and replace the ```` and ```` placeholders with + your database user's username and password. + + Save this file to a safe location for use in the next step. + +After completing these steps, you have a connection string that +contains your database username and password. + +.. include:: /includes/quick-start/troubleshoot.rst \ No newline at end of file diff --git a/source/includes/quick-start/create-deployment.rst b/source/includes/quick-start/create-deployment.rst new file mode 100644 index 00000000..30f49201 --- /dev/null +++ b/source/includes/quick-start/create-deployment.rst @@ -0,0 +1,22 @@ +You can create a free-tier MongoDB deployment on MongoDB Atlas +to store and manage your data. MongoDB Atlas hosts and manages +your MongoDB database in the cloud. + +.. procedure:: + :style: connected + + .. step:: Create a free MongoDB deployment on Atlas + + Complete the :atlas:`Get Started with Atlas ` + guide to set up a new Atlas account and load sample data into a + new free-tier MongoDB deployment. + + .. step:: Save your credentials + + After you create your database user, save that user's + username and password to a safe location for use in an upcoming step. + +After completing these steps, you have a new free-tier MongoDB deployment on +Atlas, database user credentials, and sample data loaded into your database. + +.. include:: /includes/quick-start/troubleshoot.rst diff --git a/source/includes/quick-start/troubleshoot.rst b/source/includes/quick-start/troubleshoot.rst new file mode 100644 index 00000000..f23e5e43 --- /dev/null +++ b/source/includes/quick-start/troubleshoot.rst @@ -0,0 +1,6 @@ +.. note:: + + If you run into issues, ask for help in the + :community-forum:`MongoDB Community Forums <>` or submit feedback by using + the :guilabel:`{+feedback-widget-title+}` button in the upper right + corner of the page. diff --git a/source/includes/rails-compatibility-table-mongoid.rst b/source/includes/rails-compatibility-table-mongoid.rst new file mode 100644 index 00000000..253cd840 --- /dev/null +++ b/source/includes/rails-compatibility-table-mongoid.rst @@ -0,0 +1,52 @@ +.. list-table:: + :header-rows: 1 + :stub-columns: 1 + :class: compatibility-large no-padding + + * - {+odm+} Version + - Rails 8.0 + - Rails 7.2 + - Rails 7.1 + - Rails 7.0 + - Rails 6.1 + - Rails 6.0 + - Rails 5.2 + - Rails 5.1 + + * - 9.0 + - ✓ [#rails-8.0]_ + - ✓ [#rails-7.2]_ + - ✓ [#rails-7.1]_ + - ✓ + - ✓ + - ✓ + - + - + + * - 8.1 + - ✓ [#rails-8.0]_ + - ✓ [#rails-7.2]_ + - ✓ [#rails-7.1]_ + - ✓ + - ✓ + - ✓ + - ✓ [#rails-5-ruby-3.0]_ + - + + * - 8.0 + - + - + - ✓ [#rails-7.1]_ + - ✓ + - ✓ + - ✓ + - ✓ [#rails-5-ruby-3.0]_ + - + +.. [#rails-8.0] Rails 8.0 requires {+odm+} v8.1.7 and v9.0.3 in the respective 8.1 and 9.0 stable branches. + +.. [#rails-7.2] Rails 7.2 requires {+odm+} v8.1.6 and v9.0.2 in the respective 8.1 and 9.0 stable branches. + +.. [#rails-7.1] Rails 7.1 requires {+odm+} v8.0.7 or v8.1.3 in the respective 8.0 and 8.1 stable branches. + +.. [#rails-5-ruby-3.0] Using Rails 5.x with Ruby 3 is not supported. diff --git a/source/includes/ror-compatibility-table-mongoid.rst b/source/includes/ror-compatibility-table-mongoid.rst new file mode 100644 index 00000000..b8bd7ee9 --- /dev/null +++ b/source/includes/ror-compatibility-table-mongoid.rst @@ -0,0 +1,70 @@ +.. list-table:: + :header-rows: 1 + :stub-columns: 1 + :class: compatibility-large + :widths: 20 20 60 + + * - Rails Framework + - {+odm+} Support + - Notes + + * - ``ActionCable`` + - ✓ + - There is no MongoDB adapter for ``ActionCable``, but you can + use any existing adapter, such as the `Redis Adapter + <{+active-record-docs+}/action_cable_overview.html#redis-adapter>`__, + alongside {+odm+} models. + + * - ``ActionMailbox`` + - *Unsupported* + - Depends directly on Active Record. + + * - ``ActionMailer`` + - ✓ + - + + * - ``ActionPack`` + - ✓ + - + + * - ``ActionText`` + - *Unsupported* + - Depends directly on Active Record. + + * - ``ActionView`` + - ✓ + - + + * - ``ActiveJob`` + - ✓ + - Serialization of BSON and {+odm+} objects works best if you + explicitly send ``BSON::ObjectId`` values as strings, and + reconstitute them in the job. For example: + + .. code-block:: ruby + + record = Model.find(...) + MyJob.perform_later(record._id.to_s) + + class MyJob < ApplicationJob + def perform(id_as_string) + record = Model.find(id_as_string) + # ... + end + end + + * - ``ActiveModel`` + - ✓ + - The ``Mongoid::Document`` module includes + ``ActiveModel::Model`` and leverages ``ActiveModel::Validations`` + for :ref:`mongoid-modeling-validation`. + + * - ``ActiveStorage`` + - *Unsupported* + - Depends directly on Active Record. + + * - ``ActiveSupport`` + - ✓ + - The ``Mongoid`` module requires ``ActiveSupport``. + ``Mongoid`` uses ``ActiveSupport::TimeWithZone`` for handling + time values. diff --git a/source/includes/ruby-driver-compatibility-table-mongoid.rst b/source/includes/ruby-driver-compatibility-table-mongoid.rst new file mode 100644 index 00000000..8eded198 --- /dev/null +++ b/source/includes/ruby-driver-compatibility-table-mongoid.rst @@ -0,0 +1,14 @@ +.. list-table:: + :header-rows: 1 + :stub-columns: 1 + :class: compatibility-large + + * - {+odm+} Version + - {+language+} Driver 2.18 to 2.21 + - {+language+} Driver 2.10 to 2.17 + - {+language+} Driver 2.7 to 2.9 + + * - 8.0 to 9.0 + - ✓ + - + - diff --git a/source/includes/security/encryption.rb b/source/includes/security/encryption.rb new file mode 100644 index 00000000..1fd9290d --- /dev/null +++ b/source/includes/security/encryption.rb @@ -0,0 +1,102 @@ +# start-encryption-schema +class Patient + include Mongoid::Document + include Mongoid::Timestamps + + encrypt_with key_id: '' + + # This field is not encrypted + field :category, type: String + + # This field is encrypted by using AEAD_AES_256_CBC_HMAC_SHA_512-Random + # algorithm + field :passport_id, type: String, encrypt: { + deterministic: false + } + + # This field is encrypted by using AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic + # algorithm + field :blood_type, type: String, encrypt: { + deterministic: true + } + + # This field is encrypted by using AEAD_AES_256_CBC_HMAC_SHA_512-Random + # algorithm and a different data key + field :ssn, type: Integer, encrypt: { + deterministic: false, key_id: '') + +# Create the encryption object +encryption = Mongo::ClientEncryption.new( + key_vault_client, + key_vault_namespace: 'encryption.__keyVault', + kms_providers: { + aws: { + "accessKeyId": "", + "secretAccessKey": "" + } + } +) + +encryption.rewrap_many_data_key( + {}, # Empty filter to rewrap all keys + { + provider: 'aws', + master_key: { + region: 'us-east-2', + key: 'arn:aws:kms:us-east-2:...' + } + } +) +# end-rewrap-keys + +# start-in-place + +# Print all documents in the collection. The first document is unencrypted, and +# the second is encrypted. +Patient.all.to_a +# => +# [#, +# #] + +# Querying for documents with a CSFLE-enabled client returns only the encrypted document +Patient.where(blood_type: 'AB+').to_a +# => [#] +# end-in-place \ No newline at end of file diff --git a/source/includes/unicode-ballot-x.rst b/source/includes/unicode-ballot-x.rst deleted file mode 100644 index 50c3667a..00000000 --- a/source/includes/unicode-ballot-x.rst +++ /dev/null @@ -1 +0,0 @@ -.. |x| unicode:: U+2717 diff --git a/source/includes/unicode-checkmark.rst b/source/includes/unicode-checkmark.rst deleted file mode 100644 index 16f4e947..00000000 --- a/source/includes/unicode-checkmark.rst +++ /dev/null @@ -1 +0,0 @@ -.. |checkmark| unicode:: U+2713 diff --git a/source/index.txt b/source/index.txt index bd70e418..d2b744d6 100644 --- a/source/index.txt +++ b/source/index.txt @@ -1,24 +1,102 @@ -.. _mongoid: +.. _mongoid-odm-landing: +.. _mongoid-odm: -******* -Mongoid -******* - -.. default-domain:: mongodb +======= +{+odm+} +======= .. toctree:: - :titlesonly: - - Installation & Configuration - Tutorials - Schema Configuration - Working with Data - API - Release Notes - Contributing - Additional Resources - Ecosystem - -Mongoid is the officially supported object-document mapper (ODM) for MongoDB in -Ruby. To work with Mongoid from the command line using ``rails``-like tooling, -the `railsmdb `_ utility can be used. + :titlesonly: + + Quick Start - {+ror+} + Quick Start - Sinatra + Configuration + Interact with Data + Model Your Data + Secure Your Data + Integrations & Tools + API Documentation + Compatibility + What's New + Issues & Help + View the Source + +Introduction +------------ + +Welcome to the documentation site for {+odm+}. {+odm+} is the officially +supported object-document mapper (ODM) for MongoDB in {+language+}. By +using {+odm+}, you can easily interact with your data and create +flexible data models native to {+language+} applications. + +You can add {+odm+} to your {+language+} application to connect it to +a MongoDB database. Install {+odm+} by adding it to your project's +``Gemfile`` or set up a runnable project by following one of the +Quick Start guides. + +Connect to a Compatible MongoDB Deployment +------------------------------------------ + +You can use {+odm+} to connect to MongoDB deployments running on one of +the following hosted services or editions: + +.. include:: /includes/fact-environments.rst + +Quick Start +----------- + +Learn how to establish a connection to MongoDB Atlas and begin +working with data by following one of the following guides: + +- :ref:`mongoid-quick-start-rails` +- :ref:`mongoid-quick-start-sinatra` + +Configuration +------------- + +To learn how to configure different options in your {+odm+} application, +see the :ref:`mongoid-configuration` section. + +Interact with Data +------------------ + +To learn how to use {+odm+} to interact with your MongoDB data, +see the :ref:`mongoid-interact-data` section. + +Model Your Data +--------------- + +To learn how to model your MongoDB data as {+odm+} models, +see the :ref:`mongoid-data-modeling` section. + +Secure Your Data +---------------- + +To learn how to secure your data by using encryption, +see the :ref:`mongoid-security` section. + +Integrations & Tools +-------------------- + +To learn how to add {+odm+} to an existing application, +see the :ref:`mongoid-integrations-tools` section. This section also +includes information about the {+ror+} framework and other resources. + +Compatibility +------------- + +To learn about the versions of the {+mdb-server+}, the {+language+} +language, the {+ruby-driver+}, and {+ror+} framework that are compatible +with each version of {+odm+}, see :ref:`mongoid-compatibility`. + +What's New +---------- + +To view a list of new features and changes in each version, see the +:ref:`mongoid-whats-new` section. + +Issues & Help +------------- + +To find resources for troubleshooting and to learn about contributing to +{+odm+}, see :ref:`mongoid-issues-and-help`. diff --git a/source/installation-configuration.txt b/source/installation-configuration.txt deleted file mode 100644 index 59f6e5f1..00000000 --- a/source/installation-configuration.txt +++ /dev/null @@ -1,25 +0,0 @@ -.. _installation-configuration: - -**************************** -Installation & Configuration -**************************** - -.. default-domain:: mongodb - -.. toctree:: - :titlesonly: - - Installation - Compatibility - Configuration - Rails Integration - -Overview --------- - -Learn how to install and configure Mongoid in the following sections: - -- :ref:`Installation ` -- :ref:`Compatibility ` -- :ref:`Configuration ` -- :ref:`Rails Integration ` \ No newline at end of file diff --git a/source/installation.txt b/source/installation.txt deleted file mode 100644 index bd9e2908..00000000 --- a/source/installation.txt +++ /dev/null @@ -1,66 +0,0 @@ -.. _installation: - -************ -Installation -************ - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 2 - :class: singlecol - -Install the Gem -=============== - -Mongoid is bundled as a gem, and is `hosted on Rubygems -`_. -It can be installed manually or with bundler. - -To install the gem manually: - -.. code-block:: sh - - gem install mongoid - -To install the gem with bundler, include the following in your ``Gemfile``: - -.. code-block:: ruby - - gem 'mongoid' - -Using Mongoid with a New Rails Application -========================================== - -By using the `railsmdb CLI `_ a new -Ruby on Rails application can be quickly generated using the same options as -``rails new``, but configured to work with MongoDB: - -.. code-block:: sh - - railsmdb new my_new_rails_app - -The ``rails`` CLI can also be used, however when creating a new Rails application -and where Mongoid will be used for data access, provide the ``--skip-active-record`` -flag to the ``rails new`` command to avoid depending on and configuring ActiveRecord. - -Additional examples can be found in the `tutorials `_. - -Using Mongoid with an Existing Rails Application -================================================ - -Using the `railsmdb CLI `_ an existing -Rails application can easily be configured for use with Mongoid: - -.. code-block:: sh - - railsmdb setup - -Converting an existing Rails application without using ``railsmdb`` can be done -by updating the ``config/application.rb`` file to remove the ``require 'rails/all'`` -line and explicitly include the required frameworks (which could be all of the -frameworks provided by Rails with the exception ofActiveRecord). -Any references to ActiveRecord in files in the ``config`` directory and in the -models also need to be removed. diff --git a/source/integrations-tools.txt b/source/integrations-tools.txt new file mode 100644 index 00000000..827cdb3c --- /dev/null +++ b/source/integrations-tools.txt @@ -0,0 +1,23 @@ +.. _mongoid-integrations-tools: + +==================== +Integrations & Tools +==================== + +.. facet:: + :name: genre + :values: reference + +.. meta:: + :keywords: ruby framework, odm, rails, sinatra, ecosystem + +.. toctree:: + :caption: Integrations & Tools + + Add {+odm+} to an Existing Application + Rails Integration + External Resources + +- :ref:`mongoid-add-to-existing` +- :ref:`mongoid-rails-integration` +- :ref:`mongoid-external-resources` diff --git a/source/integrations-tools/add-existing.txt b/source/integrations-tools/add-existing.txt new file mode 100644 index 00000000..790b2f59 --- /dev/null +++ b/source/integrations-tools/add-existing.txt @@ -0,0 +1,231 @@ +.. _mongoid-add-to-existing: + +====================================== +Add {+odm+} to an Existing Application +====================================== + +.. facet:: + :name: genre + :values: tutorial + +.. meta:: + :keywords: ruby framework, odm, migrate + +.. contents:: On this page + :local: + :backlinks: none + :depth: 2 + :class: singlecol + +Overview +-------- + +In this guide, you can learn how to add {+odm+} to an existing Sinatra +or {+ror+} (Rails) application. To learn how to set up a new +application that uses {+odm+}, see one of the following guides: + +- :ref:`mongoid-quick-start-rails` +- :ref:`mongoid-quick-start-sinatra` + +Sinatra Application +------------------- + +To start using {+odm+} in an existing Sinatra application, perform +the following steps: + +1. Add the ``mongoid`` dependency to your application's ``Gemfile``. + +#. Create a ``config/mongoid.yml`` configuration file and specify your + connection target, as shown in the + :ref:`mongoid-quick-start-sinatra-connect-to-mongodb` step of the + Quick Start guide. + +#. Create an application file and load your configuration file, as shown + in the :ref:`mongoid-quick-start-sinatra-view-data` step of the Quick + Start guide. + +#. Create {+odm+} models to interact with your data. + +.. _mongoid-add-existing-rails: + +Rails Application +----------------- + +You can add {+odm+} to an existing Rails application to run alongside +other Active Record adapters. To use a combination of adapters, you +can add the ``mongoid`` dependency and populate the configuration file +with your connection information to start using MongoDB in your +application. + +To adapt an existing Rails application to use only {+odm+} instead of +Active Record, you must make other configuration changes, as +described in the following sections. + +Modify Dependencies +~~~~~~~~~~~~~~~~~~~ + +Add the ``mongoid`` gem to your application's ``Gemfile``: + +.. code-block:: ruby + :caption: Gemfile + + gem 'mongoid' + +To use {+odm+} as the *only* database adapter, remove or comment out any +RDBMS libraries listed in the ``Gemfile``, such as ``sqlite`` or +``pg``. + +Then, install the dependencies by running the following command: + +.. code-block:: sh + + bundle install + +{+odm+} Configuration +~~~~~~~~~~~~~~~~~~~~~ + +Generate the default {+odm+} configuration by running the following +command: + +.. code-block:: sh + + bin/rails g mongoid:config + +This generator creates the ``config/mongoid.yml`` configuration file +used to configure the connection to the MongoDB deployment and the +``config/initializers/mongoid.rb`` initializer file that you can use +to set other options. + +In the ``config/mongoid.yml`` file, specify your connection string and +other connection options. + +Modify Frameworks +~~~~~~~~~~~~~~~~~ + +Open the ``config/application.rb`` file and examine the contents. If the +file uses the ``require "rails/all"`` statement to load all Rails components, +delete this statement. You must add a separate ``require`` statement +for each Rails component, as shown in the following sample +``config/application.rb`` file: + +.. code-block:: ruby + :caption: config/application.rb + + # Remove or comment out rails/all + #require "rails/all" + + # Add the following instead of rails/all: + require "rails" + + # Comment out unneeded frameworks + # require "active_record/railtie" rescue LoadError + # require "active_storage/engine" rescue LoadError + require "action_controller/railtie" rescue LoadError + require "action_view/railtie" rescue LoadError + require "action_mailer/railtie" rescue LoadError + require "active_job/railtie" rescue LoadError + require "action_cable/engine" rescue LoadError + # require "action_mailbox/engine" rescue LoadError + # require "action_text/engine" rescue LoadError + require "rails/test_unit/railtie" rescue LoadError + +.. note:: + + Because they rely on Active Record, the `ActionText + <{+active-record-docs+}/action_text_overview.html>`__, + `ActiveStorage <{+active-record-docs+}/active_storage_overview.html>`__, and + `ActionMailbox + <{+active-record-docs+}/action_mailbox_basics.html>`__ + adapters cannot be used alongside {+odm+}. + +Disable Active Record Adapters +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +In ``config/application.rb`` and your application's other configuration +files, remove or comment out any references to +``config.active_record`` and ``config.active_storage``. + +Adjust Models +~~~~~~~~~~~~~ + +To migrate from using Active Record to {+odm+}, you must adjust your +application's existing models. + +Active Record models derive from the ``ApplicationRecord`` class and do +not have column definitions, while {+odm+} models generally have no +superclass but must include the ``Mongoid::Document`` attribute. + +When creating {+odm+} models, you can define fields in the following +ways: + +- Define fields explicitly +- Use :ref:`dynamic fields ` + +For example, a basic Active Record ``Post`` model might resemble the +following: + +.. code-block:: ruby + :caption: app/models/post.rb + + class Post < ApplicationRecord + has_many :comments, dependent: :destroy + end + +A similar {+odm+} ``Post`` model might resemble the following: + +.. code-block:: ruby + :caption: app/models/post.rb + + class Post + include Mongoid::Document + + field :title, type: String + field :body, type: String + + has_many :comments, dependent: :destroy + end + +Instead of using predefined fields, you can define the ``Post`` model by using +dynamic fields, as shown in the following code: + +.. code-block:: ruby + :caption: app/models/post.rb + + class Post + include Mongoid::Document + include Mongoid::Attributes::Dynamic + + has_many :comments, dependent: :destroy + end + +Data Migration +~~~~~~~~~~~~~~ + +If you already have data in a relational database that you want to +move into MongoDB, you must perform a data migration. You don't have to +perform schema migration because MongoDB does not require +a predefined schema to store the data. + +Migration tools are often specific to datasets. +Even though {+odm+} supports a superset of Active Record associations, +model references are stored differently in collections when using +{+odm+} compared to Active Record. + +Visit the following resources to learn more about migrating from an +RDBMS to MongoDB: + +- `RDBMS to MongoDB Migration Guide + `__ + in the AWS documentation + +- :website:`Modernize your apps with MongoDB Atlas + ` on the MongoDB website + +Rails API +~~~~~~~~~ + +The process for creating a Rails API application that uses {+odm+} is +almost the same as for creating a normal application. The only +difference is that you must add the ``--api`` flag when running ``rails +new`` to create the application. Migrating a Rails API application to +{+odm+} follows the same process described in the preceding sections. diff --git a/source/integrations-tools/external-resources.txt b/source/integrations-tools/external-resources.txt new file mode 100644 index 00000000..01a0f4fc --- /dev/null +++ b/source/integrations-tools/external-resources.txt @@ -0,0 +1,127 @@ +.. _mongoid-external-resources: + +================== +External Resources +================== + +.. facet:: + :name: genre + :values: reference + +.. meta:: + :keywords: mongoid, ruby, libraries, ecosystem, tools + +.. contents:: On this page + :local: + :backlinks: none + :depth: 2 + :class: singlecol + +Overview +-------- + +In this guide, you can find external tools and resources that you can use to +enhance your {+odm+} applications. These resources include external projects and +libraries, and various learning resources. + +Projects and Libraries +---------------------- + +The following sections describe projects and libraries that are integrated with +or built on top of {+odm+}. + +Projects +~~~~~~~~ + +- :github:`Workarea Commerce ` is an + enterprise-grade {+ror+} commerce platform that uses {+odm+}. + +Extension Libraries +~~~~~~~~~~~~~~~~~~~ + +- :github:`Mongoid Tree ` is a tree structure + for {+odm+} documents that uses the materialized path pattern. + +- :github:`Mongoid Token ` generates random, + unique tokens for {+odm+} documents. + +- :github:`Mongoid Collection Snapshot + ` helps maintain + collections of processed data in {+odm+} applications. + +- :github:`Mongoid Locker ` provides + document-level locking for {+odm+} applications. + +- :github:`Mongo Beautiful Logger + ` is a library that + formats your MongoDB logs. + +- :github:`Mongoid Search ` provides + full-text search for {+odm+}. + +- :github:`Mongoid Fulltext Search ` + provides full-text search using n-gram matching for {+odm+}. + +Integration Libraries +~~~~~~~~~~~~~~~~~~~~~ + +- :github:`CarrierWave Mongoid + ` provides + {+odm+} support for the Carrierwave file-uploads library. + +- :github:`Mongoid RSpec ` provides RSpec + matchers and macros for {+odm+} applications. + +- :github:`RailsAdmin ` is a Rails engine that + provides an interface for managing your data. + +- :github:`ActiveAdmin Mongoid ` + provides ActiveAdmin hacks to support {+odm+}. + +- :github:`Mongoid History ` is a + multi-user, non-linear history tracker for {+odm+}. + +- :github:`Delayed Job Mongoid + ` is a {+odm+} backend for + ``delayed_job``. + +- :github:`Mongo Session Store ` is a + Rails-compatible session store for {+odm+}. + +Learning Resources +------------------ + +The following sections provide screencasts, articles, and sample +applications that you can use to learn more about {+odm+}. + +Screencasts +~~~~~~~~~~~ + +- `RailsCasts: Mongoid (revised) with Ryan Bates + `__ gives an overview of {+odm+}. + It includes the basics of setting up an application and working with data. + +- `Create a search bar in Rails with Mongoid + `__ is a tutorial that explains how + to implement text search with {+odm+}. + +Articles +~~~~~~~~ + +- `A Simple Content Management System in Sinatra + `__: + Building a content management application with Sinatra and {+odm+}. + +- `How to Create a {+language+} API With Sinatra + `__: Creating a + Sinatra API with {+odm+}. + +- `Converting an existing {+ror+} application to MongoDB + `__: Learn how to convert an + existing {+ror+} application to use MongoDB and {+odm+}. + +Sample Applications +~~~~~~~~~~~~~~~~~~~ + +- :github:`Mongoid Demo `: A repository + containing sample applications that use {+odm+}. diff --git a/source/integrations-tools/rails-integration.txt b/source/integrations-tools/rails-integration.txt new file mode 100644 index 00000000..1861d47b --- /dev/null +++ b/source/integrations-tools/rails-integration.txt @@ -0,0 +1,137 @@ +.. _mongoid-rails-integration: + +================= +Rails Integration +================= + +.. facet:: + :name: genre + :values: reference + +.. meta:: + :keywords: web framework, api, code example, ruby + +.. contents:: On this page + :local: + :backlinks: none + :depth: 2 + :class: singlecol + +Overview +-------- + +In this guide, you can learn about features that are automatically +enabled when you use {+odm+} in a {+ror+} application. This guide also +describes Rails-related functionality that you can enable in your application. + +Configuration +------------- + +You can configure {+odm+}-specific options and other Rails-environment +specific options in your main application file by accessing +``config.mongoid``. The ``mongoid:config`` generator creates an +initializer in the ``config/initializers/mongoid.rb`` file. + +.. note:: + + Any options set in your ``config/mongoid.yml`` file + take precedence over options set elsewhere. For this reason, use + ``mongoid.yml`` as the default location for {+odm+} configuration + when possible. + +To learn more about all available configuration options, +see the :ref:`mongoid-configuration` guides. + +The following code demonstrates how to create a Rails logger by +accessing ``config.mongoid``: + +.. code-block:: ruby + + module MyApplication + class Application < Rails::Application + config.mongoid.logger = Logger.new(STDERR, :warn) + end + end + +To learn more about logging settings, see the +:ref:`mongoid-logging-config` guide. + +Model Preloading +---------------- + +To set up single collection inheritance, {+odm+} must preload all +models before every request in development mode. This can slow down your +application, so if you are not using any inheritance you can turn this +feature off. + +The following code demonstrates how you can turn off preloading by +setting the ``preload_models`` feature to ``false``: + +.. code-block:: ruby + + config.mongoid.preload_models = false + +Exceptions +---------- + +Similar to Active Record, {+odm+} configures Rails to automatically +convert certain exceptions to HTTP status codes. The following list +provides the conversions between {+odm+} exceptions and HTTP codes: + +- ``Mongoid::Errors::DocumentNotFound``: Converted to ``404 Not Found`` +- ``Mongoid::Errors::Validations``: Converted to ``422 Unprocessable Content`` + +Execution Time Logging +---------------------- + +{+odm+} can output the time spent executing database commands to the Rails +instrumentation event ``process_action.action_controller``. {+odm+} +obtains these values through driver command monitoring. You application +logs this time amount with view time as shown in the following output: + +.. code-block:: none + + Completed 200 OK in 2739ms (Views: 12.6ms | MongoDB: 0.2ms) + +This logging is set up automatically in your Rails application. + +.. note:: Time Calculation + + The time indicated in log entries is the time that the MongoDB + deployment takes to run MongoDB operations in addition to the time taken to + send commands and receive results from {+mdb-server+}. It does + not include time taken by the driver and {+odm+} to generate the + queries, cast types, or otherwise process the results. + +Rake Tasks +---------- + +You can use following rake tasks for {+odm+} when using the Rails +framework: + +- ``db:create_indexes``: Reads all index definitions from the models and + attempts to create them in the database +- ``db:remove_indexes``: Removes indexes for each model +- ``db:drop``: Drops all collections in the database except for system + collections +- ``db:purge``: Deletes all data, including indexes, from the database +- ``db:seed``: Seeds the database from the ``db/seeds.rb`` file +- ``db:setup``: Creates indexes and seeds the database + +The following rake tasks exist only for framework dependency purposes +and do not perform any actions: + +- ``db:test:prepare`` +- ``db:schema:load`` +- ``db:create`` +- ``db:migrate`` + +Additional Information +---------------------- + +To learn about how to set up a new Rails application that uses {+odm+}, +see the :ref:`mongoid-quick-start-rails` guide. + +To learn how to add {+odm+} to an existing Rails application, see the +:ref:`mongoid-add-existing-rails` section of the Add {+odm+} to an +Existing Application guide. diff --git a/source/interact-data.txt b/source/interact-data.txt new file mode 100644 index 00000000..3ab217ab --- /dev/null +++ b/source/interact-data.txt @@ -0,0 +1,47 @@ +.. _mongoid-interact-data: + +================== +Interact with Data +================== + +.. facet:: + :name: genre + :values: reference + +.. meta:: + :keywords: ruby framework, odm, crud, query + +.. toctree:: + :caption: Interact with Data + + Perform Data Operations + Specify a Query + Modify Query Results + Aggregation + Search Text + Transactions and Sessions + Nested Attributes + +In this section, you can learn how to use {+odm+} to interact with your +MongoDB data. + +- :ref:`mongoid-data-crud`: Learn how to create, read, update, and + delete documents in a collection. + +- :ref:`mongoid-data-specify-query`: Learn how to construct + queries to match specific documents in a MongoDB collection. + +- :ref:`mongoid-data-modify-results`: Learn how to modify the way that + {+odm+} returns results from queries. + +- :ref:`mongoid-aggregation`: Learn how to transform your data by using + MongoDB aggregation. + +- :ref:`mongoid-data-text-search`: Learn how to perform efficient + searches on text fields. + +- :ref:`mongoid-data-txn`: Learn how to perform multi-document + transactions to make atomic data changes. + +- :ref:`mongoid-data-nested-attr`: Learn how to modify documents and + their associations in a single operation. \ No newline at end of file diff --git a/source/interact-data/aggregation.txt b/source/interact-data/aggregation.txt new file mode 100644 index 00000000..a5b1f6f4 --- /dev/null +++ b/source/interact-data/aggregation.txt @@ -0,0 +1,232 @@ +.. _mongoid-aggregation: + +==================================== +Transform Your Data with Aggregation +==================================== + +.. facet:: + :name: genre + :values: reference + +.. meta:: + :keywords: code example, transform, pipeline + +.. contents:: On this page + :local: + :backlinks: none + :depth: 2 + :class: singlecol + +Overview +-------- + +In this guide, you can learn how to use {+odm+} to perform **aggregation +operations**. + +Aggregation operations process data in your MongoDB collections and return +computed results. The MongoDB Aggregation framework, which is part of the Query +API, is modeled on the concept of data processing pipelines. Documents enter a +pipeline that contains one or more stages, and this pipeline transforms the +documents into an aggregated result. + +Aggregation operations function similarly to car factories with assembly +lines. The assembly lines have stations with specialized tools to +perform specific tasks. For example, when building a car, the assembly +line begins with the frame. Then, as the car frame moves through the +assembly line, each station assembles a separate part. The result is a +transformed final product, the finished car. + +The assembly line represents the *aggregation pipeline*, the individual +stations represent the *aggregation stages*, the specialized tools +represent the *expression operators*, and the finished product +represents the *aggregated result*. + +Compare Aggregation and Find Operations +--------------------------------------- + +The following table lists the different tasks you can perform with find +operations, compared to what you can achieve with aggregation +operations. The aggregation framework provides expanded functionality +that allows you to transform and manipulate your data. + +.. list-table:: + :header-rows: 1 + :widths: 50 50 + + * - Find Operations + - Aggregation Operations + + * - | Select *certain* documents to return + | Select *which* fields to return + | Sort the results + | Limit the results + | Count the results + - | Select *certain* documents to return + | Select *which* fields to return + | Sort the results + | Limit the results + | Count the results + | Rename fields + | Compute new fields + | Summarize data + | Connect and merge data sets + +{+odm+} Builders +---------------- + +You can construct an aggregation pipeline by using {+odm+}'s high-level +domain-specific language (DSL). The DSL supports the following aggregation +pipeline operators: + +.. list-table:: + :header-rows: 1 + :widths: 50 50 + + * - Operator + - Method Name + + * - :manual:`$group ` + - ``group`` + + * - :manual:`$project ` + - ``project`` + + * - :manual:`$unwind ` + - ``unwind`` + +To create an aggregation pipeline by using one of the preceding operators, call +the corresponding method on an instance of ``Criteria``. Calling the method adds +the aggregation operation to the ``pipeline`` atrritbure of the ``Criteria`` +instance. To run the aggregation pipeline, pass the ``pipeline`` attribute value +to the ``Collection#aggregate`` method. + +Example +~~~~~~~ + +Consider a database that contains a collection with documents that are modeled by +the following classes: + +.. code-block:: ruby + + class Tour + include Mongoid::Document + + embeds_many :participants + + field :name, type: String + field :states, type: Array + end + + class Participant + include Mongoid::Document + + embedded_in :tour + + field :name, type: String + end + +In this example, the ``Tour`` model represents the name of a tour and the states +it travels through, and the ``Participant`` model represents the name of a +person participating in the tour. + +The following example creates an aggregation pipeline that outputs the states a +participant has visited by using the following +aggregation operations: + +- ``match``, which find documents in which the ``participants.name`` field + value is ``"Serenity"`` +- ``unwind``, which deconstructs the ``states`` array field and outputs a + document for each element in the array +- ``group``, which groups the documents by the value of their ``states`` field +- ``project``, which prompts the pipeline to return only the ``_id`` and + ``states`` fields + +.. io-code-block:: + + .. input:: /includes/aggregation/builder-dsl.rb + :language: ruby + + .. output:: + + [{"states":["OR","WA","CA"]}] + +Aggregation without Builders +---------------------------- + +You can use the ``Collection#aggregate`` method to run aggregation operations that do not have +corresponding builder methods by passing in an array of aggregation +operations. Using this method to perform the aggregation returns +raw ``BSON::Document`` objects rather than ``Mongoid::Document`` model +instances. + +Example +~~~~~~~ + +Consider a database that contains a collection with documents that are modeled +by the following classes: + +.. code-block:: ruby + + class Band + include Mongoid::Document + has_many :tours + has_many :awards + field :name, type: String + end + + class Tour + include Mongoid::Document + belongs_to :band + field :year, type: Integer + end + + class Award + include Mongoid::Document + belongs_to :band + field :name, type: String + end + +The following example creates an aggregation pipeline to retrieve all bands that +have toured since ``2000`` and have at least ``1`` award: + +.. io-code-block:: + + .. input:: /includes/aggregation/ruby-aggregation.rb + :language: ruby + + .. output:: + + [ + {"_id": "...", "name": "Deftones" }, + {"_id": "...", "name": "Tool"}, + ... + ] + +.. tip:: + + The preceding example projects only the ``_id`` field of the output + documents. It then uses the projected results to find the documents and return + them as ``Mongoid::Document`` model instances. This optional step is not + required to run an aggregation pipeline. + +Additional Information +---------------------- + +To view a full list of aggregation operators, see :manual:`Aggregation +Operators. ` + +To learn about assembling an aggregation pipeline and view examples, see +:manual:`Aggregation Pipeline. ` + +To learn more about creating pipeline stages, see :manual:`Aggregation +Stages. ` + +API Documentation +~~~~~~~~~~~~~~~~~ + +To learn more about any of the methods discussed in this +guide, see the following API documentation: + +- `group <{+api+}/Mongoid/Criteria/Queryable/Aggregable.html#group-instance_method>`__ +- `project <{+api+}/Mongoid/Criteria/Queryable/Aggregable.html#project-instance_method>`__ +- `unwind <{+api+}/Mongoid/Criteria/Queryable/Aggregable.html#unwind-instance_method>`__ \ No newline at end of file diff --git a/source/interact-data/crud.txt b/source/interact-data/crud.txt new file mode 100644 index 00000000..846c1be6 --- /dev/null +++ b/source/interact-data/crud.txt @@ -0,0 +1,841 @@ +.. _mongoid-data-crud: + +======================= +Perform Data Operations +======================= + +.. facet:: + :name: genre + :values: reference + +.. meta:: + :keywords: ruby framework, odm, create data, edit + +.. contents:: On this page + :local: + :backlinks: none + :depth: 2 + :class: singlecol + +Overview +-------- + +In this guide, you can learn how to use {+odm+} to perform CRUD (create, +read, update, delete) operations to modify the data in your MongoDB +collections. + +{+odm+} supports CRUD operations that you can perform by using other +{+language+} mappers such as Active Record or Data Mapper. +When using {+odm+}, general persistence operations perform atomic +updates on only the fields that you change instead of writing the +entire document to the database each time, as is the case with other +ODMs. + +Create Operations +----------------- + +You can perform create operations to add new documents to a collection. If +the collection doesn't exist, the operation implicitly creates the +collection. The following sections describe the methods you can use to +create new documents. + +create! +~~~~~~~ + +Use the ``create!`` method on your model class to insert one or more +documents into a collection. If any server or validation errors occur, +``create!`` raises an exception. + +To call ``create!``, pass a hash of attributes that define the document +you want to insert. If you want to create and insert multiple documents, +pass an array of hashes. + +This example shows multiple ways to call ``create!``. The first example +creates one ``Person`` document, and the second example creates two ``Person`` +documents. The third example passes a ``do..end`` block to ``create!``. {+odm+} +invokes this block with the documents passed to ``create!`` as +arguments. The ``create!`` method attempts to save the document at the +end of the block: + +.. literalinclude:: /includes/interact-data/crud.rb + :language: ruby + :start-after: start create! example + :end-before: end create! example + +create +~~~~~~ + +Use the ``create`` method to insert a new document or multiple new documents into a +database. ``create`` does not raise an exception on validation errors, +unlike the ``!``-suffixed version. ``create`` does raise exceptions on +server errors, such as if you insert a document with a duplicate ``_id`` field. + +If ``create`` encounters any validation errors, the document is not inserted +but is returned with other documents that were inserted. You can use the +``persisted?``, ``new_record?`` or ``errors`` methods to verify the +documents that were inserted into the database. + +This example shows how to use ``create`` to insert new documents +into MongoDB. The first example shows how to insert a ``Person`` +document. The second example attempts to insert two ``Post`` documents, +but the second document fails validation because it contains a duplicate +title. The example then uses the ``persisted?`` method to confirm which +documents were successfully inserted into the collection: + +.. literalinclude:: /includes/interact-data/crud.rb + :language: ruby + :start-after: start create example + :end-before: end create example + +To learn more about the ``persisted?`` and ``new_record?`` methods, see +the :ref:`mongoid-persistence-attr` section of this guide. + +save! +~~~~~ + +Use the ``save!`` method to atomically save changed attributes to the collection or +to insert a new document. ``save!`` raises an exception if there are any +server or validation errors. You can use the ``new`` method to create a new document +instance. Then, use ``save!`` to insert the document into the database. + +The following example shows how to use ``save!`` to insert a new ``Person`` +document and update the ``first_name`` field of the existing document: + +.. literalinclude:: /includes/interact-data/crud.rb + :language: ruby + :start-after: start save! example + :end-before: end save! example + +save +~~~~ + +The ``save`` method does not raise an exception if there are any +validation errors. ``save`` still raises an exception if there are any +server errors. The method returns ``true`` if all changed attributes +are saved, and ``false`` if any validation errors occur. + +You can pass the following options to ``save``: + +- ``validate: false``: To bypass validations when saving the new + document or updated attributes. + +- ``touch: false``: To not update the ``updated_at`` field when + updating the specified attributes. This option has no effect when + inserting a new document. + +The following code uses ``save`` to insert a new document. It then updates +that document and applies the ``validate: false`` option. + +.. literalinclude:: /includes/interact-data/crud.rb + :language: ruby + :start-after: start save example + :end-before: end save example + +Read Operations +--------------- + +You can perform read operations to retrieve documents from a collection. +To learn more about creating query filters to retrieve a subset of your documents, see the :ref:`mongoid-data-specify-query` guide. + +.. _mongoid-read-attributes: + +attributes +~~~~~~~~~~ + +You can use the ``attributes`` method to retrieve the attributes of a +model instance as a hash. This hash also contains the attributes of all +embedded documents. + +The following example shows how to use ``attributes``: + +.. io-code-block:: + + .. input:: /includes/interact-data/crud.rb + :language: ruby + :start-after: start attributes example + :end-before: end attributes example + + .. output:: + :language: console + + { "_id" => BSON::ObjectId('...'), + "first_name" => "James", + "last_name" => "Nan" + } + +reload +~~~~~~ + +You can use the ``reload`` method to access the most recent version of a +document from MongoDB. When you reload a document, {+odm+} also reloads any embedded +associations in the same query. However, {+odm+} does not reload +referenced associations. Instead, it clears these values so that they +are loaded from the database during the next access. + +When you call ``reload`` on a document, any unsaved changes to the document +are lost. The following code shows how to call ``reload`` on a document: + +.. literalinclude:: /includes/interact-data/crud.rb + :language: ruby + :start-after: start reload example + :end-before: end reload example + +The preceding example updates the ``name`` field on the ``band`` document, +but does not save the new value. Because {+odm+} did not persist the +change to the ``name`` value, ``name`` contains the original value saved +to the database. + +.. note:: Document Not Found Errors + + When {+odm+} cannot find a document in the database, by default it raises a + ``Mongoid::Errors::DocumentNotFound`` error. You can set the + ``raise_not_found_error`` configuration option to ``false`` in your ``mongoid.yml`` + file to direct {+odm+} to save a new document and set its attributes to + default values. Generally, it also changes the value of the ``_id`` + field. For this reason, we do not recommend using ``reload`` when + ``raise_not_found_error`` is set to ``false``. + +Reload Unsaved Documents +++++++++++++++++++++++++ + +When you call ``reload`` on a document that is not persisted, the method performs +a ``find`` query on the document's ``_id`` value. + +The following example calls ``reload`` on a document that has not been +saved and prints out the ``name`` field value. ``reload`` performs a +``find`` operation using the document's ``_id`` value, which causes +{+odm+} to retrieve the existing document in the collection: + +.. io-code-block:: + :copyable: true + + .. input:: /includes/interact-data/crud.rb + :language: ruby + :start-after: start reload unsaved example + :end-before: end reload unsaved example + + .. output:: + :visible: false + + Photek + +Update Operations +----------------- + +You can perform update operations to modify existing documents in a +collection. If you attempt to update a deleted document, {+odm+} raises +a ``FrozenError`` exception. + +update_attributes! +~~~~~~~~~~~~~~~~~~ + +You can use the ``update_attributes!`` method to update the attributes of an +existing model instance. This method raises an exception if it encounters +any validation or server errors. + +The following example shows how to use ``update_attributes!`` to update +the ``first_name`` and ``last_name`` attributes of an existing document: + +.. literalinclude:: /includes/interact-data/crud.rb + :language: ruby + :start-after: start update attributes! example + :end-before: end update attributes! example + +.. tip:: + + {+odm+} provides the nested attributes feature that allows you to + update a document and its nested associations in one call. To learn + more, see the :ref:`mongoid-data-nested-attr` guide. + +update_attributes +~~~~~~~~~~~~~~~~~ + +The ``update_attributes`` method does not raise an exception on +validation errors. The method returns ``true`` if it passes validation +and the document is updated, and ``false`` otherwise. + +The following example shows how to use ``update_attributes``: + +.. literalinclude:: /includes/interact-data/crud.rb + :language: ruby + :start-after: start update attributes example + :end-before: end update attributes example + +update_attribute +~~~~~~~~~~~~~~~~ + +You can use the ``update_attribute`` method to bypass validations and +update a *single* attribute of a model instance. + +The following example shows how to use ``update_attribute`` to update +the value of a document's ``first_name`` attribute: + +.. literalinclude:: /includes/interact-data/crud.rb + :language: ruby + :start-after: start update attribute example + :end-before: end update attribute example + +upsert +~~~~~~ + +You can use the ``upsert`` method to update, insert, or replace a +document. + +``upsert`` accepts a ``replace`` option. If you set this option to ``true`` +and the document that calls ``upsert`` already exists in the database, +the new document replaces the one in the database. Any fields in the +database that the new document does not replace are removed. + +If you set the ``replace`` option to ``false`` and the document exists in the +database, it is updated. {+odm+} does not change any fields other than the +ones specified in the update document. If the document does not exist in the +database, it is inserted with the fields and values specified in the +update document. The ``replace`` option is set to ``false`` by default. + +The following example shows how to use ``upsert`` to first insert a new +document, then replace it by setting ``replace: true``: + +.. literalinclude:: /includes/interact-data/crud.rb + :language: ruby + :start-after: start upsert example + :end-before: end upsert example + +touch +~~~~~ + +You can use the ``touch`` method to update a document's ``updated_at`` +timestamp to the current time. ``touch`` cascades the update to any of +the document's ``belongs_to`` associations. You can also pass another +time-valued field as an option to also update that field. + +The following example uses ``touch`` to update the +``updated_at`` and ``audited_at`` timestamps: + +.. literalinclude:: /includes/interact-data/crud.rb + :language: ruby + :start-after: start touch example + :end-before: end touch example + +Delete Operations +----------------- + +You can perform delete operations to remove documents from a collection. + +delete +~~~~~~ + +You can use the ``delete`` method to delete a document from the database. When you +use ``delete``, {+odm+} does not run any callbacks. If the document is not +saved to the database, ``delete`` attempts to delete any document with +the same ``_id`` value. + +The following example shows how to use the ``delete`` method and +demonstrates what happens when you delete a document that is not saved +to the database: + +.. literalinclude:: /includes/interact-data/crud.rb + :language: ruby + :start-after: start delete example + :end-before: end delete example + +In the preceding example, {+odm+} raises a ``Mongoid::Errors::DocumentNotFound`` +error when you call ``reload`` because ``unsaved_person.delete`` deletes +the ``person`` document because the two documents have the same value +for ``_id``. + +destroy +~~~~~~~ + +The ``destroy`` method operates similarly to ``delete``, except {+odm+} +runs callbacks when you call ``destroy``. If the document is not found +in the database, ``destroy`` attempts to delete any document with +the same ``_id``. + +The following example shows how to use ``destroy``: + +.. literalinclude:: /includes/interact-data/crud.rb + :language: ruby + :start-after: start destroy example + :end-before: end destroy example + +delete_all +~~~~~~~~~~ + +The ``delete_all`` method deletes all documents from the collection that +are modeled by your {+odm+} model class. ``delete_all`` does not run +callbacks. + +The following example shows how to use ``delete_all`` to delete all +``Person`` documents: + +.. literalinclude:: /includes/interact-data/crud.rb + :language: ruby + :start-after: start delete all example + :end-before: end delete all example + +destroy_all +~~~~~~~~~~~ + +The ``destroy_all`` method deletes all documents from the collection +that are modeled by your {+odm+} model class. This can be an expensive +operation because {+odm+} loads all documents into memory. + +The following example shows how to use ``destroy_all`` to delete all +``Person`` documents: + +.. literalinclude:: /includes/interact-data/crud.rb + :language: ruby + :start-after: start destroy all example + :end-before: end destroy all example + +.. _mongoid-persistence-attr: + +Persistence Attributes +---------------------- + +The following sections describe the attributes that {+odm+} provides that +you can use to check if a document is persisted to the database. + +new_record? +~~~~~~~~~~~ + +The ``new_record?`` attribute returns ``true`` if the model instance is +*not saved* to the database yet, and ``false`` otherwise. It checks for +the opposite condition as the ``persisted?`` attribute. + +The following example shows how to use ``new_record?``: + +.. io-code-block:: + + .. input:: /includes/interact-data/crud.rb + :language: ruby + :start-after: start new record example + :end-before: end new record example + + .. output:: + :visible: false + + true + + false + +persisted? +~~~~~~~~~~ + +The ``persisted?`` attribute returns ``true`` if {+odm+} persists the +model instance, and ``false`` otherwise. It checks for the opposite +condition as the ``new_record?`` attribute. + +The following example shows how to use ``persisted?``: + +.. io-code-block:: + + .. input:: /includes/interact-data/crud.rb + :language: ruby + :start-after: start persisted example + :end-before: end persisted example + + .. output:: + :visible: false + + false + + true + +Access Field Values +------------------- + +{+odm+} provides several ways to access field values on a document. The +following sections describe how you can access field values. + +Get and Set Field Values +~~~~~~~~~~~~~~~~~~~~~~~~ + +There are multiple ways to get and set field values on a document. If you explicitly +declare a field, you can get and set this field value on the document directly. +The following example shows how to set and get the ``first_name`` field for a +``Person`` instance: + +.. literalinclude:: /includes/interact-data/crud.rb + :language: ruby + :start-after: start field values default + :end-before: end field values default + +The preceding example first uses the ``first_name`` attribute to set a +value, then calls it again to retrieve the value. + +You can also use the ``[]`` and ``[] =`` methods on a {+odm+} model instance to +access attributes by using hash syntax. The ``[]`` method is an alias +for the ``read_attribute`` method and the ``[] =`` method is an alias +for the ``write_attribute`` method. The following example shows how to +get and set the aliased ``first_name`` field by using the ``[]`` and +``[]=`` methods: + +.. literalinclude:: /includes/interact-data/crud.rb + :language: ruby + :start-after: start field values hash + :end-before: end field values hash + +To learn more about these methods, see the following +:ref:`mongoid-read-write-attributes` section of this guide. + +.. _mongoid-read-write-attributes: + +read_attribute and write_attribute +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +You can use the ``read_attribute`` and ``write_attribute`` methods to specify +custom behavior when reading or writing fields. You can use these methods +when defining a model or by calling them on model instances. + +To use ``read_attribute`` to get a field, pass the name of the field to the method. +To use ``write_attribute`` to set a field, pass the name of the field and the +value to assign. + +The following example uses ``read_attribute`` and ``write_attribute`` in +a model definition to define ``first_name`` and ``first_name=`` as methods that +are used to read and write to the ``fn`` attribute: + +.. literalinclude:: /includes/interact-data/crud.rb + :language: ruby + :start-after: start read write attributes + :end-before: end read write attributes + +You can also call ``read_attribute`` and ``write_attribute`` directly on a +model instance to get and set attributes. The following example uses these methods +on a model instance to get the ``first_name`` attribute and set it to the value +``"Pushkin"``. + +.. literalinclude:: /includes/interact-data/crud.rb + :language: ruby + :start-after: start read write instance + :end-before: end read write instance + +Bulk Write Attributes +~~~~~~~~~~~~~~~~~~~~~ + +You can write to multiple fields at the same time by using the ``attributes=`` +or ``write_attributes`` methods on a model instance. + +To use the ``attributes=`` method, call the method on a model instance and +pass a hash object that contains the fields and values that you want to set. +The following example shows how to use the ``attributes=`` method to set the +``first_name`` and ``middle_name`` fields on a ``person`` document: + +.. literalinclude:: /includes/interact-data/crud.rb + :language: ruby + :start-after: start attributes= example + :end-before: end attributes= example + +To use the ``write_attributes`` method, call the method on a model instance +and pass the fields and values that you want to set. The following example +shows how to use the ``write_attributes`` method to set the ``first_name`` and +``middle_name`` fields on a ``person`` document: + +.. literalinclude:: /includes/interact-data/crud.rb + :language: ruby + :start-after: start write_attributes example + :end-before: end write_attributes example + +Atomic Update Operators +----------------------- + +{+odm+} provides support for the following update operators that you can +call as methods on model instances. These methods perform operations +atomically and skip validations and callbacks. + +The following table describe the operators supported by {+odm+}: + +.. list-table:: + :header-rows: 1 + :widths: 20 20 60 + + * - Operator + - Description + - Example + + * - ``add_to_set`` + - Adds a specified value to an array-valued field. + - ``person.add_to_set(aliases: "Bond")`` + + * - ``bit`` + - Performs a bitwise update of a field. + - ``person.bit(age: { and: 10, or: 12 })`` + + * - ``inc`` + - Increments the value of a field. + - ``person.inc(age: 1)`` + + * - ``pop`` + - Removes the first or last element of an array field. + - ``person.pop(aliases: 1)`` + + * - ``pull`` + - Removes all instances of a value or values that match a specified + condition from an array field. + - ``person.pull(aliases: "Bond")`` + + * - ``pull_all`` + - Removes all instances of the specified values from an array + field. + - ``person.pull_all(aliases: [ "Bond", "James" ])`` + + * - ``push`` + - Appends a specified value to an array field. + - ``person.push(aliases: ["007","008"])`` + + * - ``rename`` + - Renames a field in all matching documents. + - ``person.rename(bday: :dob)`` + + * - ``set`` + - | Updates an attribute on the model instance and, if the instance + is already persisted, performs an atomic ``$set`` on the field, bypassing + validations. + + | ``set`` can also deeply set values on ``Hash`` fields. + + | ``set`` can also deeply set values on ``embeds_one`` associations. + If a model instance's ``embeds_one`` association document is ``nil``, one + is created before the update. + + | ``set`` cannot be used with ``has_one`` associations. + + - .. code-block:: ruby + + person = Person.create!(name: "Ricky Bobby") + # Updates `name` in the database + person.set(name: "Tyler Durden") + + * - ``unset`` + - Deletes a particular field in all matching documents. + - ``person.unset(:name)`` + +To learn more about update operators, see :manual:`Update Operators +` in the MongoDB {+server-manual+}. + +Group Atomic Operations +~~~~~~~~~~~~~~~~~~~~~~~ + +To group atomic operations together, you can use the ``atomically`` method +on a model instance. {+odm+} sends all operations that you pass to an +``atomically`` block in a single atomic command. + +.. note:: Use Transactions to Modify Multiple Documents Atomically + + Atomic operations apply to one document at a time. Therefore, nested + ``atomically`` blocks cannot make changes to multiple documents in one + atomic operation. To make changes to multiple documents in one atomic + operation, use a multi-document transaction. To learn more about + transactions, see the :ref:`mongoid-data-txn` guide. + +The following example shows how to use ``atomically`` to atomically +update multiple fields in a document: + +.. literalinclude:: /includes/interact-data/crud.rb + :language: ruby + :start-after: start atomically example + :end-before: end atomically example + +You can nest ``#atomically`` blocks when updating a single document. By +default, {+odm+} performs atomic writes defined by each block when the +block ends. The following example shows how to nest ``atomically`` blocks: + +.. literalinclude:: /includes/interact-data/crud.rb + :language: ruby + :start-after: start default block atomic example + :end-before: end default block atomic example + +In the preceding example, the ``$inc`` and ``$set`` operations are executed at +the end of the inner ``atomically`` block. + +Join Contexts ++++++++++++++ + +The ``atomically`` method accepts a ``join_context: true`` option to specify that +operations execute at the end of the outermost ``atomically`` block. When you +enable this option, only the outermost block, or the first block where ``join_context`` +is ``false``, writes changes to the database. The following example sets +the ``join_context`` option to ``true``: + +.. literalinclude:: /includes/interact-data/crud.rb + :language: ruby + :start-after: start join_contexts atomic + :end-before: end join_contexts atomic + +In the preceding example, {+odm+} performs the ``$inc`` and ``$set`` operations at the +end of the outermost ``atomically`` block. However, since an exception is raised +before the block ends and these operations can run, the changes are not +persisted. + +You can also enable context joining globally, so that operations execute in the +outermost ``atomically`` block by default. To enable this option +globally, set the ``join_contexts`` configuration option to ``true`` in +your ``mongoid.yml`` file. To learn more about {+odm+} configuration +options, see :ref:`configuration-options`. + +When you globally set ``join_contexts`` to ``true``, you can use the +``join_context: false`` option on an ``atomically`` block to run +operations at the end of the block for that block only. + +Dirty Tracking +-------------- + +You can track changed ("dirty") fields by using a {+odm+} API similar to +the one available in Active Model. If you modify a defined field in a +model, {+odm+} marks the model as dirty and allows you to perform +special actions. The following sections describe how you can interact +with dirty models. + +View Changes +~~~~~~~~~~~~ + +{+odm+} records changes from the time a model is instantiated, either as +a new document or by retrieving one from the database, until the time it is +saved. Any persistence operation clears the changes. + +{+odm+} creates model-specific methods that allow you to explore the +changes to a model instance. The following code demonstrates ways that +you can view changes on your model instance: + +.. literalinclude:: /includes/interact-data/crud.rb + :language: ruby + :start-after: start-dirty-tracking-view + :end-before: end-dirty-tracking-view + +.. note:: Tracking Changes to Associations + + Setting the associations on a document does not modify the + ``changes`` or ``changed_attributes`` hashes. This is true for all + types of associations. However, changing the + ``_id`` field on referenced associations causes the changes to + show up in the ``changes`` and the ``changed_attributes`` hashes. + +Reset Changes +~~~~~~~~~~~~~ + +You can reset a changed field to its previous value by calling the +``reset`` method, as shown in the following code: + +.. literalinclude:: /includes/interact-data/crud.rb + :language: ruby + :start-after: start-dirty-tracking-reset + :end-before: end-dirty-tracking-reset + +Persistence +~~~~~~~~~~~ + +{+odm+} uses dirty tracking as the basis of all persistence operations. +It evaluates the changes on a document and atomically updates only what +has changed, compared to other frameworks that write the entire document on +each save. If you don't make any changes, {+odm+} does not access the +database when you call ``Model#save``. + +View Previous Changes +~~~~~~~~~~~~~~~~~~~~~ + +After you persist a model to MongoDB, {+odm+} clears the current +changes. However, you can still see what changes were made previously by +calling the ``previous_changes`` method, as shown in the following +code: + +.. literalinclude:: /includes/interact-data/crud.rb + :language: ruby + :start-after: start-dirty-tracking-prev + :end-before: end-dirty-tracking-prev + +Update Container Fields +----------------------- + +{+odm+} currently has an issue that prevents changes to attributes of +container types, such as ``Set`` or ``Array``, from saving to MongoDB. +You must assign all fields, including container types, for their values +to save to MongoDB. + +For example, adding an item to a ``Set`` instance as shown in the +following code *does not* persist changes to MongoDB: + +.. code-block:: ruby + + person = Person.new + person.interests + # => # + + person.interests << 'Hiking' + # => # + person.interests + # => # # Change does not take effect + +To persist this change, you must modify the field value *outside* of the +model and assign it back to the model as shown in the following code: + +.. literalinclude:: /includes/interact-data/crud.rb + :language: ruby + :start-after: start-container-save + :end-before: end-container-save + +.. _mongoid-crud-read-only: + +Read-only Documents +------------------- + +You can mark documents as read-only in the following ways, depending on +the value of the ``Mongoid.legacy_readonly`` feature flag: + +- If this flag is turned *off*, you can mark a document as + read-only by calling the ``readonly!`` method on that document. The + resulting read-only document raises a ``ReadonlyDocument`` error if + you attempt to perform any persistence operation, including, but not + limited to, saving, updating, deleting, and destroying. Note that + reloading *does not* reset the read-only state. + + .. code-block:: ruby + + person = Person.first + person.readonly? # => false + person.readonly! # Sets the document as read-only + person.readonly? # => true + person.name = "Larissa Shay" # Changes the document + person.save # => raises ReadonlyDocument error + person.reload.readonly? # => true + +- If this flag is turned ``on``, you can mark a document as read-only + after you project that document by using methods such as ``only`` or + ``without``. As a result, you can't delete or destroy the read-only + document because {+odm+} raises a ``ReadonlyDocument`` error, but you can + save and update it. The read-only status *is reset* if you reload the + document. + + .. code-block:: ruby + + person = Person.only(:name).first + person.readonly? # => true + person.destroy # => raises ReadonlyDocument error + person.reload.readonly? # => false + + .. tip:: Projection + + To learn more about projections, see the + :ref:`mongoid-data-projection` section of the Modify Query + Results guide. + +You can also make a document read-only by overriding the ``readonly?`` +method, as shown in the following code: + +.. literalinclude:: /includes/interact-data/crud.rb + :language: ruby + :start-after: start-override-readonly + :end-before: end-override-readonly + :emphasize-lines: 5-7 + +Additional Information +---------------------- + +To learn more about specifying query filters, see the +:ref:`mongoid-data-specify-query` guide. + +To learn more about setting validation rules on your models, see the +:ref:`mongoid-modeling-validation` guide. + +To learn more about defining callbacks, see the +:ref:`mongoid-modeling-callbacks` guide. diff --git a/source/interact-data/modify-results.txt b/source/interact-data/modify-results.txt new file mode 100644 index 00000000..f4a7af78 --- /dev/null +++ b/source/interact-data/modify-results.txt @@ -0,0 +1,316 @@ +.. _mongoid-data-modify-results: + +==================== +Modify Query Results +==================== + +.. facet:: + :name: genre + :values: reference + +.. meta:: + :keywords: ruby framework, odm, crud, print results, code example + +.. contents:: On this page + :local: + :backlinks: none + :depth: 2 + :class: singlecol + +Overview +-------- + +In this guide, you can learn how to customize the way that {+odm+} +returns results from queries. MongoDB allows you to perform the +following actions to modify the way that results appear: + +- :ref:`mongoid-data-projection` + +- :ref:`mongoid-data-sort` + +- :ref:`mongoid-data-skip-limit` + +Sample Data +~~~~~~~~~~~ + +The examples in this guide use the ``Band`` model, which represents a +band or musical group. The definition of the ``Band`` model might be +different for each section to demonstrate different query +functionalities. Some sections also use the ``Manager`` model, +which represents a person who manages a given band, or the ``Tour`` +model, which represents live performances by a given band. + +.. _mongoid-data-projection: + +Return Specified Fields +----------------------- + +In MongoDB, *projection* is the process of specifying fields to include +or exclude from results. {+odm+} provides the following operators +to project fields: + +- ``only``: Specifies fields to include +- ``without``: Specifies fields to exclude + +Include Fields +~~~~~~~~~~~~~~ + +The ``only`` method retrieves only the specified fields from the +database. + +The following code returns only the ``name`` field from documents in +which the value of the ``members`` field is ``4``: + +.. literalinclude:: /includes/interact-data/modify-results.rb + :start-after: start-only + :end-before: end-only + :language: ruby + :dedent: + +.. note:: _id Field + + In MongoDB, the ``_id`` field is included in results even if you do + not explicitly include it. + +If you attempt to reference attributes that have not been loaded, +{+odm+} raises a ``Mongoid::Errors::AttributeNotLoaded`` error. + +You can also use the ``only`` method to include fields from embedded +documents. + +Consider that the ``Band`` model embeds multiple ``Tour`` objects. You can +project fields from the ``Tour`` model such as ``year``, as shown in the +following code: + +.. literalinclude:: /includes/interact-data/modify-results.rb + :start-after: start-only-embed + :end-before: end-only-embed + :language: ruby + :dedent: + +Then, you can access the embedded fields from the returned documents: + +.. code-block:: ruby + + # Returns the first Tour object from + # the first Band in the results + bands.first.tours.first + +You can pass fields of referenced associations to the ``only`` method, +but the projection is ignored when loading the embedded objects. {+odm+} +loads all fields of the referenced associations. For example, when you +access the embedded ``Tour`` object as shown in the preceding code, +{+odm+} returns the complete object, not just the ``year`` field. + +.. note:: + + If you are connected to a deployment running MongoDB 4.4 or later, + you cannot specify an association and its fields in a projection in + the same query. + +If a document contains ``has_one`` or ``has_and_belongs_to_many`` +associations, and you want {+odm+} to load those associations when +you call the ``only`` method, you must include the fields with foreign +keys in the list of attributes. + +In the following example, the ``Band`` and ``Manager`` models have a +``has_and_belongs_to_many`` association: + +.. code-block:: ruby + + class Band + include Mongoid::Document + field :name, type: String + has_and_belongs_to_many :managers + end + + class Manager + include Mongoid::Document + has_and_belongs_to_many :bands + end + +The following code demonstrates how {+odm+} can load the associated +``Manager`` objects if you include the ``manager_ids`` field: + +.. literalinclude:: /includes/interact-data/modify-results.rb + :start-after: start-only-embed-association + :end-before: end-only-embed-association + :language: ruby + :dedent: + +Exclude Fields +~~~~~~~~~~~~~~ + +You can explicitly exclude fields from results by using the +``without`` method. + +The following code excludes the ``year`` field from returned ``Band`` +objects: + +.. literalinclude:: /includes/interact-data/modify-results.rb + :start-after: start-without + :end-before: end-without + :language: ruby + :dedent: + +.. important:: _id Field + + {+odm+} requires the ``_id`` field for various operations, so you + *cannot* exclude the ``_id`` field or the ``id`` alias from results. + If you pass ``_id`` or ``id`` to the ``without`` method, {+odm+} + ignores it. + +.. _mongoid-data-sort: + +Sort Results +------------ + +You can specify the order in which {+odm+} returns documents by using the +``order`` and ``order_by`` methods. + +These methods accept a hash that indicates which fields to order the +documents by, and whether to use an ascending or descending order for +each field. + +You can specify the sort direction by using integers, symbols, or +strings. We recommend using the same sorting syntax throughout your +application for consistency. The following list provides each syntax and +shows how to sort on the ``name`` and ``year`` fields: + +- Integers ``1`` (ascending) and ``-1`` (descending) + + - Example: ``Band.order(name: 1, year: -1)`` + +- Symbols ``:asc`` and ``:desc`` + + - Example: ``Band.order(name: :asc, year: :desc)`` + +- Strings ``"asc"`` and ``"desc"`` + + - Example: ``Band.order_by(name: "asc", year: "desc")`` + +The ``order`` method also accepts the following sort specifications: + +- Array of two-element arrays: + + - Strings + + - Example: ``Band.order([['name', 'asc'], ['year', 'desc']])`` + + - Symbols + + - Example: ``Band.order([[:name, :asc], [:year, :desc]])`` + +- ``asc`` and ``desc`` methods on symbols + + - Example: ``Band.order(:name.asc, :year.desc)`` + +- SQL syntax + + - Example: ``Band.order('name asc', 'year desc')`` + +.. tip:: + + Instead of using ``order`` or ``order_by``, you can also use the + ``asc`` and ``desc`` methods to specify sort orders: + + .. code-block:: ruby + + Band.asc('name').desc('year') + +When you chain sort specifications, the first call defines the first +sorting order and the newest call defines the last sorting order after +the previous sorts have been applied. + +.. note:: Sorting in Scopes + + If you define a :ref:`default scope ` on your + model that includes a sort specification, the scope sort takes precedence + over the sort specified in a query, because the default scope is + evaluated first. + +.. _mongoid-data-skip-limit: + +Paginate Results +---------------- + +{+odm+} provides the ``limit``, ``skip``, and ``batch_size`` +pagination methods that you can use on ``Criteria`` objects. The +following sections describe how to use these operators. + +Limit Number of Results +~~~~~~~~~~~~~~~~~~~~~~~ + +You can use the ``limit`` method to limit the number of results that +{+odm+} returns. + +The following code retrieves a maximum of ``5`` documents: + +.. literalinclude:: /includes/interact-data/modify-results.rb + :start-after: start-limit + :end-before: end-limit + :language: ruby + :dedent: + +.. note:: + + Alternatively, you can use the ``take`` method to retrieve a + specified number of documents from the database: + + .. code-block:: ruby + + Band.take(5) + +Skip Results +~~~~~~~~~~~~ + +You can skip a specified number of results by using the ``skip`` +method, or its alias ``offset``. + +If you chain a ``limit`` call to ``skip``, the limit is applied +after documents are skipped, as demonstrated in the following example: + +.. literalinclude:: /includes/interact-data/modify-results.rb + :start-after: start-skip-limit + :end-before: end-skip-limit + :language: ruby + :dedent: + +.. tip:: + + When performing pagination, use ``skip`` on :ref:`sorted results ` + to ensure consistent results. + +The following code skips the first ``3`` documents when returning results: + +.. literalinclude:: /includes/interact-data/modify-results.rb + :start-after: start-skip + :end-before: end-skip + :language: ruby + :dedent: + +Generate Batches of Results +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +When executing large queries and when iterating over query results by using +an enumerator method such as ``Criteria#each``, {+odm+} automatically +uses the MongoDB :manual:`getMore ` command +to load results in batches. The default batch size is ``1000``, but +you can set a different value by using the ``batch_size`` method. + +The following code sets the batch size to ``500``: + +.. literalinclude:: /includes/interact-data/modify-results.rb + :start-after: start-batch + :end-before: end-batch + :language: ruby + :dedent: + +Additional Information +---------------------- + +To learn more about constructing queries, see the +:ref:`mongoid-data-specify-query` guide. + +To learn about {+odm+} data modeling, see the +:ref:`mongoid-data-modeling` guides. diff --git a/source/interact-data/nested-attributes.txt b/source/interact-data/nested-attributes.txt new file mode 100644 index 00000000..e7bc673a --- /dev/null +++ b/source/interact-data/nested-attributes.txt @@ -0,0 +1,194 @@ +.. _mongoid-data-nested-attr: + +================= +Nested Attributes +================= + +.. facet:: + :name: genre + :values: reference + +.. meta:: + :keywords: ruby framework, odm, embeddings, code example, queries + +.. contents:: On this page + :local: + :backlinks: none + :depth: 2 + :class: singlecol + +Overview +-------- + +In this guide, you can learn how to define **nested attributes** on +models to enable data operations on documents and their associations. +After you define a nested attribute, you can specify updates to +top-level and associated documents in a single parameter hash. This might be +useful if your application requires editing multiple documents within a single +form. + +Behavior +-------- + +You can enable nested attributes for any association, embedded or +referenced. To add a nested attribute for an association, provide the +association name to the ``accepts_nested_attributes_for`` macro when +defining a model class. + +The following code defines embedded associations on the ``Band`` model +class and includes the ``accepts_nested_attributes_for`` macro: + +.. literalinclude:: /includes/data-modeling/nested_attr.rb + :start-after: start-simple-nested + :end-before: end-simple-nested + :language: ruby + :emphasize-lines: 5 + :dedent: + +.. note:: Autosave Enabled + + When you add nested attribute functionality to a referenced + association, {+odm+} automatically enables autosave for that + association. + +When you enable nested attributes behavior on an association, {+odm+} +adds a special method to the base model. You can use this method to +update the attributes. + +The method name is the association name suffixed with ``_attributes``. For +example, the setter method to update the ``producers`` association is +``producer_attributes``. + +You can use this method directly, or you can use the name of the method +as an attribute in the updates for the top-level class. In this case, +{+odm+} calls the appropriate setter method internally. + +The following code retrieves an instance of ``Band``, then uses the +nested attribute update method ``producer_attributes`` to set a value +for the association document: + +.. literalinclude:: /includes/data-modeling/nested_attr.rb + :start-after: start-use-method + :end-before: end-use-method + :language: ruby + :emphasize-lines: 4 + :dedent: + +There are multiple ways to update a nested attribute: + +- Use the ``_attributes`` setter method. +- Use the ``attributes`` setter method and specify ``_attributes`` in the value to update the associations. +- Use the ``update_attributes`` setter method and specify the attribute + names in the value to update the associations. +- Use the ``update`` method and specify ``_attributes`` in the value to update the associations. +- Use the ``create`` method and specify ``_attributes`` in the value to create the associations. + +The following example demonstrates how to create a ``Band`` instance +with associated ``album`` records in a single statement: + +.. literalinclude:: /includes/data-modeling/nested_attr.rb + :start-after: start-create-attr + :end-before: end-create-attr + :language: ruby + :emphasize-lines: 3-5 + :dedent: + +Creating Nested Documents +------------------------- + +You can create new nested documents by using the nested attributes +feature. When creating a document, omit the ``_id`` field. The following +code uses the ``update`` method to create a nested ``album`` document +on an existing ``Band`` instance: + +.. literalinclude:: /includes/data-modeling/nested_attr.rb + :start-after: start-update-create + :end-before: end-update-create + :language: ruby + :dedent: + +This action appends the new document to the existing set without changing +any existing nested documents. + +Updating Nested Documents +------------------------- + +You can update existing nested documents by using the nested attributes +feature. To instruct {+odm+} to update a nested document by using +attributes, pass the document's ``_id`` value to the ``update`` +method. The following example uses the ``_id`` value of an ``albums`` +entry to update the ``year`` field: + +.. literalinclude:: /includes/data-modeling/nested_attr.rb + :start-after: start-update-id + :end-before: end-update-id + :language: ruby + :dedent: + +.. important:: No Matching Document + + If {+odm+} does not match a document that has the specified ``_id`` + value, it raises a ``Mongoid::Errors::DocumentNotFound`` exception. + +Delete Nested Documents +----------------------- + +You can delete nested documents by specifying the ``_destroy`` +attribute to the ``update`` method. To enable deletion of nested +document, you must set ``allow_destroy: true`` in the +``accepts_nested_attributes_for`` declaration, as shown in the following +code: + +.. code-block:: ruby + :emphasize-lines: 3 + + class Band + # ... + accepts_nested_attributes_for :albums, allow_destroy: true + end + +The following code uses the ``_destroy`` attribute to delete the first +``albums`` entry of a ``Band`` instance: + +.. literalinclude:: /includes/data-modeling/nested_attr.rb + :start-after: start-delete-id + :end-before: end-delete-id + :language: ruby + :emphasize-lines: 6 + :dedent: + +.. important:: No Matching Document + + If {+odm+} does not match a document that has the specified ``_id`` + value, it raises a ``Mongoid::Errors::DocumentNotFound`` exception. + +Combine Operations on Nested Documents +-------------------------------------- + +You can perform multiple data operations on nested documents by using +the nested attributes feature. + +The following code creates a nested document, updates an existing +document, and deletes a document in the ``albums`` array of a ``Band`` +instance: + +.. literalinclude:: /includes/data-modeling/nested_attr.rb + :start-after: start-multiple-ops + :end-before: end-multiple-ops + :language: ruby + :dedent: + +Additional Information +---------------------- + +To learn more about querying, see the :ref:`mongoid-data-specify-query` +guide. + +To learn more about performing CRUD operations, see the +:ref:`mongoid-data-crud` guide. + +To learn more about associations, see the :ref:`mongoid-associations` +guide. diff --git a/source/interact-data/query-async.txt b/source/interact-data/query-async.txt new file mode 100644 index 00000000..0891b599 --- /dev/null +++ b/source/interact-data/query-async.txt @@ -0,0 +1,94 @@ +.. _mongoid-query-async: + +==================== +Asynchronous Queries +==================== + +.. facet:: + :name: genre + :values: reference + +.. meta:: + :keywords: ruby framework, odm, memory, background tasks, execution + +.. contents:: On this page + :local: + :backlinks: none + :depth: 2 + :class: singlecol + +Overview +-------- + +In this guide, you can learn how to perform asynchronous queries in +{+odm+}. You can run database queries asynchronously in the background, +which can be beneficial if your application retrieves documents from +multiple collections. + +Run Async Queries +----------------- + +To schedule an asynchronous query, call the ``load_async`` method on +a ``Criteria`` instance, as shown in the following code: + +.. code-block:: ruby + + @active_bands = Band.where(active: true).load_async + @public_articles = Article.where(public: true).load_async + +The preceding code schedules the queries for asynchronous execution. +You can then access the results of the queries in your view as you +normally do for synchronous queries. + +Even if you schedule a query for asynchronous execution, it might be +executed synchronously on the caller's thread. The following list +describes possible scenarios in which this situation might occur: + +- If {+odm+} completes the scheduled asynchronous task, it returns + the results. + +- If {+odm+} starts but does not complete the task, the caller's + thread blocks until {+odm+} finishes the task. + +- If {+odm+} has not started a task yet, it is removed from the + execution queue, and {+odm+} runs the query synchronously on the + caller's thread. + +.. note:: + + Even though the ``load_async`` method returns a ``Criteria`` object, + do not perform any operations on this object other than accessing query results. + {+odm+} schedules the query for execution immediately after calling + ``load_async``, so later changes to the ``Criteria`` object might not + be applied. + +Configure Query Performance +--------------------------- + +Asynchronous queries are disabled by default. When asynchronous queries +are disabled, the ``load_async`` method performs the query immediately +on the current thread, blocking as required. Therefore, calling +``load_async`` on a ``Criteria`` instance in this situation is similar +to calling the ``to_a`` method to force query execution. + +To enable asynchronous query execution, you must set the following +configuration options: + +.. code-block:: xml + + development: + ... + options: + # Execute asynchronous queries using a global thread pool. + async_query_executor: :global_thread_pool + # Number of threads in the pool. The default is 4. + # global_executor_concurrency: 4 + +Additional Information +---------------------- + +To learn more about configuring {+odm+} in your application, see the +:ref:`mongoid-app-config` guide. + +To learn more about performing CRUD operations, see the +:ref:`mongoid-data-crud` guide. diff --git a/source/interact-data/query-cache.txt b/source/interact-data/query-cache.txt new file mode 100644 index 00000000..d1cefd31 --- /dev/null +++ b/source/interact-data/query-cache.txt @@ -0,0 +1,111 @@ +.. _mongoid-query-cache: + +=========== +Query Cache +=========== + +.. facet:: + :name: genre + :values: reference + +.. meta:: + :keywords: ruby framework, odm, memory, storage, execution + +.. contents:: On this page + :local: + :backlinks: none + :depth: 2 + :class: singlecol + +Overview +-------- + +In this guide, you can learn about **query caching**. The +query cache saves the results of previous find and aggregation queries +and reuses them in the future. This prevents {+odm+} from performing +the queries again, increasing application performance and reducing +the database load. + +To learn more about this feature, see :ruby:`Query Cache +` in the {+ruby-driver+} documentation. + +Enable Query Caching +-------------------- + +In this section, you can learn how to enable the query caching feature +in your application. You can enable the query cache by using the +driver's namespace or {+odm+}'s namespace. + +Automatic +~~~~~~~~~ + +The {+ruby-driver+} provides middleware to automatically enable the +query cache for Rack web requests and Active Job job runs. To view +instructions on automatically enabling the query cache, see the +:ref:`mongoid-query-cache-rack` section of the Query Cache Middleware +Configuration guide. + +.. note:: + + Query cache middleware does not apply to code run outside web + requests or jobs. + +Manual +~~~~~~ + +To enable the query cache manually for a specific code segment, you can +run your code within the following block: + +.. code-block:: ruby + + Mongo::QueryCache.cache do + # Include code here ... + end + +You can explicitly enable and disable the query cache, but we recommend +using the block form in the preceding code example. The following code +demonstrates how to enable and disable the query cache: + +.. code-block:: ruby + + begin + Mongo::QueryCache.enabled = true + # Include code here + ensure + Mongo::QueryCache.enabled = false + end + +Cache the Result of the first Method +------------------------------------ + +Calling the ``first`` method on a model class uses an ascending sort on +the ``_id`` field when returning the result. This might produce unexpected +behavior if you enable query caching. + +For example, if you call the ``all`` method on a model class before +calling ``first``, you might expect the ``first`` method to use the +cached results from ``all``. However, because {+odm+} applies a sort +to the second call, both methods query the database and separately cache +results. + +To use the cached results when calling the ``first`` method, call +``all.to_a.first`` on the model class, as shown in the following example +code: + +.. code-block:: + + Band.all.to_a.first + +In the preceding example, chaining the ``to_a`` method runs the query +and converts the results into an array in memory. Then, the ``first`` +method simply returns the first array entry instead of triggering +another query and caching the results. + +Additional Information +---------------------- + +To learn more about creating filter criteria, see the +:ref:`mongoid-data-specify-query` guide. + +To learn how to customize your persistence target, see the +:ref:`mongoid-persistence` guide. diff --git a/source/interact-data/query-persistence.txt b/source/interact-data/query-persistence.txt new file mode 100644 index 00000000..2578f7a2 --- /dev/null +++ b/source/interact-data/query-persistence.txt @@ -0,0 +1,143 @@ +.. _mongoid-query-persistence: + +========================= +Persist Data from Queries +========================= + +.. facet:: + :name: genre + :values: reference + +.. meta:: + :keywords: ruby framework, odm, crud, filter, code example + +.. contents:: On this page + :local: + :backlinks: none + :depth: 2 + :class: singlecol + +Overview +-------- + +In this guide, you can learn how how to persist data off of your queries +in {+odm+}. {+odm+} supports persistence operations off of criteria in a +limited capacity, allowing you to to expressively perform multi-document +insert, update, and delete operations. + +To learn more about creating filter criteria, see the +:ref:`mongoid-data-specify-query` guide. + +To learn more about performing CRUD operations, see the +:ref:`mongoid-data-crud` guide. + +Persistence Methods +------------------- + +This section describes methods that you can chain to your queries to +create, update, and delete data in your MongoDB collections. + +Create a Document +~~~~~~~~~~~~~~~~~ + +You can use the following methods to create new documents from your +query criteria: + +- ``create``: Saves a model instance to MongoDB + + - Example: ``Band.where(name: 'Daft Punk').create`` + +- ``create!``: Saves a model instance to MongoDB or raises an exception + if a validation error occurs + + - Example: ``Band.where(name: 'Daft Punk').create!`` + +- ``build``: Creates an unsaved model instance + + - Example: ``Band.where(name: 'Daft Punk').build`` + +- ``new``: Creates an unsaved model instance + + - Example: ``Band.where(name: 'Daft Punk').new`` + +Update Documents +~~~~~~~~~~~~~~~~ + +You can use the following methods to update documents based on your +query criteria: + +- ``update``: Updates attributes of the first matching document + + - Example: ``Band.where(name: 'Sundown').update(label: 'ABC Records')`` + +- ``update_all``: Updates attributes of all matching documents + + - Example: ``Band.where(country: 'Canada').update_all(label: 'ABC Records')`` + +- ``add_to_set``: Adds a value to a specified array in all matching documents + + - Example: ``Band.where(name: 'Sun Down').add_to_set(label: 'ABC Records')`` + +- ``bit``: Performs a bitwise update of a field + + - Example: ``Band.where(name: 'Sun Down').bit(likes: { and: 14, or: 4 })`` + +- ``inc``: Increments the value of a field + + - Example: ``Band.where(name: 'Sun Down').inc(likes: 14)`` + +- ``pop``: Removes the first or last element of an array field + + - Example: ``Band.where(name: 'Sun Down').pop(members: -1)`` + +- ``pull``: Removes all instances of a value or values that match a + specified condition from an array field + + - Example: ``Band.where(name: 'Sun Down').pull(members: 'Jonah Larsen')`` + +- ``pull_all``: Removes all instances of the specified values from an array field + + - Example: ``Band.where(name: 'Sun Down').pull_all(:members, [ 'Jonah Larsen', 'Dan Jones' ])`` + +- ``push``: Appends a specified value to an array field + + - Example: ``Band.where(name: 'Sun Down').push(members: 'Jonah Larsen')`` + +- ``push_all``: Appends a specified value by using the ``$each`` + operator in an array field + + - Example: ``Band.where(name: 'Sun Down').push_all(members: [ 'Jonah Larsen', 'Dan Jones' ])`` + +- ``rename``: Renames a field in all matching documents + + - Example: ``Band.where(name: 'Sun Down').rename(name: :title)`` + +- ``set``: Sets a new value for a specified field in all matching + documents + + - Example: ``Band.where(name: 'Sun Down').set(likes: 10000)`` + +- ``unset``: Deletes a particular field in all matching documents + + - Example: ``Band.where(name: 'Sun Down').unset(:likes)`` + +Delete Documents +~~~~~~~~~~~~~~~~ + +You can use the following methods to delete documents based on your +query criteria: + +- ``delete``: Deletes all matching documents. + + - Example: ``Band.where(label: 'ABC Records').delete`` + +- ``destroy``: Deletes all matching documents while + running callbacks. This method loads all documents into memory. + + - Example: ``Band.where(label: 'ABC Records').destroy`` + +Additional Information +---------------------- + +To learn how to customize your persistence target, see the +:ref:`mongoid-persistence` guide. diff --git a/source/interact-data/scoping.txt b/source/interact-data/scoping.txt new file mode 100644 index 00000000..b16c1fb6 --- /dev/null +++ b/source/interact-data/scoping.txt @@ -0,0 +1,267 @@ +.. _mongoid-data-scoping: + +======= +Scoping +======= + +.. facet:: + :name: genre + :values: reference + +.. meta:: + :keywords: ruby framework, odm, crud, filter, code example + +.. contents:: On this page + :local: + :backlinks: none + :depth: 2 + :class: singlecol + +Overview +-------- + +In this guide, you can learn how to implement **scopes** into your +{+odm+} models. Scopes provide a convenient way to reuse common filter +criteria. To learn more about creating filter criteria, see the +:ref:`mongoid-data-specify-query` guide. + +You might implement scopes into your application to reduce repeated code +if you are applying the same criteria to most queries. + +Named Scopes +------------ + +Named scopes are criteria defined at class load that are referenced by a +provided name. Similar to filter criteria, they are lazily loaded and +chainable. + +This example defines a ``Band`` model that includes the following named +scopes: + +- ``japanese``: Matches documents in which the value of the ``country`` + field is ``"Japan"`` + +- ``rock``: Matches documents in which the value of the ``genre`` + field includes ``"rock"`` + +.. literalinclude:: /includes/interact-data/scoping.rb + :start-after: start-named-scope-1 + :end-before: end-named-scope-1 + :language: ruby + :dedent: + :emphasize-lines: 7-8 + +Then, you can query by using the named scopes. The following query uses +the named scopes to match documents in which value of the ``country`` +field is ``"Japan"`` and value of the ``genre`` field includes +``"rock"``: + +.. literalinclude:: /includes/interact-data/scoping.rb + :start-after: start-query-named-scope + :end-before: end-query-named-scope + :language: ruby + :dedent: + +Advanced Scoping +~~~~~~~~~~~~~~~~ + +You can define ``Proc`` objects and blocks in named scopes so that they +accept parameters and extend functionality. + +This example defines a ``Band`` model that includes the ``based_in`` scope, +which matches documents in which the ``country`` field value +is the specified value passed as a parameter: + +.. literalinclude:: /includes/interact-data/scoping.rb + :start-after: start-named-scope-2 + :end-before: end-named-scope-2 + :language: ruby + :emphasize-lines: 7 + :dedent: + +Then, you can query by using the ``based_in`` scope, as shown in the following +code: + +.. literalinclude:: /includes/interact-data/scoping.rb + :start-after: start-query-named-scope-2 + :end-before: end-query-named-scope-2 + :language: ruby + :dedent: + +{+odm+} allows you to define a scope that shadows an existing class +method, as shown in the following example: + +.. literalinclude:: /includes/interact-data/scoping.rb + :start-after: start-named-scope-3 + :end-before: end-named-scope-3 + :language: ruby + :dedent: + +You can direct {+odm+} to raise an error when a scope overwrites an +existing class method by setting the ``scope_overwrite_exception`` +configuration option to ``true``. + +To learn more about this setting, see the :ref:`mongoid-app-config` +guide. + +Default Scopes +-------------- + +Default scopes are useful for cases where you apply the same +criteria to most queries. By defining a default scope, you specify these +criteria as the default for any queries that use the model. Default +scopes return ``Criteria`` objects. + +To create a default scope, you must define the ``default_scope`` method +on your model class. + +The following code defines the ``default_scope`` method on the ``Band`` +model to only retrieve documents in which the ``active`` field value is ``true``: + +.. literalinclude:: /includes/interact-data/scoping.rb + :start-after: start-default-scope-1 + :end-before: end-default-scope-1 + :language: ruby + :dedent: + :emphasize-lines: 7 + +Then, any queries on the ``Band`` model pre-filter for documents in which the +``active`` value is ``true``. + +Field Initialization +~~~~~~~~~~~~~~~~~~~~ + +Specifying a default scope initializes the fields of new models to +the values given in the default scope if those values are literals, such +as boolean values or integers. + +.. note:: Field and Scope Conflicts + + If you provide a default value in a field definition and in the + default scope, the value in the default scope takes precedence, as + shown in the following example: + + .. literalinclude:: /includes/interact-data/scoping.rb + :start-after: start-default-scope-2 + :end-before: end-default-scope-2 + :language: ruby + :dedent: + :emphasize-lines: 5, 7 + +We do not recommend using dot notation to reference nested fields in default +scopes. This can direct {+odm+} to initialize unexpected fields in new +models. + +For example, if you define a default scope that references the +``tour.year`` field, a new model is initialized with the field +``tour.year`` instead of a ``tour`` field with a nested object that +contains a ``year`` field. + +When *querying*, {+odm+} interprets the dot notation correctly and matches +documents in which a nested field has the specified value. + +Associations +~~~~~~~~~~~~ + +If you define a default scope on a model that is part of an +association, you must reload the association to have scoping reapplied. +This is necessary for when you change a value of a document in the +association that affects its visibility when the scope is applied. + +This example uses the following models: + +.. literalinclude:: /includes/interact-data/scoping.rb + :start-after: start-scope-association + :end-before: end-scope-association + :language: ruby + :dedent: + +Suppose you create a ``Label`` model that contains an association to a +``Band`` in which the value of ``active`` is ``true``. When you update +the ``active`` field to ``false``, {+odm+} still loads it despite the +default scope. To view the documents in the association with the scope +applied, you must call the ``reload`` operator. + +The following code demonstrates this sequence: + +.. literalinclude:: /includes/interact-data/scoping.rb + :start-after: start-scope-association-steps + :end-before: end-scope-association-steps + :language: ruby + :dedent: + :emphasize-lines: 5, 7 + +or and nor Query Behavior +~~~~~~~~~~~~~~~~~~~~~~~~~ + +{+odm+} treats the criteria in a default scope the same way as any other +query conditions. This can lead to surprising behavior when using the +``or`` and ``nor`` methods. + +The following examples demonstrate how {+odm+} interprets queries on +models with a default scope: + +.. literalinclude:: /includes/interact-data/scoping.rb + :start-after: start-scope-query-behavior + :end-before: end-scope-query-behavior + :language: ruby + :dedent: + +To learn more about logical operations, see +:ref:`mongoid-query-logical-operations` in the Specify a Query guide. + +Disable Scope When Querying +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +You can direct {+odm+} to not apply the default scope by using the +``unscoped`` operator, as shown in the following examples: + +.. literalinclude:: /includes/interact-data/scoping.rb + :start-after: start-unscoped + :end-before: end-unscoped + :language: ruby + :dedent: + +Override Default Scope at Runtime +--------------------------------- + +You can use the ``with_scope`` method to change the default scope in a +block at runtime. + +The following model defines the *named* scope ``mexican``: + +.. literalinclude:: /includes/interact-data/scoping.rb + :start-after: start-override-scope + :end-before: end-override-scope + :language: ruby + :dedent: + :emphasize-lines: 7 + +You can use the ``with_scope`` method to set the ``mexican`` named +scope as the default scope at runtime, as shown in the following code: + +.. literalinclude:: /includes/interact-data/scoping.rb + :start-after: start-override-scope-block + :end-before: end-override-scope-block + :language: ruby + :dedent: + +Class Methods +------------- + +{+odm+} treats class methods that return ``Criteria`` objects +as scopes. You can query by using these class methods, as shown in +the following example: + +.. literalinclude:: /includes/interact-data/scoping.rb + :start-after: start-class-methods + :end-before: end-class-methods + :language: ruby + :dedent: + :emphasize-lines: 7-9, 12 + +Additional Information +---------------------- + +To learn more about customizing your {+odm+} models, see the +:ref:`mongoid-data-modeling` guides. diff --git a/source/interact-data/specify-query.txt b/source/interact-data/specify-query.txt new file mode 100644 index 00000000..6b77b622 --- /dev/null +++ b/source/interact-data/specify-query.txt @@ -0,0 +1,924 @@ +.. _mongoid-data-specify-query: + +=============== +Specify a Query +=============== + +.. facet:: + :name: genre + :values: reference + +.. meta:: + :keywords: ruby framework, odm, crud, filter, code example + +.. toctree:: + :caption: Queries + + /interact-data/scoping + /interact-data/query-persistence + /interact-data/query-cache + /interact-data/query-async + +.. contents:: On this page + :local: + :backlinks: none + :depth: 2 + :class: singlecol + +Overview +-------- + +In this guide, you can learn how to specify a **query** by using {+odm+}. + +You can refine the set of documents that a query returns by creating a +**query filter**. A query filter is an expression that specifies the search +criteria MongoDB uses to match documents in a read or write operation. +When creating a query filter, you can prompt the driver to search for +documents with an exact match to your query, or you can compose query +filters to express more complex matching criteria. + +{+odm+} provides a query domain-specific language (DSL) similar to the +one used in Active Record. + +Sample Data +~~~~~~~~~~~ + +The examples in this guide use the ``Band`` model, which represents a +band or musical group. The definition of the ``Band`` model might be +different for each section to demonstrate different query +functionalities. Some sections might use other models +to demonstrate query functionality. + +Queries in {+odm+} +------------------ + +{+odm+} query methods return ``Mongoid::Criteria`` objects, which are +chainable and lazily evaluated wrappers for the MongoDB Query API. +The queries are executed when you iterate through the results. The +following example demonstrates the return type for a simple query: + +.. code-block:: ruby + + # Creates a simple query + Band.where(name: "Deftones") + + # Returns a Criteria object + # => #"Deftones"} + # options: {} + # class: Band + # embedded: false> + + # Evaluate the query by converting to JSON + Band.where(name: "Deftones").to_json + + # Returns matching documents + # => [{"_id":"...","name":"Deftones"}] + +You can use methods such as ``first`` and ``last`` to return +individual documents. You can also iterate a ``Criteria`` object by using +methods such as ``each`` or ``map`` to retrieve documents from the +server. You can use ``to_json`` to convert a ``Criteria`` object to +JSON. + +.. tip:: Chaining methods + + If you chain other query methods on an existing ``Criteria`` object, + {+odm+} merges the filter criteria. + +Create a Query Filter +--------------------- + +This section describes the syntax patterns that you can use to create +filter criteria. You can specify queries in {+odm+} by using any of the +following syntax patterns: + +- Field syntax +- Query API syntax +- Symbol operator syntax + +.. note:: Syntax Behaviors + + These syntaxes support querying embedded documents by using dot notation. + The syntaxes also respect :ref:`field aliases ` + and field types, if the field being queried is defined in the model class. + +The examples in this section use the following model definition: + +.. code-block:: ruby + + class Band + include Mongoid::Document + + field :name, type: String + field :founded, type: Integer + field :m, as: :member_count, type: Integer + + embeds_one :manager + end + + class Manager + include Mongoid::Document + + embedded_in :band + + field :name, type: String + end + +Field Syntax +~~~~~~~~~~~~ + +The field querying syntax uses the basic {+language+} hashes. The keys +can be symbols or strings and correspond to field names in MongoDB +documents. + +The following code shows two equivalent queries that use field querying +syntax to retrieve documents in which the ``name`` field value is +``'Depeche Mode'``: + +.. literalinclude:: /includes/interact-data/query.rb + :start-after: start-simple-field-query + :end-before: end-simple-field-query + :language: ruby + :dedent: + +Query API Syntax +~~~~~~~~~~~~~~~~ + +You can specify a Query API operator on any field by using the hash +syntax, as shown by the following equivalent queries: + +.. literalinclude:: /includes/interact-data/query.rb + :start-after: start-query-api-query + :end-before: end-query-api-query + :language: ruby + :dedent: + +Symbol Operator Syntax +~~~~~~~~~~~~~~~~~~~~~~ + +You can specify Query API operators as methods on symbols for the +respective field name, as shown in the following code: + +.. literalinclude:: /includes/interact-data/query.rb + :start-after: start-symbol-query + :end-before: end-symbol-query + :language: ruby + :dedent: + +Query on Different Field Types +------------------------------ + +This section describes how to perform queries on fields with different +types of values. + +Defined Fields +~~~~~~~~~~~~~~ + +To query on a field, the field does not need to be in the +the model class definition. However, if a field is defined in +the model class, {+odm+} coerces query values to match the defined field +types when constructing the query. + +The following code specifies a string value when querying on the +``founded`` field. Because the ``founded`` field is defined in the model +class to have ``Integer`` values, {+odm+} coerces the string ``'2020'`` +to ``2020`` when performing the query: + +.. literalinclude:: /includes/interact-data/query.rb + :start-after: start-defined-field-query + :end-before: end-defined-field-query + :language: ruby + :dedent: + +To learn more about defining fields in {+odm+}, see the +:ref:`mongoid-field-types` guide. + +Raw Values +~~~~~~~~~~ + +To bypass {+odm+}'s query type coercion behavior and query +directly for the raw-typed value in the database, wrap the query value in +the ``Mongoid::RawValue`` class, as shown in the following code: + +.. literalinclude:: /includes/interact-data/query.rb + :start-after: start-raw-field-query + :end-before: end-raw-field-query + :language: ruby + :dedent: + +.. _mongoid-query-field-alias: + +Field Aliases +~~~~~~~~~~~~~ + +Queries follow the :ref:`storage field +names ` and :ref:`field aliases +` that you might have set in your +model class definition. + +The ``id`` and ``_id`` fields are aliases, so you can use either field +name in queries: + +.. literalinclude:: /includes/interact-data/query.rb + :start-after: start-id-field-query + :end-before: end-id-field-query + :language: ruby + :dedent: + +Embedded Documents +~~~~~~~~~~~~~~~~~~ + +To query on values of fields of embedded documents, you can use dot +notation. The following code retrieves documents in which the ``name`` +field of the embedded ``Manager`` document is ``'Smith'``: + +.. literalinclude:: /includes/interact-data/query.rb + :start-after: start-embedded-query + :end-before: end-embedded-query + :language: ruby + :dedent: + +The following code demonstrates how to use a symbol operator when +querying on an embedded field: + +.. literalinclude:: /includes/interact-data/query.rb + :start-after: start-embedded-ne-query + :end-before: end-embedded-ne-query + :language: ruby + :dedent: + +.. note:: + + Queries always return top-level model instances, even if all the + conditions reference embedded document fields. + +.. _mongoid-query-logical-operations: + +Logical Operations +------------------ + +{+odm+} supports the following logical operations on ``Criteria`` +objects: + +- ``and`` +- ``or`` +- ``nor`` +- ``not`` + +These methods take one or more hashes of conditions or another +``Criteria`` object as their arguments. The ``not`` operation has an +argument-free version. + +The following code demonstrates how to use the logical operations in +queries: + +.. literalinclude:: /includes/interact-data/query.rb + :start-after: start-logical-ops + :end-before: end-logical-ops + :language: ruby + :dedent: + +.. note:: Array Parameters + + To ensure backwards compatibility with earlier {+odm+} versions, the + logical operation methods accept arrays of parameters, which are + flattened to obtain the criteria. + + Passing arrays to logical operations is deprecated and might be removed + in a future version. + +The following queries produce the same conditions: + +.. literalinclude:: /includes/interact-data/query.rb + :start-after: start-logical-and-ops + :end-before: end-logical-and-ops + :language: ruby + :dedent: + +Operator Combinations +~~~~~~~~~~~~~~~~~~~~~ + +The logical operators have the the same semantics as those from Active +Record. + +When conditions are specified on the same field multiple times, all +conditions are added to the criteria, as shown by the queries in the +following code: + +.. literalinclude:: /includes/interact-data/query.rb + :start-after: start-logical-combination-ops + :end-before: end-logical-combination-ops + :language: ruby + :dedent: + +The ``any_of``, ``none_of``, ``nor``, and ``not`` operations +behave similarly. + +When you use ``and``, ``or``, and ``nor`` logical operators, they +operate on the criteria built up to that point: + +.. literalinclude:: /includes/interact-data/query.rb + :start-after: start-logical-combination-ops-2 + :end-before: end-logical-combination-ops-2 + :language: ruby + :dedent: + +not Behavior +~~~~~~~~~~~~ + +You can use the ``not`` method without arguments, in which case it +negates the next condition that is specified. The ``not`` method can +be called with one or more hash conditions or ``Criteria`` objects, +which are all negated and added to the criteria. + +The following examples demonstrate the behavior of ``not``: + +.. literalinclude:: /includes/interact-data/query.rb + :start-after: start-not-logical + :end-before: end-not-logical + :language: ruby + :dedent: + +.. note:: + + You cannot use the ``$not`` operator in MongoDB with a string argument. + Mongoid uses the ``$ne`` operator to achieve negation: + + .. literalinclude:: /includes/interact-data/query.rb + :start-after: start-not-logical-note + :end-before: end-not-logical-note + :language: ruby + :dedent: + +Similarly to ``and``, the ``not`` operation negates individual +conditions for simple field criteria. For complex conditions and when a +field already has a condition defined on it, {+odm+} emulates ``$not`` +by using an ``{'$and' => [{'$nor' => ...}]}`` construct, because MongoDB +supports the ``$not`` operator only on a per-field basis rather than +globally: + +.. literalinclude:: /includes/interact-data/query.rb + :start-after: start-not-behavior + :end-before: end-not-behavior + :language: ruby + :dedent: + +If you are using ``not`` with arrays or regular expressions, view the +limitations of ``$not`` in the :manual:`{+server-manual+} +`. + +Incremental Query Construction +------------------------------ + +By default, when you add conditions to a query, {+odm+} considers each +condition complete and independent from any other conditions +present in the query. For example, calling ``in`` twice adds two separate +``$in`` conditions: + +.. literalinclude:: /includes/interact-data/query.rb + :start-after: start-incremental-1 + :end-before: end-incremental-1 + :language: ruby + :dedent: + +Some operator methods support building the condition incrementally. When +you add a condition which uses one of the supported operators, {+odm+} +sees if there already is a condition on the same field using the +same operator. If so, the operator expressions are combined according to the +specified *merge strategy*. The following section describes the available merge +strategies. + +.. _mongoid-merge-strategies: + +Merge Strategies +~~~~~~~~~~~~~~~~ + +{+odm+} provides the following merge strategies: + +- **Override**: The new operator instance replaces any existing + conditions on the same field by using the same operator. +- **Intersect**: If there already is a condition using the same operator + on the same field, the values of the existing condition are + intersected with the values of the new condition and the result is + stored as the operator value. +- **Union**: If there already is a condition using the same operator on + the same field, the values of the new condition are added to the + values of the existing condition and the result is stored as the + operator value. + +The following code demonstrates how the merge strategies produce +criteria by using ``in`` as the example operator: + +.. literalinclude:: /includes/interact-data/query.rb + :start-after: start-in-merge + :end-before: end-in-merge + :language: ruby + :dedent: + +The strategy is requested by calling ``override``, ``intersect`` or ``union`` +on a ``Criteria`` instance. The requested strategy applies to the next +condition method called on the query. If the next condition method called does +not support merge strategies, the strategy is reset, as shown in the following +example: + +.. literalinclude:: /includes/interact-data/query.rb + :start-after: start-merge-reset + :end-before: end-merge-reset + :language: ruby + :dedent: + +Because ``ne`` does not support merge strategies, the ``union`` strategy +is ignored and reset. Then, when ``in`` is invoked the second time, +there is no active strategy. + +.. warning:: + + Merge strategies assume that the previous conditions have been added + to the top level of the query. However, this is not always the case, + as conditions might be nested under an ``$and`` clause. Using merge + strategies with complex criteria can generate incorrect queries. + +Supported Operator Methods +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The following operator methods support merge strategies: + +- ``all`` +- ``in`` +- ``nin`` + +The set of methods might be expanded in future releases of {+odm+}. To ensure +future compatibility, invoke a strategy method only when the next method call +is an operator that supports merge strategies. + +Merge strategies are applied only when conditions are +added through the designated methods. In the following example, the +merge strategy is not applied because the second condition is added as +``where``, not by using ``in``: + +.. literalinclude:: /includes/interact-data/query.rb + :start-after: start-merge-where + :end-before: end-merge-where + :language: ruby + :dedent: + +Operator Value Expansion +~~~~~~~~~~~~~~~~~~~~~~~~ + +Operator methods that support merge strategies take ``Array`` as their +value type. {+odm+} expands ``Array``-compatible types, such as a +``Range``, when they are used with these operator methods. + +The following example demonstrates how you can pass a ``Range`` object +as the query value when using the ``in`` method: + +.. literalinclude:: /includes/interact-data/query.rb + :start-after: start-range-query + :end-before: end-range-query + :language: ruby + :dedent: + +{+odm+} wraps non-``Array`` values in arrays, +as the shown in the following example: + +.. code-block:: ruby + + Band.in(year: 1950) + # Interpreted query: {"year"=>{"$in"=>[1950]}} + +Element Match +------------- + +You can use the ``elem_match`` method to match documents that contain +an array field with at least one element that matches all the specified +query criteria. + +The following example creates a sample document that contains an array +field. Then, it uses the ``elem_match`` method to match documents in +which the ``tour`` array field contains an entry in which the ``city`` +value is ``'London'``: + +.. literalinclude:: /includes/interact-data/query.rb + :start-after: start-elem-match-1 + :end-before: end-elem-match-1 + :language: ruby + :dedent: + +Associations +~~~~~~~~~~~~ + +You can use the ``elem_match`` method to match embedded associations. + +This example uses the following models that define an embedded +association between ``Band`` and ``Tour``: + +.. literalinclude:: /includes/interact-data/query.rb + :start-after: start-elemmatch-embedded-class + :end-before: end-elemmatch-embedded-class + :language: ruby + :dedent: + +The following code creates a ``Band`` object and embedded ``Tour`` +objects, then uses the ``elem_match`` method to query on the ``city`` +field: + +.. literalinclude:: /includes/interact-data/query.rb + :start-after: start-elemmatch-embedded-operations + :end-before: end-elemmatch-embedded-operations + :language: ruby + :dedent: + +.. note:: + + You cannot use ``elem_match`` on non-embedded associations because + MongoDB does not perform a join operation on the collections. + If you perform this query, the conditions are added to the collection + that is the source of the non-embedded association rather than the + collection of the association. + +You can use ``elem_match`` to query recursively embedded associations, +as shown in the following example: + +.. literalinclude:: /includes/interact-data/query.rb + :start-after: start-elemmatch-recursive + :end-before: end-elemmatch-recursive + :language: ruby + :dedent: + +To learn more about associations, see the :ref:`mongoid-associations` +guide. + +Querying by _id Value +--------------------- + +{+odm+} provides the ``find`` method, which allows you to query +documents by their ``_id`` values. + +The following example uses the ``find`` method to match a document +with the specified ``_id`` field value: + +.. code-block:: ruby + + Band.find('6725342d4cb3e161059f91d7') + +.. note:: Type Conversion + + When you pass an ID value to the ``find`` method, the method + converts it to the data type declared for the ``_id`` field in the + model. By default, the ``_id`` field is defined as a + ``BSON::ObjectId`` type. + + The preceding example is equivalent to the following code, which + passes an ``BSON::ObjectId`` instance as the argument to ``find``: + + .. code-block:: ruby + + Band.find(BSON::ObjectId.from_string('5f0e41d92c97a64a26aabd10')) + + If you use the {+ruby-driver+} to query on the ``_id`` field, + ``find`` does not internally perform the type conversion. + +The ``find`` method accepts multiple arguments, or an array of arguments. +{+odm+} interprets each argument or array element as an +``_id`` value, and returns documents with all the specified ``_id`` +values in an array, as shown in the following example: + +.. literalinclude:: /includes/interact-data/query.rb + :start-after: start-id-query-multiple + :end-before: end-id-query-multiple + :language: ruby + :dedent: + +The ``find`` method exhibits the following behavior: + +- If you provide the same ``_id`` value more than once, {+odm+} + returns only one document, if one exists. + +- {+odm+} does not return documents in an ordered way. Documents might + be returned in different order from the order of the provided ``_id`` + values. + +- If any of the ``_id`` values are not found in the database, the result + depends on the value of the ``raise_not_found_error`` configuration + option. + + If you set the ``raise_not_found_error`` option to ``true``, + ``find`` raises a ``Mongoid::Errors::DocumentNotFound`` error if any + of the ``_id`` values are not found. + + If you set the ``raise_not_found_error`` option to ``false`` and query + for a single ``_id`` value, ``find`` returns ``nil`` if {+odm+} does + not match a document. If you pass multiple ``_id`` values and + some or all are not matched, the return value is an array of any documents + that match, or an empty array if no documents match. + +find Variations +--------------- + +This section describes methods that are similar to the ``find`` method +described in the preceding section. + +You can use the ``find_by`` method to retrieve documents based on the +provided criteria. If no documents are found, it raises an error or +returns ``nil`` depending on how you set the ``raise_not_found_error`` +configuration option. + +The following code demonstrates how to use the ``find_by`` method: + +.. literalinclude:: /includes/interact-data/query.rb + :start-after: start-query-findby + :end-before: end-query-findby + :language: ruby + :dedent: + +You can use the ``find_or_create_by`` method to retrieve documents +based on the provided criteria. If no documents are found, it creates +and returns an instance that is saved to MongoDB. + +The following code demonstrates how to use the ``find_or_create_by`` +method: + +.. literalinclude:: /includes/interact-data/query.rb + :start-after: start-query-find-or-create + :end-before: end-query-find-or-create + :language: ruby + :dedent: + +You can use the ``find_or_initialize_by`` method to retrieve documents +based on the provided criteria. If no documents are found, it returns a +new one, without persisting it to MongoDB. Use the same syntax for +``find_or_initialize_by`` as you do for the ``find_or_create_by`` +method. + +.. _mongoid-query-regex: + +Regular Expressions +------------------- + +{+odm+} allows you to query documents by using regular expressions in +your filter criteria. + +The following code creates a sample ``Band`` model: + +.. code-block:: ruby + + Band.create!(name: 'Tame Impala', description: "Tame\nImpala is an American band") + +You can perform queries by using {+language+} regular expressions, as +shown in the following code: + +.. code-block:: ruby + + # Matches documents in which the "name" field includes the string "impala" + Band.where(name: /impala/i) + # => Returns sample document + +You can also perform queries by using Perl Compatible Regular Expression +(PCRE) syntax and ``BSON::Regexp::Raw`` objects: + +.. literalinclude:: /includes/interact-data/query.rb + :start-after: start-regex + :end-before: end-regex + :language: ruby + :dedent: + +.. _mongoid-query-field-type-conversions: + +Field Type Query Conversions +---------------------------- + +When you specify a query on a field defined in a model, if the field has +a *specified data type*, {+odm+} converts the query value based on how the +field is defined. + +Consider the following ``Album`` model definition that +contains a ``Date``-valued field, a ``Time``-valued field, and an +implicit ``Object``-valued field. The model also intentionally *does not define* +a field named ``last_reviewed``: + +.. literalinclude:: /includes/interact-data/query.rb + :start-after: start-field-conversion-model + :end-before: end-field-conversion-model + :language: ruby + :dedent: + +You can query on the ``release_date`` and ``last_commented`` fields +by using ``Date`` and ``Time`` values, as shown in the following code: + +.. literalinclude:: /includes/interact-data/query.rb + :start-after: start-date-queries-1 + :end-before: end-date-queries-1 + :language: ruby + :dedent: + +However, if you query by using only ``Date`` values on fields defined +as other types, the generated queries display the default conversion +behavior, as shown in the following example: + +.. literalinclude:: /includes/interact-data/query.rb + :start-after: start-date-queries-2 + :end-before: end-date-queries-2 + :language: ruby + :dedent: + +In the preceding example, the following conversions apply: + +- When using a ``Date`` value to query the ``Time``-valued + ``last_commented`` field, {+odm+} interprets the date to be in local + time and applies the :ref:`configured time zone + `. + +- When querying on the ``last_purchased`` field, which has no explicit + type, the date is used unmodified in the constructed query. + +- When querying on the undefined ``last_reviewed`` field, {+odm+} + interprets the ``Date`` to be in UTC and converts to a time, matching + the behavior of querying a ``Date``-valued field such as + ``release_date``. + +Additional Query Methods +------------------------ + +This section describes more query methods that you can use in {+odm+}. + +Count Documents +~~~~~~~~~~~~~~~ + +You can use the ``count`` and ``estimated_count`` methods to count +the number of documents in a collection. + +You can count the number of documents that match filter criteria by +using the ``count`` method: + +.. code-block:: ruby + + # Counts all documents in collection + Band.count + + # Counts documents that match criteria + Band.where(country: 'England').count + +.. tip:: length and size Methods + + You can also use the ``length`` or ``size`` method to count documents. + These methods cache subsequent calls to the database, which might + produce performance improvements. + +You can get an approximate number of documents in the collection from +the collection metadata by using the ``estimated_count`` method: + +.. code-block:: ruby + + Band.estimated_count + +The ``estimated_count`` method does not accept query conditions, +including conditions set by a :ref:`scope ` on +the model. If you are calling this method on a model that has a +default scope, you must first call the ``unscoped`` method to +disable the scope. + +Ordinal Methods +~~~~~~~~~~~~~~~ + +The methods described in the following list allow you to select a specific +result from the list of returned documents based on its position. + +- ``first``: Returns the first matching document. You can get the + first ``n`` documents by passing an integer-valued parameter. This method + automatically uses a sort on the ``_id`` field. *See lines 1-8 in the + following code for examples.* + +- ``last``: Returns the last matching document. You can get the + last ``n`` documents by passing an integer-valued parameter. This method + automatically uses a sort on the ``_id`` field. *See line 11 in the + following code for an example.* + +- ``first_or_create``: Returns the first matching document. If no + document matches, creates and returns a newly saved one. + +- ``first_or_initialize``: Returns the first matching document. If no + document matches, returns a new one. + +- ``second``: Returns the second matching document. Automatically uses + a sort on the ``_id`` field. + +- ``third``: Returns the third matching document. Automatically uses + a sort on the ``_id`` field. + +- ``fourth``: Returns the fourth matching document. Automatically uses + a sort on the ``_id`` field. + +- ``fifth``: Returns the fifth matching document. Automatically uses + a sort on the ``_id`` field. + +- ``second_to_last``: Returns the second-to-last matching document. + Automatically uses a sort on the ``_id`` field. *See line 14 in the + following code for an example.* + +- ``third_to_last``: Returns the third-to-last matching document. + Automatically uses a sort on the ``_id`` field. + +The following code demonstrates how to use some methods described +in the preceding list: + +.. literalinclude:: /includes/interact-data/query.rb + :start-after: start-ordinal-examples + :end-before: end-ordinal-examples + :language: ruby + :dedent: + :linenos: + +.. tip:: Error Generation + + Each method described in this section has a variation that is + suffixed with ``!`` that returns an error if {+odm+} doesn't match + any documents. For example, to implement error handling in your + application when your query returns no results, use the ``first!`` + method instead of ``first``. + +Survey Field Values +~~~~~~~~~~~~~~~~~~~ + +To inspect the values of specified fields of documents in a +collection, you can use the following methods: + +- ``distinct``: Gets a list of distinct values for a single field. + *See lines 1-7 in the following code for examples.* + +- ``pick``: Gets the values from one document for the provided fields. + Returns ``nil`` for unset fields and for non-existent fields. + *See line 10 in the following code for an example.* + +- ``pluck``: Gets all values for the provided field. Returns ``nil`` + for unset fields and for non-existent fields. + *See line 13 in the following code for an example.* + +- ``tally``: Gets a mapping of values to counts for the specified + field. *See line 16 in the following code for an example.* + +The preceding methods accept field names referenced by using dot +notation, which allows you to reference fields in embedded associations. +They also respect :ref:`field aliases +`, including those defined in embedded +documents. + +The following code demonstrates how to use these methods: + +.. literalinclude:: /includes/interact-data/query.rb + :start-after: start-field-val-examples + :end-before: end-field-val-examples + :language: ruby + :dedent: + :linenos: + +Miscellaneous +~~~~~~~~~~~~~ + +The following list describes {+odm+} methods that do not fit into another +category: + +- ``each``: Iterates over all matching documents. + +.. code-block:: ruby + + # Print each matching document "name" to console + Band.where(:members.gt => 1).each do |band| + p band.name + end + +- ``exists?``: Determines if any matching documents exist, returning + ``true`` if at least one matching document is found. + +.. code-block:: ruby + + # Checks existence of any document + Band.exists? + + # Checks existence based on query + Band.where(name: "Le Tigre").exists? + Band.exists?(name: "Le Tigre") + + # Checks existence based on "_id" value + Band.exists?('6320d96a3282a48cfce9e72c') + + # Always returns false + Band.exists?(false) + Band.exists?(nil) + +Additional Information +---------------------- + +To learn how to modify the way that {+odm+} returns results to you, see +:ref:`mongoid-data-modify-results`. + +To learn more about defining scopes on your models, see +:ref:`mongoid-data-scoping`. + +To learn about methods that you can chain to your queries to persist +data, see :ref:`mongoid-query-persistence`. + +To learn about the query cache feature, see :ref:`mongoid-query-cache`. + +To learn about performing asychronous queries, see +:ref:`mongoid-query-async`. diff --git a/source/interact-data/text-search.txt b/source/interact-data/text-search.txt new file mode 100644 index 00000000..c39e90c1 --- /dev/null +++ b/source/interact-data/text-search.txt @@ -0,0 +1,215 @@ +.. _mongoid-data-text-search: + +=========== +Search Text +=========== + +.. facet:: + :name: genre + :values: reference + +.. meta:: + :keywords: ruby framework, odm, crud, filter, code example + +.. contents:: On this page + :local: + :backlinks: none + :depth: 2 + :class: singlecol + +Overview +-------- + +In this guide, you can learn how to use {+odm+} to run a **text +search**. A text search allows you to efficiently query fields that have +string values. + +MongoDB provides text indexes to support text search queries on +fields that have string values or values that are arrays of string +elements. To learn more about text indexes, see :manual:`Text Indexes on +Self-Managed Deployments ` in the +{+server-manual+}. + +.. note:: Atlas Search + + This guide focuses on text search. If your database is hosted on + MongoDB Atlas, you can use the Atlas Search feature + to perform more powerful and flexible text searches. To learn more + about Atlas Search, see the :atlas:`Atlas Search Overview + ` in the Atlas documentation. + +You can run a text search by performing the following steps: + +1. Define a text index on a model. +#. Create the text index on the target collection. +#. Perform a text search query. + +The following sections describe how to perform each of these actions. + +Define a Text Index on Your Model +--------------------------------- + +Use the ``index`` macro to specify the text index in your model +definition. The following code creates a ``Dish`` model class that +includes a text index on the ``description`` field: + +.. literalinclude:: /includes/interact-data/text-search.rb + :start-after: start-text-index-model + :end-before: end-text-index-model + :language: ruby + :emphasize-lines: 7 + :dedent: + +.. note:: + + You must specify the index type as a string, as shown by ``'text'`` + in the preceding code. + +Create the Text Index +--------------------- + +Next, you must create the text index in your collection. You can +create the index by using an interface such as the :atlas:`Atlas UI +` or :compass:`Compass `. If you are using +the Rails framework to develop your application, you can run the following +Rake task to create the index based on your model specification: + +.. code-block:: bash + + bundle exec rake db:mongoid:create_indexes + +To learn more about using indexes with {+odm+}, see the +:ref:`mongoid-indexes` guide. + +Perform Text Searches +--------------------- + +To perform a text search, use the ``$text`` evaluation query operator, +followed by the ``$search`` field in your query filter. The ``$text`` operator +performs a text search on the text indexed fields. The ``$search`` field +specifies the text to search in the text indexed fields. To learn more +about this operator, see the :manual:`$text reference +` in the {+server-manual+}. + +.. _mongoid-term-search: + +Search by a Term +~~~~~~~~~~~~~~~~ + +To search for a term, specify the term as a string in your query filter. +To search for multiple terms, separate each term with spaces in the string. + +.. note:: Searching for Multiple Terms + + When searching for multiple terms, {+odm+} returns + documents with at least one of the terms in text indexed fields. + + Suppose you search by using the string ``'cake coffee cream'``. The + following list describes values that match this text query: + + - ``'Has strong coffee notes.'`` + - ``'Good for creamy coffee fans.'`` + - ``'A rich but light cake.'`` + - ``'A creamy coffee cake with cranberries.'`` + +The following example runs a text search for ``description`` values that contain +the term ``'herb'``: + +.. io-code-block:: + :copyable: true + + .. input:: /includes/interact-data/text-search.rb + :start-after: start-term + :end-before: end-term + :language: rust + :dedent: + + .. output:: + :language: none + :visible: false + + # Sample output + {"_id":"...","description":"A bright, herb-based salad. A perfect starter for vegetarians and vegans.","name":"Kale Tabbouleh"} + {"_id":"...","description":"Grilled whole fish stuffed with herbs and pomegranate seeds. Serves 3-4.","name":"Herbed Whole Branzino"} + +.. tip:: + + Although the search term was ``'herb'``, the method also matches + descriptions containing ``'herbs'`` because a MongoDB text index uses *suffix + stemming* to match similar words. To learn more about how + MongoDB matches terms, see :manual:`Text Index Properties + ` in the + {+server-manual}. + +Search by a Phrase +~~~~~~~~~~~~~~~~~~ + +To search for a phrase, specify the phrase with escaped quotes as a +string in your query filter. If you don't add escaped quotes around the +phrase, {+odm+} runs a :ref:`term search `. + +.. tip:: + + Escaped quotes are a backslash character (``\``) followed by a double + quote character (``"``). + +The following example runs a text search for ``description`` values that +contain the phrase ``"serves 2"``: + +.. io-code-block:: + :copyable: true + + .. input:: /includes/interact-data/text-search.rb + :start-after: start-phrase + :end-before: end-phrase + :language: rust + :dedent: + + .. output:: + :language: none + :visible: false + + # Sample output + {"_id":"...","description":"A vegetarian take on the classic dish that uses lentils as a base. Serves 2.","name":"Shepherd’s Pie"} + {"_id":"...","description":"Baked trout seasoned with garlic, lemon, dill, and, of course, butter. Serves 2.","name":"Garlic Butter Trout"} + +Search with Excluded Terms +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +For each term or phrase to exclude from your text search, +specify the term or phrase prefixed with a minus sign (``-``) as a string in +your query filter. + +.. important:: + + You must search for at least one term to exclude + terms from your search. If you don't search for any terms, {+odm+} + doesn't return any documents. + +The following example runs a text search for ``description`` values that +contain the term ``'vegan'``, but do not contain the term ``'tofu'``: + +.. io-code-block:: + :copyable: true + + .. input:: /includes/interact-data/text-search.rb + :start-after: start-exclude + :end-before: end-exclude + :language: rust + :dedent: + + .. output:: + :language: none + :visible: false + + # Sample output + {"_id":"...","description":"A bright, herb-based salad. A perfect starter for vegetarians and vegans.","name":"Kale Tabbouleh"} + +Additional Information +---------------------- + +To learn more about constructing query filters, see +:ref:`mongoid-data-specify-query`. + +To learn more about performing CRUD operations, see the +:ref:`mongoid-data-crud` guide. diff --git a/source/interact-data/transaction.txt b/source/interact-data/transaction.txt new file mode 100644 index 00000000..71ec9ae2 --- /dev/null +++ b/source/interact-data/transaction.txt @@ -0,0 +1,336 @@ +.. _mongoid-data-txn: + +========================= +Transactions and Sessions +========================= + +.. facet:: + :name: genre + :values: reference + +.. meta:: + :keywords: code example, ACID compliance, multi-document, ruby odm + +.. contents:: On this page + :local: + :backlinks: none + :depth: 2 + :class: singlecol + +Overview +-------- + +In this guide, you can learn how to use {+odm+} to perform +**transactions**. Transactions allow you to perform a series of operations +that change data only if the entire transaction is committed. +If any operation in the transaction does not succeed, the driver stops the +transaction and discards all data changes before they ever become +visible. This feature is called :wikipedia:`atomicity `. + +In MongoDB, transactions run within logical **sessions**. A +session is a grouping of related read or write operations that you +want to run sequentially. Sessions enable causal consistency for a group +of operations, which means that all processes in your application agree on +the order of causally-related operations. + +Sessions allow you to run operations in an **ACID-compliant** +transaction that meets an expectation of atomicity, consistency, +isolation, and durability. MongoDB guarantees that the data involved in +your transaction operations remains consistent, even if the operations +encounter unexpected errors. + +In {+odm+}, you can perform transactions by using either of the +following APIs: + +- :ref:`mongoid-txn-high-level`: {+odm+} manages the life cycle of the + transaction. You can use this API in {+odm+} v9.0 and later. + +- :ref:`mongoid-txn-low-level`: You must manage the life cycle of the + transaction. You can use this API in {+odm+} v6.4 and later. + +The :ref:`mongoid-txn-session` section describes how to make changes to +your data from within a session without performing a transaction. + +.. _mongoid-txn-high-level: + +High-Level Transaction API +-------------------------- + +You can use the High-Level Transaction API to internally manage the +lifecycle of your transaction. This API either commits your transaction +or ends it and incorporates error handling logic. + +You can start a transaction by calling the ``transaction`` method on +an instance of a model, on the model class, or on a ``{+odm+}`` module. + +When you call the ``transaction`` method, {+odm+} performs the +following tasks: + +1. Creates a session on the client. + +#. Starts a transaction on the session. + +#. Performs the specified data changes. + +#. Commits the transaction to the database if no errors occur, or ends + the transaction if there is an error. + +#. Closes the session. + +If your transaction is committed, {+odm+} calls any ``after_commit`` +callbacks for all objects modified inside the transaction. If there is +an error and the transaction is rolled back, {+odm+} calls any +``after_rollback`` callbacks for all objects modified inside the +transaction. To learn more about these callbacks and their behavior, see +the :ref:`mongoid-txn-callbacks` section of this guide. + +Example +~~~~~~~ + +This example uses the following models to represent documents that +describe books and films: + +.. literalinclude:: /includes/interact-data/transaction.rb + :start-after: start-example-models + :end-before: end-example-models + :language: ruby + :dedent: + +The following code demonstrates how to perform a transaction on +different objects to change data in multiple collections: + +.. literalinclude:: /includes/interact-data/transaction.rb + :start-after: start-txn-operations + :end-before: end-txn-operations + :language: ruby + :dedent: + +Client Behavior +~~~~~~~~~~~~~~~ + +Only operations on the same client are in the scope of a transaction, +because each transaction is attached to a specific client. Ensure +that you use objects from the same client inside the transaction method +block. + +The following example defines model classes that use different clients +and demonstrates how operations are run based on the origin client: + +.. literalinclude:: /includes/interact-data/transaction.rb + :start-after: start-different-clients + :end-before: end-different-clients + :language: ruby + :dedent: + +.. note:: + + When you call the ``transaction`` method on the ``{+odm+}`` module, + {+odm+} creates the transaction by using the ``:default`` client. + +Ending Transactions +~~~~~~~~~~~~~~~~~~~ + +Any exception raised inside the transaction method block ends the +transaction and rolls back data changes. {+odm+} displays all +exceptions except for the ``Mongoid::Errors::Rollback`` exception. You +can raise this exception in your application to explicitly end the +transaction without returning the exception to you. For example, you +might implement this transaction exception to end a transaction when a +certain condition is not met, but without raising an exception message. + +.. _mongoid-txn-callbacks: + +Callbacks +~~~~~~~~~ + +This transaction API introduces the ``after_commit`` and +``after_rollback`` callbacks. + +{+odm+} triggers the ``after_commit`` callback for an object that was +created, saved, or deleted in the following cases: + +- After the transaction is committed if the object was modified inside + the transaction. + +- After the object is persisted if the object was modified outside + the transaction block. + +The ``after_commit`` callback is triggered only after all +other callbacks are performed successfully. Therefore, if an object is +modified outside of a transaction, it is possible that the object is then +persisted, but the ``after_commit`` callback is not triggered. This +might occur, for example, if {+odm+} raised an exception in the +``after_save`` callback because this callback must complete successfully +to trigger ``after_commit``. + +The ``after_rollback`` callback is triggered for an object that was +created, saved, or deleted inside a transaction, if the transaction was +unsuccessful and changes were rolled back. {+odm+} never triggers +``after_rollback`` outside of a transaction. + +To learn more about callbacks, see the :ref:`mongoid-modeling-callbacks` +guide. + +.. _mongoid-txn-low-level: + +Low-Level Transaction API +------------------------- + +When using the low-level API, you must create a session before +starting a transaction. You can create a session by calling the +``with_session`` method on a model class or an instance of a model. + +Then, you can start a transaction by calling the ``start_transaction`` +method on a session. When using this API, you must manually commit or +end the transaction. You can use the ``commit_transaction`` and +``abort_transaction`` methods on the session instance to manage the +transaction lifecycle. + +Example +~~~~~~~ + +This example uses the low-level transaction API to perform the following +actions: + +1. Creates a session +#. Starts a transaction +#. Performs data operations +#. Commits the transaction, or ends it if there are errors + +.. literalinclude:: /includes/interact-data/transaction.rb + :start-after: start-lower-lvl-api + :end-before: end-lower-lvl-api + :language: ruby + :dedent: + +.. note:: + + If a session ends and includes an open transaction, the transaction is + automatically ended. + +Transaction Retry +~~~~~~~~~~~~~~~~~ + +You can retry the transaction commit if it fails initially. The +following example demonstrates how to retry the transaction when {+odm+} +raises the ``UNKNOWN_TRANSACTION_COMMIT_RESULT_LABEL`` exception: + +.. literalinclude:: /includes/interact-data/transaction.rb + :start-after: start-commit-retry + :end-before: end-commit-retry + :language: ruby + :dedent: + +Options +~~~~~~~ + +You can specify a read concern, write concern or read +preference when starting a transaction by passing options to the +``start_transaction`` method: + +.. code-block:: ruby + + session.start_transaction( + read_concern: {level: :majority}, + write_concern: {w: 3}, + read: {mode: :primary} + ) + +To learn more about the available transaction options, see +:ruby-api:`start_transaction +` in the +{+ruby-driver+} API documentation. + +Client Behavior +~~~~~~~~~~~~~~~ + +To perform operations within a transaction, operations must use the same +client that the session was initiated on. By default, all operations +are performed by using the default client. + +To explicitly use a different client, use the ``with`` method: + +.. literalinclude:: /includes/interact-data/transaction.rb + :start-after: start-other-client + :end-before: end-other-client + :language: ruby + :dedent: + +.. _mongoid-txn-session: + +Session API +----------- + +You can use sessions in {+odm+} in a similar way that you can +perform a transaction. You can call the ``with_session`` method on a +model class or on an instance of a model and perform some operations in +a block. All operations in the block will be performed in the context of +single session. + +The following limitations apply when using sessions: + +- You cannot share a session across threads. Sessions are not thread-safe. + +- You cannot nest sessions. For example, you cannot call the ``with_session`` + method on a model class within the block passed to + the ``with_session`` method on another model class. The following + code demonstrates a nested session that results in an error: + + .. code-block:: ruby + + Book.with_session(causal_consistency: true) do + # Nesting sessions results in errors + Film.with_session(causal_consistency: true) do + ... + end + end + +- All model classes and instances used within the session block must use + the same driver client. For example, if you specify different + a different client for a model used in the block than those of the + model or instance that you called ``with_session`` on, {+odm+} returns + an error. + +Examples +~~~~~~~~ + +You can use the ``with_session`` method on a model class and pass it session +options to perform a block of operations in the context of a session. + +The following code enables the ``causal_consistency`` option to +guarantee the order of operations when creating a session on the +``Book`` model, then performs data operations: + +.. literalinclude:: /includes/interact-data/transaction.rb + :start-after: start-model-session + :end-before: end-model-session + :language: ruby + :dedent: + +To learn more about the available session options, see the +:ruby-api:`Session class constructor details +` in the +{+ruby-driver+} API documentation. + +Alternatively, you can use the ``with_session`` method on an instance of a +model and pass it session options to perform a block of operations in +the context of a session. + +The following code enables the ``causal_consistency`` option to +guarantee the order of operations when creating a session on an instance +of ``Book``, then performs data operations: + +.. literalinclude:: /includes/interact-data/transaction.rb + :start-after: start-instance-session + :end-before: end-instance-session + :language: ruby + :dedent: + +Additional Information +---------------------- + +To learn more about transactions, see :manual:`Transactions ` in +the {+server-manual+}. + +To learn more about performing CRUD operations, see the +:ref:`mongoid-data-crud` guide. diff --git a/source/issues-and-help.txt b/source/issues-and-help.txt new file mode 100644 index 00000000..7b19a3af --- /dev/null +++ b/source/issues-and-help.txt @@ -0,0 +1,85 @@ +.. _mongoid-issues-and-help: + +============= +Issues & Help +============= + +.. facet:: + :name: genre + :values: reference + +.. meta:: + :keywords: mongoid, ruby, troubleshooting, feedback + +.. contents:: On this page + :local: + :backlinks: none + :depth: 1 + :class: singlecol + +.. toctree:: + :titlesonly: + + Code Documentation + +We are lucky to have a vibrant MongoDB {+language+} community that includes users +with varying levels of experience using {+odm+} and the {+ruby-driver+}. +The quickest way to get support for general questions is through the +:community-forum:`MongoDB Community Forums `. + +Bugs / Feature Requests +----------------------- + +If you have feedback about {+odm+}, visit the `MongoDB +Feedback Engine `__ and select +:guilabel:`Drivers` from the list of products on the right side of +your screen. You can propose improvements, report issues, and provide +other types of feedback by using this site. + +You can also open a case in Jira, our issue management tool, to identify +bugs or propose improvements. The following steps describe how to create +a Jira issue: + +1. Visit the `MongoDB Jira issue tracker `__ and click the + `signup link. `__ + Create an account, and then log in to Jira. +#. Navigate to the `MONGOID Jira project. `__ +#. Click :guilabel:`Create` to create a ticket. Please provide as much + information as possible about the issue or request in the ticket. + +.. note:: + + Bug reports in the MONGOID Jira project are publicly viewable. + +If you’ve identified a security vulnerability in any official MongoDB +product, please report it according to the instructions found in the +:manual:`Create a Vulnerability Report +` guide. + +Pull Requests +------------- + +We are happy to accept contributions to help improve the driver. We will guide +user contributions to ensure they meet the standards of the codebase. Please +ensure that any pull requests include documentation, tests, and pass +code checks. To learn about {+odm+}'s code documentation conventions, +see the :ref:`mongoid-code-documentation` guide. + +To get started, clone the source repository and work on a branch by +running the following commands: + +.. code-block:: bash + + git clone https://github.com/mongodb/mongoid.git + cd mongoid + git checkout -b my-new-feature + +.. tip:: + + If your changes correspond to a specific Jira ticket, title your pull + request by using the following convention: + + ``MONGOID-: `` + +The {+odm+} developer team will review your pull request and make +comments or suggest changes. diff --git a/source/reference/map-reduce.txt b/source/legacy-files/map-reduce.txt similarity index 98% rename from source/reference/map-reduce.txt rename to source/legacy-files/map-reduce.txt index 2251ffa9..48aa0ae5 100644 --- a/source/reference/map-reduce.txt +++ b/source/legacy-files/map-reduce.txt @@ -19,7 +19,7 @@ custom map/reduce jobs or simple aggregations. .. note:: The map-reduce operation is deprecated. - The :ref:`aggregation framework ` provides better + The :ref:`aggregation framework ` provides better performance and usability than map-reduce operations, and should be preferred for new development. diff --git a/source/legacy-files/readme b/source/legacy-files/readme new file mode 100644 index 00000000..20941ba0 --- /dev/null +++ b/source/legacy-files/readme @@ -0,0 +1 @@ +This folder contains pages that we haven't standardized yet. \ No newline at end of file diff --git a/source/release-notes/upgrading.txt b/source/legacy-files/upgrading.txt similarity index 96% rename from source/release-notes/upgrading.txt rename to source/legacy-files/upgrading.txt index db27af62..ef6bdd3b 100644 --- a/source/release-notes/upgrading.txt +++ b/source/legacy-files/upgrading.txt @@ -51,7 +51,7 @@ Before you Upgrade - *Test Coverage:* The best way to be sure that your application still works after upgrading is to have good test coverage before you start the process. -- *Upgrade Ruby and Rails:* See `"Upgrading Ruby on Rails" `_ +- *Upgrade Ruby and Rails:* See `"Upgrading {+ror+}" <{+active-record-docs+}/upgrading_ruby_on_rails.html>`_ for more information diff --git a/source/meta/404.txt b/source/meta/404.txt deleted file mode 100644 index 4143ded8..00000000 --- a/source/meta/404.txt +++ /dev/null @@ -1,7 +0,0 @@ -:orphan: - -************** -File not found -************** - -The URL you requested does not exist or has been removed. diff --git a/source/nesting-levels.txt b/source/nesting-levels.txt deleted file mode 100644 index 2973410d..00000000 --- a/source/nesting-levels.txt +++ /dev/null @@ -1,20 +0,0 @@ -This file is not part of Mongoid documentation proper, it is an internal -reference for the nesting levels that other files should be using. - -Mongoid documentation nesting levels: - -********** -Page Title -********** - -First Level Heading -=================== - -Second Level Heading --------------------- - -Third Level Heading -``````````````````` - -Fourth Level Heading -~~~~~~~~~~~~~~~~~~~~ diff --git a/source/quick-start-rails.txt b/source/quick-start-rails.txt new file mode 100644 index 00000000..17fb1571 --- /dev/null +++ b/source/quick-start-rails.txt @@ -0,0 +1,76 @@ +.. _mongoid-quick-start-rails: + +=========================== +Quick Start - {+ror+} +=========================== + +.. facet:: + :name: genre + :values: tutorial + +.. meta:: + :keywords: ruby framework, odm + +.. contents:: On this page + :local: + :backlinks: none + :depth: 1 + :class: singlecol + +Overview +-------- + +This guide shows you how to use {+odm+} in a new **{+ror+} 8 (Rails)** +web application, connect to a MongoDB cluster hosted on MongoDB +Atlas, and perform read and write operations on the data in your +cluster. + +To learn how to integrate {+odm+} into an existing application, see the +:ref:`mongoid-add-to-existing` guide. + +.. tip:: + + If you prefer to connect to MongoDB by using the {+ruby-driver+} without + {+odm+}, see the {+ruby-driver+} :ruby:`Quick Start guide + `. + +{+odm+} is an Object-Document Mapper (ODM) framework for MongoDB in +{+language+}. By using {+odm+}, you can easily interact with your data and +create flexible data models. + +{+ror+} is a web application framework for +{+language+}. Rails applications use a model-view-controller (MVC) +architecture that allows you to easily control how your data is +modeled and displayed. {+odm+} replaces the default Active Record +adapter for data modeling in Rails. + +To learn more about {+ror+}, see the `Getting Started +with Rails <{+active-record-docs+}/getting_started.html>`__ +guide in the Rails documentation. + +MongoDB Atlas is a fully managed cloud database service that hosts your +MongoDB deployments. You can create your own free (no credit card +required) MongoDB Atlas deployment by following the steps in this guide. + +Follow the steps in this guide to create a sample {+odm+} web application +that connects to a MongoDB deployment. + +.. tip:: Other Framework Tutorials + + If you prefer to use Sinatra as your web framework, see the + :ref:`mongoid-quick-start-sinatra` guide. + +.. TODO .. tip:: +.. +.. You can download the complete web application project by cloning the +.. `mongoid-quickstart <>`__ GitHub repository. + +.. toctree:: + + /quick-start-rails/download-and-install/ + /quick-start-rails/create-a-deployment/ + /quick-start-rails/create-a-connection-string/ + /quick-start-rails/configure-mongodb/ + /quick-start-rails/view-data/ + /quick-start-rails/write-data/ + /quick-start-rails/next-steps/ diff --git a/source/quick-start-rails/configure-mongodb.txt b/source/quick-start-rails/configure-mongodb.txt new file mode 100644 index 00000000..7847e0c5 --- /dev/null +++ b/source/quick-start-rails/configure-mongodb.txt @@ -0,0 +1,62 @@ +.. _mongoid-quick-start-rails-connect-to-mongodb: + +================================= +Configure Your MongoDB Connection +================================= + +.. facet:: + :name: genre + :values: tutorial + +.. meta:: + :keywords: test connection, runnable, code example + +.. procedure:: + :style: connected + + .. step:: Configure application for MongoDB + + To configure your application to use MongoDB and {+odm+} as your + ODM, run the following command from the root of your project: + + .. code-block:: bash + + bin/rails g mongoid:config + + After the command completes successfully, your application + contains the ``config/mongoid.yml`` file to configure the + connection to the MongoDB deployment. Your application also + includes the ``config/initializers/mongoid.rb`` file for more + advanced configuration. + + .. step:: Specify target database in connection string + + When connecting to an Atlas cluster, you must specify the database that + you want to interact with as the default database in your connection string. + You must add the database name to your connection string *after the hostname*. + + The following example specifies the ``sample_restaurants`` target database + in a sample connection string: + + .. code-block:: none + + mongodb+srv://user0:pass123@mongo0.example.com/sample_restaurants + + .. step:: Specify connection in mongoid.yml + + Paste the following configuration into the ``config/mongoid.yml`` file, + making sure to replace the ```` placeholder + with your connection string that references the target database: + + .. code-block:: yaml + :emphasize-lines: 4 + + development: + clients: + default: + uri: + +After completing these steps, your Rails web application is ready to +connect to MongoDB. + +.. include:: /includes/quick-start/troubleshoot.rst diff --git a/source/quick-start-rails/create-a-connection-string.txt b/source/quick-start-rails/create-a-connection-string.txt new file mode 100644 index 00000000..e9b0a095 --- /dev/null +++ b/source/quick-start-rails/create-a-connection-string.txt @@ -0,0 +1,7 @@ +.. _mongoid-quick-start-rails-create-cxn-str: + +========================== +Create a Connection String +========================== + +.. include:: /includes/quick-start/create-cxn-str.rst \ No newline at end of file diff --git a/source/quick-start-rails/create-a-deployment.txt b/source/quick-start-rails/create-a-deployment.txt new file mode 100644 index 00000000..edfdfed0 --- /dev/null +++ b/source/quick-start-rails/create-a-deployment.txt @@ -0,0 +1,7 @@ +.. _mongoid-quick-start-rails-create-deployment: + +=========================== +Create a MongoDB Deployment +=========================== + +.. include:: /includes/quick-start/create-deployment.rst diff --git a/source/quick-start-rails/download-and-install.txt b/source/quick-start-rails/download-and-install.txt new file mode 100644 index 00000000..009325dd --- /dev/null +++ b/source/quick-start-rails/download-and-install.txt @@ -0,0 +1,115 @@ +.. _mongoid-quick-start-rails-download-and-install: + +==================== +Download and Install +==================== + +.. facet:: + :name: genre + :values: tutorial + +.. meta:: + :keywords: ruby framework, odm, code example + +Prerequisites +------------- + +To create the Quick Start application by using {+ror+} 8, you need the +following software installed in your development environment: + +- `{+language+} language. `__ + Rails requires {+language+} v3.1.0 or later. Use the latest version + to prevent version conflicts. +- :rubygems:`RubyGems package manager. ` +- A terminal app and shell. For MacOS users, use Terminal or a similar app. + For Windows users, use PowerShell. + +Download and Install the {+odm+} and Framework Gems +--------------------------------------------------- + +In {+language+}, packages are called **gems**. + +Complete the following steps to install and add the {+odm+} and Rails +gems to your web application. + +.. procedure:: + :style: connected + + .. step:: Install Rails + + Install the ``rails`` gem, which provides a command-line + interface to create a basic application structure and application + components. + + Run the following command to install ``rails``: + + .. code-block:: bash + + gem install rails + + .. step:: Create a Rails app with default scaffolding + + Run the following commands to create a new Rails application + directory with default scaffolding and enter the application: + + .. code-block:: bash + + rails new {+quickstart-rails-app-name+} --skip-active-record + cd {+quickstart-rails-app-name+} + + The ``--skip-active-record`` flag instructs Rails to not add + Active Record as a dependency. You don't need this + dependency because you will use {+odm+} instead. + + .. tip:: MacOS Installation Issue + + If you are using macOS, you might encounter issues when creating a + new Rails app during the automatic bundle installation step. + First, make sure that your macOS and `Xcode + `__ versions are up to + date. If you receive an error message similar to the following, + you must update or configure your build tools: + + .. code-block:: none + :copyable: false + + The compiler failed to generate an executable file. + ... + (RuntimeError) You have to install development tools first. + + Run the following commands to install Xcode command line tools: + + .. code-block:: bash + + xcode-select --install + xcodebuild -license accept + + Then, try to run the ``bundle install`` command again. + + .. step:: Add the {+odm+} gem + + Open the ``Gemfile`` in your application and add the following + entry: + + .. code-block:: ruby + + gem 'mongoid' + + .. step:: Install gems + + Run the following command to install the gems into your + application: + + .. code-block:: bash + + gem install bundler + bundle install + + When the command runs successfully, the output in your + shell contains a ``Bundle complete!`` message and describes the + number of new gems installed. + + After completing these steps, you have a new Rails web application with + {+odm+} installed. + +.. include:: /includes/quick-start/troubleshoot.rst diff --git a/source/quick-start-rails/next-steps.txt b/source/quick-start-rails/next-steps.txt new file mode 100644 index 00000000..aadc3e60 --- /dev/null +++ b/source/quick-start-rails/next-steps.txt @@ -0,0 +1,30 @@ +.. _mongoid-quick-start-rails-next-steps: + +========== +Next Steps +========== + +.. facet:: + :name: genre + :values: reference + +.. meta:: + :keywords: learn more + +Congratulations on completing the Quick Start tutorial for {+ror+} 8! + +After you complete these steps, you have a Rails web application that +uses {+odm+} to connect to your MongoDB deployment, run a query on +the sample data, and render retrieved results. + +.. TODO You can download the completed web application project by cloning the +.. `mongoid-quickstart <>`__ +.. GitHub repository. + +Learn more about {+odm+} features from the following sections: + +- :ref:`mongoid-data-modeling`: Learn how to customize your {+odm+} + models. + +- :ref:`mongoid-interact-data`: Learn how to interact with your MongoDB + data by using {+odm+} models. diff --git a/source/quick-start-rails/view-data.txt b/source/quick-start-rails/view-data.txt new file mode 100644 index 00000000..3e06b140 --- /dev/null +++ b/source/quick-start-rails/view-data.txt @@ -0,0 +1,91 @@ +.. _mongoid-quick-start-rails-view-data: + +================= +View MongoDB Data +================= + +.. facet:: + :name: genre + :values: tutorial + +.. meta:: + :keywords: test connection, runnable, code example + +.. procedure:: + :style: connected + + .. step:: Create a data model + + Run the following command from your project root to create a + ``Restaurant`` model with ``name``, ``cuisine``, and ``borough`` + fields: + + .. code-block:: bash + + bin/rails g scaffold Restaurant name:string cuisine:string borough:string + + This command also creates the controller and view files for the + ``Restaurant`` model. You can find the directories that contain + these files in the ``app`` directory of your application. + + .. step:: Retrieve specific documents + + The ``app/controllers/restaurants_controller.rb`` file contains + methods that specify how your app handles different + requests. Replace the ``index`` method body with the following code: + + .. code-block:: ruby + + def index + @restaurants = Restaurant + .where(name: /earth/i) + end + + This controller method retrieves ``Restaurant`` documents in which the + value of the ``name`` field contains the string ``"earth"``. The + results are rendered at the ``/restaurants`` route by default. + + .. step:: Start your Rails application + + Run the following command from the application root directory + to start your {+language+} web server: + + .. code-block:: bash + + bin/rails s + + After the server starts, it outputs the following message + indicating that the application is running on port ``3000``: + + .. code-block:: none + :copyable: false + + => Booting Puma + => Rails 8.0.1 application starting in development + => Run `bin/rails server --help` for more startup options + Puma starting in single mode... + * Puma version: 6.4.3 (ruby 3.2.5-p208) ("The Eagle of Durango") + * Min threads: 3 + * Max threads: 3 + * Environment: development + * PID: 66973 + * Listening on http://127.0.0.1:3000 + * Listening on http://[::1]:3000 + * Listening on http://127.0.2.2:3000 + * Listening on http://127.0.2.3:3000 + Use Ctrl-C to stop + + .. step:: View the restaurant data + + Open the URL http://127.0.2.2:3000/restaurants in your web browser. + The page shows a list of restaurants and details about each of + them: + + .. figure:: /includes/figures/quickstart-rails-list.png + :alt: The rendered list of restaurants + + Rails provides a default interface that allows you to view, edit, + and delete models. In the next section, you can learn how to use the + interface to interact with MongoDB data. + +.. include:: /includes/quick-start/troubleshoot.rst diff --git a/source/quick-start-rails/write-data.txt b/source/quick-start-rails/write-data.txt new file mode 100644 index 00000000..601c9b0e --- /dev/null +++ b/source/quick-start-rails/write-data.txt @@ -0,0 +1,41 @@ +.. _mongoid-quick-start-rails-write-data: + +===================== +Write Data to MongoDB +===================== + +.. facet:: + :name: genre + :values: tutorial + +.. meta:: + :keywords: test connection, runnable, code example + +.. procedure:: + :style: connected + + .. step:: Create a new restaurant + + In your browser at http://127.0.2.2:3000/restaurants, you can + scroll to the bottom of the list and click the :guilabel:`New + restaurant` link to navigate to the ``/restaurants/new`` route. On + this page, you can fill out the form to create a new ``Restaurant`` + model and save it to MongoDB. + + The following sample values satisfy the filter criteria so that + the document will appear in the list of restaurants: + + - **Name**: Wild Earth Company + - **Cuisine**: American + - **Borough**: Queens + + Click the :guilabel:`Create Restaurant` button to create the + restaurant model and save it. + + .. step:: View the data + + Refresh http://127.0.2.2:3000/restaurants in your web browser + to view the new ``Restaurant`` entry that you submitted at the + bottom of the list. + +.. include:: /includes/quick-start/troubleshoot.rst diff --git a/source/quick-start-sinatra.txt b/source/quick-start-sinatra.txt new file mode 100644 index 00000000..d9d88d84 --- /dev/null +++ b/source/quick-start-sinatra.txt @@ -0,0 +1,69 @@ +.. _mongoid-quick-start-sinatra: + +===================== +Quick Start - Sinatra +===================== + +.. facet:: + :name: genre + :values: tutorial + +.. meta:: + :keywords: ruby framework, odm + +.. contents:: On this page + :local: + :backlinks: none + :depth: 1 + :class: singlecol + +Overview +-------- + +This guide shows you how to use {+odm+} in a new Sinatra web application, +connect to a MongoDB cluster hosted on MongoDB Atlas, and perform +read and write operations on the data in your cluster. + +To learn how to integrate {+odm+} into an existing application, see the +:ref:`mongoid-add-to-existing` guide. + +.. tip:: + + If you prefer to connect to MongoDB by using the {+ruby-driver+} without + {+odm+}, see the {+ruby-driver+} :ruby:`Quick Start guide + `. + +{+odm+} is an Object-Document Mapper (ODM) framework for MongoDB in +{+language+}. By using {+odm+}, you can easily interact with your data and +create flexible data models. + +Sinatra is a domain-specific language (DSL) for creating web +applications in {+language+}. Sinatra applications are simple to set up +and can provide faster request processing than other frameworks. + +MongoDB Atlas is a fully managed cloud database service that hosts your +MongoDB deployments. You can create your own free (no credit card +required) MongoDB Atlas deployment by following the steps in this guide. + +Follow the steps in this guide to create a sample {+odm+} web application +that connects to a MongoDB deployment. + +.. tip:: Other Framework Tutorials + + If you prefer to use {+ror+} 8 as your web framework, see the + :ref:`mongoid-quick-start-rails` guide. + +.. TODO .. tip:: +.. +.. You can download the complete web application project by cloning the +.. `mongoid-quickstart <>`__ GitHub repository. + +.. toctree:: + + /quick-start-sinatra/download-and-install/ + /quick-start-sinatra/create-a-deployment/ + /quick-start-sinatra/create-a-connection-string/ + /quick-start-sinatra/configure-mongodb/ + /quick-start-sinatra/view-data/ + /quick-start-sinatra/write-data/ + /quick-start-sinatra/next-steps/ diff --git a/source/quick-start-sinatra/configure-mongodb.txt b/source/quick-start-sinatra/configure-mongodb.txt new file mode 100644 index 00000000..8f79908c --- /dev/null +++ b/source/quick-start-sinatra/configure-mongodb.txt @@ -0,0 +1,50 @@ +.. _mongoid-quick-start-sinatra-connect-to-mongodb: + +================================= +Configure Your MongoDB Connection +================================= + +.. facet:: + :name: genre + :values: tutorial + +.. meta:: + :keywords: test connection, runnable, code example + +.. procedure:: + :style: connected + + .. step:: Specify target database in connection string + + When connecting to an Atlas cluster, you must specify the database that + you want to interact with as the default database in your connection string. + You must add the database name to your connection string *after the hostname*. + + The following example specifies the ``sample_restaurants`` target database + in a sample connection string: + + .. code-block:: none + + mongodb+srv://user0:pass123@mongo0.example.com/sample_restaurants + + .. step:: Specify connection in mongoid.yml + + At the root level of your project, create a ``config`` directory. + Then, create a file in this directory called ``mongoid.yml``. + + Paste the following configuration into the ``mongoid.yml`` file, + making sure to replace the ```` placeholder + with your connection string that references the target database: + + .. code-block:: yaml + :emphasize-lines: 4 + + development: + clients: + default: + uri: + +After completing these steps, your Sinatra web application is ready to +connect to MongoDB. + +.. include:: /includes/quick-start/troubleshoot.rst diff --git a/source/quick-start-sinatra/create-a-connection-string.txt b/source/quick-start-sinatra/create-a-connection-string.txt new file mode 100644 index 00000000..cb44afc2 --- /dev/null +++ b/source/quick-start-sinatra/create-a-connection-string.txt @@ -0,0 +1,7 @@ +.. _mongoid-quick-start-sinatra-create-cxn-str: + +========================== +Create a Connection String +========================== + +.. include:: /includes/quick-start/create-cxn-str.rst \ No newline at end of file diff --git a/source/quick-start-sinatra/create-a-deployment.txt b/source/quick-start-sinatra/create-a-deployment.txt new file mode 100644 index 00000000..758a7807 --- /dev/null +++ b/source/quick-start-sinatra/create-a-deployment.txt @@ -0,0 +1,7 @@ +.. _mongoid-quick-start-sinatra-create-deployment: + +=========================== +Create a MongoDB Deployment +=========================== + +.. include:: /includes/quick-start/create-deployment.rst diff --git a/source/quick-start-sinatra/download-and-install.txt b/source/quick-start-sinatra/download-and-install.txt new file mode 100644 index 00000000..fa762905 --- /dev/null +++ b/source/quick-start-sinatra/download-and-install.txt @@ -0,0 +1,112 @@ +.. _mongoid-quick-start-sinatra-download-and-install: + +==================== +Download and Install +==================== + +.. facet:: + :name: genre + :values: tutorial + +.. meta:: + :keywords: ruby framework, odm, code example + +Prerequisites +------------- + +To create the Quick Start application by using Sinatra, you need the +following software installed in your development environment: + +- `{+language+} language. `__ +- :rubygems:`RubyGems package manager. ` +- A terminal app and shell. For MacOS users, use Terminal or a similar app. + For Windows users, use PowerShell. + +Download and Install the {+odm+} and Framework Gems +--------------------------------------------------- + +In {+language+}, packages are called **gems**. + +Complete the following steps to install and add the {+odm+} and Sinatra +gems to your web application. + +.. procedure:: + :style: connected + + .. step:: Install {+odm+} + + Ensure that the version of {+odm+} you install is compatible with the + version of {+language+} installed on your operating system. To + learn which versions are compatible, see the + :ref:`mongoid-compatibility` page. + + Run the following command to install the {+odm+} gem: + + .. code-block:: bash + + gem install mongoid + + When the installation completes, the command outputs the following + message: + + .. code-block:: none + :copyable: false + + Successfully installed mongoid-{+full-version+} + Parsing documentation for mongoid-{+full-version+} + Installing ri documentation for mongoid-{+full-version+} + Done installing documentation for mongoid after 1 seconds + 1 gem installed + + .. step:: Create a Quick Start application directory + + Run the following commands to generate a new directory + called ``{+quickstart-sinatra-app-name+}`` and enter it: + + .. code-block:: bash + + mkdir {+quickstart-sinatra-app-name+} + cd {+quickstart-sinatra-app-name+} + + .. step:: Create a ``Gemfile`` and add gems + + All {+language+} applications must have a ``Gemfile`` that lists + the required gems. Run the following command to create a + ``Gemfile`` in your application: + + .. code-block:: bash + + touch Gemfile + + Paste the following content into the ``Gemfile`` to add the + required gems: + + .. code-block:: ruby + + source 'https://rubygems.org' + + gem 'sinatra' + gem 'mongoid' + gem 'rackup' + + The ``rackup`` gem provides a web server interface for your + application. + + .. step:: Install gems + + Run the following command to install the specified gems into your + application: + + .. code-block:: bash + + gem install bundler + bundle install + + When the command runs successfully, the output in your + shell contains a ``Bundle complete!`` message and describes the + number of new gems installed. + + After completing these steps, you have a new Sinatra web application with + {+odm+} installed. + +.. include:: /includes/quick-start/troubleshoot.rst diff --git a/source/quick-start-sinatra/next-steps.txt b/source/quick-start-sinatra/next-steps.txt new file mode 100644 index 00000000..f27b0c2f --- /dev/null +++ b/source/quick-start-sinatra/next-steps.txt @@ -0,0 +1,30 @@ +.. _mongoid-quick-start-sinatra-next-steps: + +========== +Next Steps +========== + +.. facet:: + :name: genre + :values: reference + +.. meta:: + :keywords: learn more + +Congratulations on completing the Quick Start tutorial for Sinatra! + +After you complete these steps, you have a Sinatra web application that +uses {+odm+} to connect to your MongoDB deployment, run a query on +the sample data, and render retrieved results. + +.. TODO You can download the completed web application project by cloning the +.. `mongoid-quickstart <>`__ +.. GitHub repository. + +Learn more about {+odm+} features from the following sections: + +- :ref:`mongoid-data-modeling`: Learn how to customize your {+odm+} + models. + +- :ref:`mongoid-interact-data`: Learn how to interact with your MongoDB + data by using {+odm+} models. diff --git a/source/quick-start-sinatra/view-data.txt b/source/quick-start-sinatra/view-data.txt new file mode 100644 index 00000000..3925552a --- /dev/null +++ b/source/quick-start-sinatra/view-data.txt @@ -0,0 +1,150 @@ +.. _mongoid-quick-start-sinatra-view-data: + +================= +View MongoDB Data +================= + +.. facet:: + :name: genre + :values: tutorial + +.. meta:: + :keywords: test connection, runnable, code example + +.. procedure:: + :style: connected + + .. step:: Create an application file + + At the root level of your project, create a file called ``app.rb``. + Paste the following contents into the ``app.rb`` file to + load the necessary gems and your configuration file: + + .. code-block:: ruby + + require 'sinatra' + require 'mongoid' + + Mongoid.load!(File.join(File.dirname(__FILE__), 'config', 'mongoid.yml')) + + .. step:: Create a data model + + In the ``app.rb`` file, create a model called ``Restaurant`` + to represent data from the sample ``restaurants`` collection in + the ``sample_restaurants`` database: + + .. code-block:: ruby + + class Restaurant + include Mongoid::Document + + field :name, type: String + field :cuisine, type: String + field :borough, type: String + + end + + .. step:: Generate a view + + Create a **view** to display your data in a specified way by using + HTML and {+language+}. + + At the root level of your project, create a directory + called ``views``. Then, create a file called + ``list_restaurants.erb``. Paste the following code into the + ``list_restaurants.erb`` file: + + .. code-block:: html + + + + + Restaurants List + + +

Restaurants List

+ + + + + + + <% @restaurants.each do |restaurant| %> + + + + + + <% end %> +
NameCuisineBorough
<%= restaurant.name %><%= restaurant.cuisine %><%= restaurant.borough %>
+ + + + .. step:: Add a web route + + In the ``app.rb`` file, add a ``get`` route called + ``list_restaurants``, as shown in the following code: + + .. code-block:: ruby + + get '/list_restaurants' do + @restaurants = Restaurant + .where(name: /earth/i) + + erb :list_restaurants + end + + This route retrieves ``Restaurant`` documents in which the value of + the ``name`` field contains the string ``"earth"``. The route + uses the ``list_restaurants`` view to render the results. + + .. _mongoid-quick-start-sinatra-json: + + .. step:: (Optional) View your results as JSON documents + + Instead of generating a view to render your results, you can + use the ``to_json`` method to display your results in JSON + format. + + Replace the ``list_restaurants`` route in the ``app.rb`` file with + the following code to return the results as JSON documents: + + .. code-block:: ruby + + get '/list_restaurants' do + restaurants = Restaurant + .where(name: /earth/i) + + restaurants.to_json + end + + .. step:: Start your Sinatra application + + Run the following command from the application root directory + to start your {+language+} web server: + + .. code-block:: bash + + bundle exec ruby app.rb + + After the server starts, it outputs the following message + indicating that the application is running on port ``4567``: + + .. code-block:: none + :copyable: false + + [2024-10-01 12:36:49] INFO WEBrick 1.8.2 + [2024-10-01 12:36:49] INFO ruby 3.2.5 (2024-07-26) [arm64-darwin23] + == Sinatra (v4.0.0) has taken the stage on 4567 for development with backup from WEBrick + [2024-10-01 12:36:49] INFO WEBrick::HTTPServer#start: pid=79176 port=4567 + + .. step:: View the restaurant data + + Open the URL http://localhost:4567/list_restaurants in your web browser. + The page shows a list of restaurants and details about each of + them: + + .. figure:: /includes/figures/quickstart-sinatra-list.png + :alt: The rendered list of restaurants + +.. include:: /includes/quick-start/troubleshoot.rst diff --git a/source/quick-start-sinatra/write-data.txt b/source/quick-start-sinatra/write-data.txt new file mode 100644 index 00000000..5eebb2af --- /dev/null +++ b/source/quick-start-sinatra/write-data.txt @@ -0,0 +1,46 @@ +.. _mongoid-quick-start-sinatra-write-data: + +===================== +Write Data to MongoDB +===================== + +.. facet:: + :name: genre + :values: tutorial + +.. meta:: + :keywords: test connection, runnable, code example + +.. procedure:: + :style: connected + + .. step:: Add a web route to insert data + + In the ``app.rb`` file, add a ``post`` route called + ``add_restaurant``, as shown in the following code: + + .. code-block:: ruby + + post '/add_restaurant' do + Restaurant.create!(params[:restaurant]) + end + + .. step:: Post a request to create a restaurant entry + + Send a ``Restaurant`` instance to the ``add_restaurant`` endpoint + by running the following command from the application root + directory: + + .. code-block:: bash + + curl -d \ + 'restaurant[name]=Good+Earth+Cafe&restaurant[cuisine]=Cafe&restaurant[borough]=Queens' \ + http://localhost:4567/add_restaurant + + .. step:: View the data + + Refresh http://localhost:4567/list_restaurants in your web browser + to view the new restaurant entry that you submitted. The inserted + restaurant appears at the bottom of the list. + +.. include:: /includes/quick-start/troubleshoot.rst diff --git a/source/reference/aggregation.txt b/source/reference/aggregation.txt deleted file mode 100644 index aa6ed8b7..00000000 --- a/source/reference/aggregation.txt +++ /dev/null @@ -1,203 +0,0 @@ -.. _aggregation-pipeline: - -******************** -Aggregation Pipeline -******************** - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 2 - :class: singlecol - - -Mongoid exposes `MongoDB's aggregation pipeline -`_, -which is used to construct flows of operations that process and return results. -The aggregation pipeline is a superset of the deprecated -:ref:`map/reduce framework ` functionality. - - -Basic Usage -=========== - -.. _aggregation-pipeline-example-multiple-collections: - -Querying Across Multiple Collections -```````````````````````````````````` - -The aggregation pipeline may be used for queries involving multiple -referenced associations at the same time: - -.. code-block:: ruby - - class Band - include Mongoid::Document - has_many :tours - has_many :awards - field :name, type: String - end - - class Tour - include Mongoid::Document - belongs_to :band - field :year, type: Integer - end - - class Award - include Mongoid::Document - belongs_to :band - field :name, type: String - end - -To retrieve bands that toured since 2000 and have at least one award, one -could do the following: - -.. code-block:: ruby - - band_ids = Band.collection.aggregate([ - { '$lookup' => { - from: 'tours', - localField: '_id', - foreignField: 'band_id', - as: 'tours', - } }, - { '$lookup' => { - from: 'awards', - localField: '_id', - foreignField: 'band_id', - as: 'awards', - } }, - { '$match' => { - 'tours.year' => {'$gte' => 2000}, - 'awards._id' => {'$exists' => true}, - } }, - {'$project' => {_id: 1}}, - ]) - bands = Band.find(band_ids.to_a) - -Note that the aggregation pipeline, since it is implemented by the Ruby driver -for MongoDB and not Mongoid, returns raw ``BSON::Document`` objects rather than -``Mongoid::Document`` model instances. The above example projects only -the ``_id`` field which is then used to load full models. An alternative is -to not perform such a projection and work with raw fields, which would eliminate -having to send the list of document ids to Mongoid in the second query -(which could be large). - - -.. _aggregation-pipeline-builder-dsl: - -Builder DSL -=========== - -Mongoid provides limited support for constructing the aggregation pipeline -itself using a high-level DSL. The following aggregation pipeline operators -are supported: - -- `$group `_ -- `$project `_ -- `$unwind `_ - -To construct a pipeline, call the corresponding aggregation pipeline methods -on a ``Criteria`` instance. Aggregation pipeline operations are added to the -``pipeline`` attribute of the ``Criteria`` instance. To execute the pipeline, -pass the ``pipeline`` attribute value to ``Collection#aggregate`` method. - -For example, given the following models: - -.. code-block:: ruby - - class Tour - include Mongoid::Document - - embeds_many :participants - - field :name, type: String - field :states, type: Array - end - - class Participant - include Mongoid::Document - - embedded_in :tour - - field :name, type: String - end - -We can find out which states a participant visited: - -.. code-block:: ruby - - criteria = Tour.where('participants.name' => 'Serenity',). - unwind(:states). - group(_id: 'states', :states.add_to_set => '$states'). - project(_id: 0, states: 1) - - pp criteria.pipeline - # => [{"$match"=>{"participants.name"=>"Serenity"}}, - # {"$unwind"=>"$states"}, - # {"$group"=>{"_id"=>"states", "states"=>{"$addToSet"=>"$states"}}}, - # {"$project"=>{"_id"=>0, "states"=>1}}] - - Tour.collection.aggregate(criteria.pipeline).to_a - - -group -````` - -The ``group`` method adds a `$group aggregation pipeline stage -`_. - -The field expressions support Mongoid symbol-operator syntax: - -.. code-block:: ruby - - criteria = Tour.all.group(_id: 'states', :states.add_to_set => '$states') - criteria.pipeline - # => [{"$group"=>{"_id"=>"states", "states"=>{"$addToSet"=>"$states"}}}] - -Alternatively, standard MongoDB aggregation pipeline syntax may be used: - -.. code-block:: ruby - - criteria = Tour.all.group(_id: 'states', states: {'$addToSet' => '$states'}) - - -project -``````` - -The ``project`` method adds a `$project aggregation pipeline stage -`_. - -The argument should be a Hash specifying the projection: - -.. code-block:: ruby - - criteria = Tour.all.project(_id: 0, states: 1) - criteria.pipeline - # => [{"$project"=>{"_id"=>0, "states"=>1}}] - - -.. _unwind-dsl: - -unwind -`````` - -The ``unwind`` method adds an `$unwind aggregation pipeline stage -`_. - -The argument can be a field name, specifiable as a symbol or a string, or -a Hash or a ``BSON::Document`` instance: - -.. code-block:: ruby - - criteria = Tour.all.unwind(:states) - criteria = Tour.all.unwind('states') - criteria.pipeline - # => [{"$unwind"=>"$states"}] - - criteria = Tour.all.unwind(path: '$states') - criteria.pipeline - # => [{"$unwind"=>{:path=>"$states"}}] diff --git a/source/reference/associations.txt b/source/reference/associations.txt deleted file mode 100644 index dbe95cac..00000000 --- a/source/reference/associations.txt +++ /dev/null @@ -1,1701 +0,0 @@ -.. _associations: - -************ -Associations -************ - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 2 - :class: singlecol - -Referenced Associations -======================= - -Mongoid supports the ``has_one``, ``has_many``, ``belongs_to`` and -``has_and_belongs_to_many`` associations familiar to ActiveRecord users. - - -Has One -------- - -Use the ``has_one`` macro to declare that the parent has a child stored in -a separate collection. The child is optional by default: - -.. code-block:: ruby - - class Band - include Mongoid::Document - - has_one :studio - end - -When using ``has_one``, the child model must use ``belongs_to`` to declare the -association with the parent: - -.. code-block:: ruby - - class Studio - include Mongoid::Document - - belongs_to :band - end - -Given the above definitions, every child document contains a reference to -its respective parent document: - -.. code-block:: ruby - - band = Band.create!(studio: Studio.new) - # => # - - band.studio - # => # - -Use validations to require that the child is present: - -.. code-block:: ruby - - class Band - include Mongoid::Document - - has_one :studio - - validates_presence_of :studio - end - - -Has Many --------- - -Use the ``has_many`` association to declare that the parent has zero or more -children stored in a separate collection: - -.. code-block:: ruby - - class Band - include Mongoid::Document - - has_many :members - end - -Like with ``has_one``, the child model must use ``belongs_to`` to declare the -association with the parent: - -.. code-block:: ruby - - class Member - include Mongoid::Document - - belongs_to :band - end - -Also as with ``has_one``, the child documents contain references to their -respective parents: - -.. code-block:: ruby - - band = Band.create!(members: [Member.new]) - # => # - - band.members - # => [#] - -Use validations to require that at least one child is present: - -.. code-block:: ruby - - class Band - include Mongoid::Document - - has_many :members - - validates_presence_of :members - end - - -Queries -``````` - -.. _has-many-any: - -``any?`` -~~~~~~~~ - -Use the ``any?`` method on the association to efficiently determine whether -the association contains any documents, without retrieving the entire set -of documents from the database: - -.. code-block:: ruby - - band = Band.first - band.members.any? - -``any?`` also implements the `Enumerable#any? API -`_, allowing -filtering with a block: - -.. code-block:: ruby - - band = Band.first - band.members.any? { |member| member.instrument == 'piano' } - -... or by a class name which can be useful for polymorphic associations: - -.. code-block:: ruby - - class Drummer < Member - end - - band = Band.first - band.members.any?(Drummer) - -If the association is already loaded, ``any?`` inspects the loaded -documents and does not query the database: - -.. code-block:: ruby - - band = Band.first - # Queries the database - band.members.any? - - band.members.to_a - - # Does not query the database - band.members.any? - -Note that simply calling ``any?`` would *not* load the association -(since ``any?`` only retrieves the _id field of the first matching document). - -``exists?`` -~~~~~~~~~~~ - -The ``exists?`` method on the association determines whether there are -any *persisted* documents in the association. Unlike the ``any?`` method: - -- ``exists?`` always queries the database, even if the association is already - loaded. -- ``exists?`` does not consider non-persisted documents. -- ``exists?`` does not allow filtering in the application like ``any?`` does, - and does not take any arguments. - -The following example illustrates the difference between ``exists?`` and -``any?``: - -.. code-block:: ruby - - band = Band.create! - # Member is not persisted. - band.members.build - - band.members.any? - # => true - band.members.exists? - # => false - - # Persist the member. - band.members.map(&:save!) - - band.members.any? - # => true - band.members.exists? - # => true - - -Belongs To ----------- - -Use the ``belongs_to`` macro to associate a child with a parent stored in a -separate collection. The ``_id`` of the parent (if a parent is associated) -is stored in the child. - -By default, if a ``belongs_to`` association is defined on a model, it must be -provided a value for a model instance to be saved. Use the ``optional: true``` -option to make the instances persistable without specifying the parent: - -.. code-block:: ruby - - class Band - include Mongoid::Document - - has_one :studio - end - - class Studio - include Mongoid::Document - - belongs_to :band, optional: true - end - - studio = Studio.create! - # => # - -To change the default behavior of ``belongs_to`` associations to not require -their respective parents globally, set the ``belongs_to_required_by_default`` -:ref:`configuration option ` to ``false``. - -Although ``has_one`` and ``has_many`` associations require the -corresponding ``belongs_to`` association to be defined on the child, -``belongs_to`` may also be used without a corresponding ``has_one`` or -``has_many`` macro. In this case the child is not accessible from the parent -but the parent is accessible from the child: - -.. code-block:: ruby - - class Band - include Mongoid::Document - end - - class Studio - include Mongoid::Document - - belongs_to :band - end - -For clarity it is possible to add the ``inverse_of: nil`` option in cases when -the parent does not define the association: - -.. code-block:: ruby - - class Band - include Mongoid::Document - end - - class Studio - include Mongoid::Document - - belongs_to :band, inverse_of: nil - end - - -Has And Belongs To Many ------------------------ - -Use the ``has_and_belongs_to_many`` macro to declare a many-to-many -association: - -.. code-block:: ruby - - class Band - include Mongoid::Document - - has_and_belongs_to_many :tags - end - - class Tag - include Mongoid::Document - - has_and_belongs_to_many :bands - end - -Both model instances store a list of ids of the associated models, if any: - -.. code-block:: ruby - - band = Band.create!(tags: [Tag.create!]) - # => # - - band.tags - # => [#] - -You can create a one-sided ``has_and_belongs_to_many`` association to store -the ids only in one document using the ``inverse_of: nil`` option: - -.. code-block:: ruby - - class Band - include Mongoid::Document - - has_and_belongs_to_many :tags, inverse_of: nil - end - - class Tag - include Mongoid::Document - end - - band = Band.create!(tags: [Tag.create!]) - # => # - - band.tags - # => [#] - -A one-sided ``has_and_belongs_to_many`` association is, naturally, only -usable from the model where it is defined. - -.. note:: - - Given two models, A and B where A ``has_and_belongs_to_many`` B, - when adding a document of type B to the HABTM association on a document of - type A, Mongoid will not update the ``updated_at`` field for the document of - type A, but will update the ``updated_at`` field for the document of type B. - -Querying Referenced Associations --------------------------------- - -In most cases, efficient queries across referenced associations (and in general -involving data or conditions or multiple collections) are performed using -the aggregation pipeline. Mongoid helpers for constructing aggregation pipeline -queries are described in the :ref:`aggregation pipeline ` -section. - -For simple queries, the use of aggregation pipeline may be avoided and -associations may be queried directly. When querying associations directly, -all conditions must be on that association's collection only (which typically -means association in question and any associations embedded in it). - -For example, given the following models: - -.. code-block:: ruby - - class Band - include Mongoid::Document - - has_many :tours - has_many :awards - - field :name, type: String - end - - class Tour - include Mongoid::Document - - belongs_to :band - - field :year, type: Integer - end - - class Award - include Mongoid::Document - - belongs_to :band - - field :name, type: String - end - -One could retrieve all bands that have toured since 2000 as follows: - -.. code-block:: ruby - - band_ids = Tour.where(year: {'$gte' => 2000}).pluck(:band_id) - bands = Band.find(band_ids) - -The conditions on ``Tour`` can be arbitrarily complex, but they must all -be on the same ``Tour`` document (or documents embedded in ``Tour``). - -To find awards for bands that have toured since 2000: - -.. code-block:: ruby - - band_ids = Tour.where(year: {'$gte' => 2000}).pluck(:band_id) - awards = Award.where(band_id: {'$in' => band_ids}) - - -Embedded Associations -===================== - -Thanks to MongoDB's document model, Mongoid also offers embedded associations -which allow documents of different types to be stored hierarchically -in the same collection. Embedded associations are defined using -``embeds_one``, ``embeds_many`` and ``embedded_in`` macros, plus -``recursively_embeds_one`` and ``recursively_embeds_many`` for recursive -embedding. - -Embeds One ----------- - -One to one associations where the children are embedded in the parent -document are defined using Mongoid's ``embeds_one`` and ``embedded_in`` macros. - -Defining -```````` - -The parent document of the association should use the ``embeds_one`` macro to -indicate is has one embedded child, where the document that is embedded uses -``embedded_in``. Definitions are required on both sides to the association -in order for it to work properly. - -.. code-block:: ruby - - class Band - include Mongoid::Document - embeds_one :label - end - - class Label - include Mongoid::Document - field :name, type: String - embedded_in :band - end - -Storage -``````` - -Documents that are embedded using the ``embeds_one`` macro are stored as a -hash inside the parent in the parent's database collection. - -.. code-block:: ruby - - { - "_id" : ObjectId("4d3ed089fb60ab534684b7e9"), - "label" : { - "_id" : ObjectId("4d3ed089fb60ab534684b7e0"), - "name" : "Mute", - } - } - -You can optionally tell Mongoid to store the embedded document in a different -attribute other than the name, by providing the ``:store_as`` option. - -.. code-block:: ruby - - class Band - include Mongoid::Document - embeds_one :label, store_as: "lab" - end - - -Embeds Many ------------ - -One to many relationships where the children are embedded in the parent -document are defined using Mongoid's ``embeds_many`` and ``embedded_in`` macros. - -Defining -```````` - -The parent document of the association should use the ``embeds_many`` macro -to indicate it has many embedded children, where the document that is -embedded uses ``embedded_in``. Definitions are required on both sides of -the association in order for it to work properly. - -.. code-block:: ruby - - class Band - include Mongoid::Document - embeds_many :albums - end - - class Album - include Mongoid::Document - field :name, type: String - embedded_in :band - end - -Storage -``````` - -Documents that are embedded using the ``embeds_many`` macro are stored as -an array of hashes inside the parent in the parent's database collection. - -.. code-block:: ruby - - { - "_id" : ObjectId("4d3ed089fb60ab534684b7e9"), - "albums" : [ - { - "_id" : ObjectId("4d3ed089fb60ab534684b7e0"), - "name" : "Violator", - } - ] - } - -You can optionally tell Mongoid to store the embedded document in a different -attribute other than the name, by providing the ``:store_as`` option. - -.. code-block:: ruby - - class Band - include Mongoid::Document - embeds_many :albums, store_as: "albs" - end - -Recursive Embedding -------------------- - -A document can recursively embed itself using ``recursively_embeds_one`` or -``recursively_embeds_many``, which provides accessors for the parent and -children via ``parent_`` and ``child_`` methods. - -.. code-block:: ruby - - class Tag - include Mongoid::Document - field :name, type: String - recursively_embeds_many - end - - root = Tag.new(name: "programming") - child_one = root.child_tags.build - child_two = root.child_tags.build - - root.child_tags # [ child_one, child_two ] - child_one.parent_tag # [ root ] - child_two.parent_tag # [ root ] - - class Node - include Mongoid::Document - recursively_embeds_one - end - - root = Node.new - child = Node.new - root.child_node = child - - root.child_node # child - child.parent_node # root - -Referencing Vs Embedding ------------------------- - -While a complete discussion of referencing vs embedding is beyond the scope -of this tutorial, here are some high level considerations for choosing -one over the other. - -When an association is embedded, both parent and child documents are stored -in the same collection. This permits efficient persistence and retrieval -when both are used/needed. For example, if the navigation bar on a web site -shows attributes of a user that are stored in documents themselves, it is -often a good idea to use embedded associations. - -Using embedded associations allows using MongoDB tools like the -`aggregation pipeline -`_ to query -these documents in a powerful way. - -Because embedded documents are stored as part of their parent top-level -documents, it is not possible to persist an embedded document by itself, -nor is it possible to retrieve embedded documents directly. However, -embedded documents can still be efficiently queried and retrieved with the -help of MongoDB projection operation: - -.. code-block:: ruby - - class Band - include Mongoid::Document - field :started_on, type: Date - embeds_one :label - end - - class Label - include Mongoid::Document - field :name, type: String - embedded_in :band - end - - # Retrieve labels for bands started in the last year. - # - # Sends a find query like this: - # {"find"=>"bands", - # "filter"=>{"started_on"=>{"$gt"=>2018-07-01 00:00:00 UTC}}, - # "projection"=>{"_id"=>1, "label"=>1}} - Band.where(started_on: {'$gt' => Time.now - 1.year}).only(:label).map(&:label).compact.uniq - -Setting Stale Values on Referenced Associations -``````````````````````````````````````````````` - -Setting a stale value to a referenced association can sometimes result in -a ``nil`` value being persisted to the database. Take the following case: - -.. code-block:: ruby - - class Post - include Mongoid::Document - - has_one :comment, inverse_of: :post - end - - class Comment - include Mongoid::Document - - belongs_to :post, inverse_of: :comment, optional: true - end - - post.comment = comment1 - post.reload - -At this point, ``post.comment`` is set to ``comment1``, however since a reload -happened, ``post.comment`` does not refer to the same object as ``comment1``. -Meaning, updating one object does not implicitly update the other. This matters -for the next operation: - -.. code-block:: ruby - - post.comment = comment2 - post.reload - -Now, ``post.comment`` is set to ``comment2``, and the ``post_id`` of the old -comment is set to ``nil``. However, the value that was assigned to -``post.comment`` did not refer to the same object as ``comment1``, therefore, -while the old value of ``post.comment`` was updated to have a ``nil`` -``post_id``, ``comment1`` still has the ``post_id`` set. - -.. code-block:: ruby - - post.comment = comment1 - post.reload - -Finally, this last assignment attempts to set the ``post_id`` on ``comment1``, -which should be ``nil`` at this point, but is set to the old ``post_id``. -During this operation, the ``post_id`` is cleared from ``comment2``, and the -new ``post_id`` is set on ``comment1``. However, since the ``post_id`` was -already set on ``comment1``, nothing is persisted, and we end up with both -comments having a ``nil`` ``post_id``. At this point, running ``post.comment`` -returns ``nil``. - - -Querying Embedded Associations ------------------------------- - -When querying top-level documents, conditions can be specified on documents -in embedded associations using the dot notation. For example, given the -following models: - -.. code-block:: ruby - - class Band - include Mongoid::Document - embeds_many :tours - embeds_many :awards - field :name, type: String - end - - class Tour - include Mongoid::Document - embedded_in :band - field :year, type: Integer - end - - class Award - include Mongoid::Document - embedded_in :band - field :name, type: String - end - -To retrieve bands based on tour attributes, use the dot notation as follows: - -.. code-block:: ruby - - # Get all bands that have toured since 2000 - Band.where('tours.year' => {'$gte' => 2000}) - -To retrieve only documents of embedded associations, without retrieving -top-level documents, use the ``pluck`` projection method: - -.. code-block:: ruby - - # Get awards for bands that have toured since 2000 - Band.where('tours.year' => {'$gte' => 2000}).pluck(:awards) - -.. _embedded-matching: - -Querying Loaded Associations -```````````````````````````` - -Mongoid query methods can be used on embedded associations of documents which -are already loaded in the application. This mechanism is called -"embedded matching" and it is implemented entirely in Mongoid--the queries -are NOT sent to the server. - -The following operators are supported: - -- :manual:`Comparison operators ` -- :manual:`Logical operators ` -- :manual:`Array query operators ` -- :manual:`$exists ` -- :manual:`$mod ` -- :manual:`$type ` -- :manual:`$regex ` (``$options`` field - is only supported when the ``$regex`` argument is a string) -- :manual:`Bitwise operators ` - -For example, using the model definitions just given, we could query -tours on a loaded band: - -.. code-block:: ruby - - band = Band.where(name: 'Astral Projection').first - tours = band.tours.where(year: {'$gte' => 2000}) - -Embedded Matching vs Server Behavior -```````````````````````````````````` - -Mongoid's embedded matching aims to support the same functionality and -semantics as native queries on the latest MongoDB server version. -Note the following known limitations: - -- Embedded matching is not implemented for :ref:`text search `, - :manual:`geospatial query operators `, - operators that execute JavaScript code (:manual:`$where `) - and operators that are implemented via other server functionality such as - :manual:`$expr ` - and :manual:`$jsonSchema `. - -- Mongoid DSL expands ``Range`` arguments to hashes with ``$gte`` and ``$lte`` - conditions. `In some cases `_ - this creates bogus queries. Embedded matchers raise the ``InvalidQuery`` - exception in these cases. The operators that are known to be affected are - ``$elemMatch``, ``$eq``, ``$gt``, ``$gte``, ``$lt``, ``$lte`` and ``$ne``. - -- When performing embedded matching with ``$regex``, `it is not currently - possible `_ to specify a - regular expression object as the pattern and also provide options. - -- MongoDB Server 4.0 and earlier servers do not validate ``$type`` arguments strictly - (for example, allowing invalid arguments like 0). This is validated more strictly on - the client side. - - -.. _omit-id: - -Omitting ``_id`` Fields ------------------------ - -By default, Mongoid adds an ``_id`` field to each embedded document. This -permits easy referencing of and operations on the embedded documents. - -These ``_id`` fields may be omitted to save storage space. To do so, -:ref:`override the _id field definition in the child documents ` -and remove the default value: - -.. code-block:: ruby - - class Order - include Mongoid::Document - - embeds_many :line_items - end - - class LineItem - include Mongoid::Document - - embedded_in :order - - field :_id, type: Object - end - -In the current version of Mongoid the field definition is required, but -without a default value specified no value will be stored in the database. -A future version of Mongoid may allow removing previously defined fields. - -.. note:: - - Removing the ``_id`` field means that embedded documents must be identified - by their content attribute values during queries, updates and deletes. - - -Deleting --------- - -Mongoid provides three methods for deleting children from ``embeds_many`` -associations: ``clear``, ``destroy_all`` and ``delete_all``. - -``clear`` -````````` - -The ``clear`` method uses the :manual:`$unset operator -` to remove the entire association from the -host document. It does not run destroy callbacks on the documents being removed, -acting like ``delete_all`` in this regard: - -.. code-block:: ruby - - band = Band.find(...) - band.tours.clear - -If ``clear`` is called on an association in an unsaved host document, it will -still try to remove the association from the database based on the host -document's ``_id``: - -.. code-block:: ruby - - band = Band.find(...) - band.tours << Tour.new(...) - - unsaved_band = Band.new(id: band.id, tours: [Tour.new]) - # Removes all tours from the persisted band due to _id match. - unsaved_band.tours.clear - - band.tours - # => [] - -``delete_all`` -`````````````` - -The ``delete_all`` method removes the documents that are in the association -using the :manual:`$pullAll operator `. -Unlike ``clear``, ``delete_all``: - -- Loads the association, if it wasn't yet loaded; -- Only removes the documents that exist in the application. - -``delete_all`` does not run destroy callbacks on the documents being removed. - -Example: - -.. code-block:: ruby - - band = Band.find(...) - band.tours.delete_all - - -``destroy_all`` -``````````````` - -The ``delete_all`` method removes the documents that are in the association -using the :manual:`$pullAll operator ` -while running the destroy callbacks. Like ``delete_all``, ``destroy_all`` -loads the entire association if it wasn't yet loaded and it only removes -documents that exist in the application: - -.. code-block:: ruby - - band = Band.find(...) - band.tours.destroy_all - - -.. _hash-assignment: - -Hash Assignment ---------------- - -Embedded associations allow the user to assign a ``Hash`` instead of a document -to an association. On assignment, this hash is coerced into a document of the -class of the association that it's being assigned to. Take the following -example: - -.. code:: ruby - - class Band - include Mongoid::Document - embeds_many :albums - end - - class Album - include Mongoid::Document - field :name, type: String - embedded_in :band - end - - band = Band.create! - band.albums = [ { name: "Narrow Stairs" }, { name: "Transatlanticism" } ] - p band.albums - # => [ #, # ] - -This works for ``embeds_one``, ``embeds_many``, and ``embedded_in`` associations. -Note that you cannot assign hashes to referenced associations. - - -Common Behavior -=============== - -Extensions ----------- - -All associations can have extensions, which provides a way to add application specific -functionality to the association. They are defined by providing a block to the association definition. - -.. code-block:: ruby - - class Person - include Mongoid::Document - embeds_many :addresses do - def find_by_country(country) - where(country: country).first - end - def chinese - _target.select { |address| address.country == "China" } - end - end - end - - person.addresses.find_by_country("Mongolia") # returns address - person.addresses.chinese # returns [ address ] - -Custom Association Names ------------------------- - -You can name your associations whatever you like, but if the class cannot be inferred by -Mongoid from the name, and neither can the opposite side you'll want to provide the -macro with some additional options to tell Mongoid how to hook them up. - -.. code-block:: ruby - - class Car - include Mongoid::Document - embeds_one :engine, class_name: "Motor", inverse_of: :machine - end - - class Motor - include Mongoid::Document - embedded_in :machine, class_name: "Car", inverse_of: :engine - end - -Custom Primary & Foreign Keys ------------------------------ - -The fields used when looking up associations can be explicitly specified. -The default is to use ``id`` on the "parent" association and ``#{association_name}_id`` -on the "child" association, for example with a has_many/belongs_to: - -.. code-block:: ruby - - class Company - include Mongoid::Document - has_many :emails - end - - class Email - include Mongoid::Document - belongs_to :company - end - - company = Company.find(id) - # looks up emails where emails.company_id == company.id - company.emails - -Specify a different ``primary_key`` to change the field name on the "parent" -association and ``foreign_key`` to change the field name on the "child" -association: - -.. code-block:: ruby - - class Company - include Mongoid::Document - field :c, type: String - has_many :emails, foreign_key: 'c_ref', primary_key: 'c' - end - - class Email - include Mongoid::Document - # This definition of c_ref is automatically generated by Mongoid: - # field :c_ref, type: Object - # But the type can also be specified: - field :c_ref, type: String - belongs_to :company, foreign_key: 'c_ref', primary_key: 'c' - end - - company = Company.find(id) - # looks up emails where emails.c_ref == company.c - company.emails - -With a has_and_belongs_to_many association, since the data is stored on both -sides of the association, there are 4 fields configurable when the association -is defined: - -- ``:primary_key`` is the field on the remote model that contains the value - by which the remote model is looked up. -- ``:foreign_key`` is the field on the local model which stores the - ``:primary_key`` values. -- ``:inverse_primary_key`` is the field on the local model that the remote - model uses to look up the local model documents. -- ``:inverse_foreign_key`` is the field on the remote model storing the - values in ``:inverse_primary_key``. - -An example might make this more clear: - -.. code-block:: ruby - - class Company - include Mongoid::Document - - field :c_id, type: Integer - field :e_ids, type: Array - - has_and_belongs_to_many :employees, - primary_key: :e_id, foreign_key: :e_ids, - inverse_primary_key: :c_id, inverse_foreign_key: :c_ids - end - - class Employee - include Mongoid::Document - - field :e_id, type: Integer - field :c_ids, type: Array - - has_and_belongs_to_many :companies, - primary_key: :c_id, foreign_key: :c_ids, - inverse_primary_key: :e_id, inverse_foreign_key: :e_ids - end - - company = Company.create!(c_id: 123) - # => # - - employee = Employee.create!(e_id: 456) - # => # - - company.employees << employee - - company - # => # - - employee - # => # - -Note that just like with the default ``#{association_name}_id`` field, -Mongoid automatically adds a field for the custom foreign key ``c_ref`` to -the model. However, since Mongoid doesn't know what type of data should be -allowed in the field, the field is created with a type of Object. It is a -good idea to explicitly define the field with the appropriate type. - - -.. _association-scope: - -Custom Scopes -------------- - -You may set a specific scope on an association using the ``:scope`` parameter. -The scope is an additional filter that restricts which objects are considered -to be a part of the association - a scoped association will return only -documents which satisfy the scope condition.. The scope may be either: - -- a ``Proc`` with arity zero, or -- a ``Symbol`` which references a :ref:`named scope ` on the - associated model. - -.. code-block:: ruby - - class Trainer - has_many :pets, scope: -> { where(species: 'dog') } - has_many :toys, scope: :rubber - end - - class Pet - belongs_to :trainer - end - - class Toy - scope :rubber, where(material: 'rubber') - belongs_to :trainer - end - -.. note:: - - It is possible to add documents that do not satisfy an association's scope - to that association. In this case, such documents will appear associated - in memory, and will be saved to the database, but will not be present when - the association is queried in the future. For example: - - .. code-block:: ruby - - trainer = Trainer.create! - dog = Pet.create!(trainer: trainer, species: 'dog') - cat = Pet.create!(trainer: trainer, species: 'cat') - - trainer.pets #=> [dog, cat] - - trainer.reload.pets #=> [dog] - -.. note:: - - Mongoid's syntax for scoped association differs from that of ActiveRecord. - Mongoid uses the ``:scope`` keyword argument for consistency with other - association options, whereas in ActiveRecord the scope is a positional - argument. - - -Validations ------------ - -It is important to note that by default, Mongoid will validate the children of any -association that are loaded into memory via a ``validates_associated``. The associations that -this applies to are: - -- ``embeds_many`` -- ``embeds_one`` -- ``has_many`` -- ``has_one`` -- ``has_and_belongs_to_many`` - -If you do not want this behavior, you may turn it off when defining the association. - -.. code-block:: ruby - - class Person - include Mongoid::Document - - embeds_many :addresses, validate: false - has_many :posts, validate: false - end - - -Polymorphism ------------- - -One to one and one to many associations support polymorphism, which is -having a single association potentially contain objects of different classes. -For example, we could model an organization in which departments and teams -have managers as follows: - -.. code-block:: ruby - - class Department - include Mongoid::Document - - has_one :manager, as: :unit - end - - class Team - include Mongoid::Document - - has_one :manager, as: :unit - end - - class Manager - include Mongoid::Document - - belongs_to :unit, polymorphic: true - end - - dept = Department.create! - team = Team.create! - - alice = Manager.create!(unit: dept) - alice.unit == dept - # => true - dept.manager == alice - # => true - -To provide another example, suppose we want to track price history for -products and bundles. This can be achieved via an embedded one to many -polymorphic association: - -.. code-block:: ruby - - class Product - include Mongoid::Document - - field :name, type: String - has_and_belongs_to_many :bundles - - embeds_many :prices, as: :item - end - - class Bundle - include Mongoid::Document - - field :name, type: String - has_and_belongs_to_many :products - - embeds_many :prices, as: :item - end - - class Price - include Mongoid::Document - - embedded_in :item, polymorphic: true - end - - pants = Product.create!(name: 'Pants', - prices: [Price.new, Price.new]) - costume = Bundle.create!(name: 'Costume', products: [pants], - prices: [Price.new, Price.new]) - -To define a polymorphic association, specify the ``polymorphic: true`` option -on the child association and add the ``as: :association_name`` option to the -parent association. - -Note that Mongoid currently supports polymorphism only in one direction - from -the child to the parent. For example, polymorphism cannot be used to specify -that a bundle may contain other bundles or products: - -.. code-block:: ruby - - class Bundle - include Mongoid::Document - - # Does not work: - has_many :items, polymorphic: true - end - -Starting in version 9.0.2, Mongoid adds support for custom polymorphic types through -a global registry. You can specify alternative keys to represent different -classes, thus decoupling the code from the data. The following example specifies -the string ``"dept"`` as an alternate key for the ``Department`` class: - -.. code-block:: ruby - - class Department - include Mongoid::Document - identify_as 'dept' - has_many :managers, as: :unit - end - -In the preceding example, the ``identify_as 'dept'`` directive instructs Mongoid -to store this class in the database as the string ``"dept"``. You can also specify -multiple aliases. For example, you can specify the option as: -``identify_as 'dept', 'div', 'agency'``, in which case the first key is the "default", -and the others are used only for looking up records. This lets you refactor your -code without breaking the associations in your data. - -Polymorphic type aliases are global. The keys you specify must be unique across your -entire code base. However, it is possible to register alternative resolvers, which -may be used for different subsets of your models. In this case, the keys must -only be unique for each resolver. The following example shows how to register -alternate resolvers: - -.. code-block:: ruby - - Mongoid::ModelResolver.register_resolver Mongoid::ModelResolver.new, :eng - Mongoid::ModelResolver.register_resolver Mongoid::ModelResolver.new, :purch - - module Engineering - class Department - include Mongoid::Document - identify_as 'dept', resolver: :eng - end - - module Purchasing - class Department - include Mongoid::Document - identify_as 'dept', resolver: :purch - end - - -Both ``Engineering::Department`` and ``Purchasing::Department`` are aliased as -``"dept"``, but use their own resolver to avoid conflicts. - -``has_and_belongs_to_many`` associations do not support polymorphism. - - -Cascading Callbacks -------------------- - -If you want the embedded document callbacks to fire when calling a persistence -operation on its parent, you will need to provide the cascade callbacks option -to the association. - -.. code-block:: ruby - - class Band - include Mongoid::Document - embeds_many :albums, cascade_callbacks: true - embeds_one :label, cascade_callbacks: true - end - - band.save # Fires all save callbacks on the band, albums, and label. - - -.. _dependent-behavior: - -Dependent Behavior ------------------- - -You can provide dependent options to referenced associations to instruct Mongoid -how to handle situations where one side of the association is deleted, or is attempted -to be deleted. The options are as follows: - -- ``:delete_all``: Delete the child document(s) without running any of the model callbacks. -- ``:destroy``: Destroy the child document(s) and run all of the model callbacks. -- ``:nullify``: Set the foreign key field of the child document to nil. The child may become orphaned if it is ordinarily only referenced via the parent. -- ``:restrict_with_exception``: ``raise`` an error if the child is not empty. -- ``:restrict_with_error``: Cancel operation and return false if the child is not empty. - -If no ``:dependent`` option is provided, deleting the parent document leaves the child document unmodified -(in other words, the child document continues to reference the now deleted parent document via the foreign key field). -The child may become orphaned if it is ordinarily only referenced via the parent. - -.. code-block:: ruby - - class Band - include Mongoid::Document - has_many :albums, dependent: :delete_all - belongs_to :label, dependent: :nullify - end - - class Album - include Mongoid::Document - belongs_to :band - end - - class Label - include Mongoid::Document - has_many :bands, dependent: :restrict_with_exception - end - - label = Label.first - label.bands.push(Band.first) - label.delete # Raises an error since bands is not empty. - - Band.first.destroy # Will delete all associated albums. - - -Autosaving ----------- - -One core difference between Mongoid and ActiveRecord is that Mongoid does not -automatically save associated documents for referenced (i.e., non-embedded) -associations when the parent is saved, for performance reasons. - -If autosaving is not used, it is possible to create dangling references -to non-existent documents via associations: - -.. code-block:: ruby - - class Band - include Mongoid::Document - - has_many :albums - end - - class Album - include Mongoid::Document - - belongs_to :band - end - - band = Band.new - album = Album.create!(band: band) - - # The band is not persisted at this point. - - album.reload - - album.band_id - # => BSON::ObjectId('6257699753aefe153121a3d5') - - # Band does not exist. - album.band - # => nil - -To make referenced associations save automatically when the parent is saved, -add the ``:autosave`` option to the association: - -.. code-block:: ruby - - class Band - include Mongoid::Document - - has_many :albums - end - - class Album - include Mongoid::Document - - belongs_to :band, autosave: true - end - - band = Band.new - album = Album.create!(band: band) - - # The band is persisted at this point. - - album.reload - - album.band_id - # => BSON::ObjectId('62576b4b53aefe178b65b8e3') - - album.band - # => # - -The autosaving functionality is automatically added to an association when -using ``accepts_nested_attributes_for``, so that the application does not -need to track which associations were modified when processing a form -submission. - -Embedded associations always autosave, because they are stored as part of the -parent document. - -Some operations on associations always save the parent and the child documents -as part of the operation, regardless of whether autosaving is enabled. -A non-exhaustive list of these operations is as follows: - -- Assignment to the association: - - .. code-block:: ruby - - # Saves the band and the album. - band.albums = [Album.new] - -- ``push``, ``<<``: - - .. code-block:: ruby - - band.albums << Album.new - band.albums.push(Album.new) - - -Existence Predicates --------------------- - -All associations have existence predicates on them in the form of ``name?`` and ``has_name?`` -to check if the association is blank. - -.. code-block:: ruby - - class Band - include Mongoid::Document - embeds_one :label - embeds_many :albums - end - - band.label? - band.has_label? - band.albums? - band.has_albums? - -Autobuilding ------------- - -One to one associations (``embeds_one``, ``has_one``) have an autobuild option which tells -Mongoid to instantiate a new document when the association is accessed and it is ``nil``. - -.. code-block:: ruby - - class Band - include Mongoid::Document - embeds_one :label, autobuild: true - has_one :producer, autobuild: true - end - - band = Band.new - band.label # Returns a new empty label. - band.producer # Returns a new empty producer. - -Touching --------- -Any ``belongs_to`` association can take an optional ``:touch`` option which -will cause the parent document to be touched whenever the child document is -updated: - -.. code-block:: ruby - - class Band - include Mongoid::Document - field :name - belongs_to :label, touch: true - end - - band = Band.first - band.name = "The Rolling Stones" - band.save! # Calls touch on the parent label. - band.touch # Calls touch on the parent label. - -``:touch`` can also take a string or symbol argument specifying a field to -be touched on the parent association in addition to updated_at: - -.. code-block:: ruby - - class Label - include Mongoid::Document - include Mongoid::Timestamps - field :bands_updated_at, type: Time - has_many :bands - end - - class Band - include Mongoid::Document - belongs_to :label, touch: :bands_updated_at - end - - label = Label.create! - band = Band.create!(label: label) - - band.touch # Updates updated_at and bands_updated_at on the label. - -When an embedded document is touched, its parents are recursively touched -through the composition root (because all of the parents are necessarily saved -when the embedded document is saved). The ``:touch`` attribute therefore is -unnecessary on ``embedded_in`` associations. - -Mongoid currently `does not support specifying an additional field to be -touched on an embedded_in association `_. - -``:touch`` should not be set to ``false`` on an ``embedded_in`` association, -since composition hierarchy is always updated upon a touch of an embedded -document. This is currently not enforced but enforcement is `intended in the -future `_. - -The counter_cache Option ------------------------- - -As with ActiveRecord, the ``:counter_cache`` option can be used on an association -to make finding the number of belonging objects more efficient. Also similar -to ActiveRecord, you must take into account that there will be an extra -attribute on the associated model. This means that with Mongoid, -you need to include ``Mongoid::Attributes::Dynamic`` on the associated model. -For example: - -.. code-block:: ruby - - class Order - include Mongoid::Document - belongs_to :customer, counter_cache: true - end - - class Customer - include Mongoid::Document - include Mongoid::Attributes::Dynamic - has_many :orders - end - -Association Proxies -------------------- - -Associations employ transparent proxies to the target objects. This can -cause surprising behavior in some situations. - -The method visibility may be lost when methods on association targets are -accessed, depending on the association: - -.. code-block:: ruby - - class Order - include Mongoid::Document - belongs_to :customer - - private - - def internal_status - 'new' - end - end - - class Customer - include Mongoid::Document - has_many :orders - - private - - def internal_id - 42 - end - end - - order = Order.new - customer = Customer.create!(orders: [order]) - - # has_many does not permit calling private methods on the target - customer.orders.first.internal_status - # NoMethodError (private method `internal_status' called for #) - - # belongs_to permits calling private methods on the target - order.customer.internal_id - # => 42 - -Association Metadata -==================== - -All associations in Mongoid contain metadata that holds information about the association in -question, and is a valuable tool for third party developers to use to extend Mongoid. - -You can access the association metadata of the association in a few different ways. - -.. code-block:: ruby - - # Get the metadata for a named association from the class or document. - Model.reflect_on_association(:association_name) - model.reflect_on_association(:association_name) - - # Get the metadata with a specific association itself on a specific - # document. - model.associations[:association_name] - -Attributes ----------- - -All associations contain a ``_target``, which is the proxied document or documents, a ``_base`` -which is the document the association hangs off, and ``_association`` which provides information -about the association. - -.. code-block:: ruby - - class Person - include Mongoid::Document - embeds_many :addresses - end - - person.addresses = [ address ] - person.addresses._target # returns [ address ] - person.addresses._base # returns person - person.addresses._association # returns the association metadata - -The Association Object ----------------------- - -The association object itself contains more information than one might know what to do -with, and is useful for developers of extensions to Mongoid. - - -.. list-table:: - :header-rows: 1 - :widths: 30 60 - - * - Method - - Description - * - ``Association#as`` - - Returns the name of the parent to a polymorphic child. - * - ``Association#as?`` - - Returns whether or not an as option exists. - * - ``Association#autobuilding?`` - - Returns whether or not the association is autobuilding. - * - ``Association#autosaving?`` - - Returns whether or not the association is autosaving. - * - ``Association#cascading_callbacks?`` - - Returns whether the association has callbacks cascaded down from the parent. - * - ``Association#class_name`` - - Returns the class name of the proxied document. - * - ``Association#cyclic?`` - - Returns whether the association is a cyclic association. - * - ``Association#dependent`` - - Returns the association's dependent option. - * - ``Association#destructive?`` - - Returns true if the association has a dependent delete or destroy. - * - ``Association#embedded?`` - - Returns whether the association is embedded in another document. - * - ``Association#forced_nil_inverse?`` - - Returns whether the association has a nil inverse defined. - * - ``Association#foreign_key`` - - Returns the name of the foreign key field. - * - ``Association#foreign_key_check`` - - Returns the name of the foreign key field dirty check method. - * - ``Association#foreign_key_setter`` - - Returns the name of the foreign key field setter. - * - ``Association#indexed?`` - - Returns whether the foreign key is auto indexed. - * - ``Association#inverses`` - - Returns the names of all inverse association. - * - ``Association#inverse`` - - Returns the name of a single inverse association. - * - ``Association#inverse_class_name`` - - Returns the class name of the association on the inverse side. - * - ``Association#inverse_foreign_key`` - - Returns the name of the foreign key field on the inverse side. - * - ``Association#inverse_klass`` - - Returns the class of the association on the inverse side. - * - ``Association#inverse_association`` - - Returns the metadata of the association on the inverse side. - * - ``Association#inverse_of`` - - Returns the explicitly defined name of the inverse association. - * - ``Association#inverse_setter`` - - Returns the name of the method used to set the inverse. - * - ``Association#inverse_type`` - - Returns the name for the polymorphic type field of the inverse. - * - ``Association#inverse_type_setter`` - - Returns the name for the polymorphic type field setter of the inverse. - * - ``Association#key`` - - Returns the name of the field in the attributes hash to use to get the association. - * - ``Association#klass`` - - Returns the class of the proxied documents in the association. - * - ``Association#name`` - - Returns the association name. - * - ``Association#options`` - - Returns self, for API compatibility with ActiveRecord. - * - ``Association#order`` - - Returns the custom sorting options on the association. - * - ``Association#polymorphic?`` - - Returns whether the association is polymorphic. - * - ``Association#setter`` - - Returns the name of the field to set the association. - * - ``Association#store_as`` - - Returns the name of the attribute to store an embedded association in. - * - ``Association#touchable?`` - - Returns whether or not the association has a touch option. - * - ``Association#type`` - - Returns the name of the field to get the polymorphic type. - * - ``Association#type_setter`` - - Returns the name of the field to set the polymorphic type. - * - ``Association#validate?`` - - Returns whether the association has an associated validation. diff --git a/source/reference/callbacks.txt b/source/reference/callbacks.txt deleted file mode 100644 index 972451dd..00000000 --- a/source/reference/callbacks.txt +++ /dev/null @@ -1,120 +0,0 @@ -.. _callbacks: - -********* -Callbacks -********* - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 2 - :class: singlecol - -Mongoid implements many of the `ActiveRecord callbacks -`_. - - -Document Callbacks -================== - -Mongoid supports the following callbacks for :doc:`documents `: - -- ``after_initialize`` -- ``after_build`` -- ``before_validation`` -- ``after_validation`` -- ``before_create`` -- ``around_create`` -- ``after_create`` -- ``after_find`` -- ``before_update`` -- ``around_update`` -- ``after_update`` -- ``before_upsert`` -- ``around_upsert`` -- ``after_upsert`` -- ``before_save`` -- ``around_save`` -- ``after_save`` -- ``before_destroy`` -- ``around_destroy`` -- ``after_destroy`` - -Callbacks are available on any document, whether it is embedded within -another document or not. Note that to be efficient, Mongoid only invokes -the callback on the document that the persistence action was executed on. -This enables Mongoid to support large hierarchies and to handle optimized -atomic updates efficiently (without invoking callbacks throughout the document -hierarchy). - -Note that using callbacks for domain logic is a bad design practice, and can -lead to unexpected errors that are hard to debug when callbacks in -the chain halt execution. It is our recommendation to only use them -for cross-cutting concerns, like queueing up background jobs. - -.. code-block:: ruby - - class Article - include Mongoid::Document - field :name, type: String - field :body, type: String - field :slug, type: String - - before_create :send_message - - after_save do |document| - # Handle callback here. - end - - protected - def send_message - # Message sending code here. - end - end - -Callbacks are coming from Active Support, so you can use the new -syntax as well: - -.. code-block:: ruby - - class Article - include Mongoid::Document - field :name, type: String - - set_callback(:create, :before) do |document| - # Message sending code here. - end - end - - -Association Callbacks -===================== - -Mongoid has a set of callbacks that are specific to associations - these are: - -- ``after_add`` -- ``after_remove`` -- ``before_add`` -- ``before_remove`` - -Each time a document is added or removed from any of the following -associations, the respective callbacks are invoked: ``embeds_many``, -``has_many`` and ``has_and_belongs_to_many``. - -Association callbacks are specified as options on the respective association. -The document added/removed will be passed as the parameter to the specified -callback. Example: - -.. code-block:: ruby - - class Person - include Mongoid::Document - - has_many :posts, after_add: :send_email_to_subscribers - end - - def send_email_to_subscribers(post) - Notifications.new_post(post).deliver - end diff --git a/source/reference/collection-configuration.txt b/source/reference/collection-configuration.txt deleted file mode 100644 index 26bea016..00000000 --- a/source/reference/collection-configuration.txt +++ /dev/null @@ -1,107 +0,0 @@ -.. _collection_configuration: - -************************ -Collection Configuration -************************ - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 2 - :class: singlecol - -Configuring a Document Collection -================================= - -You can specify collection options for documents using the ``store_in`` macro. -This macro accepts ``:collection_options`` argument, which can contain any collection -options that are supported by the driver. - -.. note:: - - In order to apply the options, the collection must be explicitly created up-front. - This should be done using :ref:`Collection Management Rake Task`. - -Please refer to `the driver collections page -`_ -for the more information about collection options. - -.. note:: - - Collection options depend on the driver version and MongoDB server version. - It is possible that some options, like time series collections, are not available - on older server versions. - -Time Series Collection ----------------------- - -.. code-block:: ruby - - class Measurement - include Mongoid::Document - - field :temperature, type: Integer - field :timestamp, type: Time - - store_in collection_options: { - time_series: { - timeField: "timestamp", - granularity: "minutes" - }, - expire_after: 604800 - } - end - - - -Capped Collections ------------------- - -.. code-block:: ruby - - class Name - include Mongoid::Document - - store_in collection_options: { - capped: true, - size: 1024 - } - end - -Set a Default Collation on a Collection ---------------------------------------- - -.. code-block:: ruby - - class Name - include Mongoid::Document - - store_in collection_options: { - collation: { - locale: 'fr' - } - } - end - -.. _collection-management-task: - -Collection Management Rake Task -=============================== - -If you specify collection options for a document, then the corresponding collection -must be explicitly created prior to use. To do so, use the provided -``db:mongoid:create_collections`` Rake task: - -.. code-block:: bash - - $ rake db:mongoid:create_collections - -The create collections command also works for just one model by running -in Rails console: - -.. code-block:: ruby - - # Create collection for Model - Model.create_collection diff --git a/source/reference/compatibility.txt b/source/reference/compatibility.txt deleted file mode 100644 index ae867bd1..00000000 --- a/source/reference/compatibility.txt +++ /dev/null @@ -1,535 +0,0 @@ -.. _compatibility: - -************* -Compatibility -************* - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 2 - :class: singlecol - -Ruby MongoDB Driver Compatibility -================================= - -The following compatibility table specifies the versions of `Ruby driver for -MongoDB `_ -(the ``mongo`` gem) supported by the most recent patch releases of the -specified Mongoid versions. - -.. note:: - - Older versions of Mongoid within the same minor release may support older - driver versions. For example, Mongoid 7.0.5 supports driver versions 2.5 and - newer, whereas Mongoid 7.0.6 requires driver version 2.7 or newer. - -.. list-table:: - :header-rows: 1 - :stub-columns: 1 - :class: compatibility-large no-padding - - * - Mongoid - - Driver 2.18+ - - Driver 2.17-2.10 - - Driver 2.9-2.7 - - * - 8.0 through 9.0 - - |checkmark| - - - - - - * - 7.2 through 7.5 - - |checkmark| - - |checkmark| - - - - * - 7.0 through 7.1 - - |checkmark| - - |checkmark| - - |checkmark| - - -Ruby Compatibility -================== - -The following compatibility table specifies the versions of Ruby interpreters -supported by Mongoid. "D" in a column means support for that Ruby version -is deprecated. - -.. list-table:: - :header-rows: 1 - :stub-columns: 1 - :class: compatibility-large no-padding - - * - Mongoid - - Ruby 3.3 - - Ruby 3.2 - - Ruby 3.1 - - Ruby 3.0 - - Ruby 2.7 - - Ruby 2.6 - - Ruby 2.5 - - Ruby 2.4 - - Ruby 2.3 - - Ruby 2.2 - - JRuby 9.4 - - JRuby 9.3 - - JRuby 9.2 - - * - 9.0 - - |checkmark| [#ruby-3.3]_ - - |checkmark| - - |checkmark| - - |checkmark| - - |checkmark| - - - - - - - - - - - - |checkmark| - - - - - - * - 8.1 - - |checkmark| [#ruby-3.3]_ - - |checkmark| - - |checkmark| - - |checkmark| - - |checkmark| - - |checkmark| - - - - - - - - - - - - |checkmark| - - - - * - 8.0 - - - - - - |checkmark| - - |checkmark| - - |checkmark| - - |checkmark| - - - - - - - - - - - - |checkmark| - - - - * - 7.5 - - - - - - |checkmark| - - |checkmark| - - |checkmark| - - |checkmark| - - D - - - - - - - - - - |checkmark| - - D - - * - 7.4 - - - - - - |checkmark| - - |checkmark| - - |checkmark| - - |checkmark| - - |checkmark| - - - - - - - - - - - - |checkmark| - - * - 7.3 - - - - - - |checkmark| [#mongoid-7.3-ruby-3.0]_ - - |checkmark| [#mongoid-7.3-ruby-3.0]_ - - |checkmark| - - |checkmark| - - |checkmark| - - D - - D - - - - - - - - |checkmark| - - * - 7.2 - - - - - - |checkmark| [#mongoid-7.2-ruby-3.0]_ - - |checkmark| [#mongoid-7.2-ruby-3.0]_ - - |checkmark| - - |checkmark| - - |checkmark| - - D - - D - - - - - - - - |checkmark| - - * - 7.1 - - - - - - |checkmark| [#mongoid-7.1-ruby-3.0]_ - - |checkmark| [#mongoid-7.1-ruby-3.0]_ - - |checkmark| - - |checkmark| - - |checkmark| - - |checkmark| [#ruby-2.4]_ - - |checkmark| - - - - - - - - |checkmark| - - * - 7.0 - - - - - - - - - - |checkmark| - - |checkmark| - - |checkmark| - - |checkmark| [#ruby-2.4]_ - - |checkmark| - - |checkmark| [#ruby-2.2]_ - - - - - - |checkmark| - -.. [#ruby-3.3] Ruby 3.3 requires Mongoid 8.1.7 and 9.0.3 in the respective - 8.1 and 9.0 stable branches. - -.. [#mongoid-7.3-ruby-3.0] Mongoid version 7.3.2 or higher is required. - -.. [#mongoid-7.2-ruby-3.0] Mongoid version 7.2.5 or higher is required. - -.. [#mongoid-7.1-ruby-3.0] Mongoid version 7.1.10 or higher is required. - -.. [#ruby-2.4] Ruby version 2.4.1 or higher is required. - -.. [#ruby-2.2] Ruby version 2.2.2 or higher is required. - - -MongoDB Server Compatibility -============================ - -The following compatibility table specifies the recommended -version(s) of Mongoid for use with a specific version of MongoDB server. - -Note that in order to use features of a particular MongoDB server version, -both the driver and Mongoid must support that server version. -Please refer to `the driver compatibility page -`_ -for driver compatibility matrices. - -"D" in a column means support for that MongoDB server version is deprecated -and will be removed in a next version. - -.. list-table:: - :header-rows: 1 - :stub-columns: 1 - :class: compatibility-large no-padding - - * - Mongoid - - MongoDB 8.0 - - MongoDB 7.0 - - MongoDB 6.0 - - MongoDB 5.0 - - MongoDB 4.4 - - MongoDB 4.2 - - MongoDB 4.0 - - MongoDB 3.6 - - MongoDB 3.4 - - MongoDB 3.2 - - MongoDB 3.0 - - MongoDB 2.6 - - * - 9.0+ - - |checkmark| - - |checkmark| - - |checkmark| - - |checkmark| - - |checkmark| - - |checkmark| - - |checkmark| - - |checkmark| - - - - - - - - - - * - 8.0 through 9.0 - - - - |checkmark| - - |checkmark| - - |checkmark| - - |checkmark| - - |checkmark| - - |checkmark| - - |checkmark| - - - - - - - - - - * - 7.4 through 7.5 - - - - |checkmark| - - |checkmark| - - |checkmark| - - |checkmark| - - |checkmark| - - |checkmark| - - |checkmark| - - D - - D - - D - - D - - * - 7.0 through 7.3 - - - - - - - - |checkmark| - - |checkmark| - - |checkmark| - - |checkmark| - - |checkmark| - - D - - D - - D - - D - - -.. _rails-compatibility: - -Rails Compatibility -=================== - -The following compatibility table specifies which versions of Ruby on Rails -are supported by Mongoid. - -.. list-table:: - :header-rows: 1 - :stub-columns: 1 - :class: compatibility-large no-padding - - * - Mongoid - - Rails 8.0 - - Rails 7.2 - - Rails 7.1 - - Rails 7.0 - - Rails 6.1 - - Rails 6.0 - - Rails 5.2 - - Rails 5.1 - - * - 9.0 - - |checkmark| [#rails-8.0]_ - - |checkmark| [#rails-7.2]_ - - |checkmark| [#rails-7.1]_ - - |checkmark| - - |checkmark| - - |checkmark| - - - - - - * - 8.1 - - |checkmark| [#rails-8.0]_ - - |checkmark| [#rails-7.2]_ - - |checkmark| [#rails-7.1]_ - - |checkmark| - - |checkmark| - - |checkmark| - - |checkmark| [#rails-5-ruby-3.0]_ - - - - * - 8.0 - - - - - - |checkmark| [#rails-7.1]_ - - |checkmark| - - |checkmark| - - |checkmark| - - |checkmark| [#rails-5-ruby-3.0]_ - - - - * - 7.5 - - - - - - - - |checkmark| - - |checkmark| - - |checkmark| - - |checkmark| [#rails-5-ruby-3.0]_ - - D - - * - 7.4 - - - - - - - - |checkmark| - - |checkmark| - - |checkmark| - - |checkmark| [#rails-5-ruby-3.0]_ - - |checkmark| [#rails-5-ruby-3.0]_ - - * - 7.3 - - - - - - - - |checkmark| [#rails-7-Mongoid-7.3]_ - - |checkmark| - - |checkmark| - - |checkmark| [#rails-5-ruby-3.0]_ - - |checkmark| [#rails-5-ruby-3.0]_ - - * - 7.2 - - - - - - - - - - |checkmark| [#rails-6.1]_ - - |checkmark| - - |checkmark| [#rails-5-ruby-3.0]_ - - |checkmark| [#rails-5-ruby-3.0]_ - - * - 7.1 - - - - - - - - - - |checkmark| [#rails-6.1]_ - - |checkmark| - - |checkmark| - - |checkmark| - - * - 7.0 - - - - - - - - - - |checkmark| [#rails-6.1]_ - - |checkmark| [#rails-6]_ - - |checkmark| - - |checkmark| - -.. [#rails-5-ruby-3.0] Using Rails 5.x with Ruby 3 is not supported. - -.. [#rails-6] Rails 6.0 requires Mongoid 7.0.5 or later. - -.. [#rails-6.1] Rails 6.1 requires Mongoid 7.0.12, 7.1.7 or 7.2.1 in the - respective 7.0, 7.1 and 7.2 stable branches. - -.. [#rails-7-Mongoid-7.3] Rails 7.x requires Mongoid 7.3.4 or later. - -.. [#rails-7.1] Rails 7.1 requires Mongoid 8.0.7 or 8.1.3 in the respective - 8.0 and 8.1 stable branches. - -.. [#rails-7.2] Rails 7.2 requires Mongoid 8.1.6 and 9.0.2 in the respective - 8.1 and 9.0 stable branches. - -.. [#rails-8.0] Rails 8.0 requires Mongoid 8.1.7 and 9.0.3 in the respective - 8.1 and 9.0 stable branches. - -.. include:: /includes/unicode-checkmark.rst -.. include:: /includes/unicode-ballot-x.rst - -Rails Frameworks Support ------------------------- - -Ruby on Rails is comprised of a number of frameworks, which Mongoid attempts to -provide compatibility with wherever possible. - -Though Mongoid attempts to offer API compatibility with `Active Record `_, -libraries that depend directly on Active Record may not work as expected when -Mongoid is used as a drop-in replacement. - -.. note:: - - Mongoid can be used alongside Active Record within the same application without issue. - -.. list-table:: - :header-rows: 1 - :stub-columns: 1 - :class: compatibility-large no-padding - - * - Rails Framework - - Supported? - - * - ``ActionCable`` - - |checkmark| [#rails-actioncable-dependency]_ - - * - ``ActionMailbox`` - - |x| [#rails-activerecord-dependency]_ - - * - ``ActionMailer`` - - |checkmark| - - * - ``ActionPack`` - - |checkmark| - - * - ``ActionText`` - - |x| [#rails-activerecord-dependency]_ - - * - ``ActionView`` - - |checkmark| - - * - ``ActiveJob`` - - |checkmark| [#rails-activejob-dependency]_ - - * - ``ActiveModel`` - - |checkmark| [#rails-activemodel-dependency]_ - - * - ``ActiveStorage`` - - |x| [#rails-activerecord-dependency]_ - - * - ``ActiveSupport`` - - |checkmark| [#rails-activesupport-dependency]_ - -.. [#rails-actioncable-dependency] There is currently no MongoDB adapter for - ``ActionCable``, however any existing adapter (such as `Redis `_) - can be used successfully in conjunction with Mongoid models - -.. [#rails-activerecord-dependency] Depends directly on ``ActiveRecord`` - -.. [#rails-activemodel-dependency] ``Mongoid::Document`` includes ``ActiveModel::Model`` - and leverages ``ActiveModel::Validations`` for validations - -.. [#rails-activesupport-dependency] ``Mongoid`` requires ``ActiveSupport`` and - uses it extensively, including ``ActiveSupport::TimeWithZone`` for time handling. - - - -.. [#rails-activejob-dependency] Serialization of BSON & Mongoid objects works best - if you explicitly send ``BSON::ObjectId``'s as strings, and reconstitute them in the job: - - .. code-block:: ruby - - record = Model.find(...) - MyJob.perform_later(record._id.to_s) - - class MyJob < ApplicationJob - def perform(id_as_string) - record = Model.find(id_as_string) - # ... - end - end diff --git a/source/reference/configuration.txt b/source/reference/configuration.txt deleted file mode 100644 index 2d82faf2..00000000 --- a/source/reference/configuration.txt +++ /dev/null @@ -1,1027 +0,0 @@ -.. _configuration: - -************* -Configuration -************* - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 2 - :class: singlecol - -Mongoid is customarily configured through a ``mongoid.yml`` file that specifies -options and clients. The simplest configuration is as follows, which configures -Mongoid to talk to a MongoDB server at "localhost:27017" and use the database -named "mongoid". - -.. code-block:: yaml - - development: - clients: - default: - database: mongoid - hosts: - - localhost:27017 - -The top level key in the configuration file, ``development`` in the above -example, refers to the environment name which the application is executing in, -i.e. ``development``, ``test`` or ``production``. The third level key, -``default`` in the above example, refers to the Mongo client name. -Most applications will use a single client named ``default``. - - -Generating Default Configuration -================================ - -If you are using Ruby on Rails, you can have Mongoid generate a default -configuration file for you by running the following command: - -.. code-block:: bash - - rails g mongoid:config - -The configuration file will be placed in ``config/mongoid.yml``. An -initializer will also be created and placed in -``config/initializers/mongoid.rb``. It is recommended that all configuration -be specified in ``config/mongoid.yml``, but if you prefer, the ``mongoid.rb`` -initializer may also be used to set configuration options. Note, though, that -settings in ``mongoid.yml`` always take precedence over settings in the -initializer. - -If you are not using Ruby on Rails, you can copy the minimal configuration -given above and save it as ``config/mongoid.yml``. - - -Loading Mongoid Configuration -============================= - -If you are using Ruby on Rails, Mongoid configuration is automatically loaded -for the current environment as stored in ``Rails.env`` when the application -loads. - -You may need to configure the ORM for your application to be Mongoid by -adding the following to ``application.rb``: - -.. code-block:: ruby - - config.generators do |g| - g.orm :mongoid - end - -If you are not using Ruby on Rails, Mongoid configuration must be loaded -manually. This can be done via the ``Mongoid.load!`` method, which takes -the configuration file path as its argument, as follows: - -.. code-block:: ruby - - # Use automatically detected environment name - Mongoid.load!("path/to/your/mongoid.yml") - - # Specify environment name manually - Mongoid.load!("path/to/your/mongoid.yml", :production) - -When Mongoid is asked to automatically detect the environment name, -it does so by examining the following sources, in order: - -- If ``Rails`` top level constant is defined, ``Rails.env``. -- If ``Sinatra`` top level constant is defined, ``Sinatra::Base.environment``. -- The ``RACK_ENV`` environment variable. -- The ``MONGOID_ENV`` environment variable. - -It is also possible to configure Mongoid directly in Ruby, without using -a configuration file. This configuration style does not support the concept -of environments - whatever configuration is provided, it is applied to the -current environment - but it does support defining multiple clients. - -.. code-block:: ruby - - Mongoid.configure do |config| - config.clients.default = { - hosts: ['localhost:27017'], - database: 'my_db', - } - - config.log_level = :warn - end - -.. note:: - - Mongoid must be configured *before* any component of it is used or referenced. - Once a component is used or referenced, changing configuration may not apply - changes to already instantiated components. - - -.. _configuration-options: - -Mongoid Configuration Options -============================= - -The following annotated example ``mongoid.yml`` demonstrates how Mongoid -can be configured. - -Mongoid delegates to the Ruby driver for client configuration. Please review -`the driver documentation `_ -for details on driver options. - -.. code-block:: yaml - - development: - # Configure available database clients. (required) - clients: - # Defines the default client. (required) - default: - # Mongoid can connect to a URI accepted by the driver: - # uri: mongodb://user:password@mongodb.domain.com:27017/my_db_development - - # Otherwise define the parameters separately. - # This defines the name of the default database that Mongoid can connect to. - # (required). - database: my_db_development - # Provides the hosts the default client can connect to. Must be an array - # of host:port pairs. (required) - hosts: - - localhost:27017 - options: - # Note that all options listed below are Ruby driver client options (the mongo gem). - # Please refer to the driver documentation of the version of the mongo gem you are using - # for the most up-to-date list of options. - - # Change the default write concern. (default = { w: 1 }) - # write: - # w: 1 - - # Change the default read preference. Valid options for mode are: :secondary, - # :secondary_preferred, :primary, :primary_preferred, :nearest - # (default: primary) - # read: - # mode: :secondary_preferred - # tag_sets: - # - use: web - - # The name of the user for authentication. - # user: 'user' - - # The password of the user for authentication. - # password: 'password' - - # The user's database roles. - # roles: - # - 'dbOwner' - - # Change the default authentication mechanism. Valid options include: - # :scram, :scram256, :mongodb_cr, :mongodb_x509, :gssapi, :aws, :plain. - # MongoDB Server defaults to :scram, which will use "SCRAM-SHA-256" if available, - # otherwise fallback to "SCRAM-SHA-1" (:scram256 will always use "SCRAM-SHA-256".) - # This setting is handled by the MongoDB Ruby Driver. Please refer to: - # https://mongodb.com/docs/ruby-driver/current/reference/authentication/ - # auth_mech: :scram - - # The database or source to authenticate the user against. - # (default: the database specified above or admin) - # auth_source: admin - - # Force a the driver cluster to behave in a certain manner instead of auto- - # discovering. Can be one of: :direct, :replica_set, :sharded. Set to :direct - # when connecting to hidden members of a replica set. - # connect: :direct - - # Changes the default time in seconds the server monitors refresh their status - # via hello commands. (default: 10) - # heartbeat_frequency: 10 - - # The time in seconds for selecting servers for a near read preference. (default: 0.015) - # local_threshold: 0.015 - - # The timeout in seconds for selecting a server for an operation. (default: 30) - # server_selection_timeout: 30 - - # The maximum number of connections in the connection pool. (default: 5) - # max_pool_size: 5 - - # The minimum number of connections in the connection pool. (default: 1) - # min_pool_size: 1 - - # The time to wait, in seconds, in the connection pool for a connection - # to be checked in before timing out. (default: 5) - # wait_queue_timeout: 5 - - # The time to wait to establish a connection before timing out, in seconds. - # (default: 10) - # connect_timeout: 10 - - # How long to wait for a response for each operation sent to the - # server. This timeout should be set to a value larger than the - # processing time for the longest operation that will be executed - # by the application. Note that this is a client-side timeout; - # the server may continue executing an operation after the client - # aborts it with the SocketTimeout exception. - # (default: nil, meaning no timeout) - # socket_timeout: 5 - - # The name of the replica set to connect to. Servers provided as seeds that do - # not belong to this replica set will be ignored. - # replica_set: name - - # Compressors to use for wire protocol compression. (default is to not use compression) - # "zstd" requires zstd-ruby gem. "snappy" requires snappy gem. - # Refer to: https://www.mongodb.com/docs/ruby-driver/current/reference/create-client/#compression - # compressors: ["zstd", "snappy", "zlib"] - - # Whether to connect to the servers via ssl. (default: false) - # ssl: true - - # The certificate file used to identify the connection against MongoDB. - # ssl_cert: /path/to/my.cert - - # The private keyfile used to identify the connection against MongoDB. - # Note that even if the key is stored in the same file as the certificate, - # both need to be explicitly specified. - # ssl_key: /path/to/my.key - - # A passphrase for the private key. - # ssl_key_pass_phrase: password - - # Whether to do peer certification validation. (default: true) - # ssl_verify: true - - # The file containing concatenated certificate authority certificates - # used to validate certs passed from the other end of the connection. - # ssl_ca_cert: /path/to/ca.cert - - # Whether to truncate long log lines. (default: true) - # truncate_logs: true - - # Configure Mongoid-specific options. (optional) - options: - # Allow BSON::Decimal128 to be parsed and returned directly in - # field values. When BSON 5 is present and the this option is set to false - # (the default), BSON::Decimal128 values in the database will be returned - # as BigDecimal. - # - # @note this option only has effect when BSON 5+ is present. Otherwise, - # the setting is ignored. - # allow_bson5_decimal128: false - - # Application name that is printed to the MongoDB logs upon establishing - # a connection. Note that the name cannot exceed 128 bytes in length. - # It is also used as the database name if the database name is not - # explicitly defined. (default: nil) - # app_name: nil - - # When this flag is false, callbacks for embedded documents will not be - # called. This is the default in 9.0. - # - # Setting this flag to true restores the pre-9.0 behavior, where callbacks - # for embedded documents are called. This may lead to stack overflow errors - # if there are more than cicrca 1000 embedded documents in the root - # document's dependencies graph. - # See https://jira.mongodb.org/browse/MONGOID-5658 for more details. - # around_callbacks_for_embeds: false - - # Sets the async_query_executor for the application. By default the thread pool executor - # is set to `:immediate`. Options are: - # - # - :immediate - Initializes a single +Concurrent::ImmediateExecutor+ - # - :global_thread_pool - Initializes a single +Concurrent::ThreadPoolExecutor+ - # that uses the +async_query_concurrency+ for the +max_threads+ value. - # async_query_executor: :immediate - - # Mark belongs_to associations as required by default, so that saving a - # model with a missing belongs_to association will trigger a validation - # error. - # belongs_to_required_by_default: true - - # Set the global discriminator key. - # discriminator_key: "_type" - - # Raise an exception when a field is redefined. - # duplicate_fields_exception: false - - # Defines how many asynchronous queries can be executed concurrently. - # This option should be set only if `async_query_executor` is set - # to `:global_thread_pool`. - # global_executor_concurrency: nil - - # When this flag is true, any attempt to change the _id of a persisted - # document will raise an exception (`Errors::ImmutableAttribute`). - # This is the default in 9.0. Setting this flag to false restores the - # pre-9.0 behavior, where changing the _id of a persisted - # document might be ignored, or it might work, depending on the situation. - # immutable_ids: true - - # Include the root model name in json serialization. - # include_root_in_json: false - - # # Include the _type field in serialization. - # include_type_for_serialization: false - - # Whether to join nested persistence contexts for atomic operations - # to parent contexts by default. - # join_contexts: false - - # When this flag is false (the default as of Mongoid 9.0), a document that - # is created or loaded will remember the storage options that were active - # when it was loaded, and will use those same options by default when - # saving or reloading itself. - # - # When this flag is true you'll get pre-9.0 behavior, where a document will - # not remember the storage options from when it was loaded/created, and - # subsequent updates will need to explicitly set up those options each time. - # - # For example: - # - # record = Model.with(collection: 'other_collection') { Model.first } - # - # This will try to load the first document from 'other_collection' and - # instantiate it as a Model instance. Pre-9.0, the record object would - # not remember that it came from 'other_collection', and attempts to - # update it or reload it would fail unless you first remembered to - # explicitly specify the collection every time. - # - # As of Mongoid 9.0, the record will remember that it came from - # 'other_collection', and updates and reloads will automatically default - # to that collection, for that record object. - # legacy_persistence_context_behavior: false - - # When this flag is false, a document will become read-only only once the - # #readonly! method is called, and an error will be raised on attempting - # to save or update such documents, instead of just on delete. When this - # flag is true, a document is only read-only if it has been projected - # using #only or #without, and read-only documents will not be - # deletable/destroyable, but they will be savable/updatable. - # When this feature flag is turned on, the read-only state will be reset on - # reload, but when it is turned off, it won't be. - # legacy_readonly: false - - # The log level. - # - # It must be set prior to referencing clients or Mongo.logger, - # changes to this option are not be propagated to any clients and - # loggers that already exist. - # - # Additionally, only when the clients are configured via the - # configuration file is the log level given by this option honored. - # log_level: :info - - # Store BigDecimals as Decimal128s instead of strings in the db. - # map_big_decimal_to_decimal128: true - - # Preload all models in development, needed when models use inheritance. - # preload_models: false - - # When this flag is true, callbacks for every embedded document will be - # called only once, even if the embedded document is embedded in multiple - # documents in the root document's dependencies graph. - # This is the default in 9.0. Setting this flag to false restores the - # pre-9.0 behavior, where callbacks are called for every occurrence of an - # embedded document. The pre-9.0 behavior leads to a problem that for multi - # level nested documents callbacks are called multiple times. - # See https://jira.mongodb.org/browse/MONGOID-5542 - # prevent_multiple_calls_of_embedded_callbacks: true - - # Raise an error when performing a #find and the document is not found. - # raise_not_found_error: true - - # Raise an error when defining a scope with the same name as an - # existing method. - # scope_overwrite_exception: false - - # Return stored times as UTC. - # use_utc: false - - # Configure Driver-specific options. (optional) - driver_options: - # When this flag is off, an aggregation done on a view will be executed over - # the documents included in that view, instead of all documents in the - # collection. When this flag is on, the view filter is ignored. - # broken_view_aggregate: true - - # When this flag is set to false, the view options will be correctly - # propagated to readable methods. - # broken_view_options: true - - # When this flag is set to true, the update and replace methods will - # validate the parameters and raise an error if they are invalid. - # validate_update_replace: false - - -.. _load-defaults: - -Version Based Defaults -====================== - -Mongoid supports setting the configuration options to the defaults for specific -versions. This is useful for upgrading to a new Mongoid version. When upgrading -your Mongoid version, the following should be set on ``Mongoid::Config``: - -.. code:: ruby - - Mongoid.configure do |config| - config.load_defaults - end - -This way, when upgrading to a new version of Mongoid, your code will run with -the configuration options from the previous version of Mongoid. Then, -one-by-one, you can change the feature flags for the new version, and test that -your code still acts as expected. Once all of the new feature flags have been -accounted for, the call to ``load_defaults`` may be changed to take in the *new* -version, and all of the changed feature flags may be removed. - -For example, suppose we're upgrading from 7.5 to 8.0. Between these two versions, -two feature flags were added: ``legacy_attributes`` and ``map_big_decimal_to_decimal128``. -Before upgrading to Mongoid 8, add the following to your ``Mongoid::Config``: - -.. code:: ruby - - Mongoid.configure do |config| - config.load_defaults 7.5 - end - -After upgrading to Mongoid 8.0 in your ``Gemfile``, any feature flags will -remain set to their 7.5 default behavior: ``legacy_attributes: true, -map_big_decimal_to_decimal128: false``. You may then flip these feature flags -one-by-one to their 8.0 behavior: - -.. code:: ruby - - Mongoid.configure do |config| - config.load_defaults 7.5 - config.legacy_attributes = false - # config.map_big_decimal_to_decimal128 = true - end - -We recommend do these one at a time, so in the example above we leave the -second flag commented out. After verifying your code works as expected with the -``legacy_attributes`` flag turned off, the ``map_big_decimal_to_decimal128`` -setting can be uncommented. Once that functionality is verified as well, both -of those lines can be removed and the ``load_defaults`` replaced with: - -.. code:: ruby - - Mongoid.configure do |config| - config.load_defaults 8.0 - end - - -ERb Preprocessing -================= - -When loading a configuration file, Mongoid processes it with ERb before -parsing it as YAML. This allows, for example, constructing the contents of -the configuration file at runtime based on environment variables: - -.. code-block:: yaml - - development: - clients: - default: - uri: "<%= ENV['MONGODB_URI'] %>" - -.. note:: - - When outputting values from ERb, ensure the values are valid YAML and - escape them as needed. - -.. note:: - - Since ERb rendering is performed prior to YAML parsing, all ERb directives - in the configuration file are evaluated, including those occurring in YAML - comments. - -Logging -======= - -When configuring logging, it is important to keep in mind that Mongoid -provides a model layer on top of the MongoDB Ruby driver, and the driver -dispatches the CRUD operations to the MongoDB deployment. Therefore, some -of the logging output in an application using Mongoid comes from Mongoid -itself, and some comes from the driver. - -The Mongo client is a Ruby driver client instance, therefore -the logger of a Mongo client is the Ruby driver logger, not the Mongoid -logger. In other words: - -.. code-block:: ruby - - # Ruby driver logger, not Mongoid logger - Mongoid.client(:default).logger - -Depending on whether Mongoid is used in a Ruby on Rails application, and how -both Mongoid and Ruby driver are configured, they may use the same logger -instance or different instances, potentially with different configurations. - -In Ruby on Rails Application ----------------------------- - -When used in a Ruby on Rails application, Mongoid by default inherits -the logger and the log level from Rails, and sets the driver's logger -to the same logger instance: - -.. code-block:: ruby - - Rails.logger === Mongoid.logger - # => true - - Mongoid.logger === Mongo::Logger.logger - # => true - -To change the log level, use `standard Rails configuration -`_. -Place the following in one of environment configuration files, such as -``config/environments/production.rb``: - -.. code-block:: ruby - - Rails.application.configure do - config.log_level = :debug - end - -.. note:: - - The ``log_level`` Mongoid configuration option is not used when Mongoid operates - in a Rails application, because Mongoid inherits Rails' log level in this case. - -To configure either Mongoid or driver logger differently from the Rails logger, -use an initializer as follows: - -.. code-block:: ruby - - Rails.application.configure do - config.after_initialize do - # Change Mongoid log destination and/or level - Mongoid.logger = Logger.new(STDERR).tap do |logger| - logger.level = Logger::DEBUG - end - - # Change driver log destination and/or level - Mongo::Logger.logger = Logger.new(STDERR).tap do |logger| - logger.level = Logger::DEBUG - end - end - end - -.. note:: - - There is currently no provision in the Ruby standard library ``Logger`` - to return the log device (i.e. the ``IO`` object) that a logger is using. - To have, for example, Mongoid and/or the Ruby driver log to the - standard Rails log file (e.g. ``log/development.log``) but with a - different level from standard Rails logger (``Rails.logger``), the - file must be opened separately and the resulting ``IO`` object passed to - the ``Logger`` constructor. - -.. note:: - - Since by default Mongoid sets its own logger and the driver's logger to the - same instance as the Rails logger, modifying any of the instances affects - all of them. For example the following changes log level for all three - loggers, unless the application assigned a separate ``Logger`` instance - to ``Mongo::Logger.logger`` as described above: - - .. code-block:: ruby - - Mongoid::Logger.logger.level = Logger::DEBUG - -Standalone ----------- - -When not loaded in a Ruby on Rails application, Mongoid respects the -``log_level`` top level configuration option. It can be given in the -configuration file as follows: - -.. code-block:: yaml - - development: - clients: - default: - # ... - options: - log_level: :debug - -... or when configuring Mongoid inline: - -.. code-block:: ruby - - Mongoid.configure do |config| - config.log_level = :debug - end - -The default log destination in Mongoid 7.1 and higher is standard error. -The default log destination in Mongoid 7.0 and lower is standard output. -To change the log destination, create a new logger instance as follows: - -.. code-block:: ruby - - Mongoid.logger = Logger.new(STDERR).tap do |logger| - logger.level = Logger::DEBUG - end - -To change the Ruby driver log level or destination: - -.. code-block:: ruby - - Mongo::Logger.logger = Logger.new(STDERR).tap do |logger| - logger.level = Logger::DEBUG - end - -To set the driver logger to be the same as the Mongoid logger: - -.. code-block:: ruby - - Mongo::Logger.logger = Mongoid.logger - -.. note:: - - Mongoid does not alter the driver's logger when running in - standalone mode. - -.. _time-zones: - -Time Zones -========== - -Mongoid uses ActiveSupport's time zone functionality, which is far -more robust than Ruby's standard library. Importantly, ActiveSupport -allows configuration of ``Time.zone``, a thread-global variable which -provides context for working with date and time values. - -While a thorough treatment of time zones in Ruby is outside the scope -of this tutorial, the easiest and most reliable way of achieving correct -time zone handling is as follows: - -1. Set the operating system's time zone to UTC. For example, on Linux: - -.. code-block:: bash - - cp /usr/share/zoneinfo/UTC /etc/localtime - -2. Set your application default time zone to UTC: - -.. code-block:: ruby - - # If using Rails, in application.rb: - class Application < Rails::Application - config.time_zone = 'UTC' - end - - # If not using Rails: - Time.zone = 'UTC' - -3. In each controller and job class, set the appropriate time zone in a - ``before_filter`` at the earliest possible stage. As an example, - if each user of your application can set their own time zone, - you may wish to do: - -.. code-block:: ruby - - class ApplicationController < ActionController::Base - before_filter :fetch_user, - :set_time_zone - - def set_time_zone - Time.zone = @user.time_zone - end - end - -4. From here, you may work with times naturally in the local time zone. - For example, in a view: - -.. code-block:: ruby - - Turned into a pumpkin after <%= cinderella.updated_at.seconds_after_midnight %> seconds! - -5. Use ActiveSupport methods instead of the Ruby standard library. - - - ``Time.zone.now`` or ``Time.current` instead of ``Time.now`` - - ``Date.current`` instead of ``Date.today`` - - Critically, note that the latter Ruby standard library methods reference - your system time zone (e.g. UTC) and not the value of ``Time.zone``. - As it is very easy to mistake these similarly named methods, we recommend to - use `Rubocop's Rails/TimeZone cop - `_ in your CI. - -Setting time zone on data loaded from MongoDB ---------------------------------------------- - -MongoDB stores all times in UTC without time zone information. -Mongoid models load and returns time values as instances of -``ActiveSupport::TimeWithZone``. You may set the ``use_utc`` option -to control how Mongoid sets the time zone when loading from the database: - -- If false (default), Mongoid will use ``Time.zone`` to set the time - zone of time values are loaded from database. -- If true, Mongoid will always set the time zone as UTC on loaded - time values. - -``use_utc`` only affects how data is loaded, and does not affect -how data is persisted. For example, if you assign a ``Time`` or -``ActiveSupport::TimeWithZone`` instance to a time field, the time -zone information of the assigned instance always will be used -irrespective of the ``use_utc`` setting. Alternatively, if you -assign a string value to a time field, any time zone information -in the string will be used if present. If the string does not include -time zone information it will be parsed according to ``Time.zone``. -To illustrate: - -.. code-block:: ruby - - Time.use_zone("Asia/Kolkata") do - - # String does not include time zone, so "Asia/Kolkata" will be used - ghandi.born_at = "1869-10-02 7:10 PM" - - # Time zone in string (-0600) will be used - amelia.born_at = "1897-07-24 11:30 -0600" - end - - -Configuring ``SSLContext`` -========================== -It may be desirable to further configure TLS options in your application, for -example by enabling or disabling certain ciphers. - -This can be done by setting TLS context hooks on the Ruby driver -- TLS context -hooks are user-provided ``Proc``\(s) that will be invoked before any TLS socket -connection in the driver and can be used to modify the underlying -``OpenSSL::SSL::SSLContext`` object used by the socket. - -To set TLS context hooks, add ``Proc``\(s) to the ``Mongo.tls_context_hooks`` -array. This can be done in an initializer. The example below adds a hook -that only enables the "AES256-SHA" cipher. - -.. code-block:: ruby - - Mongo.tls_context_hooks.push( - Proc.new { |context| - context.ciphers = ["AES256-SHA"] - } - ) - - # Only the AES256-SHA cipher will be enabled from this point forward - -Every ``Proc`` in ``Mongo.tls_context_hooks`` will be passed an -``OpenSSL::SSL::SSLContext`` object as its sole argument. These procs will -be executed sequentially during socket creation. - -.. warning:: - - TLS context hooks are global and will affect all ``Mongo::Client`` instances - in an application. - -For more information about TLS context hooks, including best practices for -assigning and removing them, see `the Ruby driver documentation `_. - - -Network Compression -=================== - -Mongoid supports compression of messages to and from MongoDB servers. This functionality is provided by -the Ruby driver, which implements the three algorithms that are supported by MongoDB servers: - -- `Snappy `_: ``snappy`` compression - can be used when connecting to MongoDB servers starting with the 3.4 release, - and requires the `snappy `_ library to be - installed. -- `Zlib `_: ``zlib`` compression can be used when - connecting to MongoDB servers starting with the 3.6 release. -- `Zstandard `_: ``zstd`` compression can be - used when connecting to MongoDB servers starting with the 4.2 release, and - requires the `zstd-ruby `_ library to - be installed. - -To use wire protocol compression, configure the Ruby driver options within ``mongoid.yml``: - -.. code-block:: yaml - - development: - # Configure available database clients. (required) - clients: - # Define the default client. (required) - default: - # ... - options: - # These options are Ruby driver options, documented in - # https://mongodb.com/docs/ruby-driver/current/reference/create-client/ - # ... - # Compressors to use. (default is to not use compression) - # Valid values are zstd, zlib or snappy - or any combination of the three - compressors: ["zstd", "snappy"] - -If no compressors are explicitly requested, the driver will not use compression, -even if the required dependencies for one or more compressors are present on the -system. - -The driver chooses the first compressor of the ones requested that is also supported -by the server. The ``zstd`` compressor is recommended as it produces the highest -compression at the same CPU consumption compared to the other compressors. - -For maximum server compatibility all three compressors can be specified, e.g. -as ``compressors: ["zstd", "snappy", "zlib"]``. - - -Client-Side Encryption -====================== - -When loading the configuration file, Mongoid permits the file to contain -``BSON::Binary`` instances which are used for specifying ``keyId`` in -the schema map for `client-side encryption -`_, -as the following example shows: - -.. code-block:: yaml - - development: - clients: - default: - database: blog_development - hosts: [localhost:27017] - options: - auto_encryption_options: - key_vault_namespace: 'keyvault.datakeys' - kms_providers: - local: - key: "z7iYiYKLuYymEWtk4kfny1ESBwwFdA58qMqff96A8ghiOcIK75lJGPUIocku8LOFjQuEgeIP4xlln3s7r93FV9J5sAE7zg8U" - schema_map: - blog_development.comments: - properties: - message: - encrypt: - keyId: - - !ruby/object:BSON::Binary - data: !binary |- - R/AgNcxASFiiJWKXqWGo5w== - type: :uuid - bsonType: "string" - algorithm: "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - bsonType: "object" - - -Usage with Forking Servers -========================== - -When using Mongoid with a forking web server such as Puma, or any application -that otherwise forks to spawn child processes, special considerations apply. - -If possible, we recommend to not perform any MongoDB operations in the parent -process prior to forking, which will avoid any forking-related pitfalls. - -A detailed technical explanation of how the Mongo Ruby Driver handles forking -is given in the `driver's "Usage with Forking Servers" documentation -`. -In a nutshell, to avoid various connection errors such as ``Mongo::Error::SocketError`` -and ``Mongo::Error::NoServerAvailable``, you must do the following: - -1. Disconnect MongoDB clients in the parent Ruby process immediately *before* - forking using ``Mongoid.disconnect_clients``. This ensures the parent and child - process do not accidentally reuse the same sockets and have I/O conflicts. - Note that ``Mongoid.disconnect_clients`` does not disrupt any in-flight - MongoDB operations, and will automatically reconnect when you perform new - operations. -2. Reconnect your MongoDB clients in the child Ruby process immediately *after* - forking using ``Mongoid.reconnect_clients``. This is required to respawn - the driver's monitoring threads in the child process. - -Most web servers provide hooks that can be used by applications to -perform actions when the worker processes are forked. The following -are configuration examples for several common Ruby web servers. - -Puma ----- - -Use the ``on_worker_boot`` hook to reconnect clients in the workers and -the ``before_fork`` and ``on_refork`` hooks to close clients in the -parent process (`Puma documentation `_). - -.. code-block:: ruby - - # config/puma.rb - - # Runs in the Puma master process before it forks a child worker. - before_fork do - Mongoid.disconnect_clients - end - - # Required when using Puma's fork_worker option. Runs in the - # child worker 0 process before it forks grandchild workers. - on_refork do - Mongoid.disconnect_clients - end - - # Runs in each Puma child process after it forks from its parent. - on_worker_boot do - Mongoid.reconnect_clients - end - -Unicorn -------- - -Use the ``after_fork`` hook to reconnect clients in the workers and -the ``before_fork`` hook to close clients in the parent process -(`Unicorn documentation `_): - -.. code-block:: ruby - - # config/unicorn.rb - - before_fork do |_server, _worker| - Mongoid.disconnect_clients - end - - after_fork do |_server, _worker| - Mongoid.reconnect_clients - end - -Passenger ---------- - -Use the ``starting_worker_process`` hook to reconnect clients in the workers -(`Passenger documentation -`_). -Passenger does not appear to have a hook that is invoked in the parent process -before the workers are forked. - -.. code-block:: ruby - - if defined?(PhusionPassenger) - PhusionPassenger.on_event(:starting_worker_process) do |forked| - Mongoid.reconnect_clients if forked - end - end - - -.. _query-cache-middleware: - -Query Cache Middleware -====================== - -Enabling Query Cache for Rack Web Requests ------------------------------------------- - -The MongoDB Ruby Driver provides a Rack middleware which enables the :ref:`Query Cache -` for the duration of each web request. Below is an example of -how to enable the Query Cache Middleware in a Ruby on Rails application: - -.. code-block:: ruby - - # config/application.rb - - # Add Mongo::QueryCache::Middleware at the bottom of the middleware stack - # or before other middleware that queries MongoDB. - config.middleware.use Mongo::QueryCache::Middleware - -Please refer to the `Rails on Rack guide -`_ -for more information about using Rack middleware in Rails applications. - -Enabling Query Cache for ActiveJob ----------------------------------- - -The MongoDB Ruby Driver also provides Query Cache middleware for ActiveJob. -You may enable it for all jobs in an initializer: - -.. code-block:: ruby - - # config/initializers/active_job.rb - - # Enable Mongo driver query cache for ActiveJob - ActiveSupport.on_load(:active_job) do - include Mongo::QueryCache::Middleware::ActiveJob - end - -Or for a specific job class: - -.. code-block:: ruby - - class MyJob < ActiveJob::Base - include Mongo::QueryCache::Middleware::ActiveJob - end - - -Development Configuration -========================= - -Driver's default configuration is suitable for production deployment. -In development, some settings can be adjusted to provide a better developer -experience. - -- ``:server_selection_timeout``: set this to a low value (e.g., ``1``) - if your MongoDB server is running locally and you start it manually. A low - server selection timeout will cause the driver to fail quickly when there is - no server running. - -Sample recommended development configuration: - -.. code-block:: yaml - - development: - clients: - default: - database: mongoid - hosts: - - localhost:27017 - options: - server_selection_timeout: 1 diff --git a/source/reference/crud.txt b/source/reference/crud.txt deleted file mode 100644 index ff70fa6f..00000000 --- a/source/reference/crud.txt +++ /dev/null @@ -1,1074 +0,0 @@ -.. _crud: - -*************** -CRUD Operations -*************** - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 2 - :class: singlecol - - -Saving Documents -================ - -Mongoid supports all expected CRUD operations for those familiar with other -Ruby mappers like Active Record or Data Mapper. What distinguishes Mongoid -from other mappers for MongoDB is that the general persistence operations -perform atomic updates on only the fields that have changed instead of -writing the entire document to the database each time. - -The persistence sections will provide examples on what database operation is -performed when executing the documented command. - -Standard --------- - -Mongoid's standard persistence methods come in the form of common methods you -would find in other mapping frameworks. The following table shows all standard -operations with examples. - -.. list-table:: - :header-rows: 1 - :widths: 30 60 - - * - Operation - - Example - - * - ``Model#attributes`` - - *Returns the document's attributes as a ``Hash`` with string keys, and - its values in Mongoized form (i.e. the way they are stored in the db).* - - *The attributes hash also contains the attributes of all embedded - documents, as well as their embedded documents, etc. If an embedded - association is empty, its key will not show up in the returned hash.* - - - - .. code-block:: ruby - - person = Person.new(first_name: "Heinrich", last_name: "Heine") - - person.attributes - # => { "_id" => BSON::ObjectId('633467d03282a43784c2d56e'), "first_name" => "Heinrich", "last_name" => "Heine" } - - * - ``Model.create!`` - - *Insert a document or multiple documents into the database, raising an - error if a validation or server error occurs.* - - *Pass a hash of attributes to create one document with the specified - attributes, or an array of hashes to create multiple documents. - If a single hash is passed, the corresponding document is returned. - If an array of hashes is passed, an array of documents corresponding - to the hashes is returned.* - - *If a block is given to* ``create!`` *, it will be invoked with each - document as the argument in turn prior to attempting to save that - document.* - - *If there is a problem saving any of the documents, such as - a validation error or a server error, an exception is raised - and, consequently, none of the documents are returned. - However, if an array of hashes was passed and previous documents were - successfully saved, those documents will remain in the database.* - - - .. code-block:: ruby - - Person.create!( - first_name: "Heinrich", - last_name: "Heine" - ) # => Person instance - - Person.create!([ - { first_name: "Heinrich", last_name: "Heine" }, - { first_name: "Willy", last_name: "Brandt" } - ]) # => Array of two Person instances - - Person.create!(first_name: "Heinrich") do |doc| - doc.last_name = "Heine" - end # => Person instance - - * - ``Model.create`` - - *Instantiate a document or multiple documents and, if validations pass, - insert them into the database.* - - ``create`` *is similar to* ``create!`` *but does not raise - exceptions on validation errors. It still raises errors on server - errors, such as trying to insert a document with an* ``_id`` *that - already exists in the collection.* - - *If any validation errors are encountered, the respective document - is not inserted but is returned along with documents that were inserted. - Use* ``persisted?`` *,* ``new_record?`` *or* ``errors`` *methods - to check which of the returned documents were inserted into the - database.* - - - .. code-block:: ruby - - Person.create( - first_name: "Heinrich", - last_name: "Heine" - ) # => Person instance - - Person.create([ - { first_name: "Heinrich", last_name: "Heine" }, - { first_name: "Willy", last_name: "Brandt" } - ]) # => Array of two Person instances - - Person.create(first_name: "Heinrich") do |doc| - doc.last_name = "Heine" - end # => Person instance - - class Post - include Mongoid::Document - - validates_uniqueness_of :title - end - - posts = Post.create([{title: "test"}, {title: "test"}]) - # => array of two Post instances - posts.map { |post| post.persisted? } # => [true, false] - - * - ``Model#save!`` - - *Save the changed attributes to the database atomically, or insert the document if - new. Raises an exception if validations fail or there is a server error.* - - *Returns true if the changed attributes were saved, raises an exception otherwise.* - - - .. code-block:: ruby - - person = Person.new( - first_name: "Heinrich", - last_name: "Heine" - ) - person.save! - - person.first_name = "Christian Johan" - person.save! - - * - ``Model#save`` - - *Save the changed attributes to the database atomically, or insert the document - if new.* - - *Returns true if the changed attributes were saved. Returns false - if there were any validation errors. Raises an exception if - the document passed validation but there was a server error during - the save.* - - *Pass* ``validate: false`` *option to bypass validations.* - - *Pass* ``touch: false`` *option to ignore the updates to the updated_at - field. If the document being save has not been previously persisted, - this option is ignored and the created_at and updated_at fields will be - updated with the current time.* - - - .. code-block:: ruby - - person = Person.new( - first_name: "Heinrich", - last_name: "Heine" - ) - person.save - person.save(validate: false) - person.save(touch: false) - - person.first_name = "Christian Johan" - person.save - - * - ``Model#update_attributes`` - - *Update the document attributes in the database. Will return true if validation passed, - false if not.* - - - .. code-block:: ruby - - person.update_attributes( - first_name: "Jean", - last_name: "Zorg" - ) - - * - ``Model#update_attributes!`` - - *Update the document attributes in the database and raise an error if validation failed.* - - - .. code-block:: ruby - - person.update_attributes!( - first_name: "Leo", - last_name: "Tolstoy" - ) - - * - ``Model#update_attribute`` - - *Update a single attribute, bypassing validations.* - - - .. code-block:: ruby - - person.update_attribute(:first_name, "Jean") - - * - ``Model#upsert`` - - *Performs a MongoDB replace with upsert on the document. If the document - exists in the database and the* ``:replace`` *option is set to true, it - will get overwritten with the current document in the application (any - attributes present in the database but not in the application's document - instance will be lost). If the* ``:replace`` *option is false (default), - the document will be updated, and any attributes not in the application's - document will be maintained. - If the document does not exist in the database, it will be inserted. - Note that this only runs the* ``{before|after|around}_upsert`` *callbacks.* - - - .. code-block:: ruby - - person = Person.new( - first_name: "Heinrich", - last_name: "Heine" - ) - person.upsert - person.upsert(replace: true) - - * - ``Model#touch`` - - *Update the document's updated_at timestamp, optionally with one extra - provided time field. This will cascade the touch to all* - ``belongs_to`` *associations of the document with the option set. - This operation skips validations and callbacks.* - - *Attempting to touch a destroyed document will raise* ``FrozenError``, - *same as if attempting to update an attribute on a destroyed - document.* - - - .. code-block:: ruby - - person.touch - person.touch(:audited_at) - - * - ``Model#delete`` - - *Deletes the document from the database without running callbacks.* - - *If the document is not persisted, Mongoid will attempt to delete from - the database any document with the same* ``_id``. - - - .. code-block:: ruby - - person.delete - - person = Person.create!(...) - unsaved_person = Person.new(id: person.id) - unsaved_person.delete - person.reload - # raises Mongoid::Errors::DocumentNotFound because the person was deleted - - * - ``Model#destroy`` - - *Deletes the document from the database while running destroy callbacks.* - - *If the document is not persisted, Mongoid will attempt to delete from - the database any document with the same* ``_id``. - - - .. code-block:: ruby - - person.destroy - - person = Person.create!(...) - unsaved_person = Person.new(id: person.id) - unsaved_person.destroy - person.reload - # raises Mongoid::Errors::DocumentNotFound because the person was deleted - - * - ``Model.delete_all`` - - *Deletes all documents from the database without running any callbacks.* - - - .. code-block:: ruby - - Person.delete_all - - * - ``Model.destroy_all`` - - *Deletes all documents from the database while running callbacks. This is a - potentially expensive operation since all documents will be loaded into memory.* - - - .. code-block:: ruby - - Person.destroy_all - -Mongoid provides the following persistence-related attributes: - -.. list-table:: - :header-rows: 1 - :widths: 30 60 - - * - Attribute - - Example - - - * - ``Model#new_record?`` - - *Returns* ``true`` *if the model instance has not yet been saved - to the database. Opposite of* ``persisted?`` - - - .. code-block:: ruby - - person = Person.new( - first_name: "Heinrich", - last_name: "Heine" - ) - person.new_record? # => true - person.save! - person.new_record? # => false - - * - ``Model#persisted?`` - - *Returns* ``true`` *if the model instance has been saved - to the database. Opposite of* ``new_record?`` - - - .. code-block:: ruby - - person = Person.new( - first_name: "Heinrich", - last_name: "Heine" - ) - person.persisted? # => false - person.save! - person.persisted? # => true - - -Atomic ------- - -Mongoid exposes :manual:`MongoDB update operators ` -as methods on Mongoid documents. When these methods are used, callbacks are -not invoked and validations are not performed. The supported update operators -are: - -.. list-table:: - :header-rows: 1 - :widths: 30 60 - - * - Operation - - Example - - * - ``Model#add_to_set`` - - *Performs an atomic $addToSet on the field.* - - - .. code-block:: ruby - - person.add_to_set(aliases: "Bond") - - * - ``Model#bit`` - - *Performs an atomic $bit on the field.* - - - .. code-block:: ruby - - person.bit(age: { and: 10, or: 12 }) - - * - ``Model#inc`` - - *Performs an atomic $inc on the field.* - - - .. code-block:: ruby - - person.inc(age: 1) - - * - ``Model#pop`` - - *Performs an atomic $pop on the field.* - - - .. code-block:: ruby - - person.pop(aliases: 1) - - * - ``Model#pull`` - - *Performs an atomic $pull on the field.* - - - .. code-block:: ruby - - person.pull(aliases: "Bond") - - * - ``Model#pull_all`` - - *Performs an atomic $pullAll on the field.* - - - .. code-block:: ruby - - person.pull_all(aliases: [ "Bond", "James" ]) - - * - ``Model#push`` - - *Performs an atomic $push on the field.* - - - .. code-block:: ruby - - person.push(aliases: ["007","008"]) - - * - ``Model#rename`` - - *Performs an atomic $rename on the field.* - - - .. code-block:: ruby - - person.rename(bday: :dob) - - * - ``Model#set`` - - *Updates an attribute on the model instance and, if the instance - is already persisted, performs an atomic $set on the field, bypassing - validations.* - - ``set`` *can also deeply set values on Hash fields.* - - ``set`` *can also deeply set values on* ``embeds_one`` *associations. - If such an association's document is nil, one will be created prior - to the update.* - - ``set`` *should not be used with* ``has_one`` *associations, as it - does not correctly work in such cases.* - - - - .. code-block:: ruby - - person = Person.create!(name: "Ricky Bobby") - person.set(name: "Tyler Durden") # updates name in the database - - - person = Person.new - person.set(name: "Tyler Durden") # does not write to database - person.name # => "Tyler Durden" - person.persisted? # => true - - - class Post - include Mongoid::Document - - field :metadata, type: Hash - end - - post = Post.create! - post.set('metadata.published_at' => Time.now) - post.metadata['published_at'] # => Time instance - - post.set('metadata.approved.today' => true) - post.metadata['approved'] # => {'today' => true} - - - class Flight - include Mongoid::Document - - embeds_one :plan - end - - class Plan - include Mongoid::Document - - embedded_in :flight - - field :route, type: String - end - - flight = Flight.create! - flight.plan # => nil - flight.set('plan.route', 'test route') - flight.plan # => Plan instance - flight.plan.route # => "test route" - - - * - ``Model#unset`` - - *Performs an atomic $unset on the field.* - - - .. code-block:: ruby - - person.unset(:name) - -Note that, because these methods skip validations, it is possible to both -save invalid documents into the database and end up with invalid documents -in the application (which would subsequently fail to save via a ``save`` -call due to the failing validations). - - -.. _atomic-operation-grouping: - -Atomic Operation Grouping -````````````````````````` - -Atomic operations may be grouped together using the ``#atomically`` method -on a document. All operations inside the block given to ``#atomically`` -are sent to the cluster in a single atomic command. For example: - -.. code-block:: ruby - - person.atomically do - person.inc(age: 1) - person.set(name: 'Jake') - end - -``#atomically`` blocks may be nested. The default behavior is to write -changes performed by each block as soon as the block ends: - -.. code-block:: ruby - - person.atomically do - person.atomically do - person.inc(age: 1) - person.set(name: 'Jake') - end - raise 'An exception' - # name and age changes are still persisted - end - -This behavior can be changed by specifying the ``join_context: true`` option -to ``#atomically``, or globally by setting the ``join_contexts`` -:ref:`configuration option ` to ``true``. When -context joining is enabled, nested ``#atomically`` blocks are joined with -the outer blocks, and only the outermost block (or the first block where -``join_contexts`` is false) actually writes changes to the cluster. -For example: - -.. code-block:: ruby - - person.atomically do - person.atomically(join_context: true) do - person.inc(age: 1) - person.set(name: 'Jake') - end - raise 'An exception' - # name and age changes are not persisted - end - -The context joining behavior can be enabled globally by default by setting -``join_context`` option in Mongoid configuration. In this case specifying -``join_context: false`` on an ``#atomically`` block can be used to -obtain the independent persistence context behavior. - -If an exception is raised in an ``#atomically`` block which has not yet -persisted its changes to the cluster, any pending attribute changes on -Mongoid models are reverted. For example: - -.. code-block:: ruby - - person = Person.new(name: 'Tom') - begin - person.atomically do - person.inc(age: 1) - person.set(name: 'Jake') - person.name # => 'Jake' - raise 'An exception' - end - rescue Exception - person.name # => 'Tom' - end - -Atomic operations described in this section apply to one document at a time, -therefore nesting ``#atomically`` blocks invoked on multiple documents does -not make changes to the different documents be persisted atomically together. -However, MongoDB offers :ref:`multi-document transactions ` -as of server version 4.0 which provide atomic persistence across multiple -documents. - - -Reloading -========= - -Use the ``reload`` method to fetch the most recent version of a document from -the database. Any unsaved modifications to the document's attributes are lost: - -.. code-block:: ruby - - band = Band.create!(name: 'foo') - # => # - - band.name = 'bar' - band - # => # - - band.reload - # => # - -When a document is reloaded, all of its embedded associations are also reloaded -in the same query (since embedded documents are stored in the parent document -on the server). If a document has referenced associations, the loaded -associations' are not reloaded but their values are cleared, such that these -associations would be loaded from the database at the next access. - -.. note:: - - Some operations on associations, for example assignment, persists the new - document. In these cases there may not be any unsaved modifications to - revert by reloading. In the following example, the assignment of the - empty array to the association is immediately persisted and reloading - does not make any changes to the document: - - .. code-block:: ruby - - # Assuming band has many tours, which could be referenced: - band = Band.create!(tours: [Tour.create!]) - # ... or embedded: - band = Band.create!(tours: [Tour.new]) - - # This writes the empty tour list into the database. - band.tours = [] - - # There are no unsaved modifications in band at this point to be reverted. - band.reload - - # Returns the empty array since this is what is in the database. - band.tours - # => [] - -If the model has a :ref:`shard key ` defined, the shard key value -is included in the reloading query. - -If the database does not contain a matching document, Mongoid normally raises -``Mongoid::Errors::DocumentNotFound``. However, if the configuration option -``raise_not_found_error`` is set to ``false``, and the database does not -contain a matching document, Mongoid replaces the current document with a newly -created document whose attributes are set to default values. Importantly, this -generally causes the ``_id`` of the document to change, as the following -example demonstrates: - -.. code-block:: ruby - - band = Band.create! - # => # - - Mongoid.raise_not_found_error = false - band.destroy - - band.reload - # => # - -For this reason, it is not recommended to use ``reload`` when -``raise_not_found_error`` is set to ``false``. - - -Reloading Unsaved Documents ---------------------------- - -``reload`` can be called when the document has not yet been persisted. -In this case ``reload`` performs a ``find`` query using the ``id`` value -specified in the document (and the shard key value, if a shard key is defined): - -.. code-block:: ruby - - existing = Band.create!(name: 'Photek') - - # Unsaved document - band = Band.new(id: existing.id) - band.reload - band.name - # => "Photek" - - -Accessing Field Values -====================== - -Mongoid provides several ways of accessing field values. - -.. note:: - - All of the access methods described below raise - ``Mongoid::Errors::AttributeNotLoaded`` when the field being accessed is - :ref:`projected out `, either by virtue of not being included in - :ref:`only ` or by virtue of being included in - :ref:`without `. This applies to both reads and writes. - - -Getters & Setters ------------------ - -The recommended way is to use the getter and setter methods generated for -each declared field: - -.. code-block:: ruby - - class Person - include Mongoid::Document - - field :first_name - end - - person = Person.new - - person.first_name = "Artem" - person.first_name - # => "Artem" - -To use this mechanism, each field must be explicitly declared, or the -model class must enable :ref:`dynamic fields `. - - -Custom Getters & Setters ------------------------- - -It is possible to explicitly define the getter and setter methods to provide -custom behavior when reading or writing fields, for example value -transformations or storing values under different field names. In this case -``read_attribute`` and ``write_attribute`` methods can be used to read and -write the values directly into the attributes hash: - -.. code-block:: ruby - - class Person - include Mongoid::Document - - def first_name - read_attribute(:fn) - end - - def first_name=(value) - write_attribute(:fn, value) - end - end - - person = Person.new - - person.first_name = "Artem" - person.first_name - # => "Artem" - - person.attributes - # => {"_id"=>BSON::ObjectId('606477dc2c97a628cf47075b'), "fn"=>"Artem"} - -.. note:: - - The custom setters are called during the assignment of - :ref:`nested attributes `, however they are called before - the associations are set up. Because of this, associations may not always - be available during these methods, and it is encouraged to include checks - for their presence whenever referring to them. :ref:`Callbacks ` - can also be used to perform operations on certain events, and associations - will have already been setup and are available during their execution. - -.. _read-write-attribute: - -``read_attribute`` & ``write_attribute`` ----------------------------------------- - -The ``read_attribute`` and ``write_attribute`` methods can be used explicitly -as well. Note that if a field specifies its :ref:`storage field name -`, both ``read_attribute`` and ``write_attribute`` -accept either the declared field name or the storage field name for operations: - -.. code-block:: ruby - - class Person - include Mongoid::Document - - field :first_name, as: :fn - field :last_name, as: :ln - end - - person = Person.new(first_name: "Artem") - # => # - - person.read_attribute(:first_name) - # => "Artem" - - person.read_attribute(:fn) - # => "Artem" - - person.write_attribute(:last_name, "Pushkin") - person - # => # - - person.write_attribute(:ln, "Medvedev") - person - # => # - -``read_attribute`` and ``write_attribute`` do not require that a field with -the used name is defined, but writing field values with ``write_attribute`` -does not cause the respective field to be defined either: - -.. code-block:: ruby - - person.write_attribute(:undefined, "Hello") - person - # => # - person.attributes - # => {"_id"=>BSON::ObjectId('60647b212c97a6292c195b4c'), "first_name"=>"Artem", "last_name"=>"Medvedev", "undefined"=>"Hello"} - - person.read_attribute(:undefined) - # => "Hello" - person.undefined - # raises NoMethodError - -When ``read_attribute`` is used to access a missing field, it returns ``nil``. - - -Hash Access ------------ - -Mongoid model instances define the ``[]`` and ``[]=`` methods to provide -``Hash`` style access to the attributes. ``[]`` is an alias for -``read_attribute`` and ``[]=`` is an alias for ``write_attribute``; see -the section on :ref:`read_attribute and write_attribute ` -for the detailed description of their behavior. - -.. code-block:: ruby - - class Person - include Mongoid::Document - - field :first_name, as: :fn - field :last_name, as: :ln - end - - person = Person.new(first_name: "Artem") - - person["fn"] - # => "Artem" - - person[:first_name] - # => "Artem" - - person[:ln] = "Medvedev" - person - # => # - - person["last_name"] = "Pushkin" - person - # => # - - -Bulk Attribute Writes ---------------------- - -In cases where you want to set multiple field values at once, there are a few -different ways of accomplishing this as well. - -.. code-block:: ruby - - # Get the field values as a hash. - person.attributes - - # Set the field values in the document. - Person.new(first_name: "Jean-Baptiste", middle_name: "Emmanuel") - person.attributes = { first_name: "Jean-Baptiste", middle_name: "Emmanuel" } - person.write_attributes( - first_name: "Jean-Baptiste", - middle_name: "Emmanuel", - ) - - -Dirty Tracking -============== - -Mongoid supports tracking of changed or "dirty" fields with an API that mirrors that of -Active Model. If a defined field has been modified in a model the model will be marked as -dirty and some additional behavior comes into play. - - -Viewing Changes ---------------- - -There are various ways to view what has been altered on a model. Changes are recorded -from the time a document is instantiated, either as a new document or via loading from -the database up to the time it is saved. Any persistence operation clears the changes. - -.. code-block:: ruby - - class Person - include Mongoid::Document - field :name, type: String - end - - person = Person.first - person.name = "Alan Garner" - - # Check to see if the document has changed. - person.changed? # true - - # Get an array of the names of the changed fields. - person.changed # [ :name ] - - # Get a hash of the old and changed values for each field. - person.changes # { "name" => [ "Alan Parsons", "Alan Garner" ] } - - # Check if a specific field has changed. - person.name_changed? # true - - # Get the changes for a specific field. - person.name_change # [ "Alan Parsons", "Alan Garner" ] - - # Get the previous value for a field. - person.name_was # "Alan Parsons" - -.. note:: - - Setting the associations on a document does not cause the ``changes`` or - ``changed_attributes`` hashes to be modified. This is true for all associations - whether referenced or embedded. Note that changing the _id(s) field on - referenced associations does cause the changes to show up in the ``changes`` - and the ``changed_attributes`` hashes. - - -Resetting Changes ------------------ - -You can reset changes of a field to its previous value by calling the reset method. - -.. code-block:: ruby - - person = Person.first - person.name = "Alan Garner" - - # Reset the changed name back to the original - person.reset_name! - person.name # "Alan Parsons" - - -Persistence ------------ - -Mongoid uses dirty tracking as the core of its persistence operations. It looks at the -changes on a document and atomically updates only what has changed, unlike other frameworks -that write the entire document on each save. If no changes have been made, Mongoid will -not hit the database on a call to ``Model#save``. - - -Viewing Previous Changes ------------------------- - -After a document has been persisted, you can see what the changes were previously by -calling ``Model#previous_changes``. - -.. code-block:: ruby - - person = Person.first - person.name = "Alan Garner" - person.save # Clears out current changes. - - # View the previous changes. - person.previous_changes # { "name" => [ "Alan Parsons", "Alan Garner" ] } - - -Updating Container Fields -========================= - -Be aware that, until -`MONGOID-2951 `_ -is resolved, all fields including container ones must be assigned to for -their values to be persisted to the database. - -For example, adding to a set like this does not work: - -.. code-block:: ruby - - class Band - include Mongoid::Document - - field :tours, type: Set - end - - band = Band.new - band.tours - # => # - - band.tours << 'London' - # => # - band.tours - # => # - -Instead, the field value must be modified outside of the model and assigned -back to the model as follows: - -.. code-block:: ruby - - class Band - include Mongoid::Document - - field :tours, type: Set - end - - band = Band.new - - tours = band.tours - # => # - - tours << 'London' - # => # - - band.tours = tours - # => # - - band.tours - # => # - - -.. _readonly-documents: - -Readonly Documents -================== - -Documents can be marked read-only in two ways, depending on the value of the -``Mongoid.legacy_readonly`` feature flag: - -If this flag is turned off, a document is marked read-only when the ``#readonly!`` -method is called on that documnet. A read-only document, with this flag turned off, -will raise a ReadonlyDocument error on attempting to perform any persistence -operation, including (but not limited to) saving, updating, deleting and -destroying. Note that reloading does not reset the read-only state. - -.. code:: ruby - - band = Band.first - band.readonly? # => false - band.readonly! - band.readonly? # => true - band.name = "The Rolling Stones" - band.save # => raises ReadonlyDocument error - band.reload.readonly? # => true - -If this flag is turned on, a document is marked read-only when that document -has been projected (i.e. using ``#only`` or ``#without``). A read-only document, -with this flag turned on, will not be deletable or destroyable (a -``ReadonlyDocument`` error will be raised), but will be saveable and updatable. -The read-only status is reset on reloading the document. - -.. code:: ruby - - class Band - include Mongoid::Document - field :name, type: String - field :genre, type: String - end - - band = Band.only(:name).first - band.readonly? # => true - band.destroy # => raises ReadonlyDocument error - band.reload.readonly? # => false - - -Overriding ``readonly?`` ------------------------- - -Another way to make a document read-only is by overriding the readonly? method: - -.. code:: ruby - - class Band - include Mongoid::Document - field :name, type: String - field :genre, type: String - - def readonly? - true - end - end - - band = Band.first - band.readonly? # => true - band.destroy # => raises ReadonlyDocument error diff --git a/source/reference/fields.txt b/source/reference/fields.txt deleted file mode 100644 index 5e29bfee..00000000 --- a/source/reference/fields.txt +++ /dev/null @@ -1,1778 +0,0 @@ -.. _fields: - -**************** -Field Definition -**************** - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 2 - :class: singlecol - - -.. _field-types: - -Field Types -=========== - -MongoDB stores underlying document data using -`BSON types `_, and -Mongoid converts BSON types to Ruby types at runtime in your application. -For example, a field defined with ``type: :float`` will use the Ruby ``Float`` -class in-memory and will persist in the database as the the BSON ``double`` type. - -Field type definitions determine how Mongoid behaves when constructing queries -and retrieving/writing fields from/to the database. Specifically: - -1. When assigning values to fields at runtime, the values are converted to the - specified type. -2. When persisting data to MongoDB, the data is sent in an appropriate - type, permitting richer data manipulation within MongoDB or by other - tools. -3. When querying documents, query parameters are converted to the specified - type before being sent to MongoDB. -4. When retrieving documents from the database, field values are converted - to the specified type. - -Changing the field definitions in a model class does not alter data already stored in -MongoDB. To update type or contents of fields of existing documents, -the field must be re-saved to the database. Note that, due to Mongoid -tracking which attributes on a model change and only saving the changed ones, -it may be necessary to explicitly write a field value when changing the -type of an existing field without changing the stored values. - -Consider a simple class for modeling a person in an application. A person may -have a name, date_of_birth, and weight. We can define these attributes -on a person by using the ``field`` macro. - -.. code-block:: ruby - - class Person - include Mongoid::Document - field :name, type: String - field :date_of_birth, type: Date - field :weight, type: Float - end - -The valid types for fields are as follows: - -- ``Array`` -- ``BSON::Binary`` -- :ref:`BigDecimal ` -- ``Mongoid::Boolean``, which may be specified simply as ``Boolean`` in the - scope of a class which included ``Mongoid::Document``. -- :ref:`Date ` -- :ref:`DateTime ` -- ``Float`` -- :ref:`Hash ` -- ``Integer`` -- :ref:`Object ` -- ``BSON::ObjectId`` -- ``Range`` -- :ref:`Regexp ` -- ``Set`` -- ``String`` -- :ref:`Mongoid::StringifiedSymbol `, - which may be specified simply as ``StringifiedSymbol`` in the scope of a - class which included ``Mongoid::Document``. -- :ref:`Symbol ` -- :ref:`Time ` -- ``ActiveSupport::TimeWithZone`` - -Mongoid also recognizes the string ``"Boolean"`` as an alias for the -``Mongoid::Boolean`` class. - -To define custom field types, refer to :ref:`Custom Field Types ` below. - -.. note:: - - Using the ``BSON::Int64`` and ``BSON::Int32`` types as field types is unsupported. - Saving these types to the database will work as expected, however, querying them - will return the native Ruby ``Integer`` type. Querying fields of type - ``BSON::Decimal128`` will return values of type ``BSON::Decimal128`` in - BSON <=4 and values of type ``BigDecimal`` in BSON 5+. - - -.. _untyped-fields: - -Untyped Fields --------------- - -Not specifying a type for a field is the same as specifying the ``Object`` -type. Such fields are untyped: - -.. code-block:: ruby - - class Product - include Mongoid::Document - - field :properties - # Equivalent to: - field :properties, type: Object - end - -An untyped field can store values of any type which is directly serializable -to BSON. This is useful when a field may contain values of different types -(i.e. it is a variant type field), or when the type of values is not known -ahead of time: - -.. code-block:: ruby - - product = Product.new(properties: "color=white,size=large") - product.properties - # => "color=white,size=large" - - product = Product.new(properties: {color: "white", size: "large"}) - product.properties - # => {:color=>"white", :size=>"large"} - -When values are assigned to the field, Mongoid still performs mongoization but -uses the class of the value rather than the field type for mongoization logic. - -.. code-block:: ruby - - product = Product.new(properties: 0..10) - product.properties - # The range 0..10, mongoized: - # => {"min"=>0, "max"=>10} - -When reading data from the database, Mongoid does not perform any type -conversions on untyped fields. For this reason, even though it is possible -to write any BSON-serializable value into an untyped fields, values which -require special handling on the database reading side will generally not work -correctly in an untyped field. Among field types supported by Mongoid, -values of the following types should not be stored in untyped fields: - -- ``Date`` (values will be returned as ``Time``) -- ``DateTime`` (values will be returned as ``Time``) -- ``Range`` (values will be returned as ``Hash``) - - -.. _field-type-stringified-symbol: - -Field Type: StringifiedSymbol ------------------------------ - -The ``StringifiedSymbol`` field type is the recommended field type for storing -values that should be exposed as symbols to Ruby applications. When using the ``Symbol`` field type, -Mongoid defaults to storing values as BSON symbols. For more information on the -BSON symbol type, see :ref:`here `. -However, the BSON symbol type is deprecated and is difficult to work with in programming languages -without native symbol types, so the ``StringifiedSymbol`` type allows the use of symbols -while ensuring interoperability with other drivers. The ``StringifiedSymbol`` type stores all data -on the database as strings, while exposing values to the application as symbols. - -An example usage is shown below: - -.. code-block:: ruby - - class Post - include Mongoid::Document - - field :status, type: StringifiedSymbol - end - - post = Post.new(status: :hello) - # status is stored as "hello" on the database, but returned as a Symbol - post.status - # => :hello - - # String values can be assigned also: - post = Post.new(status: "hello") - # status is stored as "hello" on the database, but returned as a Symbol - post.status - # => :hello - -All non-string values will be stringified upon being sent to the database (via ``to_s``), and -all values will be converted to symbols when returned to the application. Values that cannot be -converted directly to symbols, such as integers and arrays, will first be converted to strings and -then symbols before being returned to the application. - -For example, setting an integer as ``status``: - -.. code-block:: ruby - - post = Post.new(status: 42) - post.status - # => :"42" - -If the ``StringifiedSymbol`` type is applied to a field that contains BSON symbols, the values -will be stored as strings instead of BSON symbols on the next save. This permits transparent lazy -migration from fields that currently store either strings or BSON symbols in the database to the -``StringifiedSymbol`` field type. - - -.. _field-type-symbol: - -Field Type: Symbol ------------------- - -New applications should use the :ref:`StringifiedSymbol field type ` -to store Ruby symbols in the database. The ``StringifiedSymbol`` field type -provides maximum compatibility with other applications and programming languages -and has the same behavior in all circumstances. - -Mongoid also provides the deprecated ``Symbol`` field type for serializing -Ruby symbols to BSON symbols. Because the BSON specification deprecated the -BSON symbol type, the ``bson`` gem will serialize Ruby symbols into BSON strings -when used on its own. However, in order to maintain backwards compatibility -with older datasets, the ``mongo`` gem overrides this behavior to serialize Ruby -symbols as BSON symbols. This is necessary to be able to specify queries for -documents which contain BSON symbols as fields. - -To override the default behavior and configure the ``mongo`` gem (and thereby -Mongoid as well) to encode symbol values as strings, include the following code -snippet in your project: - -.. code-block:: ruby - - class Symbol - def bson_type - BSON::String::BSON_TYPE - end - end - - -.. _field-type-hash: - -Field Type: Hash ----------------- - -When using a field of type Hash, be wary of adhering to the -`legal key names for mongoDB `_, -or else the values will not store properly. - -.. code-block:: ruby - - class Person - include Mongoid::Document - field :first_name - field :url, type: Hash - - # will update the fields properly and save the values - def set_vals - self.first_name = 'Daniel' - self.url = {'home_page' => 'http://www.homepage.com'} - save - end - - # all data will fail to save due to the illegal hash key - def set_vals_fail - self.first_name = 'Daniel' - self.url = {'home.page' => 'http://www.homepage.com'} - save - end - end - - -.. _field-type-time: - -Field Type: Time ----------------- - -``Time`` fields store values as ``Time`` instances in the :ref:`configured -time zone `. - -``Date`` and ``DateTime`` instances are converted to ``Time`` instances upon -assignment to a ``Time`` field: - -.. code-block:: ruby - - class Voter - include Mongoid::Document - - field :registered_at, type: Time - end - - Voter.new(registered_at: Date.today) - # => # - -In the above example, the value was interpreted as the beginning of today in -local time, because the application was not configured to use UTC times. - -.. note:: - - When the database contains a string value for a ``Time`` field, Mongoid - parses the string value using ``Time.parse`` which considers values without - time zones to be in local time. - - -.. _field-type-date: - -Field Type: Date ----------------- - -Mongoid allows assignment of values of several types to ``Date`` fields: - -- ``Date`` - the provided date is stored as is. -- ``Time``, ``DateTime``, ``ActiveSupport::TimeWithZone`` - the date component - of the value is taken in the value's time zone. -- ``String`` - the date specified in the string is used. -- ``Integer``, ``Float`` - the value is taken to be a UTC timestamp which is - converted to the :ref:`configured time zone ` (note that - ``Mongoid.use_utc`` has no effect on this conversion), then the date is - taken from the resulting time. - -In other words, if a date is specified in the value, that date is used without -first converting the value to the configured time zone. - -As a date & time to date conversion is lossy (it discards the time component), -especially if an application operates with times in different time zones it is -recommended to explicitly convert ``String``, ``Time`` and ``DateTime`` -objects to ``Date`` objects before assigning the values to fields of type -``Date``. - -.. note:: - - When the database contains a string value for a ``Date`` field, Mongoid - parses the string value using ``Time.parse``, discards the time portion of - the resulting ``Time`` object and uses the date portion. ``Time.parse`` - considers values without time zones to be in local time. - - -.. _field-type-date-time: - -Field Type: DateTime ---------------------- - -MongoDB stores all times as UTC timestamps. When assigning a value to a -``DateTime`` field, or when querying a ``DateTime`` field, Mongoid -converts the passed in value to a UTC ``Time`` before sending it to the -MongoDB server. - -``Time``, ``ActiveSupport::TimeWithZone`` and ``DateTime`` objects embed -time zone information, and the value persisted is the specified moment in -time, in UTC. When the value is retrieved, the time zone in which it is -returned is defined by the :ref:`configured time zone settings `. - -.. code-block:: ruby - - class Ticket - include Mongoid::Document - field :opened_at, type: DateTime - end - - Time.zone = 'Berlin' - - ticket = Ticket.create!(opened_at: '2018-02-18 07:00:08 -0500') - - ticket.opened_at - # => Sun, 18 Feb 2018 13:00:08 +0100 - ticket - # => # - - Time.zone = 'America/New_York' - ticket.opened_at - # => Sun, 18 Feb 2018 07:00:08 -0500 - - Mongoid.use_utc = true - ticket.opened_at - # => Sun, 18 Feb 2018 12:00:08 +0000 - -Mongoid also supports casting integers and floats to ``DateTime``. When -doing so, the integers/floats are assumed to be Unix timestamps (in UTC): - -.. code-block:: ruby - - ticket.opened_at = 1544803974 - ticket.opened_at - # => Fri, 14 Dec 2018 16:12:54 +0000 - -If a string is used as a ``DateTime`` field value, the behavior depends on -whether the string includes a time zone. If no time zone is specified, -the :ref:`default Mongoid time zone ` is used: - -.. code-block:: ruby - - Time.zone = 'America/New_York' - ticket.opened_at = 'Mar 4, 2018 10:00:00' - ticket.opened_at - # => Sun, 04 Mar 2018 15:00:00 +0000 - -If a time zone is specified, it is respected: - -.. code-block:: ruby - - ticket.opened_at = 'Mar 4, 2018 10:00:00 +01:00' - ticket.opened_at - # => Sun, 04 Mar 2018 09:00:00 +0000 - -.. note:: - - When the database contains a string value for a ``DateTime`` field, Mongoid - parses the string value using ``Time.parse`` which considers values without - time zones to be in local time. - - -.. _field-type-regexp: - -Field Type: Regexp ------------------- - -MongoDB supports storing regular expressions in documents, and querying using -regular expressions. Note that MongoDB uses -`Perl-compatible regular expressions (PCRE) `_ -and Ruby uses `Onigmo `_, which is a -fork of `Oniguruma regular expression engine `_. -The two regular expression implementations generally provide equivalent -functionality but have several important syntax differences. - -When a field is declared to be of type Regexp, Mongoid converts Ruby regular -expressions to BSON regular expressions and stores the result in MongoDB. -Retrieving the field from the database produces a ``BSON::Regexp::Raw`` -instance: - -.. code-block:: ruby - - class Token - include Mongoid::Document - - field :pattern, type: Regexp - end - - token = Token.create!(pattern: /hello.world/m) - token.pattern - # => /hello.world/m - - token.reload - token.pattern - # => # - -Use ``#compile`` method on ``BSON::Regexp::Raw`` to get back the Ruby regular -expression: - -.. code-block:: ruby - - token.pattern.compile - # => /hello.world/m - -Note that, if the regular expression was not originally a Ruby one, calling -``#compile`` on it may produce a different regular expression. For example, -the following is a PCRE matching a string that ends in "hello": - -.. code-block:: ruby - - BSON::Regexp::Raw.new('hello$', 's') - # => # - -Compiling this regular expression produces a Ruby regular expression that -matches strings containing "hello" before a newline, besides strings ending in -"hello": - -.. code-block:: ruby - - BSON::Regexp::Raw.new('hello$', 's').compile =~ "hello\nworld" - # => 0 - -This is because the meaning of ``$`` is different between PCRE and Ruby -regular expressions. - -.. _field-type-big-decimal: - -BigDecimal Fields ------------------ - -The ``BigDecimal`` field type is used to store numbers with increased precision. - -The ``BigDecimal`` field type stores its values in two different ways in the -database, depending on the value of the ``Mongoid.map_big_decimal_to_decimal128`` -global config option. If this flag is set to false (which is the default), -the ``BigDecimal`` field will be stored as a string, otherwise it will be stored -as a ``BSON::Decimal128``. - -The ``BigDecimal`` field type has some limitations when converting to and from -a ``BSON::Decimal128``: - -- ``BSON::Decimal128`` has a limited range and precision, while ``BigDecimal`` - has no restrictions in terms of range and precision. ``BSON::Decimal128`` has - a max value of approximately ``10^6145`` and a min value of approximately - ``-10^6145``, and has a maximum of 34 bits of precision. When attempting to - store values that don't fit into a ``BSON::Decimal128``, it is recommended to - have them stored as a string instead of a ``BSON::Decimal128``. You can do - that by setting ``Mongoid.map_big_decimal_to_decimal128`` to ``false``. If a - value that does not fit in a ``BSON::Decimal128`` is attempted to be stored - as one, an error will be raised. - -- ``BSON::Decimal128`` is able to accept signed ``NaN`` values, while - ``BigDecimal`` is not. When retrieving signed ``NaN`` values from - the database using the ``BigDecimal`` field type, the ``NaN`` will be - unsigned. - -- ``BSON::Decimal128`` maintains trailing zeroes when stored in the database. - ``BigDecimal``, however, does not maintain trailing zeroes, and therefore - retrieving ``BSON::Decimal128`` values using the ``BigDecimal`` field type - may result in a loss of precision. - -There is an additional caveat when storing a ``BigDecimal`` in a field with no -type (i.e. a dynamically typed field) and ``Mongoid.map_big_decimal_to_decimal128`` -is ``false``. In this case, the ``BigDecimal`` is stored as a string, and since a -dynamic field is being used, querying for that field with a ``BigDecimal`` will -not find the string for that ``BigDecimal``, since the query is looking for a -``BigDecimal``. In order to query for that string, the ``BigDecimal`` must -first be converted to a string with ``to_s``. Note that this is not a problem -when the field has type ``BigDecimal``. - -If you wish to avoid using ``BigDecimal`` altogether, you can set the field -type to ``BSON::Decimal128``. This will allow you to keep track of trailing -zeroes and signed ``NaN`` values. - -Migration to ``decimal128``-backed ``BigDecimal`` Field -``````````````````````````````````````````````````````` -In a future major version of Mongoid, the ``Mongoid.map_big_decimal_to_decimal128`` -global config option will be defaulted to ``true``. When this flag is turned on, -``BigDecimal`` values in queries will not match to the strings that are already -stored in the database; they will only match to ``decimal128`` values that are -in the database. If you have a ``BigDecimal`` field that is backed by strings, -you have three options: - -1. The ``Mongoid.map_big_decimal_to_decimal128`` global config option can be - set to ``false``, and you can continue storing your ``BigDecimal`` values as - strings. Note that you are surrendering the advantages of storing ``BigDecimal`` - values as a ``decimal128``, like being able to do queries and aggregations - based on the numerical value of the field. - -2. The ``Mongoid.map_big_decimal_to_decimal128`` global config option can be - set to ``true``, and you can convert all values for that field from strings to - ``decimal128`` values in the database. You should do this conversion before - setting the global config option to true. An example query to accomplish this - is as follows: - - .. code-block:: javascript - - db.bands.updateMany({ - "field": { "$exists": true } - }, [ - { - "$set": { - "field": { "$toDecimal": "$field" } - } - } - ]) - - This query updates all documents that have the given field, setting that - field to its corresponding ``decimal128`` value. Note that this query only - works in MongoDB 4.2+. - -3. The ``Mongoid.map_big_decimal_to_decimal128`` global config option can be - set to ``true``, and you can have both strings and ``decimal128`` values for - that field. This way, only ``decimal128`` values will be inserted into and - updated to the database going forward. Note that you still don't get the - full advantages of using only ``decimal128`` values, but your dataset is - slowly migrating to all ``decimal128`` values, as old string values are - updated to ``decimal128`` and new ``decimal128`` values are added. With this - setup, you can still query for ``BigDecimal`` values as follows: - - .. code-block:: ruby - - Mongoid.map_big_decimal_to_decimal128 = true - big_decimal = BigDecimal('2E9') - Band.in(sales: [big_decimal, big_decimal.to_s]).to_a - - This query will find all values that are either a ``decimal128`` value or - a string that match that value. - - -Using Symbols Or Strings Instead Of Classes -------------------------------------------- - -Mongoid permits using symbols or strings instead of classes to specify the -type of fields, for example: - -.. code-block:: ruby - - class Order - include Mongoid::Document - - field :state, type: :integer - # Equivalent to: - field :state, type: "integer" - # Equivalent to: - field :state, type: Integer - end - -Only standard field types as listed below can be specified using symbols or -strings in this manner. Mongoid recognizes the following expansions: - -- ``:array`` => ``Array`` -- ``:big_decimal`` => ``BigDecimal`` -- ``:binary`` => ``BSON::Binary`` -- ``:boolean`` => ``Mongoid::Boolean`` -- ``:date`` => ``Date`` -- ``:date_time`` => ``DateTime`` -- ``:float`` => ``Float`` -- ``:hash`` => ``Hash`` -- ``:integer`` => ``Integer`` -- ``:object_id`` => ``BSON::ObjectId`` -- ``:range`` => ``Range`` -- ``:regexp`` => ``Regexp`` -- ``:set`` => ``Set`` -- ``:string`` => ``String`` -- ``:stringified_symbol`` => ``StringifiedSymbol`` -- ``:symbol`` => ``Symbol`` -- ``:time`` => ``Time`` - - -.. _field-default-values: - -Specifying Field Default Values -------------------------------- - -A field can be configured to have a default value. The default value can be -fixed, as in the following example: - -.. code-block:: ruby - - class Order - include Mongoid::Document - - field :state, type: String, default: 'created' - end - -The default value can also be specified as a ``Proc``: - -.. code-block:: ruby - - class Order - include Mongoid::Document - - field :fulfill_by, type: Time, default: ->{ Time.now + 3.days } - end - -.. note:: - - Default values that are not ``Proc`` instances are evaluated at class load - time, meaning the following two definitions are not equivalent: - - .. code-block:: ruby - - field :submitted_at, type: Time, default: Time.now - field :submitted_at, type: Time, default: ->{ Time.now } - - The second definition is most likely the desired one, which causes the - time of submission to be set to the current time at the moment of - document instantiation. - -To set a default which depends on the document's state, use ``self`` -inside the ``Proc`` instance which would evaluate to the document instance -being operated on: - -.. code-block:: ruby - - field :fulfill_by, type: Time, default: ->{ - # Order should be fulfilled in 2 business hours. - if (7..8).include?(self.submitted_at.hour) - self.submitted_at + 4.hours - elsif (9..3).include?(self.submitted_at.hour) - self.submitted_at + 2.hours - else - (self.submitted_at + 1.day).change(hour: 11) - end - } - -When defining a default value as a ``Proc``, Mongoid will apply the default -after all other attributes are set and associations are initialized. -To have the default be applied before the other attributes are set, -use the ``pre_processed: true`` field option: - -.. code-block:: ruby - - field :fulfill_by, type: Time, default: ->{ Time.now + 3.days }, - pre_processed: true - -The ``pre_processed: true`` option is also necessary when specifying a custom -default value via a ``Proc`` for the ``_id`` field, to ensure the ``_id`` -is set correctly via associations: - -.. code-block:: ruby - - field :_id, type: String, default: -> { 'hello' }, pre_processed: true - - -.. _storage-field-names: - -Specifying Storage Field Names ------------------------------- - -One of the drawbacks of having a schemaless database is that MongoDB must -store all field information along with every document, meaning that it -takes up a lot of storage space in RAM and on disk. A common pattern to limit -this is to alias fields to a small number of characters, while keeping the -domain in the application expressive. Mongoid allows you to do this and -reference the fields in the domain via their long names in getters, setters, -and criteria while performing the conversion for you. - -.. code-block:: ruby - - class Band - include Mongoid::Document - field :n, as: :name, type: String - end - - band = Band.new(name: "Placebo") - band.attributes # { "n" => "Placebo" } - - criteria = Band.where(name: "Placebo") - criteria.selector # { "n" => "Placebo" } - - -.. _field-aliases: - -Field Aliases -------------- - -It is possible to define field aliases. The value will be stored in the -destination field but can be accessed from either the destination field or -from the aliased field: - -.. code-block:: ruby - - class Band - include Mongoid::Document - - field :name, type: String - alias_attribute :n, :name - end - - band = Band.new(n: 'Astral Projection') - # => # - - band.attributes - # => {"_id"=>BSON::ObjectId('5fc1c1ee2c97a64accbeb5e1'), "name"=>"Astral Projection"} - - band.n - # => "Astral Projection" - -Aliases can be removed from model classes using the ``unalias_attribute`` -method. - -.. code-block:: ruby - - class Band - unalias_attribute :n - end - -.. _unalias-id: - -Unaliasing ``id`` -````````````````` - -``unalias_attribute`` can be used to remove the predefined ``id`` alias. -This is useful for storing different values in ``id`` and ``_id`` fields: - -.. code-block:: ruby - - class Band - include Mongoid::Document - - unalias_attribute :id - field :id, type: String - end - - Band.new(id: '42') - # => # - - -Reserved Names --------------- - -Attempting to define a field on a document that conflicts with a reserved -method name in Mongoid will raise an error. The list of reserved names can -be obtained by invoking the ``Mongoid.destructive_fields`` method. - - -Field Redefinition ------------------- - -By default Mongoid allows redefining fields on a model. To raise an error -when a field is redefined, set the ``duplicate_fields_exception`` -:ref:`configuration option ` to ``true``. - -With the option set to true, the following example will raise an error: - -.. code-block:: ruby - - class Person - include Mongoid::Document - - field :name - - field :name, type: String - end - -To define the field anyway, use the ``overwrite: true`` option: - -.. code-block:: ruby - - class Person - include Mongoid::Document - - field :name - - field :name, type: String, overwrite: true - end - - -.. _custom-id: - -Custom IDs ----------- - -By default, Mongoid defines the ``_id`` field on documents to contain a -``BSON::ObjectId`` value which is automatically generated by Mongoid. - -It is possible to replace the ``_id`` field definition to change the type -of the ``_id`` values or have different default values: - -.. code-block:: ruby - - class Band - include Mongoid::Document - field :name, type: String - field :_id, type: String, default: ->{ name } - end - -It is possible to omit the default entirely: - -.. code-block:: ruby - - class Band - include Mongoid::Document - field :_id, type: String - end - -If the default on ``_id`` is omitted, and no ``_id`` value is provided by -your application, Mongoid will persist the document without the ``_id`` -value. In this case, if the document is a top-level document, an ``_id`` -value will be assigned by the server; if the document is an embedded document, -no ``_id`` value will be assigned. Mongoid will not automatically retrieve -this value, if assigned, when the document is persisted - you -must obtain the persisted value (and the complete persisted document) using -other means: - -.. code-block:: ruby - - band = Band.create! - => # - band.id - => nil - band.reload - # raises Mongoid::Errors::DocumentNotFound - Band.last - => # - -Omitting ``_id`` fields is more common in :ref:`embedded documents `. - -Mongoid also defines the ``id`` field aliased to ``_id``. The ``id`` -alias can :ref:`be removed ` if desired (such as to integrate -with systems that use the ``id`` field to store value different from ``_id``. - -.. _uncastable-values: - -Uncastable Values ------------------ - -In Mongoid 8, Mongoid has standardized the treatment of the assignment and -reading of "uncastable" values. A value is considered "uncastable" when it -cannot be coerced to the type of its field. For example, an array would be an -"uncastable" value to an Integer field. - - -Assigning Uncastable Values -``````````````````````````` - -The assignment of uncastable values has been standardized to assign ``nil`` by -default. Consider the following example: - -.. code:: - - class User - include Mongoid::Document - - field :name, type: Integer - end - - User.new(name: [ "hello" ]) - -Assigning an array to a field of type Integer doesn't work since an array can't -be coerced to an Integer. The assignment of uncastable values to a field will -cause a ``nil`` to be written: - -.. code:: - - user = User.new(name: [ "Mike", "Trout" ]) - # => # - -Note that the original uncastable values will be stored in the -``attributes_before_type_cast`` hash with their field names: - -.. code:: - - user.attributes_before_type_cast["name"] - # => ["Mike", "Trout"] - -.. note:: - - Note that for numeric fields, any class that defines ``to_i`` for Integer - fields, ``to_f`` for Floats, and ``to_d`` for BigDecimals, is castable. - Strings are the exception and will only call the corresponding ``to_*`` - method if the string is numeric. If a class only defines ``to_i`` and not - ``to_f`` and is being assigned to a Float field, this is uncastable, and Mongoid - will not perform a two-step conversion (i.e. ``to_i`` and then ``to_f``). - - -Reading Uncastable Values -````````````````````````` - -When documents in the database contain values of different types than their -representations in Mongoid, if Mongoid cannot coerce them into the correct type, -it will replace the value with ``nil``. Consider the following model and document in the -database: - -.. code:: - - class User - include Mongoid::Document - - field :name, type: Integer - end - -.. code:: - - { _id: ..., name: [ "Mike", "Trout" ] } - -Reading this document from the database will result in the model's name field -containing ``nil``: - -.. code:: - - User.first.name - # => nil - -The database value of type array cannot be stored in the attribute, since the -array can't be coerced to an Integer. Note that the original uncastable values -will be stored in the ``attributes_before_type_cast`` hash with their field -names: - -.. code:: - - user.attributes_before_type_cast["name"] - # => ["Mike", "Trout"] - -.. note:: - - The ``demongoize`` methods on container objects (i.e. Hash, Array) have not - been changed to permit automatic persistence of mutated container attributes. - See `MONGOID-2951 `_ for a - longer discussion of this topic. - - -.. _customizing-field-behavior: - -Customizing Field Behavior -========================== - -Mongoid offers several ways to customize the behavior of fields. - - -.. _custom-getters-and-setters: - -Custom Getters And Setters --------------------------- - -You may override getters and setters for fields to modify the values -when they are being accessed or written. The getters and setters use the -same name as the field. Use ``read_attribute`` and ``write_attribute`` -methods inside the getters and setters to operate on the raw attribute -values. - -For example, Mongoid provides the ``:default`` field option to write a -default value into the field. If you wish to have a field default value -in your application but do not wish to persist it, you can override the -getter as follows: - -.. code-block:: ruby - - class DistanceMeasurement - include Mongoid::Document - - field :value, type: Float - field :unit, type: String - - def unit - read_attribute(:unit) || "m" - end - - def to_s - "#{value} #{unit}" - end - end - - measurement = DistanceMeasurement.new(value: 2) - measurement.to_s - # => "2.0 m" - measurement.attributes - # => {"_id"=>BSON::ObjectId('613fa0b0a15d5d61502f3447'), "value"=>2.0} - -To give another example, a field which converts empty strings to nil values -may be implemented as follows: - -.. code-block:: ruby - - class DistanceMeasurement - include Mongoid::Document - - field :value, type: Float - field :unit, type: String - - def unit=(value) - if value.blank? - value = nil - end - write_attribute(:unit, value) - end - end - - measurement = DistanceMeasurement.new(value: 2, unit: "") - measurement.attributes - # => {"_id"=>BSON::ObjectId('613fa15aa15d5d617216104c'), "value"=>2.0, "unit"=>nil} - - -.. _custom-field-types: - -Custom Field Types ------------------- - -You can define custom types in Mongoid and determine how they are serialized -and deserialized. In this example, we define a new field type ``Point``, which we -can use in our model class as follows: - -.. code-block:: ruby - - class Profile - include Mongoid::Document - field :location, type: Point - end - -Then make a Ruby class to represent the type. This class must define methods -used for MongoDB serialization and deserialization as follows: - -.. code-block:: ruby - - class Point - - attr_reader :x, :y - - def initialize(x, y) - @x, @y = x, y - end - - # Converts an object of this instance into a database friendly value. - # In this example, we store the values in the database as array. - def mongoize - [ x, y ] - end - - class << self - - # Takes any possible object and converts it to how it would be - # 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 - - # Get the object as it was stored in the database, and instantiate - # this custom class from it. - def demongoize(object) - Point.new(object[0], object[1]) - end - - # Converts the object that was supplied to a criteria and converts it - # into a query-friendly form. - def evolve(object) - case object - when Point then object.mongoize - else object - end - end - end - end - -The instance method ``mongoize`` takes an instance of your custom type object, and -converts it into a representation of how it will be stored in the database, i.e. to pass -to the MongoDB Ruby driver. In our example above, we want to store our ``Point`` -object as an ``Array`` in the form ``[ x, y ]``. - -The class method ``mongoize`` is similar to the instance method, however it must handle -objects of all possible types as inputs. The ``mongoize`` method is used when calling the -setter methods for fields of your custom type. - -.. code-block:: ruby - - point = Point.new(12, 24) - venue = Venue.new(location: point) # This uses the Point#mongoize instance method. - venue = Venue.new(location: [ 12, 24 ]) # This uses the Point.mongoize class method. - -The class method ``demongoize`` does the inverse of ``mongoize``. It takes the raw object -from the MongoDB Ruby driver and converts it to an instance of your custom type. -In this case, the database driver returns an ``Array`` and we instantiate a ``Point`` from it. -The ``demongoize`` method is used when calling the getters of fields for your custom type. -Note that in the example above, since ``demongoize`` calls ``Point.new``, a new instance of -``Point`` will be generated on each call to the getter. - -Mongoid will always call the ``demongoize`` method on values that were -retrieved from the database, but applications may, in theory, call -``demongoize`` with arbitrary input. It is recommended that applications add -handling for arbitrary input in their ``demongoize`` methods. We can rewrite -``Point``'s demongoize method as follows: - -.. code:: ruby - - def demongoize(object) - if object.is_a?(Array) && object.length == 2 - Point.new(object[0], object[1]) - end - end - -Notice that ``demongoize`` will only create a new ``Point`` if given an array -of length 2, and will return ``nil`` otherwise. Both the ``mongoize`` and -``demongoize`` methods should be prepared to receive arbitrary input and should -return ``nil`` on values that are uncastable to your custom type. See the -section on :ref:`Uncastable Values ` for more details. - -Lastly, the class method ``evolve`` is similar to ``mongoize``, however it is used -when transforming objects for use in Mongoid query criteria. - -.. code-block:: ruby - - point = Point.new(12, 24) - Venue.where(location: point) # This uses Point.evolve - -The ``evolve`` method should also be prepared to receive arbitrary input, -however, unlike the ``mongoize`` and ``demongoize`` methods, it should return -the inputted value on values that are uncastable to your custom type. See the -section on :ref:`Uncastable Values ` for more details. - - -.. _phantom-custom-field-types: - -Phantom Custom Field Types -`````````````````````````` - -The custom field type may perform conversions from user-visible attribute -values to the values stored in the database when the user-visible attribute -value type is different from the declared field type. For example, this -can be used to implement a mapping from one enumeration to another, to -have more descriptive values in the application and more compact values stored -in the database: - -.. code-block:: ruby - - class ColorMapping - - MAPPING = { - 'black' => 0, - 'white' => 1, - }.freeze - - INVERSE_MAPPING = MAPPING.invert.freeze - - class << self - - # Takes application-scope value and converts it to how it would be - # stored in the database. Converts invalid values to nil. - def mongoize(object) - MAPPING[object] - end - - # Get the value as it was stored in the database, and convert to - # application-scope value. Converts invalid values to nil. - def demongoize(object) - INVERSE_MAPPING[object] - end - - # Converts the object that was supplied to a criteria and converts it - # into a query-friendly form. Returns invalid values as is. - 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 - # => "white" - - # Writes 0 to color field - profile.save! - - -.. _custom-field-options: - -Custom Field Options --------------------- - -You may define custom options for the ``field`` macro function -which extend its behavior at the your time model classes are loaded. - -As an example, we will define a ``:max_length`` option which will add a length -validator for the field. First, declare the new field option in an initializer, -specifying its handler function as a block: - -.. code-block:: ruby - - # in /config/initializers/mongoid_custom_fields.rb - - Mongoid::Fields.option :max_length do |model, field, value| - model.validates_length_of field.name, maximum: value - end - -Then, use it your model class: - -.. code-block:: ruby - - class Person - include Mongoid::Document - - field :name, type: String, max_length: 10 - end - -Note that the handler function will be invoked whenever the option is used -in the field definition, even if the option's value is false or nil. - - -.. _dynamic-fields: - -Dynamic Fields -============== - -By default, Mongoid requires all fields that may be set on a document to -be explicitly defined using ``field`` declarations. Mongoid also supports -creating fields on the fly from an arbitrary hash or documents stored in -the database. When a model uses fields not explicitly defined, such fields -are called *dynamic fields*. - -To enable dynamic fields, include ``Mongoid::Attributes::Dynamic`` module -in the model: - -.. code-block:: ruby - - class Person - include Mongoid::Document - include Mongoid::Attributes::Dynamic - end - - bob = Person.new(name: 'Bob', age: 42) - bob.name - # => "Bob" - -It is possible to use ``field`` declarations and dynamic fields in the same -model class. Attributes for which there is a ``field`` declaration will be -treated according to the ``field`` declaration, with remaining attributes -being treated as dynamic fields. - -Attribute values in the dynamic fields must initially be set by either -passing the attribute hash to the constructor, mass assignment via -``attributes=``, mass assignment via ``[]=``, using ``write_attribute``, -or they must already be present in the database. - -.. code-block:: ruby - - # OK - bob = Person.new(name: 'Bob') - - # OK - bob = Person.new - bob.attributes = {age: 42} - - # OK - bob = Person.new - bob['age'] = 42 - - # Raises NoMethodError: undefined method age= - bob = Person.new - bob.age = 42 - - # OK - bob = Person.new - # OK - string access - bob.write_attribute('age', 42) - # OK - symbol access - bob.write_attribute(:name, 'Bob') - - # OK, initializes attributes from whatever is in the database - bob = Person.find('123') - -If an attribute is not present in a particular model instance's attributes -hash, both the reader and the writer for the corresponding field are not -defined, and invoking them raises ``NoMethodError``: - -.. code-block:: ruby - - bob = Person.new - bob.attributes = {age: 42} - - bob.age - # => 42 - - # raises NoMethodError - bob.name - - # raises NoMethodError - bob.name = 'Bob' - - # OK - bob['name'] = 'Bob' - - bob.name - # => "Bob" - -Attributes can always be read using mass attribute access or ``read_attribute`` -(this applies to models not using dynamic fields as well): - -.. code-block:: ruby - - bob = Person.new(age: 42) - - # OK - string access - bob['name'] - # => nil - - # OK - symbol access - bob[:name] - # => nil - - # OK - string access - bob['age'] - # => 42 - - # OK - symbol access - bob[:age] - # => 42 - - # OK - bob.attributes['name'] - # => nil - - # OK - bob.attributes['age'] - # => 42 - - # Returns nil - keys are always strings - bob.attributes[:age] - # => nil - - # OK - bob.read_attribute('name') - # => nil - - # OK - bob.read_attribute(:name) - # => nil - - # OK - string access - bob.read_attribute('age') - # => 42 - - # OK - symbol access - bob.read_attribute(:age) - # => 42 - -.. note:: - - The values returned from the ``read_attribute`` method, and those stored in - the ``attributes`` hash, are the ``mongoized`` values. - - -Special Characters in Field Names ---------------------------------- - -Mongoid permits dynamic field names to include spaces and punctuation: - -.. code-block:: ruby - - bob = Person.new('hello world' => 'MDB') - bob.send('hello world') - # => "MDB" - - bob.write_attribute("hello%world", 'MDB') - bob[:"hello%world"] - # => "MDB" - - -.. _localized-fields: - -Localized Fields -================ - -Mongoid supports localized fields via the `I18n gem `_. - -.. code-block:: ruby - - class Product - include Mongoid::Document - field :description, type: String, localize: true - end - -By telling the field to ``localize``, Mongoid will under the covers store the field -as a hash of locale/value pairs, but normal access to it will behave like a string. - -.. code-block:: ruby - - I18n.default_locale = :en - product = Product.new - product.description = "Marvelous!" - I18n.locale = :de - product.description = "Fantastisch!" - - product.attributes - # { "description" => { "en" => "Marvelous!", "de" => "Fantastisch!" } - -You can get and set all the translations at once by using the corresponding ``_translations`` method. - -.. code-block:: ruby - - product.description_translations - # { "en" => "Marvelous!", "de" => "Fantastisch!" } - product.description_translations = - { "en" => "Marvelous!", "de" => "Wunderbar!" } - -Localized fields can be used with any field type. For example, they can be used -with float fields for differences with currency: - -.. code:: ruby - - class Product - include Mongoid::Document - - field :price, type: Float, localize: true - field :currency, type: String, localize: true - end - -By creating the model in this way, we can separate the price from the currency -type, which allows you to use all of the number-related functionalities on the -price when querying or aggregating that field (provided that you index into the -stored translations hash). We can create an instance of this model as follows: - -.. code:: ruby - - product = Product.new - I18n.locale = :en - product.price = 1.00 - product.currency = "$" - I18n.locale = :he - product.price = 3.24 - product.currency = "₪" - - product.attributes - # => { "price" => { "en" => 1.0, "he" => 3.24 }, "currency" => { "en" => "$", "he" => "₪" } } - - -.. _present-fields: - -Localize ``:present`` Field Option ----------------------------------- - -Mongoid supports the ``:present`` option when creating a localized field: - -.. code-block:: ruby - - class Product - include Mongoid::Document - field :description, localize: :present - end - -This option automatically removes ``blank`` values (i.e. those that return true -for the ``blank?`` method) from the ``_translations`` hash: - -.. code-block:: ruby - - I18n.default_locale = :en - product = Product.new - product.description = "Marvelous!" - I18n.locale = :de - product.description = "Fantastisch!" - - product.description_translations - # { "en" => "Marvelous!", "de" => "Fantastisch!" } - - product.description = "" - product.description_translations - # { "en" => "Marvelous!" } - -When the empty string is written for the ``:de`` locale, the ``"de"`` key is -removed from the ``_translations`` hash instead of writing the empty string. - - -Fallbacks ---------- - -Mongoid integrates with -`i18n fallbacks `_. -To use the fallbacks, the respective functionality must be explicitly enabled. - -In a Rails application, set the ``config.i18n.fallbacks`` configuration setting -to ``true`` in your environment and specify the fallback languages: - -.. code-block:: ruby - - config.i18n.fallbacks = true - config.after_initialize do - I18n.fallbacks[:de] = [ :en, :es ] - end - -In a non-Rails application, include the fallbacks module into the I18n backend -you are using and specify the fallback languages: - -.. code-block:: ruby - - require "i18n/backend/fallbacks" - I18n::Backend::Simple.send(:include, I18n::Backend::Fallbacks) - I18n.fallbacks[:de] = [ :en, :es ] - -When fallbacks are enabled, if a translation is not present in the active -language, translations will be looked up in the fallback languages: - -.. code-block:: ruby - - product = Product.new - I18n.locale = :en - product.description = "Marvelous!" - I18n.locale = :de - product.description # "Marvelous!" - -Mongoid also defines a ``:fallbacks`` option on fields, which can be used to -disable fallback functionality on a specific field: - -.. code:: ruby - - class Product - include Mongoid::Document - field :description, type: String, localize: true, fallbacks: false - end - - product = Product.new - I18n.locale = :en - product.description = "Marvelous!" - I18n.locale = :de - product.description # nil - -Note that this option defaults to ``true``. - -.. note:: - - In i18n 1.1, the behavior of fallbacks `changed `_ - to always require an explicit list of fallback locales rather than falling - back to the default locale when no fallback locales have been provided. - - -Querying --------- - -When querying for localized fields using Mongoid's criteria API, Mongoid will automatically -alter the criteria to match the current locale. - -.. code-block:: ruby - - # Match all products with Marvelous as the description. Locale is en. - Product.where(description: "Marvelous!") - # The resulting MongoDB query filter: { "description.en" : "Marvelous!" } - - -Indexing --------- - -If you plan to be querying extensively on localized fields, you should index each of the -locales that you plan on searching on. - -.. code-block:: ruby - - class Product - include Mongoid::Document - field :description, localize: true - - index "description.de" => 1 - index "description.en" => 1 - end - - -.. _read-only: - -Read-Only Attributes -==================== - -You can tell Mongoid that certain attributes are read-only. This will allow -documents to be created with these attributes, but changes to them will be -ignored when using mass update methods such as ``update_attributes``: - -.. code-block:: ruby - - class Band - include Mongoid::Document - field :name, type: String - field :origin, type: String - - attr_readonly :name, :origin - end - - band = Band.create(name: "Placebo") - band.update_attributes(name: "Tool") # Filters out the name change. - -If you explicitly try to update or remove a read-only attribute by itself, -a ``ReadonlyAttribute`` exception will be raised: - -.. code-block:: ruby - - band.update_attribute(:name, "Tool") # Raises the error. - band.remove_attribute(:name) # Raises the error. - -Assignments to read-only attributes using their setters will be ignored: - -.. code-block:: ruby - - b = Band.create!(name: "The Rolling Stones") - # => # - b.name = "The Smashing Pumpkins" - # => "The Smashing Pumpkins" - b.name - # => "The Rolling Stones" - -Calls to atomic persistence operators, like ``bit`` and ``inc``, will persist -changes to read-only fields. - -Timestamp Fields -================ - -Mongoid supplies a timestamping module in ``Mongoid::Timestamps`` which -can be included to get basic behavior for ``created_at`` and -``updated_at`` fields. - -.. code-block:: ruby - - class Person - include Mongoid::Document - include Mongoid::Timestamps - end - -You may also choose to only have specific timestamps for creation or -modification. - -.. code-block:: ruby - - class Person - include Mongoid::Document - include Mongoid::Timestamps::Created - end - - class Post - include Mongoid::Document - include Mongoid::Timestamps::Updated - end - -If you want to turn off timestamping for specific calls, use the timeless -method: - -.. code-block:: ruby - - person.timeless.save - Person.timeless.create! - -If you'd like shorter timestamp fields with aliases on them to save space, -you can include the short versions of the modules. - -.. code-block:: ruby - - 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 - - -.. _field-names-with-periods-and-dollar-signs: - -Field Names with Dots/Periods (``.``) and Dollar Signs (``$``) -============================================================== - -Using dots/periods (``.``) in fields names and starting a field name with -a dollar sign (``$``) is not recommended, as Mongoid provides limited support -for retrieving and operating on the documents stored in those fields. - -Both Mongoid and MongoDB query language (MQL) generally use the dot/period -character (``.``) to separate field names in a field path that traverses -embedded documents, and words beginning with the dollar sign (``$``) as -operators. MongoDB provides `limited support -`_ -for using field names containing dots and starting with the dollar sign -for interoperability with other software, -however, due to this support being confined to specific operators -(e.g. :manual:`getField `, -:manual:`setField `) and -requiring the usage of the aggregation pipeline for both queries and updates, -applications should avoid using dots in field names and starting field names -with the dollar sign if possible. - -Mongoid, starting in version 8, now allows users to access fields that begin with -dollar signs and that contain dots/periods. They can be accessed using the ``send`` -method as follows: - -.. code:: - - class User - include Mongoid::Document - field :"first.last", type: String - field :"$_amount", type: Integer - end - - user = User.first - user.send(:"first.last") - # => Mike.Trout - user.send(:"$_amount") - # => 42650000 - -It is also possible to use ``read_attribute`` to access these fields: - -.. code:: - - user.read_attribute("first.last") - # => Mike.Trout - -Due to `server limitations `_, -updating and replacing fields containing dots and dollars requires using special -operators. For this reason, calling setters on these fields is prohibited and -will raise an error: - -.. code:: - - class User - include Mongoid::Document - field :"first.last", type: String - field :"$_amount", type: Integer - end - - user = User.new - user.send(:"first.last=", "Shohei.Ohtani") - # raises a InvalidDotDollarAssignment error - user.send(:"$_amount=", 8500000) - # raises a InvalidDotDollarAssignment error - diff --git a/source/reference/indexes.txt b/source/reference/indexes.txt deleted file mode 100644 index 7a5d11a4..00000000 --- a/source/reference/indexes.txt +++ /dev/null @@ -1,278 +0,0 @@ -.. _indexes: - -**************** -Index Management -**************** - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 2 - :class: singlecol - -Specifying Indexes -================== - -You can define indexes on documents using the index macro. Provide the key for -the index along with a direction. Additional options can be supplied in the -second options hash parameter: - -.. code-block:: ruby - - class Person - include Mongoid::Document - field :ssn - - index({ ssn: 1 }, { unique: true, name: "ssn_index" }) - end - -You can define indexes on embedded document fields as well: - -.. code-block:: ruby - - class Person - include Mongoid::Document - embeds_many :addresses - index "addresses.street" => 1 - end - -You can index on multiple fields and provide direction: - -.. code-block:: ruby - - class Person - include Mongoid::Document - field :first_name - field :last_name - - index({ first_name: 1, last_name: 1 }, { unique: true }) - end - -Indexes can be sparse: - -.. code-block:: ruby - - class Person - include Mongoid::Document - field :ssn - - index({ ssn: -1 }, { sparse: true }) - end - -For geospatial indexes, make sure the field being indexed is of type Array: - -.. code-block:: ruby - - class Person - include Mongoid::Document - field :location, type: Array - - index({ location: "2d" }, { min: -200, max: 200 }) - end - -Indexes can be scoped to a specific database: - -.. code-block:: ruby - - class Person - include Mongoid::Document - field :ssn - index({ ssn: 1 }, { database: "users", unique: true, background: true }) - end - -You may use aliased field names in index definitions. Field aliases -will also be resolved on the following options: ``partial_filter_expression``, -``weights``, ``wildcard_projection``. - -.. code-block:: ruby - - class Person - include Mongoid::Document - field :a, as: :age - index({ age: 1 }, { partial_filter_expression: { age: { '$gte' => 20 } }) - end - -.. note:: - - The expansion of field name aliases in index options such as - ``partial_filter_expression`` is performed according to the behavior of MongoDB - server 6.0. Future server versions may change how they interpret these options, - and Mongoid's functionality may not support such changes. - -Mongoid can define indexes on "foreign key" fields for associations. -This only works on the association macro that the foreign key is stored on: - -.. code-block:: ruby - - class Comment - include Mongoid::Document - belongs_to :post, index: true - has_and_belongs_to_many :preferences, index: true - end - -*Deprecated:* In MongoDB 4.0 and earlier, users could control whether to build indexes -in the foreground (blocking) or background (non-blocking, but less efficient) using the -``background`` option. - -.. code-block:: ruby - - class Person - include Mongoid::Document - field :ssn - index({ ssn: 1 }, { unique: true, background: true }) - end - -The default value of ``background`` is controlled by Mongoid's -``background_indexing`` :ref:`configuration option `. - -The ``background`` option has `no effect as of MongoDB 4.2 -`_. - - -Specifying Search Indexes on MongoDB Atlas -========================================== - -If your application is connected to MongoDB Atlas, you can declare and manage -search indexes on your models. (This feature is only available on MongoDB -Atlas.) - -To declare a search index, use the ``search_index`` macro in your model: - -.. code-block:: ruby - - class Message - include Mongoid::Document - - search_index { ... } - search_index :named_index, { ... } - end - -Search indexes may be given an explicit name; this is necessary if you have -more than one search index on a model. - - -Index Management Rake Tasks -=========================== - -When you want to create the indexes in the database, use the provided -``db:mongoid:create_indexes`` Rake task: - -.. code-block:: bash - - $ rake db:mongoid:create_indexes - -Mongoid also provides a Rake task to delete all secondary indexes. - -.. code-block:: bash - - $ rake db:mongoid:remove_indexes - -Note: the output of these Rake tasks goes to the default logger configured -by Rails. This is usually a file like ``log/development.log`` and not standard -output. - -These create/remove indexes commands also works for just one model by running -in Rails console: - -.. code-block:: ruby - - # Create indexes for Model - Model.create_indexes - - # Remove indexes for Model - Model.remove_indexes - -Managing Search Indexes on MongoDB Atlas ----------------------------------------- - -If you have defined search indexes on your model, there are rake tasks available -for creating and removing those search indexes: - -.. code-block:: bash - - $ rake db:mongoid:create_search_indexes - $ rake db:mongoid:remove_search_indexes - -By default, creating search indexes will wait for the indexes to be created, -which can take quite some time. If you want to simply let the database create -the indexes in the background, you can set the ``WAIT_FOR_SEARCH_INDEXES`` -environment variable to 0, like this: - -.. code-block:: bash - - $ rake WAIT_FOR_SEARCH_INDEXES=0 db:mongoid:create_search_indexes - -Note that the task for removing search indexes will remove all search indexes -from all models, and should be used with caution. - -You can also add and remove search indexes for a single model by invoking the -following in a Rails console: - -.. code-block:: ruby - - # Create all defined search indexes on the model; this will return - # immediately and the indexes will be created in the background. - Model.create_search_indexes - - # Remove all search indexes from the model - Model.remove_search_indexes - - # Enumerate all search indexes on the model - Model.search_indexes.each { |index| ... } - - -Telling Mongoid Where to Look For Models ----------------------------------------- - -For non-Rails applications, Mongoid's rake tasks will look for models in -``./app/models`` and ``./lib/models``. For Rails, Mongoid will look in -``./app/models`` (or wherever you've configured Rails to look for models). If -your models are in another location, you will need to tell Mongoid where to -look for them with ``Mongoid.model_paths=``. You can do this by setting it -in your application's Rakefile: - -.. code-block:: ruby - - # Rakefile - - # keep the defaults, but add more paths to look for models - Mongoid.model_paths += [ "./src/models", "./lib/documents" ] - - # or, override the defaults entirely - Mongoid.model_paths = [ "./src/models", "./lib/documents" ] - -Make sure that these paths are in your application's load path, as well. For -example: - -.. code-block:: ruby - - # Rakefile - - $LOAD_PATHS.concat [ "./src/models", "./lib/documents" ] - - -Using Rake Tasks With Non-Rails Applications --------------------------------------------- - -Mongoid's Rake tasks are automatically loaded in Rails applications using -Mongoid. When using Mongoid with a non-Rails application, these tasks must -be loaded manually: - -.. code-block:: ruby - - # Rakefile - - require 'mongoid' - load 'mongoid/tasks/database.rake' - -If your application uses Bundler, you can require ``bundler/setup`` instead of -explicitly requiring ``mongoid``: - -.. code-block:: ruby - - # Rakefile - - require 'bundler/setup' - load 'mongoid/tasks/database.rake' diff --git a/source/reference/inheritance.txt b/source/reference/inheritance.txt deleted file mode 100644 index dcba02a9..00000000 --- a/source/reference/inheritance.txt +++ /dev/null @@ -1,308 +0,0 @@ -.. _inheritance: - -*********** -Inheritance -*********** - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 2 - :class: singlecol - - -.. _inheritance-overview: - -Overview -======== - -Mongoid supports inheritance in both top level and embedded documents. When -a child document inherits from a parent document, the parent document's -fields, associations, validations and scopes are copied to the child document. - -.. code-block:: ruby - - class Canvas - include Mongoid::Document - field :name, type: String - embeds_many :shapes - end - - class Browser < Canvas - field :version, type: Integer - scope :recent, ->{ where(:version.gt => 3) } - end - - class Firefox < Browser - end - - class Shape - include Mongoid::Document - field :x, type: Integer - field :y, type: Integer - embedded_in :canvas - end - - class Circle < Shape - field :radius, type: Float - end - - class Rectangle < Shape - field :width, type: Float - field :height, type: Float - end - -In the above example, ``Canvas``, ``Browser`` and ``Firefox`` will all save in the canvases -collection. An additional attribute ``_type`` is stored in order to make sure when loaded -from the database the correct document is returned. This also holds true for the embedded -documents ``Circle``, ``Rectangle``, and ``Shape``. - -.. note:: - - When searching for a ``Circle``, the query will only return documents in the shape collection - where the ``_type`` (or whatever the discriminator key was set to) field has the value ``Circle`` (or - whatever the discriminator value was set to), all other discriminator values will be considered an object - of the Shape class. - - Similarly, when querying by parent classes (``Canvas`` in this example), any documents in the collection - that do not have a discriminator value, or whose discriminator value does not map to either the parent - or any of its descendants, will be returned as instances of the parent class. - - -.. _discriminator-key: - -Changing the Discriminator Key -============================== - -Mongoid supports changing the discriminator key from the default ``_type``. There are a few -cases where one might want to do this: - -1. For optimization: The user might want to use a shorter key like ``_t``. - -2. When trying to work with an existing system: It's possible the user is working with an existing system or dataset that has predefined keys. - - -There are two ways to change the discriminator key, on the class level and on the global level. -To change the discriminator key on the class level the user can set it directly on the parent class using -the ``discriminator_key=`` method. -Take the above example: - -.. code-block:: ruby - - class Shape - include Mongoid::Document - field :x, type: Integer - field :y, type: Integer - embedded_in :canvas - - self.discriminator_key = "shape_type" - end - - class Circle < Shape - field :radius, type: Float - end - - class Rectangle < Shape - field :width, type: Float - field :height, type: Float - end - -Here a call to the ``discriminator_key=`` setter was added to the parent class. Now, on -creation of a Rectangle or Circle, a ``shape_type`` field will be added. - -Note that the discriminator key can only be modified in the parent class, and an error -will be raised if trying to set it on the child class. - -If the discriminator key is changed after the child class is created, a new field is -added with the new discriminator key value, and the old field will remain unchanged. -For example: - -.. code-block:: ruby - - class Shape - include Mongoid::Document - field :x, type: Integer - field :y, type: Integer - embedded_in :canvas - end - - class Circle < Shape - field :radius, type: Float - end - - class Rectangle < Shape - field :width, type: Float - field :height, type: Float - end - - Shape.discriminator_key = "shape_type" - -In this case, on creation of a Rectangle or Circle, there will be both a ``shape_type`` -and a ``_type`` field that both default to ``Rectangle`` or ``Circle`` respectively. - - -The discriminator key can also be set on the global level. Meaning, all classes will -use the globally set discriminator key instead of ``_type``. Take the above example: - -.. code-block:: ruby - - Mongoid.discriminator_key = "_the_type" - - class Shape - include Mongoid::Document - field :x, type: Integer - field :y, type: Integer - embedded_in :canvas - end - - class Circle < Shape - field :radius, type: Float - end - - class Rectangle < Shape - field :width, type: Float - field :height, type: Float - end - -After setting the global discriminator key, all classes will use ``_the_type`` as -the discriminator key and will not contain a ``_type`` field. - -Note that when defining the discriminator key on the global level, it must be set before the -child class is defined for the child class to use that global value. -On the global level, however, if the user does not set the discriminator key before defining a child -class, the discriminator field will use the default ``_type`` and not the new global setting in -that child class. - - -.. _discriminator-value: - -Changing the Discriminator Value -================================ - -Mongoid also supports changing the discriminator value from the default value, which is the class name. -One can change the discriminator value by using the ``discriminator_value=`` method on that specific class. - -Take the above example: - -.. code-block:: ruby - - class Shape - include Mongoid::Document - field :x, type: Integer - field :y, type: Integer - embedded_in :canvas - end - - class Circle < Shape - field :radius, type: Float - - self.discriminator_value = "round thing" - end - - class Rectangle < Shape - field :width, type: Float - field :height, type: Float - end - -Here, a call to the ``discriminator_value=`` setter was added to ``Circle``. -Now, on creation of a ``Circle``, the document will contain a field with the key ``_type`` (or whatever the ``discriminator_key`` was changed to) -and the value "round thing." - -.. note:: - - Because the discriminator value overrides are declared in child classes, - the child classes potentially found by a query must be loaded prior to - sending that query. In the above example, the ``Circle`` class definition - must be loaded when querying on ``Shape`` if the returned documents could - potentially be instances of ``Circle`` (since autoloading wouldn't resolve - ``"round thing"`` to ``Circle``). - - -Querying Subclasses -=================== - -Querying for subclasses is handled in the normal manner, and although the documents are -all in the same collection, queries will only return documents of the correct type, -similar to Single Table Inheritance in ActiveRecord. - -.. code-block:: ruby - - # Returns Canvas documents and subclasses - Canvas.where(name: "Paper") - # Returns only Firefox documents - Firefox.where(name: "Window 1") - - -Associations -============ - -You can add any type of subclass to a has one or has many association, through -either normal setting or through the build and create methods on the association: - -.. code-block:: ruby - - firefox = Firefox.new - # Builds a Shape object - firefox.shapes.build({ x: 0, y: 0 }) - # Builds a Circle object - firefox.shapes.build({ x: 0, y: 0 }, Circle) - # Creates a Rectangle object - firefox.shapes.create({ x: 0, y: 0 }, Rectangle) - - rect = Rectangle.new(width: 100, height: 200) - firefox.shapes - - -.. _inheritance-persistence-context: - -Persistence Contexts -==================== - -Mongoid allows the persistence context of a subclass to be changed from the -persistence context of its parent. This means that, using the ``store_in`` -method, we can store the documents of the subclasses in different collections -(as well as different databases, clients) than their parents: - -.. code:: ruby - - class Shape - include Mongoid::Document - store_in collection: :shapes - end - - class Circle < Shape - store_in collection: :circles - end - - class Square < Shape - store_in collection: :squares - end - - Shape.create! - Circle.create! - Square.create! - -Setting the collection on the children causes the documents for those children -to be stored in the set collection, instead of in the parent's collection: - -.. code:: javascript - - > db.shapes.find() - { "_id" : ObjectId("62fe9a493282a43d6b725e10"), "_type" : "Shape" } - > db.circles.find() - { "_id" : ObjectId("62fe9a493282a43d6b725e11"), "_type" : "Circle" } - > db.squares.find() - { "_id" : ObjectId("62fe9a493282a43d6b725e12"), "_type" : "Square" } - -If the collection is set on some of the subclasses and not others, the subclasses -with set collections will store documents in those collections, and the -subclasses without set collections will be store documents in the parent's -collection. - -.. note:: - - Note that changing the collection that a subclass is stored in will cause - documents of that subclass to no longer be found in the results of querying - its parent class. diff --git a/source/reference/nested-attributes.txt b/source/reference/nested-attributes.txt deleted file mode 100644 index 5803206e..00000000 --- a/source/reference/nested-attributes.txt +++ /dev/null @@ -1,140 +0,0 @@ -.. _nested-attributes: - -***************** -Nested Attributes -***************** - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 2 - :class: singlecol - -Nested attributes provide a mechanism for updating documents and their -associations in a single operation by nesting attributes in a single -parameters hash. This is useful when wanting to edit multiple documents -within a single web form. - -Behavior -======== - -Nested attributes can be enabled for any association, embedded or referenced. -To enable this for an association, simply provide the association name to the -``accepts_nested_attributes_for`` macro. - -.. code-block:: ruby - - class Band - include Mongoid::Document - embeds_many :albums - belongs_to :producer - accepts_nested_attributes_for :albums, :producer - end - -Note that when you add nested attributes functionality to a referenced -association, Mongoid will automatically enable autosave for that association. - -When an association gains nested attributes behavior, an additional method is -added to the base model, which should be used to update the attributes with -the new functionality. This method is the association name plus ``_attributes=``. -You can use this method directly, or more commonly the name of the method can -be an attribute in the updates for the base class, in which case -Mongoid will call the appropriate setter under the covers. - -.. code-block:: ruby - - band = Band.first - band.producer_attributes = { name: "Flood" } - band.attributes = { producer_attributes: { name: "Flood" }} - -Note that this will work with any attribute based setter method in Mongoid, -including ``update``, ``update_attributes`` and ``attributes=``, as well as -``create`` (and all of their corresponding bang methods). For example, creating -a new person with associated address records can be done in a single -statement, like this: - -.. code-block:: ruby - - person = Person.create( - name: 'John Schmidt', - addresses_attributes: [ - { type: 'home', street: '1234 Street Ave.', city: 'Somewhere' }, - { type: 'work', street: 'Parkway Blvd.', city: 'Elsewehre' }, - ]) - - -Creating Records ----------------- - -You can create new nested records via nested attributes by omitting -an ``_id`` field: - -.. code-block:: ruby - - person = Person.first - person.update(addresses_attributes: [ - { type: 'prior', street: '221B Baker St', city: 'London' } ]) - -This will append the new record to the existing set; existing records will -not be changed. - - -Updating Records ----------------- - -If you specify an ``_id`` field for any of the nested records, the attributes -will be used to update the record with that id: - -.. code-block:: ruby - - person = Person.first - address = person.addresses.first - person.update(addresses_attributes: [ - { _id: address._id, city: 'Lisbon' } ]) - -Note that if there is no record with that id, a ``Mongoid::Errors::DocumentNotFound`` -exception will be raised. - - -Destroying Records ------------------- - -You can also destroy records this way, by specifying a special -``_destroy`` attribute. In order to use this, you must have passed -``allow_destroy: true`` with the ``accepts_nested_attributes_for`` -declaration: - -.. code-block:: ruby - - class Person - # ... - - accepts_nested_attributes_for :addresses, allow_destroy: true - end - - person = Person.first - address = person.addresses.first - person.update(addresses_attributes: [ - { _id: address._id, _destroy: true } ]) - -Note that, as with updates, if there is no record with that id, -a ``Mongoid::Errors::DocumentNotFound`` exception will be raised. - - -Combining Operations --------------------- - -Nested attributes allow you to combine all of these operations in -a single statement! Here's an example that creates an address, -updates another address, and destroys yet another address, all in -a single command: - -.. code-block:: ruby - - person = Person.first - person.update(addresses_attributes: [ - { type: 'alt', street: '1234 Somewhere St.', city: 'Cititon' }, - { _id: an_address_id, city: 'Changed City' }, - { _id: another_id, _destroy: true } ]) diff --git a/source/reference/persistence-configuration.txt b/source/reference/persistence-configuration.txt deleted file mode 100644 index 6712fb55..00000000 --- a/source/reference/persistence-configuration.txt +++ /dev/null @@ -1,268 +0,0 @@ -.. _persistence: - -************************* -Persistence Configuration -************************* - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 2 - :class: singlecol - - -Document Storage -================ - -Mongoid by default stores documents in a collection that is the pluralized form of the class name. -For the following ``Person`` class, the collection the document would get stored in would be named ``people``. - -.. code-block:: ruby - - class Person - include Mongoid::Document - end - -Model class names cannot end with "s", because it will be considered as the pluralized form of -the word. For example "Status" would be considered as the plural form of "Statu", -which will cause a few known problems. - -This is a limitation of the ``ActiveSupport::Inflector#classify`` which Mongoid uses to convert -from filenames and collection names to class names. You can overcome this by specifying a custom -inflection rule for your model class. For example, the following code will take care of the model -named ``Status``. - -.. code-block:: ruby - - ActiveSupport::Inflector.inflections do |inflect| - inflect.singular("status", "status") - end - -The collection for the model's documents can be changed at the class level if you would like -them persisted elsewhere. You can also change the database and client the model gets persisted -in from the defaults. - -.. code-block:: ruby - - class Person - include Mongoid::Document - store_in collection: "citizens", database: "other", client: "analytics" - end - -The ``store_in`` macro can also take lambdas - a common case for this is multi-tenant applications. - -.. code-block:: ruby - - class Band - include Mongoid::Document - store_in database: ->{ Thread.current[:database] } - end - -When a document is stored in the database the ruby object will get serialized into BSON -and have a structure like so: - -.. code-block:: ruby - - { - "_id" : ObjectId("4d3ed089fb60ab534684b7e9"), - "title" : "Sir", - "name" : { - "_id" : ObjectId("4d3ed089fb60ab534684b7ff"), - "first_name" : "Durran" - }, - "addresses" : [ - { - "_id" : ObjectId("4d3ed089fb60ab534684b7e0"), - "city" : "Berlin", - "country" : "Deutschland" - } - ] - } - - -Persistence Context Attributes -============================== - -Mongoid provides ``client_name``, ``database_name`` and ``collection_name`` -methods on model classes to determine the client, database and collection names -used for persistence: - -.. code-block:: ruby - - Band.client_name - # => :default - - Band.database_name - # => "mongoid" - - Band.collection_name - # => :bands - - -Custom -====== - -There may be cases where you want to persist documents to different sources from their -defaults, or with different options from the default. Mongoid provides run-time support -for this as well as support on a per-model basis. - - -Model-Level Persistence Options -------------------------------- - -On a per-model basis, you can tell it to store in a custom collection name, a different -database, or a different client. The following example would store the Band class by -default into a collection named "artists" in the database named "music", with the client "analytics". - -Note that the value supplied to the ``client`` option must be configured under ``clients`` -in your mongoid.yml. - -.. code-block:: ruby - - class Band - include Mongoid::Document - store_in collection: "artists", database: "music", client: "analytics" - end - -If no ``store_in`` macro would have been provided, Mongoid would store the model in a -collection named "bands" in the default database in the default client. - - -Runtime Persistence Options ---------------------------- - -It is possible to change the client, database and collection, as well as -any of the MongoDB client options, used for persistence for a group of -operations by using the ``with`` method on a model class or instance: - -.. code-block:: ruby - - Band.with(database: "music-non-stop") do |klass| - klass.create(...) - - band = Band.first - - Band.create(...) - end - - Band.with(collection: "artists") do |klass| - klass.delete_all - - Band.delete_all - end - - band.with(client: :tertiary) do |band_object| - band_object.save! - - band.save! - end - -The ``with`` method creates a temporary persistence context and a MongoDB -client to be used for operations in the context. For the duration of the block, -the persistence context on the model class or instance that ``with`` was -called on is changed to the temporary persistence context. For convenience, -the model class or instance that ``with`` was called on is yielded to the -block. - -The temporary persistence context applies to both queries and writes. - -Care should be taken when performing persistence operations across different -persistence contexts. For example, if a document is saved in a temporary -persistence context, it may not exist in the default persistence context, -failing subsequent updates: - -.. code-block:: ruby - - band = Band.new(name: "Scuba") - band.with(collection: "artists") do |band_object| - band_object.save! - end - - # This will not save - updates the collection "bands" which does not have - # the Scuba band - band.update_attribute(likes: 1000) - - # This will update the document. - band.with(collection: "artists") do |band_object| - band_object.update_attribute(likes: 1000) - end - -As of Mongoid 6.0, the ``with`` method must always be called with a block, -and the temporary persistence context exists only for the duration of the block. -This is because a new client is created under the covers with the options -passed to ``with``. To ensure that this client is closed and its associated -resources are freed, the scope when this client could be used must be -well-defined. - - -Global Override -``````````````` - -If you want to switch the persistence context for all operations at runtime, but don't want -to be using with all over your code, Mongoid provides the ability to do this as the client -and database level globally. The methods for this are ``Mongoid.override_client`` and -``Mongoid.override_database``. A useful case for this are internationalized applications -that store information for different locales in different databases or clients, but the -schema in each remains the same. - -.. code-block:: ruby - - class BandsController < ApplicationController - before_action :switch_database - after_action :reset_database - - private - - def switch_database - I18n.locale = params[:locale] || I18n.default_locale - Mongoid.override_database("my_db_name_#{I18n.locale}") - end - - def reset_database - Mongoid.override_database(nil) - end - end - -In the above example, all persistence operations would be stored in the alternative -database for all remaining operations on this thread. This is why the after request -set the override back to nil - it ensures subsequent requests with no local params -use the default option. - -Persistence context applies to both read and write operations. For example, -secondary reads can be performed as follows: - -.. code-block:: ruby - - Band.with(read: {mode: :secondary}) do - Band.count - end - - -Client and Collection Access ----------------------------- - -If you want to drop down to the driver level to perform operations, you can grab -the Mongo client or collection from the model or document instance: - -.. code-block:: ruby - - Band.mongo_client - band.mongo_client - Band.collection - band.collection - -From here you also have the same runtime persistence options using the client's ``#with``: - -.. code-block:: ruby - - client = Band.mongo_client.with(write: { w: 0 }, database: "musik") - client[:artists].find(...) - -You can also override the :read or :write options on the collection using the collections ``#with``: - -.. code-block:: ruby - - collection_w_0 = Band.collection.with(write: { w: 0 }) - collection_w_0[:artists].find(...) diff --git a/source/reference/queries.txt b/source/reference/queries.txt deleted file mode 100644 index 0ef52259..00000000 --- a/source/reference/queries.txt +++ /dev/null @@ -1,2480 +0,0 @@ -.. _queries: - -******* -Queries -******* - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 2 - :class: singlecol - - -Mongoid provides a rich query DSL inspired by ActiveRecord. A trivial query -looks as follows: - -.. code-block:: ruby - - Band.where(name: "Depeche Mode") - -A more complex query utilizing various Mongoid features could be as follows: - -.. code-block:: ruby - - Band. - where(:founded.gte => "1980-01-01"). - in(name: [ "Tool", "Deftones" ]). - union. - in(name: [ "Melvins" ]) - -The query methods return ``Mongoid::Criteria`` objects, which are chainable -and lazily evaluated wrappers for MongoDB query language (MQL). -The queries are executed when their result sets are iterated. For example: - -.. code-block:: ruby - - # Construct a Criteria object: - - Band.where(name: 'Deftones') - # => #"Deftones"} - # options: {} - # class: Band - # embedded: false> - - # Evaluate the query and get matching documents: - - Band.where(name: 'Deftones').to_a - # => [#] - -Methods like ``first`` and ``last`` return the individual documents immediately. -Otherwise, iterating a Criteria object with methods like ``each`` or ``map`` -retrieves the documents from the server. ``to_a`` can be used to force -execution of a query that returns an array of documents, literally converting -a Criteria object to an Array. - -When a query method is called on a Criteria instance, the method returns a new -Criteria instance with the new conditions added to the existing conditions: - -.. code-block:: ruby - - scope = Band.where(:founded.gte => "1980-01-01") - # => #{"$gte"=>"1980-01-01"}} - # options: {} - # class: Band - # embedded: false> - - scope.where(:founded.lte => "2020-01-01") - # => #{"$gte"=>"1980-01-01", "$lte"=>"2020-01-01"}} - # options: {} - # class: Band - # embedded: false> - - scope - # => #{"$gte"=>"1980-01-01"}} - # options: {} - # class: Band - # embedded: false> - - -Condition Syntax -================ - -Mongoid supports three ways of specifying individual conditions: - -1. Field syntax. -2. MQL syntax. -3. Symbol operator syntax. - -All syntaxes support querying embedded documents using the dot notation. -All syntaxes respect field types, if the field being queried is defined in the -model class, and field aliases. - -The examples in this section use the following model definition: - -.. code-block:: ruby - - class Band - include Mongoid::Document - - field :name, type: String - field :founded, type: Integer - field :m, as: :member_count, type: Integer - - embeds_one :manager - end - - class Manager - include Mongoid::Document - - embedded_in :band - - field :name, type: String - end - -Field Syntax ------------- - -The simplest querying syntax utilizes the basic Ruby hashes. Keys can be -symbols or strings, and correspond to field names in MongoDB documents: - -.. code-block:: ruby - - Band.where(name: "Depeche Mode") - # => #"Depeche Mode"} - # options: {} - # class: Band - # embedded: false> - - # Equivalent to: - - Band.where("name" => "Depeche Mode") - -MQL Syntax ----------- - -An MQL operator may be specified on any field using the hash syntax: - -.. code-block:: ruby - - Band.where(founded: {'$gt' => 1980}) - # => #{"$gt"=>1980}} - # options: {} - # class: Band - # embedded: false> - - # Equivalent to: - - Band.where('founded' => {'$gt' => 1980}) - -Symbol Operator Syntax ----------------------- - -MQL operators may be specified as methods on symbols for the respective field -name, as follows: - -.. code-block:: ruby - - Band.where(:founded.gt => 1980) - # => #{"$gt"=>1980}} - # options: {} - # class: Band - # embedded: false> - - -Fields -====== - -Querying on Defined Fields --------------------------- - -In order to query on a field, it is not necessary to add the field to -:ref:`the model class definition `. However, if a field is defined in -the model class, Mongoid will coerce query values to match defined field types -when constructing the query: - -.. code-block:: ruby - - Band.where(name: 2020, founded: "2020") - # => #"2020", "founded"=>2020} - # options: {} - # class: Band - # embedded: false> - -Querying for Raw Values ------------------------ - -If you'd like to bypass Mongoid's query type coercion behavior and query -directly for the raw-typed value in the database, wrap the query value in -``Mongoid::RawValue`` class. This can be useful when working with legacy data. - -.. code-block:: ruby - - Band.where(founded: Mongoid::RawValue("2020")) - # => #"2020"} - # options: {} - # class: Band - # embedded: false> - -Field Aliases -------------- - -Queries take into account :ref:`storage field names ` -and :ref:`field aliases `: - -.. code-block:: ruby - - Band.where(name: 'Astral Projection') - # => #"Astral Projection"} - # options: {} - # class: Band - # embedded: false> - -Since ``id`` and ``_id`` fields are aliases, either one can be used for queries: - -.. code-block:: ruby - - Band.where(id: '5ebdeddfe1b83265a376a760') - # => #BSON::ObjectId('5ebdeddfe1b83265a376a760')} - # options: {} - # class: Band - # embedded: false> - - -Embedded Documents -================== - -To match values of fields of embedded documents, use the dot notation: - -.. code-block:: ruby - - Band.where('manager.name' => 'Smith') - # => #"Smith"} - # options: {} - # class: Band - # embedded: false> - - Band.where(:'manager.name'.ne => 'Smith') - # => #{"$ne"=>"Smith"}} - # options: {} - # class: Band - # embedded: false> - -.. note:: - - Queries always return top-level model instances, even if all of the - conditions are referencing embedded documents. - - -.. _logical-operations: - -Logical Operations -================== - -Mongoid supports ``and``, ``or``, ``nor`` and ``not`` logical operations on -``Criteria`` objects. These methods take one or more hash of conditions -or another ``Criteria`` object as their arguments, with ``not`` additionally -having an argument-free version. - -.. code-block:: ruby - - # and with conditions - Band.where(label: 'Trust in Trance').and(name: 'Astral Projection') - - # or with scope - Band.where(label: 'Trust in Trance').or(Band.where(name: 'Astral Projection')) - - # not with conditions - Band.not(label: 'Trust in Trance', name: 'Astral Projection') - - # argument-less not - Band.not.where(label: 'Trust in Trance', name: 'Astral Projection') - -For backwards compatibility with earlier Mongoid versions, all of the logical -operation methods also accept arrays of parameters, which will be flattened -to obtain the criteria. Passing arrays to logical operations is deprecated and -may be removed in a future version of Mongoid. - -The following calls all produce the same query conditions: - -.. code-block:: ruby - - # Condition hashes passed to separate and invocations - Band.and(name: 'SUN Project').and(member_count: 2) - - # Multiple condition hashes in the same and invocation - Band.and({name: 'SUN Project'}, {member_count: 2}) - - # Multiple condition hashes in an array - deprecated - Band.and([{name: 'SUN Project'}, {member_count: 2}]) - - # Condition hash in where and a scope - Band.where(name: 'SUN Project').and(Band.where(member_count: 2)) - - # Condition hash in and and a scope - Band.and({name: 'SUN Project'}, Band.where(member_count: 2)) - - # Scope as an array element, nested arrays - deprecated - Band.and([Band.where(name: 'SUN Project'), [{member_count: 2}]]) - - # All produce: - # => #"SUN Project", "member_count"=>2} - # options: {} - # class: Band - # embedded: false> - - -Operator Combinations ---------------------- - -As of Mongoid 7.1, logical operators (``and``, ``or``, ``nor`` and ``not``) -have been changed to have the the same semantics as `those of ActiveRecord -`_. -To obtain the semantics of ``or`` as it behaved in Mongoid 7.0 and earlier, -use ``any_of`` which is described below. - -When conditions are specified on the same field multiple times, all -conditions are added to the criteria: - -.. code-block:: ruby - - Band.where(name: 1).where(name: 2).selector - # => {"name"=>"1", "$and"=>[{"name"=>"2"}]} - - Band.where(name: 1).or(name: 2).selector - # => {"$or"=>[{"name"=>"1"}, {"name"=>"2"}]} - -``any_of``, ``none_of``, ``nor`` and ``not`` behave similarly, with ``not`` producing -different query shapes as described below. - -When ``and``, ``or`` and ``nor`` logical operators are used, they -operate on the criteria built up to that point and its argument. -``where`` has the same meaning as ``and``: - -.. code-block:: ruby - - # or joins the two conditions - Band.where(name: 'Sun').or(label: 'Trust').selector - # => {"$or"=>[{"name"=>"Sun"}, {"label"=>"Trust"}]} - - # or applies only to the first condition, the second condition is added - # to the top level as $and - Band.or(name: 'Sun').where(label: 'Trust').selector - # => {"$or"=>[{"name"=>"Sun"}], "label"=>"Trust"} - - # Same as previous example - where and and are aliases - Band.or(name: 'Sun').and(label: 'Trust').selector - # => {"$or"=>[{"name"=>"Sun"}], "label"=>"Trust"} - - # Same operator can be stacked any number of times - Band.or(name: 'Sun').or(label: 'Trust').selector - # => {"$or"=>[{"name"=>"Sun"}, {"label"=>"Trust"}]} - - # The label: Foo condition is added to the top level as $and - Band.where(name: 'Sun').or(label: 'Trust').where(label: 'Foo').selector - # => {"$or"=>[{"name"=>"Sun"}, {"label"=>"Trust"}], "label"=>"Foo"} - - -``and`` Behavior ----------------- - -The ``and`` method will add new simple conditions to the top level of the -criteria, unless the receiving criteria already has a condition on the -respective fields, in which case the conditions will be combined with ``$and``. - -.. code-block:: ruby - - Band.where(label: 'Trust in Trance').and(name: 'Astral Projection').selector - # => {"label"=>"Trust in Trance Records", "name"=>"Astral Projection"} - - Band.where(name: /Best/).and(name: 'Astral Projection').selector - # => {"name"=>/Best/, "$and"=>[{"name"=>"Astral Projection"}]} - -As of Mongoid 7.1, specifying multiple criteria on the same field with ``and`` -combines all criteria so specified, whereas in previous versions of Mongoid -conditions on a field sometimes replaced previously specified conditions on -the same field, depending on which form of ``and`` was used. - - -``or``/``nor`` Behavior -_---------------------- - -``or`` and ``nor`` produce ``$or`` and ``$nor`` MongoDB operators, respectively, -using the receiver and all of the arguments as operands. For example: - -.. code-block:: ruby - - Band.where(name: /Best/).or(name: 'Astral Projection') - # => {"$or"=>[{"name"=>/Best/}, {"name"=>"Astral Projection"}]} - - Band.where(name: /Best/).and(name: 'Astral Projection'). - or(Band.where(label: /Records/)).and(label: 'Trust').selector - # => {"$or"=>[{"name"=>/Best/, "$and"=>[{"name"=>"Astral Projection"}]}, {"label"=>/Records/}], "label"=>"Trust"} - -If the only condition on the receiver is another ``or``/``nor``, the new -conditions are added to the existing list: - -.. code-block:: ruby - - Band.where(name: /Best/).or(name: 'Astral Projection'). - or(Band.where(label: /Records/)).selector - # => {"$or"=>[{"name"=>/Best/}, {"name"=>"Astral Projection"}, {"label"=>/Records/}]} - -Use ``any_of`` to add a disjunction to a Criteria object while maintaining -all of the conditions built up so far as they are. - - -.. _any-of: - -``any_of`` Behavior -------------------- - -``any_of`` adds a disjunction built from its arguments to the existing -conditions in the criteria. For example: - -.. code-block:: ruby - - Band.where(label: /Trust/).any_of({name: 'Astral Projection'}, {name: /Best/}) - # => {"label"=>/Trust/, "$or"=>[{"name"=>"Astral Projection"}, {"name"=>/Best/}]} - -The conditions are hoisted to the top level if possible: - -.. code-block:: ruby - - Band.where(label: /Trust/).any_of({name: 'Astral Projection'}) - # => {"label"=>/Trust/, "name"=>"Astral Projection"} - - -.. _none-of: - -``none_of`` Behavior --------------------- - -``none_of`` adds a negated disjunction ("nor") built from its arguments to -the existing conditions in the criteria. For example: - -.. code-block:: ruby - - Band.where(label: /Trust/).none_of({name: 'Astral Projection'}, {name: /Best/}) - # => {"label"=>/Trust/, "$nor"=>[{"name"=>"Astral Projection"}, {"name"=>/Best/}]} - - -``not`` Behavior ----------------- - -``not`` method can be called without arguments, in which case it will negate -the next condition that is specified. ``not`` can also be called with one -or more hash conditions or ``Criteria`` objects, which will all be negated and -added to the criteria. - -.. code-block:: ruby - - # not negates subsequent where - Band.not.where(name: 'Best').selector - # => {"name"=>{"$ne"=>"Best"}} - - # The second where is added as $and - Band.not.where(name: 'Best').where(label: /Records/).selector - # => {"name"=>{"$ne"=>"Best"}, "label"=>/Records/} - - # not negates its argument - Band.not(name: 'Best').selector - # => {"name"=>{"$ne"=>"Best"}} - -.. note:: - - ``$not`` in MongoDB server cannot be used with a string argument. - Mongoid uses ``$ne`` operator to achieve such a negation: - - .. code-block:: ruby - - # String negation - uses $ne - Band.not.where(name: 'Best').selector - # => {"name"=>{"$ne"=>"Best"}} - - # Regexp negation - uses $not - Band.not.where(name: /Best/).selector - # => {"name"=>{"$not"=>/Best/}} - -Similarly to ``and``, ``not`` will negate individual conditions for simple -field criteria. For complex conditions and when a field already has a condition -defined on it, since MongoDB server only supports the ``$not`` operator on -a per-field basis rather than globally, Mongoid emulates ``$not`` by using -an ``{'$and' => [{'$nor' => ...}]}`` construct: - -.. code-block:: ruby - - # Simple condition - Band.not(name: /Best/).selector - # => {"name"=>{"$not"=>/Best/}} - - # Complex conditions - Band.where(name: /Best/).not(name: 'Astral Projection').selector - # => {"name"=>/Best/, "$and"=>[{"$nor"=>[{"name"=>"Astral Projection"}]}]} - - # Symbol operator syntax - Band.not(:name.ne => 'Astral Projection') - # => #[{"$nor"=>[{"name"=>{"$ne"=>"Astral Projection"}}]}]} - # options: {} - # class: Band - # embedded: false> - -If using ``not`` with arrays or regular expressions, please note the -caveats/limitations of ``$not`` `stated in the MongoDB server documentation -`_. - - -Incremental Query Construction -============================== - -By default, when conditions are added to a query, Mongoid considers each -condition complete and independent from any other conditions potentially -present in the query. For example, calling ``in`` twice adds two separate -``$in`` conditions: - -.. code-block:: ruby - - Band.in(name: ['a']).in(name: ['b']) - => #{"$in"=>["a"]}, "$and"=>[{"name"=>{"$in"=>["b"]}}]} - options: {} - class: Band - embedded: false> - -Some operator methods support building the condition incrementally. In this -case, when an condition on a field which uses one of the supported operators -is being added, if there already is a condition on the same field using the -same operator, the operator expressions are combined according to the -specified *merge strategy*. - -.. _merge-strategies: - -Merge Strategies ----------------- - -Mongoid provides three merge strategies: - -- **Override**: the new operator instance replaces any existing conditions on - the same field using the same operator. -- **Intersect**: if there already is a condition using the same operator on the - same field, the values of the existing condition are intersected with the - values of the new condition and the result is stored as the operator value. -- **Union**: if there already is a condition using the same operator on the - same field, the values of the new condition are added to the values of the - existing condition and the result is stored as the operator value. - -The following snippet demonstrates all of the strategies, using ``in`` as the -example operator: - -.. code-block:: ruby - - Band.in(name: ['a']).override.in(name: ['b']) - => #{"$in"=>["b"]}} - options: {} - class: Band - embedded: false> - - Band.in(name: ['a', 'b']).intersect.in(name: ['b', 'c']) - => #{"$in"=>["b"]}} - options: {} - class: Band - embedded: false> - - Band.in(name: ['a']).union.in(name: ['b']) - => #{"$in"=>["a", "b"]}} - options: {} - class: Band - embedded: false> - -The strategy is requested by calling ``override``, ``intersect`` or ``union`` -on a ``Criteria`` instance. The requested strategy applies to the next -condition method called on the query. If the next condition method called does -not support merge strategies, the strategy is reset, as shown in the following -example: - -.. code-block:: ruby - - Band.in(name: ['a']).union.ne(name: 'c').in(name: ['b']) - => #{"$in"=>["a"], "$ne"=>"c"}, "$and"=>[{"name"=>{"$in"=>["b"]}}]} - options: {} - class: Band - embedded: false> - -Since ``ne`` does not support merge strategies, the ``union`` strategy was -ignored and reset and when ``in`` was invoked the second time there was no -strategy active. - -.. warning:: - - Merge strategies currently assume the previous condition(s) have been added - to the top level of the query, however this is not always the case - (conditions may be nested under an ``$and`` clause). Using merge strategies - with complex criteria may cause incorrect queries to be constructed. - This misbehavior is `intended to be fixed in the future - `_. - - -Supported Operator Methods --------------------------- - -The following operator methods support merge strategies: - -- ``all`` -- ``in`` -- ``nin`` - -The set of methods may be expanded in future releases of Mongoid. For -future compatibility, only invoke a strategy method when the next method call -is an operator that supports merge strategies. - -Note that the merge strategies are currently only applied when conditions are -added through the designated methods. In the following example merge strategy -is not applied because the second condition is added via ``where``, not via -``in``: - -.. code-block:: ruby - - Band.in(foo: ['a']).union.where(foo: {'$in' => 'b'}) - => #{"$in"=>["a"]}, "$and"=>[{"foo"=>{"$in"=>"b"}}]} - options: {} - class: Band - embedded: false> - -This behavior may change in a future release of Mongoid and should not be -relied upon. - -In contrast, it does not matter how the existing query was built when a -merge strategy-supporting operator method is invoked. In the following -example, the first condition was added through ``where`` but the strategy -mechanism still applies: - -.. code-block:: ruby - - Band.where(foo: {'$in' => ['a']}).union.in(foo: ['b']) - => #{"$in"=>["a", "b"]}} - options: {} - class: Band - embedded: false> - -Operator Value Expansion ------------------------- - -Operator methods that support merge strategies all take ``Array`` as their value -type. Mongoid expands ``Array``-compatible types, such as a ``Range``, -when they are used with these operator methods: - -.. code-block:: ruby - - Band.in(year: 1950..1960) - => #{"$in"=>[1950, 1951, 1952, 1953, 1954, 1955, 1956, 1957, 1958, 1959, 1960]}} - options: {} - class: Band - embedded: false> - -Additionally, Mongoid has historically wrapped non-``Array`` values in arrays, -as the following example demonstrates: - -.. code-block:: ruby - - Band.in(year: 1950) - => #{"$in"=>[1950]}} - options: {} - class: Band - embedded: false> - - -Query Methods -============= - -elem_match ----------- - -This matcher finds documents with array fields where one of the array values -matches all of the conditions. For example: - -.. code-block:: ruby - - class Band - include Mongoid::Document - field :name, type: String - field :tours, type: Array - end - - aerosmith = Band.create!(name: 'Aerosmith', tours: [ - {city: 'London', year: 1995}, - {city: 'New York', year: 1999}, - ]) - - Band.elem_match(tours: {city: 'London'}).to_a # => [aerosmith] - -``elem_match`` also works with embedded associations: - -.. code-block:: ruby - - class Band - include Mongoid::Document - field :name, type: String - embeds_many :tours - end - - class Tour - include Mongoid::Document - field :city, type: String - field :year, type: Integer - embedded_in :band - end - - dm = Band.create!(name: 'Depeche Mode') - aerosmith = Band.create!(name: 'Aerosmith') - Tour.create!(band: aerosmith, city: 'London', year: 1995) - Tour.create!(band: aerosmith, city: 'New York', year: 1999) - - Band.elem_match(tours: {city: 'London'}).to_a # => [aerosmith] - -``elem_match`` does not work with non-embedded associations because MongoDB -does not have joins - the conditions would be added to the collection -that is the source of a non-embedded association rather than the collection -of the association's target. - -``elem_match`` can also be used with recursively embedded associations, -as the following example shows: - -.. code-block:: ruby - - class Tag - include Mongoid::Document - field :name, type: String - recursively_embeds_many - end - - root = Tag.create!(name: 'root') - sub1 = Tag.new(name: 'sub1', child_tags: [Tag.new(name: 'subsub1')]) - root.child_tags << sub1 - root.child_tags << Tag.new(name: 'sub2') - root.save! - - Tag.elem_match(child_tags: {name: 'sub1'}).to_a # => [root] - - root.child_tags.elem_match(child_tags: {name: 'subsub1'}).to_a # => [sub1] - - -.. _projection: - -Projection -========== - -Mongoid provides two projection operators: ``only`` and ``without``. - - -.. _only: - -``only`` --------- - -The ``only`` method retrieves only the specified fields from the database. This -operation is sometimes called "projection". - -.. code-block:: ruby - - class Band - include Mongoid::Document - - field :name, type: String - field :label, type: String - - embeds_many :tours - end - - class Tour - include Mongoid::Document - - field :city, type: String - field :year, type: Integer - - embedded_in :band - end - - band = Band.only(:name).first - -Attempting to reference attributes which have not been loaded results in -``Mongoid::Errors::AttributeNotLoaded``. - -.. code-block:: ruby - - band.label - #=> raises Mongoid::Errors::AttributeNotLoaded - -Even though Mongoid currently allows writing to attributes that have not -been loaded, such writes will not be persisted -(`MONGOID-4701 `_) and -should therefore be avoided. - -``only`` can also be used with embedded associations: - -.. code-block:: ruby - - band = Band.only(:name, 'tours.year').last - # => # - - band.tours.first - # => # - -.. note:: - - Server versions 4.2 and lower allowed projecting both an association and - the association's fields in the same query, as follows: - - .. code-block:: ruby - - band = Band.only(:tours, 'tours.year').last - - The most recent projection specification overrides the earlier one. - For example, the above query was equivalent to: - - .. code-block:: ruby - - band = Band.only('tours.year').last - - Server versions 4.4 and higher prohibit specifying an association and its - fields in projection in the same query. - -``only`` can be specified with referenced associations (has_one, has_many, -has_and_belongs_to_many) but is currently ignored for referenced associations - -all fields of referenced associations will be loaded -(`MONGOID-4704 `_). - -Note that if a document has ``has_one`` or ``has_and_belongs_to_many`` associations, -the fields with foreign keys must be included in the list of attributes -loaded with ``only`` for those associations to be loaded. For example: - -.. code-block:: ruby - - class Band - include Mongoid::Document - - field :name, type: String - - has_and_belongs_to_many :managers - end - - class Manager - include Mongoid::Document - - has_and_belongs_to_many :bands - end - - band = Band.create!(name: 'Astral Projection') - band.managers << Manager.new - - Band.where(name: 'Astral Projection').only(:name).first.managers - # => [] - - Band.where(name: 'Astral Projection').only(:name, :manager_ids).first.managers - # => [#] - - -.. _without: - -``without`` ------------ - -The opposite of ``only``, ``without`` causes the specified fields to be omitted: - -.. code-block:: ruby - - Band.without(:name) - # => - # #{"name"=>0}} - # class: Band - # embedded: false> - -Because Mongoid requires the ``_id`` field for various operations, it (as well -as its ``id`` alias) cannot be omitted via ``without``: - -.. code-block:: ruby - - Band.without(:name, :id) - # => - # #{"name"=>0}} - # class: Band - # embedded: false> - - Band.without(:name, :_id) - # => - # #{"name"=>0}} - # class: Band - # embedded: false> - - -.. _ordering: - -Ordering -======== - -Mongoid provides the ``order`` method on ``Criteria`` objects and its alias, -``order_by``, to specify the ordering of documents. These methods take a -hash indicating which fields to order the documents by, and whether to use -ascending or descending order for each field. - -.. code-block:: ruby - - Band.order(name: 1) - # => #{"name"=>1}} - # class: Band - # embedded: false> - - Band.order_by(name: -1, description: 1) - # => #{"name"=>-1, "description"=>1}} - # class: Band - # embedded: false> - - Band.order_by(name: :desc, description: 'asc') - # => #{"name"=>-1, "description"=>1}} - # class: Band - # embedded: false> - -The direction may be specified as integers ``1`` and ``-1`` for ascending -and descending, respectively, or as symbols ``:asc`` and ``:desc``, or as -strings ``"asc"`` and ``"desc"``. - -Alternatively, ``order`` accepts an array of two-element arrays specifying -the ordering. Field names and directions may be strings or symbols. - -.. code-block:: ruby - - Band.order([['name', 'desc'], ['description', 'asc']]) - - Band.order([[:name, :desc], [:description, :asc]]) - -Another way of providing the order is to use ``#asc`` and ``#desc`` methods -on symbols, as follows: - -.. code-block:: ruby - - Band.order(:name.desc, :description.asc) - -The arguments can be provided as a string using SQL syntax: - -.. code-block:: ruby - - Band.order('name desc, description asc') - -Finally, there are ``asc`` and ``desc`` methods that can be used instead of -``order``/``order_by``: - -.. code-block:: ruby - - Band.asc('name').desc('description') - # => #{"name"=>1, "description"=>-1}} - class: Band - embedded: false> - -``order`` calls can be chained, in which case the oldest calls define the -most significant criteria and the newest calls define the least significant -ones (since in Ruby hashes maintain the order of their keys): - -.. code-block:: ruby - - Band.order('name desc').order('description asc') - # => #{"name"=>-1, "description"=>1}} - class: Band - embedded: false> - -This can sometimes lead to surprising results if there are scopes, including -the default scope, that use ``order``/``order_by``. For example, in the -following snippet bands are ordered by name first because the order in the -default scope takes precedence over the order given in the query, due to -the default scope being evaluated first: - -.. code-block:: ruby - - class Band - include Mongoid::Document - - field :name, type: String - field :year, type: Integer - - default_scope -> { order(name: :asc) } - end - - Band.order(year: :desc) - # => #{"name"=>1, "year"=>-1}} - class: Band - embedded: false> - - -Pagination -========== - -Mongoid provides the pagination operators ``limit``, ``skip``, and ``batch_size`` on ``Criteria``. - -.. _limit: - -``limit`` ---------- - -``limit`` sets the total number of documents to be returned by a query: - -.. code-block:: ruby - - Band.limit(5) - # => - # #5} - # class: Band - # embedded: false> - -.. _skip: - -``skip`` --------- - -``skip`` (alias: ``offset``) sets the number of query results to skip -before returning documents. The ``limit`` value, if specified, will be applied -after documents are skipped. When performing pagination, ``skip`` is recommended -to be combined with :ref:`ordering ` to ensure consistent results. - -.. code-block:: ruby - - Band.skip(10) - # => - # #10} - # class: Band - # embedded: false> - -.. _batch-size: - -``batch_size`` --------------- - -When executing large queries, or when iterating over query results with an enumerator method such as -``Criteria#each``, Mongoid automatically uses the `MongoDB getMore command -`_ to load results in batches. -The default ``batch_size`` is 1000, however you may set it explicitly: - -.. code-block:: ruby - - Band.batch_size(500) - # => - # #500} - # class: Band - # embedded: false> - - -Finding By ``_id`` -================== - -Mongoid provides the ``find`` method on ``Criteria`` objects to find documents -by their ``_id`` values: - -.. code-block:: ruby - - Band.find('5f0e41d92c97a64a26aabd10') - # => # - -The ``find`` method performs type conversion, if necessary, of the argument -to the type declared in the model being queried for the ``_id`` field. -By default, the ``_id`` type is ``BSON::ObjectId``, thus the query above -is equivalent to: - -.. code-block:: ruby - - Band.find(BSON::ObjectId.from_string('5f0e41d92c97a64a26aabd10')) - # => # - -.. note:: - - When querying collections directly using the driver, type conversion is not - automatically performed: - -.. code-block:: ruby - - Band.collection.find(_id: BSON::ObjectId.from_string('5f0e41d92c97a64a26aabd10')).first - # => {"_id"=>BSON::ObjectId('5f0e41d92c97a64a26aabd10'), "name"=>"Juno Reactor"} - - Band.collection.find(_id: '5f0e41d92c97a64a26aabd10').first - # => nil - -The ``find`` method can accept multiple arguments, or an array of arguments. -In either case each of the arguments or array elements is taken to be an ``_id`` -value, and documents with all of the specified ``_id`` values are returned in -an array: - -.. code-block:: ruby - - Band.find('5f0e41d92c97a64a26aabd10', '5f0e41b02c97a64a26aabd0e') - # => [#, - #] - - Band.find(['5f0e41d92c97a64a26aabd10', '5f0e41b02c97a64a26aabd0e']) - # => [#, - #] - -If the same ``_id`` value is given more than once, the corresponding document -is only returned once: - -.. code-block:: ruby - - Band.find('5f0e41b02c97a64a26aabd0e', '5f0e41b02c97a64a26aabd0e') - # => [#] - -The documents returned are *not* ordered, and may be returned in a different -order from the order of provided ``_id`` values, as illustrated in the above -examples. - -If any of the ``_id`` values are not found in the database, the behavior of -``find`` depends on the value of the ``raise_not_found_error`` configuration -option. If the option is set to ``true``, ``find`` raises -``Mongoid::Errors::DocumentNotFound`` if any of the ``_id``\s are not found. -If the option is set to ``false`` and ``find`` is given a single ``_id`` to -find and there is no matching document, ``find`` returns ``nil``. If the -option is set to ``false`` and ``find`` is given an array of ids to find -and some are not found, the return value is an array of documents that were -found (which could be empty if no documents were found at all). - - -.. _additional-query-methods: - -Additional Query Methods -======================== - -Mongoid also has some helpful methods on criteria. - -.. list-table:: - :header-rows: 1 - :widths: 30 60 - - * - Operation - - Example - - * - ``Criteria#count`` - - *Get the total number of documents matching a filter, or the total - number of documents in a collection. Note this will always hit - the database for the count.* - - *As of Mongoid 7.2, the* ``count`` *method uses the* - ``count_documents`` *driver helper to obtain the accurate count. - previously the* ``count`` *driver helper was used which used - collection metadata and was thus not necessarily accurate (but - may have returned the result faster). Use* ``estimated_count`` - *method to obtain an approximate number of documents in the collection - quickly.* - - - - .. code-block:: ruby - - Band.count - Band.where(name: "Photek").count - - * - ``Criteria#estimated_count`` - - *Get an approximate number of documents in the collection using the - collection metadata. The* ``estimated_count`` *method does not accept - query conditions; if any are given, it will raise* - ``Mongoid::Errors::InvalidEstimatedCountCriteria``. - *If a model defines a default scope,* ``estimated_count`` *must be - called on the unscoped model*. - - - - .. code-block:: ruby - - Band.count - Band.where(name: "Photek").count - - class Contract - include Mongoid::Document - - field :active, type: Boolean - - default_scope -> { where(active: true) } - end - - Contract.estimated_count - # => raises Mongoid::Errors::InvalidEstimatedCountCriteria - - Contract.unscoped.estimated_count - # => 0 - - * - ``Criteria#distinct`` - - *Get a list of distinct values for a single field. Note this will always hit - the database for the distinct values.* - - *This method accepts the dot notation, thus permitting referencing - fields in embedded associations.* - - *This method respects :ref:`field aliases `, - including those defined in embedded documents.* - - - - .. code-block:: ruby - - Band.distinct(:name) - Band.where(:fans.gt => 100000). - distinct(:name) - - Band.distinct('cities.name') - - # Assuming an aliased field: - class Manager - include Mongoid::Document - embedded_in :band - field :name, as: :n - end - - # Expands out to "managers.name" in the query: - Band.distinct('managers.n') - - * - ``Criteria#each`` - - *Iterate over all matching documents in the criteria.* - - - - .. code-block:: ruby - - Band.where(members: 1).each do |band| - p band.name - end - - * - ``Criteria#exists?`` - - *Determine if any matching documents exist. Will return true if there - are 1 or more.* - - ``#exists?`` *now takes a number of argument types:* - - - ``Hash``: *A hash of conditions.* - - ``Object``: *An _id to search for.* - - ``false``/``nil``: *Always returns false.* - - - - .. code-block:: ruby - - Band.exists? - Band.where(name: "Photek").exists? - Band.exists?(name: "Photek") - Band.exists?(BSON::ObjectId('6320d96a3282a48cfce9e72c')) - Band.exists?('6320d96a3282a48cfce9e72c') - Band.exists?(false) - Band.exists?(nil) - - * - ``Criteria#fifth`` - - *Get the fifth document for the given criteria.* - - *This method automatically adds a sort on _id if no sort is given.* - - - - .. code-block:: ruby - - Band.fifth - - * - ``Criteria#fifth!`` - - *Get the fifth document for the given criteria, or raise an error if - none exist.* - - *This method automatically adds a sort on _id if no sort is given.* - - - - .. code-block:: ruby - - Band.fifth! - - * - ``Criteria#find_by`` - - *Find a document by the provided attributes. If not found, - raise an error or return nil depending on the value of the* - ``raise_not_found_error`` *configuration option.* - - - - .. code-block:: ruby - - Band.find_by(name: "Photek") - - Band.find_by(name: "Tool") do |band| - band.impressions += 1 - end - - * - ``Criteria#find_or_create_by`` - - *Find a document by the provided attributes, and if not found - create and return a newly persisted one. Note that attributes provided in the arguments to - this method will override any set in ``create_with``*. - - - - .. code-block:: ruby - - Band.find_or_create_by(name: "Photek") - Band.where(:likes.gt => 10).find_or_create_by(name: "Photek") - - ``find_or_create_by`` can be used on any scope, but in this case - the criteria given by the scope and by ``find_or_create_by`` are - combined. The following creates three bands: - - .. code-block:: ruby - - Band.find_or_create_by(name: "Photek") - Band.where(name: "Photek").find_or_create_by(name: "Aerosmith") - # creates Aerosmith again because there is no band whose name - # is Photek and Aerosmith at the same time - Band.where(name: "Photek").find_or_create_by(name: "Aerosmith") - - * - ``Criteria#find_or_initialize_by`` - - *Find a document by the provided attributes, and if not found - return a new one.* - - - - .. code-block:: ruby - - Band.find_or_initialize_by(name: "Photek") - Band.where(:likes.gt => 10).find_or_initialize_by(name: "Photek") - - * - ``Criteria#first|last`` - - *Finds a single document given the provided criteria. Get a list of - documents by passing in a limit argument. This method automatically adds - a sort on _id. This can cause performance issues, so if the sort is - undesirable, Criteria#take can be used instead.* - - - - .. code-block:: ruby - - Band.first - Band.where(:members.with_size => 3).first - Band.where(:members.with_size => 3).last - Band.first(2) - - * - ``Criteria#first!|last!`` - - *Finds a single document given the provided criteria, or raises an error - if none are found. This method automatically adds a sort on _id if no - sort is given. This can cause performance issues, so if the sort is - undesirable, Criteria#take! can be used instead.* - - - - .. code-block:: ruby - - Band.first! - Band.where(:members.with_size => 3).first! - Band.where(:members.with_size => 3).last! - - * - ``Criteria#first_or_create`` - - *Find the first document by the provided attributes, and if not found - create and return a newly persisted one.* - - - - .. code-block:: ruby - - Band.where(name: "Photek").first_or_create - - * - ``Criteria#first_or_create!`` - - *Find the first document by the provided attributes, and if not found - create and return a newly persisted one using* ``create!``. - - - - .. code-block:: ruby - - Band.where(name: "Photek").first_or_create! - - * - ``Criteria#first_or_initialize`` - - *Find the first document by the provided attributes, and if not found - return a new one.* - - - - .. code-block:: ruby - - Band.where(name: "Photek").first_or_initialize - - * - ``Criteria#for_js`` - - *Find documents for a provided JavaScript expression, optionally with - the specified variables added to the evaluation scope. The scope - argument is supported in MongoDB 4.2 and lower.* - *Prefer* :manual:`$expr ` *over* ``for_js``. - - - - .. code-block:: ruby - - # All MongoDB versions - Band.for_js("this.name = 'Tool'") - - # MongoDB 4.2 and lower - Band.for_js("this.name = param", param: "Tool") - - * - ``Criteria#fourth`` - - *Get the fourth document for the given criteria.* - - *This method automatically adds a sort on _id if no sort is given.* - - - - .. code-block:: ruby - - Band.fourth - - * - ``Criteria#fourth!`` - - *Get the fourth document for the given criteria, or raise an error if - none exist.* - - *This method automatically adds a sort on _id if no sort is given.* - - - - .. code-block:: ruby - - Band.fourth! - - * - ``Criteria#length|size`` - - *Same as count but caches subsequent calls to the database* - - - - .. code-block:: ruby - - Band.length - Band.where(name: "FKA Twigs").size - - * - ``Criteria#pick`` - - *Get the values from one document for the provided fields. - Returns nil for unset fields and for non-existent fields.* - - *This method does not apply a sort to the documents, so it - will not necessarily return the values from the first document.* - - *This method accepts the dot notation, thus permitting referencing - fields in embedded associations.* - - *This method respects :ref:`field aliases `, - including those defined in embedded documents.* - - - - .. code-block:: ruby - - Band.all.pick(:name) - - Band.all.pick('cities.name') - - # Using the earlier definition of Manager, - # expands out to "managers.name" in the query: - Band.all.pick('managers.n') - - - * - ``Criteria#pluck`` - - *Get all the values for the provided field. - Returns nil for unset fields and for non-existent fields.* - - *This method accepts the dot notation, thus permitting referencing - fields in embedded associations.* - - *This method respects :ref:`field aliases `, - including those defined in embedded documents.* - - - - .. code-block:: ruby - - Band.all.pluck(:name) - #=> ["Daft Punk", "Aphex Twin", "Ween"] - - Band.all.pluck('address.city') - #=> ["Paris", "Limerick", "New Hope"] - - # Using the earlier definition of Manager, - # expands out to "managers.name" in the query: - Band.all.pluck('managers.n') - #=> [ ["Berry Gordy", "Tommy Mottola"], [], ["Quincy Jones"] ] - - # Accepts multiple field arguments, in which case - # the result will be returned as an Array of Arrays. - Band.all.pluck(:name, :likes) - #=> [ ["Daft Punk", 342], ["Aphex Twin", 98], ["Ween", 227] ] - - * - ``Criteria#read`` - - *Sets the read preference for the criteria.* - - - - .. code-block:: ruby - - Band.all.read(mode: :primary) - - * - ``Criteria#second`` - - *Get the second document for the given criteria.* - - *This method automatically adds a sort on _id if no sort is given.* - - - - .. code-block:: ruby - - Band.second - - * - ``Criteria#second!`` - - *Get the second document for the given criteria, or raise an error if - none exist.* - - *This method automatically adds a sort on _id if no sort is given.* - - - - .. code-block:: ruby - - Band.second! - - * - ``Criteria#second_to_last`` - - *Get the second to last document for the given criteria.* - - *This method automatically adds a sort on _id if no sort is given.* - - - - .. code-block:: ruby - - Band.second_to_last - - * - ``Criteria#second_to_last!`` - - *Get the second to last document for the given criteria, or raise an - error if none exist.* - - *This method automatically adds a sort on _id if no sort is given.* - - - - .. code-block:: ruby - - Band.second_to_last! - - * - ``Criteria#take`` - - *Get a list of n documents from the database or just one if no parameter - is provided.* - - *This method does not apply a sort to the documents, so it can return - different document(s) than #first and #last.* - - - - .. code-block:: ruby - - Band.take - Band.take(5) - - * - ``Criteria#take!`` - - *Get a document from the database or raise an error if none exist.* - - *This method does not apply a sort to the documents, so it can return - different document(s) than #first and #last.* - - - - .. code-block:: ruby - - Band.take! - - * - ``Criteria#tally`` - - *Get a mapping of values to counts for the provided field.* - - *This method accepts the dot notation, thus permitting referencing - fields in embedded associations.* - - *This method respects :ref:`field aliases `, - including those defined in embedded documents.* - - - - .. code-block:: ruby - - Band.all.tally(:name) - - Band.all.tally('cities.name') - - # Using the earlier definition of Manager, - # expands out to "managers.name" in the query: - Band.all.tally('managers.n') - - * - ``Criteria#third`` - - *Get the third document for the given criteria.* - - *This method automatically adds a sort on _id if no sort is given.* - - - - .. code-block:: ruby - - Band.third - - * - ``Criteria#third!`` - - *Get the third document for the given criteria, or raise an error if - none exist.* - - *This method automatically adds a sort on _id if no sort is given.* - - - - .. code-block:: ruby - - Band.third! - - * - ``Criteria#third_to_last`` - - *Get the third to last document for the given criteria.* - - *This method automatically adds a sort on _id if no sort is given.* - - - - .. code-block:: ruby - - Band.third_to_last - - * - ``Criteria#third_to_last!`` - - *Get the third to last document for the given criteria, or raise an - error if none exist.* - - *This method automatically adds a sort on _id if no sort is given.* - - - - .. code-block:: ruby - - Band.third_to_last! - - -Eager Loading -============= - -Mongoid provides a facility to eager load documents -from associations to prevent the n+1 issue when -iterating over documents with association access. Eager loading is supported on -all associations with the exception of polymorphic ``belongs_to`` -associations. - -.. code-block:: ruby - - class Band - include Mongoid::Document - has_many :albums - end - - class Album - include Mongoid::Document - belongs_to :band - end - - Band.includes(:albums).each do |band| - p band.albums.first.name # Does not hit the database again. - end - - -Regular Expressions -=================== - -MongoDB, and Mongoid, allow querying documents by regular expressions. - -Given the following model definitions: - -.. code-block:: ruby - - class Band - include Mongoid::Document - - field :name, type: String - field :description, type: String - end - - Band.create!(name: 'Sun Project', description: "Sun\nProject") - -... we can query using simple Ruby regular expressions in a natural way: - -.. code-block:: ruby - - Band.where(name: /project/i).first - # => # - -It is also possible to query using PCRE syntax by constructing -``BSON::Regexp::Raw`` objects explicitly: - -.. code-block:: ruby - - Band.where(description: /\AProject/).first - # => # - - Band.where(description: BSON::Regexp::Raw.new('^Project')).first - # => nil - - Band.where(description: BSON::Regexp::Raw.new('^Project', 'm')).first - # => # - - -Conditions On Fields -==================== - -When a condition uses a field defined in the model, the value being specified -in the condition is converted according to the rules of the field, if any. -For example, consider the following model definition that contains a ``Time`` -field, a ``Date`` field and an implicit ``Object`` field, and also -intentionally does not define a field called ``deregistered_at``: - -.. code-block:: ruby - - class Voter - include Mongoid::Document - - field :born_on, type: Date - field :registered_at, type: Time - field :voted_at - end - -Queries on ``born_on`` and ``registered_at`` fields using ``Date`` and ``Time`` -values, respectively, are straightforward: - -.. code-block:: ruby - - Voter.where(born_on: Date.today).selector - # => {"born_on"=>2020-12-18 00:00:00 UTC} - - Voter.where(registered_at: Time.now).selector - # => {"registered_at"=>2020-12-19 04:33:36.939788067 UTC} - -But, note the differences in behavior when providing a ``Date`` instance -in all possible scenarios: - -.. code-block:: ruby - - Voter.where(born_on: Date.today).selector - # => {"born_on"=>2020-12-18 00:00:00 UTC} - - Voter.where(registered_at: Date.today).selector - # => {"registered_at"=>2020-12-18 00:00:00 -0500} - - Voter.where(voted_at: Date.today).selector - # => {"voted_at"=>Fri, 18 Dec 2020} - - Voter.where(deregistered_at: Date.today).selector - # => {"deregistered_at"=>2020-12-18 00:00:00 UTC} - -When using the ``registered_at`` field which is of type ``Time``, the date -was interpreted to be in local time (as per the :ref:`configured time zone -`). When using the ``born_on`` field which is of type ``Date``, -the date was interpreted to be in UTC. When using the ``voted_at`` field -which was defined without a type (hence implicitly as an ``Object``), -the date was used unmodified in the constructed query. When using a -nonexistent field ``deregistered_at`` the date was interpreted to be in UTC -and converted to a time, matching the behavior of querying a ``Date`` field. - - -Scoping -======= - -Scopes provide a convenient way to reuse common criteria with more -business domain style syntax. - - -.. _named-scopes: - -Named Scopes ------------- - -Named scopes are simply criteria defined at class load that are referenced -by a provided name. Just like normal criteria, they are lazy and chainable. - -.. code-block:: ruby - - class Band - include Mongoid::Document - field :country, type: String - field :genres, type: Array - - scope :english, ->{ where(country: "England") } - scope :rock, ->{ where(:genres.in => [ "rock" ]) } - end - - Band.english.rock # Get the English rock bands. - -Named scopes can take procs and blocks for accepting parameters or -extending functionality. - -.. code-block:: ruby - - class Band - include Mongoid::Document - field :name, type: String - field :country, type: String - field :active, type: Boolean, default: true - - scope :named, ->(name){ where(name: name) } - scope :active, ->{ - where(active: true) do - def deutsch - tap do |scope| - scope.selector.store("origin" => "Deutschland") - end - end - end - } - end - - Band.named("Depeche Mode") # Find Depeche Mode. - Band.active.deutsch # Find active German bands. - -By default, Mongoid allows defining a scope that would shadow an existing -class method, as the following example shows: - -.. code-block:: ruby - - class Product - include Mongoid::Document - - def self.fresh - true - end - - scope :fresh, ->{ where(fresh: true) } - end - -To have Mongoid raise an error when a scope would overwrite an existing class -method, set the ``scope_overwrite_exception`` :ref:`configuration option -` to ``true``. - - -Default Scopes --------------- - -Default scopes can be useful when you find yourself applying the same -criteria to most queries, and wish to specify these criteria as the default. -Default scopes are procs that return criteria objects. - -.. code-block:: ruby - - class Band - include Mongoid::Document - field :name, type: String - field :active, type: Boolean - - default_scope ->{ where(active: true) } - end - - Band.each do |band| - # All bands here are active. - end - -Specifying a default scope also initializes the fields of new models to -the values given in the default scope, if the values are simple literals: - -.. code-block:: ruby - - class Band - include Mongoid::Document - field :name, type: String - field :active, type: Boolean - field :num_tours, type: Integer - - default_scope ->{ where(active: true, num_tours: {'$gt' => 1}) } - end - - # active is set, num_tours is not set - Band.new # => # - -Note that if a default value is provided both in the field definition and -in the default scope, the value in the default scope takes precedence: - -.. code-block:: ruby - - class Band - include Mongoid::Document - field :name, type: String - field :active, type: Boolean, default: true - - default_scope ->{ where(active: false) } - end - - Band.new # => # - -Because a default scope initializes fields in new models as just described, -defining a default scope with a dotted key and a simple literal value, while -possible, is not recommended: - -.. code-block:: ruby - - class Band - include Mongoid::Document - field :name, type: String - field :tags, type: Hash - - default_scope ->{ where('tags.foo' => 'bar') } - end - - Band.create! - # => Created document: {"_id"=>BSON::ObjectId('632de48f3282a404bee1877b'), "tags.foo"=>"bar"} - Band.create!(tags: { 'foo' => 'bar' }) - # => Created document: {"_id"=>BSON::ObjectId('632de4ad3282a404bee1877c'), "tags.foo"=>"bar", "tags"=>{"foo"=>"bar"}} - Band.all.to_a - # => [ #"bar"}> ] - -Mongoid 8 allows dotted keys to be used in Mongoid, and when creating a document, -the scope is added as a dotted key in the attributes: - -.. code-block:: ruby - - Band.new.attribute - # => {"_id"=>BSON::ObjectId('632de97d3282a404bee1877d'), "tags.foo"=>"bar"} - -Whereas when querying, Mongoid looks for an embedded document: - -.. code-block:: ruby - - Band.create! - # => Created document: {"_id"=>BSON::ObjectId('632de48f3282a404bee1877b'), "tags.foo"=>"bar"} - Band.where - # => #"bar"} - options: {} - class: Band - embedded: false> - # This looks for something like: { tags: { "foo" => "bar" } } - Band.count - # => 0 - -A workaround is to define the default scope as a complex query: - -.. code-block:: ruby - - class Band - include Mongoid::Document - field :name, type: String - field :tags, type: Hash - - default_scope ->{ where('tags.foo' => {'$eq' => 'bar'}) } - end - - Band.create!(tags: { hello: 'world' }) - Band.create!(tags: { foo: 'bar' }) - # does not add a "tags.foo" dotted attribute - Band.count - # => 1 - -You can tell Mongoid not to apply the default scope by using -``unscoped``, which can be inline or take a block. - -.. code-block:: ruby - - Band.unscoped.where(name: "Depeche Mode") - Band.unscoped do - Band.where(name: "Depeche Mode") - end - -You can also tell Mongoid to explicitly apply the default scope -again later to always ensure it's there. - -.. code-block:: ruby - - Band.unscoped.where(name: "Depeche Mode").scoped - -If you are using a default scope on a model that is part of an association, -you must reload the association to have scoping reapplied. -This is important to note if you change a value of a document in the association -that would affect its visibility within the scoped association. - -.. code-block:: ruby - - class Label - include Mongoid::Document - embeds_many :bands - end - - class Band - include Mongoid::Document - field :active, default: true - embedded_in :label - default_scope ->{ where(active: true) } - end - - label.bands.push(band) - label.bands # [ band ] - band.update_attribute(:active, false) - label.bands # [ band ] Must reload. - label.reload.bands # [] - -.. note:: - - After the default scope is applied, it is no longer distinguished from - other query conditions. This can lead to surprising behavior when using - ``or`` and ``nor`` operators in particular: - - .. code-block:: ruby - - class Band - include Mongoid::Document - - field :name - field :active - field :touring - - default_scope ->{ where(active: true) } - end - - Band.where(name: 'Infected Mushroom') - # => - # #true, "name"=>"Infected Mushroom"} - # options: {} - # class: Band - # embedded: false> - - Band.where(name: 'Infected Mushroom').or(touring: true) - # => - # #[{"active"=>true, "name"=>"Infected Mushroom"}, {"touring"=>true}]} - # options: {} - # class: Band - # embedded: false> - - Band.or(touring: true) - # => - # #[{"active"=>true}, {"touring"=>true}]} - # options: {} - # class: Band - # embedded: false> - - In the last example, you might expect the two conditions - (``active: true`` and ``touring: true``) to be combined with an ``$and``, - but because the ``Band`` class already has the scope applied to it, - it becomes one of the disjunction branches of the ``or``. - - -Runtime Default Scope Override ------------------------------- - -You can use the ``with_scope`` method to change the default scope in a block -at runtime: - -.. code-block:: ruby - - class Band - include Mongoid::Document - field :country, type: String - field :genres, type: Array - - scope :english, ->{ where(country: "England") } - end - - criteria = Band.with_scope(Band.english) do - Band.all - end - - criteria - # => - # #"England"} - # options: {} - # class: Band - # embedded: false> - -.. note:: - - If with_scope calls are nested, when the nested with_scope block completes - Mongoid 7 sets the current scope to nil instead of the parent scope. - Mongoid 8 will set the current scope to the correct parent scope. - To get Mongoid 8 behavior in Mongoid 7.4 and higher, set the - ``Mongoid.broken_scoping`` global option to false. - - -Class Methods -------------- - -Class methods on models that return criteria objects are also -treated like scopes, and can be chained as well. - -.. code-block:: ruby - - class Band - include Mongoid::Document - field :name, type: String - field :active, type: Boolean, default: true - - def self.active - where(active: true) - end - end - - Band.active - - -Queries + Persistence -===================== - -Mongoid supports persistence operations off of criteria -in a light capacity for when you want to expressively perform multi -document inserts, updates, and deletion. - -.. warning:: - - Criteria ordering and pagination conditions, including ``order``, ``limit``, - ``offset``, and ``batch_size``, will be ignored on the following operations. - -.. list-table:: - :header-rows: 1 - :widths: 30 60 - - * - Operation - - Example - - * - ``Criteria#create`` - - *Create a newly persisted document.* - - - - .. code-block:: ruby - - Band.where(name: "Photek").create - - * - ``Criteria#create!`` - - *Create a newly persisted document and raise an exception on validation failure.* - - - - .. code-block:: ruby - - Band.where(name: "Photek").create! - - * - ``Criteria#build|new`` - - *Create a new (unsaved) document.* - - - - .. code-block:: ruby - - Band.where(name: "Photek").build - Band.where(name: "Photek").new - - * - ``Criteria#update`` - - *Update attributes of the first matching document.* - - - - .. code-block:: ruby - - Band.where(name: "Photek").update(label: "Mute") - - * - ``Criteria#update_all`` - - *Update attributes of all matching documents.* - - - - .. code-block:: ruby - - Band.where(members: 2).update_all(label: "Mute") - - * - ``Criteria#add_to_set`` - - *Perform an $addToSet on all matching documents.* - - - - .. code-block:: ruby - - Band.where(name: "Photek").add_to_set(label: "Mute") - - * - ``Criteria#bit`` - - *Perform a $bit on all matching documents.* - - - - .. code-block:: ruby - - Band.where(name: "Photek").bit(likes: { and: 14, or: 4 }) - - * - ``Criteria#inc`` - - *Perform an $inc on all matching documents.* - - - - .. code-block:: ruby - - Band.where(name: "Photek").inc(likes: 123) - - * - ``Criteria#pop`` - - *Perform a $pop on all matching documents.* - - - - .. code-block:: ruby - - Band.where(name: "Photek").pop(members: -1) - Band.where(name: "Photek").pop(members: 1) - - * - ``Criteria#pull`` - - *Perform a $pull on all matching documents.* - - - - .. code-block:: ruby - - Band.where(name: "Tool").pull(members: "Maynard") - - * - ``Criteria#pull_all`` - - *Perform a $pullAll on all matching documents.* - - - - .. code-block:: ruby - - Band.where(name: "Tool"). - pull_all(:members, [ "Maynard", "Danny" ]) - - * - ``Criteria#push`` - - *Perform a $push on all matching documents.* - - - - .. code-block:: ruby - - Band.where(name: "Tool").push(members: "Maynard") - - * - ``Criteria#push_all`` - - *Perform a $push with $each on all matching documents.* - - - - .. code-block:: ruby - - Band.where(name: "Tool"). - push_all(members: [ "Maynard", "Danny" ]) - - * - ``Criteria#rename`` - - *Perform a $rename on all matching documents.* - - - - .. code-block:: ruby - - Band.where(name: "Tool").rename(name: :title) - - * - ``Criteria#set`` - - *Perform a $set on all matching documents.* - - - - .. code-block:: ruby - - Band.where(name: "Tool").set(likes: 10000) - - * - ``Criteria#unset`` - - *Perform a $unset on all matching documents.* - - - - .. code-block:: ruby - - Band.where(name: "Tool").unset(:likes) - - * - ``Criteria#delete`` - - *Deletes all matching documents in the database.* - - - - .. code-block:: ruby - - Band.where(label: "Mute").delete - - * - ``Criteria#destroy`` - - *Deletes all matching documents in the database while running callbacks for all. - This loads all documents into memory and can be an expensive operation.* - - - - .. code-block:: ruby - - Band.where(label: "Mute").destroy - - -.. _query-cache: - -Query Cache -=========== - -The Ruby MongoDB driver versions 2.14 and above provide query caching functionality. When enabled, the -query cache saves the results of previously executed find and aggregation -queries and reuses them in the future instead of performing the queries again, -thus increasing application performance and reducing database load. - -Please review the `driver query cache documentation -`_ -for details about the driver's query cache behavior. - -The rest of this section assumes that driver 2.14.0 or later is being used. - - -Enabling Query Cache --------------------- - -The query cache may be enabled by using the driver's namespace or Mongoid's -namespace. - - -.. _enabling-query-cache-automatically: - -Enabling Query Cache Automatically ----------------------------------- - -The MongoDB Ruby Driver provides middleware to automatically enable the query cache for -Rack web requests and ActiveJob job runs. Please see the :ref:`Query Cache Rack Middleware -` section on the configuration page for instructions. - -Note that the Query Cache Middleware does not apply to code executed outside web requests -and/or jobs. - - -.. _enabling-query-cache-manually: - -Enabling Query Cache Manually ------------------------------ - -To enable the Query Cache manually for a code segment, use: - -.. code-block:: ruby - - Mongo::QueryCache.cache do - # ... - end - -The Query Cache can also be explicitly enabled and disabled, although we -recommend to use the block form described above: - -.. code-block:: ruby - - begin - Mongo::QueryCache.enabled = true - # ... - ensure - Mongo::QueryCache.enabled = false - end - - -.. _query-cache-first-method: - -Caching the Result of ``#first`` --------------------------------- - -Calling the ``first`` method on a model class imposes an ascending sort by -the ``_id`` field on the underlying query. This may produce unexpected behavior -with query caching. - -For example, when calling ``all`` on a model class and then ``first``, -one would expect the second query to use the cached results from the first. -However, because of the sort imposed on the second query, both methods -will query the database and separately cache their results. - -.. code-block:: ruby - - Band.all.to_a - #=> Queries the database and caches the results - - Band.first - #=> Queries the database again because of the sort - -To use the cached results, call ``all.to_a.first`` on the model class. - - -.. _load-async: - -Asynchronous Queries -==================== - -Mongoid allows running database queries asynchronously in the background. -This can be beneficial when there is a need to get documents from different -collections. - -In order to schedule an asynchronous query call the ``load_async`` method on a -``Criteria``: - -.. code-block:: ruby - - class PagesController < ApplicationController - def index - @active_bands = Band.where(active: true).load_async - @best_events = Event.best.load_async - @public_articles = Article.where(public: true).load_async - end - end - -In the above example three queries will be scheduled for asynchronous execution. -Results of the queries can be later accessed as usual: - -.. code-block:: html - -
    - <%- @active_bands.each do -%> -
  • <%= band.name %>
  • - <%- end -%> -
- -Even if a query is scheduled for asynchronous execution, it might be executed -synchronously on the caller's thread. There are three possible scenarios depending -on when the query results are being accessed: - -#. If the scheduled asynchronous task has been already executed, the results are returned. -#. If the task has been started, but not finished yet, the caller's thread blocks until the task is finished. -#. If the task has not been started yet, it is removed from the execution queue, and the query is executed synchronously on the caller's thread. - -.. note:: - - Even though ``load_async`` method returns a ``Criteria`` object, you should not - do any operations on this object except accessing query results. The query is - scheduled for execution immediately after calling ``load_async``, therefore - later changes to the ``Criteria`` object may not be applied. - - -Configuring asynchronous query execution ----------------------------------------- - -Asynchronous queries are disabled by default. When asynchronous queries are -disabled, ``load_async`` will execute the query immediately on the current thread, -blocking as necessary. Therefore, calling ``load_async`` on criteria in this case -is roughly the equivalent of calling ``to_a`` to force query execution. - -In order to enable asynchronous query execution, the following config options -must be set: - -.. code-block:: yaml - - development: - ... - options: - # Execute asynchronous queries using a global thread pool. - async_query_executor: :global_thread_pool - # Number of threads in the pool. The default is 4. - # global_executor_concurrency: 4 diff --git a/source/reference/rails-integration.txt b/source/reference/rails-integration.txt deleted file mode 100644 index 0a43d96d..00000000 --- a/source/reference/rails-integration.txt +++ /dev/null @@ -1,100 +0,0 @@ -.. _rails-integration: - -***************** -Rails Integration -***************** - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 2 - :class: singlecol - -Mongoid seamlessly integrates into Ruby on Rails applications. -This page describes features that are automatically enabled in the context -of a Rails application and Rails-related functionality which can be -manually enabled. - - -Configuration -============= - -You can set Mongoid configuration options in your ``application.rb`` along with -other Rails environment specific options by accessing config.mongoid. The -``mongoid:config`` generator will create an initializer in -``config/initializers/mongoid.rb`` which can also be used for configuring -Mongoid. Note, though, that options set in your ``config/mongoid.yml`` will -take precedence over options set elsewhere; it is recommended that whenever -possible you use ``mongoid.yml`` as the default location for Mongoid -configuration. - -.. code-block:: ruby - - module MyApplication - class Application < Rails::Application - config.mongoid.logger = Logger.new(STDERR, :warn) - end - end - - -Model Preloading -================ - -In order to properly set up single collection inheritance, Mongoid needs to preload all -models before every request in development mode. This can get slow, so if you are not -using any inheritance it is recommended you turn this feature off. - -.. code-block:: ruby - - config.mongoid.preload_models = false - - -Exceptions -========== - -Similarly to ActiveRecord, Mongoid configures Rails to automatically convert -certain exceptions to well-known HTTP status codes, as follows: - -.. code-block:: ruby - - Mongoid::Errors::DocumentNotFound : 404 - Mongoid::Errors::Validations : 422 - - -Controller Runtime Instrumentation -================================== - -Mongoid provides time spent executing MongoDB commands (obtained via a -driver command monitoring subscription) to Rails' instrumentation event -``process_action.action_controller``. This time is logged together with view -time like so: - -.. code-block:: none - - Completed 200 OK in 2739ms (Views: 12.6ms | MongoDB: 0.2ms) - -This logging is set up automatically. - -Note: the time indicated is the time taken by MongoDB cluster to execute -MongoDB operations, plus the time taken to send commands and receive -results from MongoDB over the network. It does not include time taken by -the driver and Mongoid to generate the queries or type cast and otherwise -process the results. - -Rake Tasks -========== - -Mongoid provides the following rake tasks when used in a Rails environment: - -- ``db:create``: Exists only for dependency purposes, does not actually do anything. -- ``db:create_indexes``: Reads all index definitions from the models and attempts to create them in the database. -- ``db:remove_indexes``: Reads all secondary index definitions from the models. -- ``db:drop``: Drops all collections in the database with the exception of the system collections. -- ``db:migrate``: Exists only for dependency purposes, does not actually do anything. -- ``db:purge``: Deletes all data, including indexes, from the database. Since 3.1.0 -- ``db:schema:load``: Exists only for framework dependency purposes, does not actually do anything. -- ``db:seed``: Seeds the database from db/seeds.rb -- ``db:setup``: Creates indexes and seeds the database. -- ``db:test:prepare``: Exists only for framework dependency purposes, does not actually do anything. diff --git a/source/reference/sessions.txt b/source/reference/sessions.txt deleted file mode 100644 index 4faf35c0..00000000 --- a/source/reference/sessions.txt +++ /dev/null @@ -1,56 +0,0 @@ -.. _sessions: - -******** -Sessions -******** - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 2 - :class: singlecol - -You can use sessions with Mongoid in a similar way that you would execute a transaction in ActiveRecord. -Namely, you can call a method, ``#with_session`` on a model class or on an instance of a model and execute -some operations in a block. All operations in the block will be executed in the context of single session. -Please see the MongoDB Ruby driver documentation for what session options are available. - -Please note the following limitations of sessions: - -- Sessions cannot be shared across threads; sessions are not thread-safe. This is consistent with the Ruby driver's support for sessions. - -- Sessions cannot be nested. You cannot called ``#with_session`` on a model class or a model instance within the block passed to the ``#with_session`` method on another model class or model instance. - -- All model classes and instances used within the session block must use the same driver client. For example, if you have specified different ``storage_options`` for another model used in the block than that of the model class or instance on which ``#with_session`` is called, you will get an error. - -Using a Session via Model#with_session -====================================== - -Call ``#with_session`` on a model class and pass it session options to execute a block in the context -of a session. - -.. code-block:: ruby - - Person.with_session(causal_consistency: true) do - Person.create! - person = Person.first - person.name = "Emily" - person.save - end - - -Using a Session via model#with_session -====================================== - -Call ``#with_session`` on a model instance and pass it session options to execute a block in the context -of a session. - -.. code-block:: ruby - - person.with_session(causal_consistency: true) do - person.username = 'Emily' - person.save - person.posts << Post.create! - end diff --git a/source/reference/sharding.txt b/source/reference/sharding.txt deleted file mode 100644 index 6fd41522..00000000 --- a/source/reference/sharding.txt +++ /dev/null @@ -1,146 +0,0 @@ -.. _sharding: - -********************** -Sharding Configuration -********************** - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 2 - :class: singlecol - - -Mongoid can assist with setting up collection sharding in sharded environments. - - -.. _shard-keys: - -Declaring Shard Keys -==================== - -Shard keys can be declared on models using the ``shard_key`` macro: - -.. code-block:: ruby - - class Person - include Mongoid::Document - - field :ssn - - shard_key ssn: 1 - - # The collection must also have an index that starts with the shard key. - index ssn: 1 - end - -Note that in order to shard a collection, the collection must have an index -that starts with the shard key. Mongoid provides :ref:`index management -` functionality, which the examples here take -advantage of. - -Mongoid supports two syntaxes for declaring shard keys. The standard syntax -follows the format of MongoDB `shardCollection shell helper -`_ -and allows specifying ranged and hashed shard keys, compound shard keys and -collection sharding options: - -.. code-block:: ruby - - shard_key ssn: 1 - - shard_key ssn: 1, country: 1 - - shard_key ssn: :hashed - - shard_key {ssn: :hashed}, unique: true - -The alternative is the shorthand syntax, in which only the keys are given. -This syntax only supports ranged shard keys and does not allow options to -be specified: - -.. code-block:: ruby - - shard_key :ssn - - shard_key :ssn, :country - -``shard_key`` macro can take the name of a ``belongs_to`` association in -place of a field name, in which case Mongoid will use the foreign key -configured in the association as the field name: - -.. code-block:: ruby - - class Person - include Mongoid::Document - - belongs_to :country - - # Shards by country_id. - shard_key country: 1 - - # The collection must also have an index that starts with the shard key. - index country: 1 - end - -The shard key may also reference a field in an embedded document, by using -the "." character to delimit the field names: - -.. code-block:: ruby - - shard_key "location.x" => 1, "location.y" => 1 - - shard_key "location.x", "location.y" - -.. note:: - - Because the "." character is used to delimit fields in embedded documents, - Mongoid does not currently support shard key fields that themselves - literally contain the "." character. - -.. note:: - - If a model declares a shard key, Mongoid expects the respective collection - to be sharded with the specified shard key. When reloading models, Mongoid - will provide the shard key in addition to the ``id`` field value to the - ``find`` command to improve query performance, especially on `geographically - distributed sharded clusters `_. - If the collection is not sharded with the specified shard key, queries - may produce incorrect results. - - -.. _sharding-management: - -Sharding Management Rake Tasks -============================== - -To shard collections in the database according to the shard keys defined in -the models, run the ``db:mongoid:shard_collections`` Rake task. -If necessary, run the ``db:mongoid:create_indexes`` Rake task prior to -sharding collections: - -.. code-block:: bash - - rake db:mongoid:create_indexes - rake db:mongoid:shard_collections - -.. note:: - - Like with index management rake tasks, sharding management rake tasks - generally do not stop and fail when they encounter the problem with a - particular model class. Instead they log the problem (to the configured - Mongoid logger) at an appropriate level and continue with the next model. - When Mongoid is used in a Rails application, this means the results of - the rake task execution will generally be found in the per-environment - log file like ``log/development.log``. - -.. note:: - - When performing schema-related operations in a sharded cluster, such as - sharding collections as described in this document, or creating or dropping - collections or databases, cluster nodes may end up with out of date local - configuration-related cache data. Execute the `flushRouterConfig - `_ - command on each ``mongos`` node to clear these caches. diff --git a/source/reference/text-search.txt b/source/reference/text-search.txt deleted file mode 100644 index b7c3013d..00000000 --- a/source/reference/text-search.txt +++ /dev/null @@ -1,84 +0,0 @@ -.. _text-search: - -*********** -Text Search -*********** - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 2 - :class: singlecol - - -MongoDB provides :manual:`text indexes ` -to support text search queries on string content. Text indexes -can include any field whose value is a string or an array of -string elements. - -.. note:: - - MongoDB Atlas also provides - `Atlas Search `_ - which is a more powerful and flexible text search solution. - The rest of this section discusses text indexes and not Atlas Search. - -To perform text search with Mongoid, follow these steps: - -1. Define a text index on a model. -2. Create the text index on the server. -3. Build a text search query. - - -Defining Text Search Index --------------------------- - -Index definition through Mongoid is described in detail on the :ref:`indexes -` page. Text search indexes are described in detail -under `text indexes `_ -in the MongoDB manual. Below is an example definition of a Band model with -a text index utilizing the description field: - -.. code-block:: ruby - - class Band - include Mongoid::Document - - field :name, type: String - field :description, type: String - - index description: 'text' - end - -Note that the index type (``text``) must be given as a string, not as a symbol. - - -Creating Text Index -------------------- - -To create the index, invoke the ``db:mongoid:create_indexes`` Rake task: - -.. code-block:: ruby - - bundle exec rake db:mongoid:create_indexes - - -Querying Using Text Index -------------------------- - -To find bands whose description contains "ounces" or its variations, use the -`$text operator `_: - -.. code-block:: ruby - - Band.where('$text' => {'$search' => 'ounces'}).to_a - # => [#] - -Note that the description contains the word "ounce" even though the search -query was "ounces". - -Note also that when performing text search, the name of the field is not -explicitly specified - ``$text`` operator searches all fields indexed with -the text index. diff --git a/source/reference/transactions.txt b/source/reference/transactions.txt deleted file mode 100644 index ecf2465e..00000000 --- a/source/reference/transactions.txt +++ /dev/null @@ -1,232 +0,0 @@ -.. _transactions: - -************ -Transactions -************ - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 2 - :class: singlecol - -Version 4.0 of the MongoDB server introduces -`multi-document transactions `_. -(Updates to multiple fields within a single document are atomic in all -versions of MongoDB). Transactions require a non-standalone MongoDB topology -and Ruby driver version 2.6 or higher. A higher level transaction API requires -Mongoid version 9.0 or higher, while a lower level API requires Mongoid -version 6.4 or higher. - -Using Transactions -================== - -Higher Level API ----------------- - -A transaction can be started by calling the ``transaction`` method on an instance -of a Mongoid document class, on a Mongoid document class, on or ``Mongoid`` module: - -.. code-block:: ruby - - Band.transaction do - Band.create(title: 'Led Zeppelin') - end - - band = Band.create(title: 'Deep Purple') - band.transaction do - band.active = false - band.save! - end - - Mongoid.transaction do - band.destroy - end - -When the ``transaction`` method is called, Mongoid does the following: - -* creates a session on a client that is used by the receiver of the - ``transaction`` method call; -* starts a transaction on the session; -* executes the given block; -* commits the transaction if no exception raised in the block; - - * calls ``after_commit`` callbacks for all objects modified inside the transaction -* aborts the transaction if an exception is raised in the block; - - * calls ``after_rollback`` callbacks for all objects modified inside the transaction -* closes the session - -.. note:: - - Since a transaction is tied to a particular client, _only_ operations on - the same client will be in scope of the transaction. Therefore it - is recommended that only objects that use the same client are used inside the - ``transaction`` method block. - - .. code-block:: ruby - - class Author - include Mongoid::Document - store_in client: :encrypted_client - end - - class User - include Mongoid::Document - store_in client: :encrypted_client - end - - class Article - include Mongoid::Document - # This class uses the :default client - end - - # Transaction is started on the :encrypted_client - Author.transaction do - # This operation uses the same client, so it is in the transaction - Author.create! - # This operation also uses the same client, so it is in the transaction - User.create! - # This operation uses a different client, so it is NOT in the transaction - Article.create! - end - -.. note:: - When ``transaction`` method is called on ``Mongoid`` module, the transaction - is created using the ``:default`` client. - -Aborting Transaction -~~~~~~~~~~~~~~~~~~~~ - -Any exception raised inside the ``transaction`` method block aborts the -transaction. Normally the raised exception passed on, except for the -``Mongoid::Errors::Rollback``. This error should be raised if you want to -explicitly abort the transaction without passing on an exception. - -Callbacks -~~~~~~~~~ - -Transaction API introduces two new callbacks - ``after_commit`` and ``after_rollback``. - -``after_commit`` callback is triggered for an object that was created, saved, -or destroyed: - -* after transaction is committed if the object was modified inside the transaction; -* after the object was persisted if the object was modified outside a transaction. - -.. note:: - In any case ``after_commit`` callback is triggered only after all other callbacks - were executed successfully. Therefore, if the object is modified without a - transaction, it is possible that the object was persisted, but ``after_commit`` - callback was not triggered (for example, an exception raised in ``after_save`` - callback). - -``after_rollback`` callback is triggered for an object that was created, saved, -or destroyed inside a transaction if the transaction was aborted. ``after_rollback`` -is never triggered without a transaction. - - -Lower Level API ---------------- - -In order to start a transaction, the application must have a :ref:`session `. - -A transaction can be started by calling the ``start_transaction`` method on a session, which can be -obtained by calling the ``with_session`` method on either a model class or instance: - -.. code-block:: ruby - - class Person - include Mongoid::Document - end - - Person.with_session do |session| - session.start_transaction - end - - person = Person.new - person.with_session do |session| - session.start_transaction - end - -It is also possible to specify read concern, write concern and read preference -when starting a transaction: - -.. code-block:: ruby - - Person.with_session do |session| - session.start_transaction( - read_concern: {level: :majority}, - write_concern: {w: 3}, - read: {mode: :primary}) - end - -A transaction may be committed or aborted. The corresponding methods to do so are -``commit_transaction`` and ``abort_transaction``, again on the session instance: - -.. code-block:: ruby - - Person.with_session do |session| - session.commit_transaction - end - - Person.with_session do |session| - session.abort_transaction - end - -If a session ends with an open transaction, -`the transaction is aborted `_. - -The transaction commit `can be retried `_ -if it fails. Here is the Ruby code to do so: - -.. code-block:: ruby - - begin - session.commit_transaction - rescue Mongo::Error => e - if e.label?(Mongo::Error::UNKNOWN_TRANSACTION_COMMIT_RESULT_LABEL) - retry - else - raise - end - end - -Note that in order to perform operations within the transaction, operations must use the same client -that the session was initiated on. By default, all operations will be done on the default client: - -.. code-block:: ruby - - class Person - include Mongoid::Document - end - - class Post - include Mongoid::Document - end - - Person.with_session do |s| - s.start_transaction - Person.create! - Person.create! - Post.create! - s.commit_transaction - end - -To explicitly use a different client, use the ``with`` method: - -.. code-block:: ruby - - Post.with(client: :other) do - Person.with(client: :other) do - Person.with_session do |s| - s.start_transaction - Person.create! - Person.create! - Post.create! - s.commit_transaction - end - end - end diff --git a/source/reference/validation.txt b/source/reference/validation.txt deleted file mode 100644 index a855c006..00000000 --- a/source/reference/validation.txt +++ /dev/null @@ -1,66 +0,0 @@ -.. _validation: - -********** -Validation -********** - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 2 - :class: singlecol - -Mongoid includes ``ActiveModel::Validations`` to supply the basic -validation plus an additional associated and uniqueness validator. - -See the `Active Record Validations -`_ -Rails guide and `ActiveModel::Validations -`_ -documentation for more information. - -Mongoid behaves slightly differently to Active Record when using ``#valid?`` -on already persisted data. Active Record's ``#valid?`` will run all -validations whereas Mongoid's ``#valid?`` will only run validations on -documents that are in memory as an optimization. - - -``validates_uniqueness_of`` and ``:conditions`` Option -======================================================= - -The ``:conditions`` option to ``validates_uniqueness_of`` can be used to -provide additional conditions to add to the database query looking for -identical documents. This option does not influence when the validation -is executed because it is not considered when Mongoid retrieves the present -value of the respective field from the model. Consider the following example: - -.. code-block:: ruby - - class Band - include Mongoid::Document - - field :name, type: String - field :year, type: Integer - - validates_uniqueness_of :name, conditions: -> { where(:year.gte => 2000) } - end - - # OK - Band.create!(name: "Sun Project", year: 2000) - - # Fails validation because there is a band with the "Sun Project" name - # and year 2000 in the database, even though the model being created now - # does not have a year. - Band.create!(name: "Sun Project") - - -Read preference with ``validates_uniqueness_of`` -================================================ - -In order to validate the uniqueness of an attribute, Mongoid must check that -the value for that attribute does not already exist in the database. If Mongoid -queries a secondary member of the replica set, there is a possibility that it -is reading stale data. Because of this, the queries used to check a -``validates_uniqueness_of`` validation always use read preference ``primary``. diff --git a/source/release-notes.txt b/source/release-notes.txt deleted file mode 100644 index 770f9008..00000000 --- a/source/release-notes.txt +++ /dev/null @@ -1,21 +0,0 @@ -.. _release-notes: - -************* -Release Notes -************* - -.. default-domain:: mongodb - -.. toctree:: - :titlesonly: - - Upgrade - Mongoid 9.0 - -Overview --------- - -See the following sections to learn more about upgrading Mongoid: - -- :ref:`Upgrading Mongoid ` -- :ref:`Mongoid 9.0 ` \ No newline at end of file diff --git a/source/release-notes/mongoid-9.0.txt b/source/release-notes/mongoid-9.0.txt deleted file mode 100644 index f6870f8e..00000000 --- a/source/release-notes/mongoid-9.0.txt +++ /dev/null @@ -1,598 +0,0 @@ -.. _mongoid-9.0: - -*********** -Mongoid 9.0 -*********** - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 2 - :class: singlecol - -This page describes significant changes and improvements in Mongoid 9.0. -The complete list of releases is available `on GitHub -`_ and `in JIRA -`_; -please consult GitHub releases for detailed release notes and JIRA for -the complete list of issues fixed in each release, including bug fixes. - -Railsmdb for Mongoid --------------------- - -To coincide with the release of Mongoid 9.0 a new command-line utility for creating, -updating, managing, and maintaining Rails applications has also -been made generally available! - -``railsmdb`` makes it easier to work with MongoDB from the command line through -common generators that Ruby on Rails developers are already familiar with. - -For example, you can use ``railsmdb`` to generate stubs for new Mongoid models: - -.. code-block:: sh - - $ bin/railsmdb generate model person - -This will create a new model at ``app/models/person.rb``: - -.. code-block:: ruby - - class Person - include Mongoid::Document - include Mongoid::Timestamp - end - -You can specify the fields of the model as well: - -.. code-block:: ruby - - # bin/railsmdb generate model person name:string birth:date - - class Person - include Mongoid::Document - include Mongoid::Timestamp - field :name, type: String - field :birth, type: Date - end - -You can instruct the generator to make the new model a subclass of another, -by passing the ``--parent`` option: - -.. code-block:: ruby - - # bin/railsmdb generate model student --parent=person - - class Student < Person - include Mongoid::Timestamp - end - -And if you need to store your models in a different collection than can be -inferred from the model name, you can specify ``--collection``: - -.. code-block:: ruby - - # bin/railsmdb generate model course --collection=classes - - class Course - include Mongoid::Document - include Mongoid::Timestamp - store_in collection: 'classes' - end - -For more information see the `GitHub Repository `_. - - -Support for Ruby 2.6 and JRuby 9.3 Dropped -------------------------------------------- - -Mongoid 9 requires Ruby 2.7 or newer or JRuby 9.4. Earlier Ruby and JRuby -versions are not supported. - - -Support for Rails 5 Dropped ------------------------------ - -Mongoid 9 requires Rails 6.0 or newer. Earlier Rails versions are not supported. - - -Deprecated class ``Mongoid::Errors::InvalidStorageParent`` removed ------------------------------------------------------------------- - -The deprecated class ``Mongoid::Errors::InvalidStorageParent`` has been removed. - - -``around_*`` callbacks for embedded documents are now ignored -------------------------------------------------------------- - -Mongoid 8.x and older allows user to define ``around_*`` callbacks for embedded -documents. Starting from 9.0 these callbacks are ignored and will not be executed. -A warning will be printed to the console if such callbacks are defined. - -If you want to restore the old behavior, you can set -``Mongoid.around_embedded_document_callbacks`` to true in your application. - -.. note:: - - Enabling ``around_*`` callbacks for embedded documents is not recommended - as it may cause ``SystemStackError`` exceptions when a document has many - embedded documents. See `MONGOID-5658 `_ - for more details. - - -``for_js`` method is deprecated -------------------------------- - -The ``for_js`` method is deprecated and will be removed in Mongoid 10.0. - - -Deprecated options removed --------------------------- - -**Breaking change:** The following config options are removed in Mongoid 9.0. -Please ensure you have removed all references to these from your app. -If you were using ``config.load_defaults 8.1`` prior to upgrading, you will not -experience any behavior change. Refer to earlier release notes for the meaning -of each option. - -- ``:use_activesupport_time_zone`` -- ``:broken_aggregables`` -- ``:broken_alias_handling`` -- ``:broken_and`` -- ``:broken_scoping`` -- ``:broken_updates`` -- ``:compare_time_by_ms`` -- ``:legacy_attributes`` -- ``:legacy_pluck_distinct`` -- ``:legacy_triple_equals`` -- ``:object_id_as_json_oid`` -- ``:overwrite_chained_operators`` - -In addition, support for ``config.load_defaults`` versions 7.5 and -prior has been dropped (you must use a minimum of version 8.0.) - - -Deprecated functionality removed --------------------------------- - -**Breaking change:** The following deprecated functionality is now removed: - -- The ``Mongoid::QueryCache`` module has been removed. Please replace any usages 1-for-1 with ``Mongo::QueryCache``. - The method ``Mongoid::QueryCache#clear_cache`` should be replaced with ``Mongo::QueryCache#clear``. - All other methods and submodules are identically named. Refer to the `driver query cache documentation - `_ for more details. -- ``Object#blank_criteria?`` method is removed (was previously deprecated.) -- ``Document#as_json :compact`` option is removed. Please call ```#compact`` on the - returned ``Hash`` object instead. -- The deprecated class ``Mongoid::Errors::InvalidStorageParent`` has been removed. - - -``touch`` method now clears changed state ------------------------------------------ - -In Mongoid 8.x and older ``touch`` method leaves models in the changed state: - -.. code-block:: ruby - - # Mongoid 8.x behaviour - band = Band.create! - band.touch - band.changed? # => true - band.changes # => {"updated_at"=>[2023-01-30 13:12:57.477191135 UTC, 2023-01-30 13:13:11.482975646 UTC]} - -Starting from 9.0 Mongoid now correctly clears changed state after using ``touch`` -method. - -.. code-block:: ruby - - # Mongoid 9.0 behaviour - band = Band.create! - band.touch - band.changed? # => false - band.changes # => {} - -Sandbox Mode for Rails Console ------------------------------- - -Mongoid now supports Rails console sandbox mode. If the Rails console started -with ``--sandbox`` flag, Mongoid starts a transaction on the ``:default`` client -before opening the console. This transaction won't be committed; therefore, all -the commands executed in the console using the ``:default`` client won't -be persisted in the database. - -.. note:: - - If you execute commands in the sandbox mode *using any other client than default*, - these changes will be persisted as usual. - -New Transactions API --------------------- - -Mongoid 9.0 introduces new transactions API that is inspired by ActiveRecord: - -.. code-block:: ruby - - Band.transaction do - Band.create(title: 'Led Zeppelin') - end - - band = Band.create(title: 'Deep Purple') - band.transaction do - band.active = false - band.save! - end - -Please consult :ref:`transactions documentation ` for more details. - -Embedded Documents Always Use Parent Persistence Context --------------------------------------------------------- - -Mongoid 8.x and older allows user to specify persistence context for an -embedded document (using ``store_in`` macro). In Mongoid 9.0 these settings are -ignored for embedded documents; an embedded document now always uses the persistence -context of its parent. - - -Support for Passing Raw Values into Queries -------------------------------------------- - -When performing queries, it is now possible skip Mongoid's type coercion logic -using the ``Mongoid::RawValue`` wrapper class. This can be useful when legacy -data in the database is of a different type than the field definition. - -.. code-block:: ruby - - class Person - include Mongoid::Document - field :age, type: Integer - end - - # Query for the string "42", not the integer 42 - Person.where(age: Mongoid::RawValue("42")) - - -Raise AttributeNotLoaded error when accessing fields omitted from query projection ----------------------------------------------------------------------------------- - -When attempting to access a field on a model instance which was -excluded with the ``.only`` or ``.without`` query projections methods -when the instance was loaded, Mongoid will now raise a -``Mongoid::Errors::AttributeNotLoaded`` error. - -.. code-block:: ruby - - Band.only(:name).first.label - #=> raises Mongoid::Errors::AttributeNotLoaded - - Band.without(:label).first.label = 'Sub Pop Records' - #=> raises Mongoid::Errors::AttributeNotLoaded - -In earlier Mongoid versions, the same conditions would raise an -``ActiveModel::MissingAttributeError``. Please check your code for -any Mongoid-specific usages of this class, and change them to -``Mongoid::Errors::AttributeNotLoaded``. Note additionally that -``AttributeNotLoaded`` inherits from ``Mongoid::Errors::MongoidError``, -while ``ActiveModel::MissingAttributeError`` does not. - - -Use configured time zone to typecast Date to Time in queries -------------------------------------------------------------- - -When querying for a Time field using a Date value, Mongoid now correctly -considers ``Time.zone`` to perform type conversion. - -.. code-block:: ruby - - class Magazine - include Mongoid::Document - - field :published_at, type: Time - end - - Time.zone = 'Asia/Tokyo' - - Magazine.gte(published_at: Date.parse('2022-09-26')) - #=> will return all results on or after Sept 26th, 2022 - # at 0:00 in Asia/Tokyo time zone. - -In prior Mongoid versions, the above code would ignore the ``Time.zone`` -(irrespective of the now-removed ``:use_activesupport_time_zone`` -setting) and always using the system time zone to perform the type conversion. - -Note that in prior Mongoid versions, typecasting Date to Time during -persistence operations was already correctly using time zone. - - -```#touch`` method on embedded documents correctly handles ``touch: false`` option ----------------------------------------------------------------------------------- - -When the ``touch: false`` option is set on an ``embedded_in`` relation, -calling the ``#touch`` method on an embedded child document will not -invoke ``#touch`` on its parent document. - -.. code-block:: ruby - - class Address - include Mongoid::Document - include Mongoid::Timestamps - - embedded_in :mall, touch: false - end - - class Mall - include Mongoid::Document - include Mongoid::Timestamps - - embeds_many :addresses - end - - mall = Mall.create! - address = mall.addresses.create! - - address.touch - #=> updates address.updated_at but not mall.updated_at - -In addition, the ``#touch`` method has been optimized to perform one -persistence operation per parent document, even when using multiple -levels of nested embedded documents. - - -``embedded_in`` associations now default to ``touch: true`` ------------------------------------------------------------ - -Updating an embedded subdocument will now automatically touch the parent, -unless you explicitly set ``touch: false`` on the relation: - -.. code-block:: ruby - - class Address - include Mongoid::Document - include Mongoid::Timestamps - - embedded_in :mall, touch: false - end - -For all other associations, the default remains ``touch: false``. - - -Flipped default for ``:replace`` option in ``#upsert`` ------------------------------------------------------- - -Mongoid 8.1 added the ``:replace`` option to the ``#upsert`` method. This -option was used to specify whether or not the existing document should be -updated or replaced. - -Mongoid 9.0 flips the default of this flag from ``true`` => ``false``. - -This means that, by default, Mongoid 9 will update the existing document and -will not replace it. - - -The immutability of the ``_id`` field is now enforced ------------------------------------------------------ - -Prior to Mongoid 9.0, mutating the ``_id`` field behaved inconsistently -depending on whether the document was top-level or embedded, and depending on -how the update was performed. As of 9.0, changing the ``_id`` field will now -raise an exception when the document is saved, if the document had been -previously persisted. - -Mongoid 9.0 also introduces a new feature flag, ``immutable_ids``, which -defaults to ``true``. - -.. code-block:: ruby - - Mongoid::Config.immutable_ids = true - -When set to false, the older, inconsistent behavior is restored. - - -Support Field Aliases on Index Options --------------------------------------- - -Support has been added to use aliased field names in the following options -of the ``index`` macro: ``partial_filter_expression``, ``weights``, -``wildcard_projection``. - -.. code-block:: ruby - - class Person - include Mongoid::Document - field :a, as: :age - index({ age: 1 }, { partial_filter_expression: { age: { '$gte' => 20 } }) - end - -.. note:: - - The expansion of field name aliases in index options such as - ``partial_filter_expression`` is performed according to the behavior of MongoDB - server 6.0. Future server versions may change how they interpret these options, - and Mongoid's functionality may not support such changes. - - -BSON 5 and BSON::Decimal128 Fields ----------------------------------- - -When BSON 4 or earlier is present, any field declared as BSON::Decimal128 will -return a BSON::Decimal128 value. When BSON 5 is present, however, any field -declared as BSON::Decimal128 will return a BigDecimal value by default. - -.. code-block:: ruby - - class Model - include Mongoid::Document - - field :decimal_field, type: BSON::Decimal128 - end - - # under BSON <= 4 - Model.first.decimal_field.class #=> BSON::Decimal128 - - # under BSON >= 5 - Model.first.decimal_field.class #=> BigDecimal - -If you need literal BSON::Decimal128 values with BSON 5, you may instruct -Mongoid to allow literal BSON::Decimal128 fields: - -.. code-block:: ruby - - Model.first.decimal_field.class #=> BigDecimal - - Mongoid.allow_bson5_decimal128 = true - Model.first.decimal_field.class #=> BSON::Decimal128 - -.. note:: - - The ``allow_bson5_decimal128`` option only has any effect under - BSON 5 and later. BSON 4 and earlier ignore the setting entirely. - - -Search Index Management with MongoDB Atlas ------------------------------------------- - -When connected to MongoDB Atlas, Mongoid now supports creating and removing -search indexes. You may do so programmatically, via the Mongoid::SearchIndexable -API: - -.. code-block:: ruby - - class SearchablePerson - include Mongoid::Document - - search_index { ... } # define the search index here - end - - # create the declared search indexes; this returns immediately, but the - # search indexes may take several minutes before they are available. - SearchablePerson.create_search_indexes - - # query the available search indexes - SearchablePerson.search_indexes.each do |index| - # ... - end - - # remove all search indexes from the model's collection - SearchablePerson.remove_search_indexes - -If you are not connected to MongoDB Atlas, the search index definitions are -ignored. Trying to create, enumerate, or remove search indexes will result in -an error. - -There are also rake tasks available, for convenience: - -.. code-block:: bash - - # create search indexes for all models; waits for indexes to be created - # and shows progress on the terminal. - $ rake mongoid:db:create_search_indexes - - # as above, but returns immediately and lets the indexes be created in the - # background - $ rake WAIT_FOR_SEARCH_INDEXES=0 mongoid:db:create_search_indexes - - # removes search indexes from all models - $ rake mongoid:db:remove_search_indexes - - -``Time.configured`` has been removed ------------------------------------- - -``Time.configured`` returned either the time object wrapping the configured -time zone, or the standard Ruby ``Time`` class. This allowed you to query -a time value even if no time zone had been configured. - -Mongoid now requires that you set a time zone if you intend to do -anything with time values (including using timestamps in your documents). -Any uses of ``Time.configured`` must be replaced with ``Time.zone``. - -.. code-block:: ruby - - # before: - puts Time.configured.now - - # after: - puts Time.zone.now - - # or, better for finding the current Time specifically: - puts Time.current - -If you do not set a time zone, you will see errors in your code related -to ``nil`` values. If you are using Rails, the default time zone is already -set to UTC. If you are not using Rails, you may set a time zone at the start -of your program like this: - -.. code-block:: ruby - - Time.zone = 'UTC' - -This will set the time zone to UTC. You can see all available time zone names -by running the following command: - -.. code-block:: bash - - $ ruby -ractive_support/values/time_zone \ - -e 'puts ActiveSupport::TimeZone::MAPPING.keys' - - -Records now remember the persistence context in which they were loaded/created ------------------------------------------------------------------------------- - -Consider the following code: - -.. code-block:: ruby - - record = Model.with(collection: 'other_collection') { Model.first } - record.update(field: 'value') - -Prior to Mongoid 9.0, this could would silently fail to execute the update, -because the storage options (here, the specification of an alternate -collection for the model) would not be remembered by the record. Thus, the -record would be loaded from "other_collection", but when updated, would attempt -to look for and update the document in the default collection for Model. To -make this work, you would have had to specify the collection explicitly for -every update. - -As of Mongoid 9.0, records that are created or loaded under explicit storage -options, will remember those options (including a named client, -a different database, or a different collection). - -If you need the legacy (pre-9.0) behavior, you can enable it with the following -flag: - -.. code-block:: ruby - - Mongoid.legacy_persistence_context_behavior = true - -This flag defaults to false in Mongoid 9. - - -Bug Fixes and Improvements --------------------------- - -This section will be for smaller bug fixes and improvements: - -- The ``.unscoped`` method now also clears scopes declared using ``.with_scope`` - `MONGOID-5214 `_. -- When evolving a ``String`` to a ``BigDecimal`` (i.e. when querying a - ``BigDecimal`` field with a ``String`` object), if the - ``map_big_decimal_to_decimal128`` flag set to true, the conversion will - return a ``BSON::Decimal128`` and not a ``String`` - `MONGOID-5484 `_. -- Created new error ``Mongoid::Errors::InvalidEstimatedCountCriteria`` for - when calling ``estimated_document_count`` on a document class with a - default scope - `MONGOID-4960 `_. -- Mongoid now uses primary reads for validations in all cases - `MONGOID-5150 `_. -- Added support for symbol keys in localized field translation hashes - `MONGOID-5334 `_. -- Added index wildcard option - `MONGOID-5388 `_. -- With the ``map_big_decimal_to_decimal128`` flag set to false, ``demongoizing`` - a non-numeric, non-string value that implements ``:to_d`` will return a string - rather than a ``BigDecimal`` - `MONGOID-5507 `_. -- Added support for serializing and deserializing BSON::ObjectId values - when passed as ActiveJob arguments - `MONGOID-5611 `_. diff --git a/source/schema-configuration.txt b/source/schema-configuration.txt deleted file mode 100644 index d974fef2..00000000 --- a/source/schema-configuration.txt +++ /dev/null @@ -1,31 +0,0 @@ -.. _schema-configuration: - -******************** -Schema Configuration -******************** - -.. default-domain:: mongodb - -.. toctree:: - :titlesonly: - - Field Definition - Inheritance - Associations - Validation - Collection Configuration - Index Management - Sharding Configuration - -Overview --------- - -See the following sections to learn how to configure a schema with Mongoid: - -- :ref:`Field Definition ` -- :ref:`Inheritance ` -- :ref:`Associations ` -- :ref:`Validation ` -- :ref:`Collection Configuration ` -- :ref:`Index Management ` -- :ref:`Sharding Management ` \ No newline at end of file diff --git a/source/security.txt b/source/security.txt new file mode 100644 index 00000000..9e2470f2 --- /dev/null +++ b/source/security.txt @@ -0,0 +1,22 @@ +.. _mongoid-security: + +================ +Secure Your Data +================ + +.. facet:: + :name: genre + :values: reference + +.. meta:: + :keywords: ruby framework, odm, security + +.. toctree:: + :caption: Secure Your Data + + In-Use Encryption + +In this section, you can learn how to secure your data when using {+odm+}. + +- :ref:`Client-Side Field Level Encryption ` Learn + how to encrypt your data with {+odm+}. diff --git a/source/security/encryption.txt b/source/security/encryption.txt new file mode 100644 index 00000000..641bf12a --- /dev/null +++ b/source/security/encryption.txt @@ -0,0 +1,334 @@ +.. _automatic-encryption: +.. _mongoid-encryption: + +================================== +Client-Side Field Level Encryption +================================== + +.. facet:: + :name: genre + :values: reference + +.. meta:: + :keywords: ruby framework, odm, security, encrypt data, csfle, code example + +.. contents:: On this page + :local: + :backlinks: none + :depth: 2 + :class: singlecol + +Overview +-------- + +In this guide, you can learn how to encrypt your data by using **client-side +field level encryption** (CSFLE). CSFLE allows you to encrypt data in your +application before sending it over the network to MongoDB. This means that no +MongoDB product has access to your data in unencrypted form. + +You can set up CSFLE by using one of the following mechanisms: + +- Automatic encryption: Allows you to perform encrypted read and write + operations without specifying how to encrypt fields +- Explicit encryption: Allows you to perform encrypted read and write operations + with specified encryption logic throughout your application. + +This guide describes how to set up CSFLE with automatic encryption. To learn more +about using explicit encryption, see the :ruby:`Explicit Encryption +` guide +in the {+ruby-driver+} documentation. + +Install Dependencies +-------------------- + +To use CSFLE with {+odm+}, you must first install the following dependencies: + +- ``libmongocrypt`` +- {+shared-library+} if you are using {+ruby-driver+} v2.19 or later. Or + ``mongocryptd`` if you are using {+ruby-driver+} v2.18 or earlier. +- ``ffi`` + +The following sections provide details on how to install these +dependencies. + +libmongocrypt +~~~~~~~~~~~~~ + +You can install ``libmongocrypt`` by adding the :github:`libmongocrypt-helper gem +` to your ``Gemfile`` +or by downloading the library manually. + +To install ``libmongocrypt`` by adding the gem file, navigate to the folder in +which your application is located and run the following command in your shell: + +.. code-block:: bash + + gem install libmongocrypt-helper --pre + +.. note:: + + Because the version number of ``libmongocrypt-helper`` might contain letters, + which indicates a pre-release version in {+language+}, the ``--pre`` flag is + required. + +To learn how to download and install the library manually, see the +:ruby:`libmongocrypt installation guide +` +in the {+ruby-driver+} documentation. + +Shared Library (Driver v2.19 or later) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The following steps detail how to install the {+shared-library+}: + +1. In a browser, navigate to the :website:`MongoDB Download Center + `. +#. Select the latest current version in the ``Version`` dropdown, denoted by the + ``(current)`` tag. +#. Select your platform in the ``Platform`` dropdown. +#. Select ``crypt_shared`` in the ``Package`` dropdown. +#. Click the ``Download`` button to download the shared library. + +After you download the file, extract the contents and save the +file in a location that your application can access. Then, configure your +``mongoid.yml`` file in your application to point to the library, as shown in +the following example: + +.. code-block:: ruby + :emphasize-lines: 5-7 + + development: + clients: + default: + options: + auto_encryption_options: + extra_options: + crypt_shared_lib_path: '' + +mongocryptd (Driver v2.18 or earlier) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +If you are using {+ruby-driver+} version 2.18 or earlier, you must use ``mongocryptd`` +instead of the {+shared-library+}. ``mongocryptd`` comes +pre-packaged with enterprise builds of {+mdb-server+}. For instructions on how to +install and configure ``mongocryptd``, see the :manual:`Install mongocryptd guide +` in the MongoDB +{+server-manual+}. + +ffi +~~~ + +{+odm+} uses the :github:`ffi gem ` to call functions from +``libmongocrypt``. Add the gem to your ``Gemfile`` by running the following +command in your shell: + +.. code-block:: bash + + gem 'ffi' + +Create a Customer Master Key +---------------------------- + +To encrypt and decrypt data, you must first create a Customer Master Key (CMK). +A CMK is a key that you use to encrypt your :ref:`Data Encryption Key +` (DEK). Without access to a CMK, your client application cannot decrypt +DEKs associated with your encrypted data. + +You can create a locally-stored key to use as a local CMK for testing purposes +by running the following {+language+} code: + +.. code-block:: ruby + + require 'securerandom' + + SecureRandom.hex(48) + +.. warning:: + + Using a local CMK in a production environment is insecure. For production + environments, use a remote key management service to create and + store your CMK. + + To learn more about key management service providers, see the :manual:`KMS Providers + guide ` in the MongoDB + {+server-manual+}. + +Configure your Client +--------------------- + +You must configure your MongoDB client to implement CSFLE. To configure a +client for CSFLE, add the following code to your ``mongoid.yml`` file: + +.. code-block:: bash + + development: + clients: + default: + uri: "" + options: + auto_encryption_options: # This key enables automatic encryption + key_vault_namespace: 'encryption.__keyVault' # Database and collection in which to store data keys + kms_providers: # Tells the driver where to obtain master keys + local: # Specifies that the key is local + key: "" + extra_options: + crypt_shared_lib_path: '' # Only required for Ruby versions 2.19 or later + +.. note:: + + Ensure that you replace the placeholders surrounded by brackets (``<>``) in the preceding code + example. + +.. _mongoid-dek: + +Create a Data Encryption Key +---------------------------- + +A Data Encryption Key (DEK) is a key that you use to encrypt the fields in your +documents. MongoDB stores DEKs, encrypted with your CMK, in the Key Vault +collection as BSON documents. MongoDB can never decrypt the DEKs, as key +management is client-side and customer controlled. + +To create a DEK in {+odm+}, you can use the +``db:mongoid:encryption:create_data_key`` rake task, as shown in the following +example: + +.. code-block:: bash + + rake db:mongoid:encryption:create_data_key + +You can create multiple DEKs by repeating the preceding command for the number +of keys you want to generate. + +You can also provide an alternate name for your DEK. This allows you to reference +the DEK by name when configuring encryption for your fields and +to dynamically assign a DEK to a field at runtime. + +The following example creates an alternate name when generating a new DEK: + +.. code-block:: bash + + rake db:mongoid:encryption:create_data_key -- --key-alt-name= + +Configure Encryption Schema +--------------------------- + +You can specify which fields to encrypt by adding the ``encrypt`` option to the +field definition in your models and specifying the ``deterministic`` and +``key_id`` options, as shown in the following example: + +.. literalinclude:: /includes/security/encryption.rb + :language: ruby + :start-after: start-encryption-schema + :end-before: end-encryption-schema + +.. note:: + + If you are developing a Rails application, we recommend setting the + ``preload_models`` option to ``true`` in your ``mongoid.yml`` file. This + ensures that {+odm+} loads all models and configures the encryption schema + before any data is read or written. + +Limitations +----------- + +The following limitations apply when using CSFLE with {+odm+}: + +- {+odm+} does not support encryption of ``embeds_many`` associations. +- If you use the ``:key_name_field`` option, you must encrypt the field by using + a non-deterministic algorithm. To encrypt your field deterministically, you must + specify the ``:key_id`` option instead. +- The limitations listed on the :manual:`CSFLE Limitations + ` page in the MongoDB {+server-manual+} + also apply to {+odm+}. + +Working with Data +----------------- + +Usually, automatic CSLFE works transparently in your application. After +your application is configured for CSFLE, you can create documents as usual and {+odm+} +automatically encrypts and decrypts them according to your configuration. + +The following example creates a new ``Patient`` document in an application +configured for CSFLE. It then uses a client called ``unencrypted_client`` that +is connected to the database but not configured for CSFLE to read the document. + +.. io-code-block:: + + .. input:: /includes/security/encryption.rb + :language: ruby + :start-after: start-query-encrypted + :end-before: end-query-encrypted + + .. output:: + + {"_id"=>BSON::ObjectId('6446a1d046ebfd701f9f4292'), + "category"=>"ER", + "passport_id"=>, + "blood_type"=>, + "ssn"=>, + "insurance"=>{"_id"=>BSON::ObjectId('6446a1d046ebfd701f9f4293'), + "insurer"=>"TK", "policy_number"=>}, "policy_number_key"=>"my_data_key"} + +In the preceding example, the ``unencrypted_client`` client is unable to read +the encrypted fields. However, if you query the document with a client that *is* +configured for CSFLE, {+odm+} automatically decrypts the fields. + +Rotate Encryption Keys +---------------------- + +You can rotate your encryption keys by using the ``rewrap_many_data_key`` {+ruby-driver+} +method. This method automatically decrypts multiple data encryption keys +and re-encrypts them using a specified CMK. It then updates the rotated keys in +the key vault collection. + +The ``rewrap_many_data_key`` method takes the following parameters: + +- Filter, used to specify which fields to rotate. If no data key matches the + given filter, no keys will be rotated. Omit the filter to rotate all keys in + your key vault collection. +- Object that represents a new CMK with which to re-encrypt the DEKs. Omit + this object to rotate the data keys by using their current CMKs. + +The following example rotates encryption keys by using the AWS KMS: + +.. literalinclude:: /includes/security/encryption.rb + :language: ruby + :start-after: start-rewrap-keys + :end-before: end-rewrap-keys + +Add Automatic Encryption to Existing Project +-------------------------------------------- + +Automatic CSFLE with {+odm+} supports encryption in place. You can enable +encryption on an existing database and still read unencrypted data. However, +once you enable encryption, all new data is encrypted, and any query operation +uses only the encrypted documents. This means that queries might not return all documents +if some were saved before enabling encryption. + +The following example queries a collection that has one encrypted document and +one unencrypted document: + +.. literalinclude:: /includes/security/encryption.rb + :language: ruby + :start-after: start-in-place + :end-before: end-in-place + +You can encrypt existing data in a collection by reading and then writing back +all data with a CSFLE-enabled client. When doing so, ensure that all existing +data is the expected type and that empty values are not set as ``nil``. + +Additional Information +---------------------- + +To learn more about CSFLE, see the :manual:`Client-Side Field Level Encryption +` guide in the MongoDB {+server-manual+}. + +To learn more about using CSFLE with the {+ruby-driver+}, see the +:ruby:`Client-Side Encryption +` guide in the +{+ruby-driver+} documentation. + +To learn more about configuring {+odm+} in your application, see the +:ref:`mongoid-app-config` guide. diff --git a/source/tutorials.txt b/source/tutorials.txt deleted file mode 100644 index 51ce0167..00000000 --- a/source/tutorials.txt +++ /dev/null @@ -1,29 +0,0 @@ -.. _tutorials: - -********* -Tutorials -********* - -.. default-domain:: mongodb - -.. toctree:: - :titlesonly: - - Sinatra - Rails 7 - Rails 6 - Documents - Common Errors - Field Level Encryption - -Overview --------- - -See the following sections to learn more about working with Mongoid: - -- :ref:`Getting Started (Sinatra) ` -- :ref:`Getting Started (Rails 7) ` -- :ref:`Getting Started (Rails 6) ` -- :ref:`Documents ` -- :ref:`Common Errors ` -- :ref:`Automatic Client-Side Field Level Encryption ` \ No newline at end of file diff --git a/source/tutorials/automatic-encryption.txt b/source/tutorials/automatic-encryption.txt deleted file mode 100644 index 62e68c27..00000000 --- a/source/tutorials/automatic-encryption.txt +++ /dev/null @@ -1,439 +0,0 @@ -.. _automatic-encryption: - -******************************************** -Automatic Client-Side Field Level Encryption -******************************************** - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 2 - :class: singlecol - - -Since version 4.2 MongoDB supports `Client-Side Field Level Encryption -(CSFLE) `_. This is a feature -that enables you to encrypt data in your application before you send it over the -network to MongoDB. With CSFLE enabled, no MongoDB product has access to your -data in an unencrypted form. - -You can set up CSFLE using the following mechanisms: - -* `Automatic Encryption `_: - Enables you to perform encrypted read and write operations without you having - to write code to specify how to encrypt fields. -* `Explicit Encryption `_: - Enables you to perform encrypted read and write operations through your - MongoDB driver's encryption library. You must specify the logic for encryption - with this library throughout your application. - -Starting with version 9.0, Mongoid supports CSFLE's Automatic Encryption -feature. This tutorial walks you through the process of setting up and using -CSFLE in Mongoid. - -.. note:: - This tutorial does not cover all CSLFE features. - You can find more information about MongoDB CSFLE in - `the server documentation. `_ - -.. note:: - If you want to use explicit FLE, please follow `the Ruby driver documentation. - `_ - - -Installation -============ - -You can find the detailed description of how to install the necessary -dependencies in `the driver documentation. `_ - -Note the version of the Ruby driver being used in your application and select -the appropriate steps below. - -Install ``libmongocrypt`` -~~~~~~~~~~~~~~~~~~~~~~~~~ - -This can be done one of two ways. - -* Add the `libmongocrypt-helper gem `_ to - your ``Gemfile`` or -* Download the ``libmongocrypt`` `release archive `_, - extract the version that matches your operating system, and set the - ``LIBMONGOCRYPT_PATH`` environment variable accordingly. - -Install the automatic encryption shared library (Ruby driver 2.19+) -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -If you use the Ruby driver version 2.19 and above, the automatic encryption -shared library should be installed by following the instructions on the -:manual:`Automatic Encryption Shared Library for Queryable Encryption -` -page in the Server manual. - -The steps required are as follows: - -1. Navigate to the `MongoDB Download Center `_ -2. From the Version dropdown, select ``x.y.z (current)`` (the latest current version). -3. In the Platform dropdown, select your platform. -4. In the Package dropdown, select ``crypt_shared``. -5. Click Download. - -Once extracted, ensure the full path to the library is configured within your -``mongoid.yml`` as follows: - -.. code-block:: yaml - - development: - clients: - default: - options: - auto_encryption_options: - extra_options: - crypt_shared_lib_path: '/path/to/mongo_crypt_v1.so' - -Install the ``mongocryptd`` (Ruby driver 2.18 or older) -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -If you are using an older version of the Ruby driver ``mongocryptd`` will -need to be installed manually. ``mongocryptd`` comes pre-packaged with -enterprise builds of the MongoDB server (versions 4.2 and newer). -For installation instructions, see the `MongoDB manual `_. - -Add ``ffi`` to your Gemfile -~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -The MongoDB Ruby driver uses the `ffi gem `_ to call -functions from ``libmongocrypt``. As this gem is not a dependency of -the driver, it will need to be manually added to your ``Gemfile``: - -.. code-block:: ruby - - gem 'ffi' - -Create a Customer Master Key -============================ - -A Customer Master Key (CMK) is used to encrypt Data Encryption Keys. -The easiest way is to use a locally stored 96-bytes key. You can generate such -a key using the following Ruby code: - -.. code-block:: ruby - - require 'securerandom' - - SecureRandom.hex(48) # => "f54ab...." - -Later in this tutorial we assume that the Customer Master Key is -available from the ``CUSTOMER_MASTER_KEY`` environment variable. - -.. warning:: - - Using a local master key is insecure. It is recommended that you use a remote - Key Management Service to create and store your master key. To do so, follow - steps of the `"Set up a Remote Master Key" `_ - in the MongoDB Client-Side Encryption documentation. - - For more information about creating a master key, see the - `Create a Customer Master Key `_ - section of the MongoDB manual. - -Configure Clients -================= - -Automatic CSFLE requires some additional configuration for the MongoDB client. -Assuming that your application has just one ``default`` client, you need to -add the following to your ``mongoid.yml``: - -.. code-block:: yaml - - development: - clients: - default: - uri: mongodb+srv://user:pass@yourcluster.mongodb.net/blog_development?retryWrites=true&w=majority - options: - auto_encryption_options: # This key enables automatic encryption - key_vault_namespace: 'encryption.__keyVault' # Database and collection to store data keys - kms_providers: # Tells the driver where to obtain master keys - local: # We use the local key in this tutorial - key: "<%= ENV['CUSTOMER_MASTER_KEY'] %>" # Key that we generated earlier - extra_options: - crypt_shared_lib_path: '/path/to/mongo_crypt_v1.so' # Only if you use the library - - -Create a Data Encryption Key -============================ - -A Data Encryption Key (DEK) is the key you use to encrypt the fields in your -MongoDB documents. You store your Data Encryption Key in your Key Vault -collection encrypted with your CMK. - -To create a DEK in Mongoid you can use the -``db:mongoid:encryption:create_data_key`` ``Rake`` task: - -.. code-block:: sh - - % rake db:mongoid:encryption:create_data_key - Created data key with id: 'KImyywsTQWi1+cFYIHdtlA==' for kms provider: 'local' in key vault: 'encryption.__keyVault'. - -You can create multiple DEKs, if necessary. - -.. code-block:: sh - - % rake db:mongoid:encryption:create_data_key - Created data key with id: 'Vxr5m+5cQISjDOruzZgE0w==' for kms provider: 'local' in key vault: 'encryption.__keyVault'. - -You can also provide an alternate name for the DEK. This allows you to reference -the DEK by name when configuring encryption for your fields. It also allows you -to dynamically assign a DEK to a field at runtime. - -.. code-block:: sh - - % rake db:mongoid:encryption:create_data_key -- --key-alt-name=my_data_key - Created data key with id: 'yjF8hKmKQsqGeFGXlB9Sow==' with key alt name: 'my_data_key' for kms provider: 'local' in key vault: 'encryption.__keyVault'. - - -Configure Encryption Schema -=========================== - -Now we can tell Mongoid what should be encrypted: - -.. code-block:: ruby - - class Patient - include Mongoid::Document - include Mongoid::Timestamps - - # Tells Mongoid what DEK should be used to encrypt fields of the document - # and its embedded documents. - encrypt_with key_id: 'KImyywsTQWi1+cFYIHdtlA==' - - # This field is not encrypted. - field :category, type: String - - # This field is encrypted using AEAD_AES_256_CBC_HMAC_SHA_512-Random - # algorithm. - field :passport_id, type: String, encrypt: { - deterministic: false - } - # This field is encrypted using AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic - # algorithm. - field :blood_type, type: String, encrypt: { - deterministic: true - } - # This field is encrypted using AEAD_AES_256_CBC_HMAC_SHA_512-Random - # algorithm and using a different data key. - field :ssn, type: Integer, encrypt: { - deterministic: false, key_id: 'Vxr5m+5cQISjDOruzZgE0w==' - } - - embeds_one :insurance - end - - class Insurance - include Mongoid::Document - include Mongoid::Timestamps - - field :insurer, type: String - - # This field is encrypted using AEAD_AES_256_CBC_HMAC_SHA_512-Random - # algorithm using the key which alternate name is stored in the - # policy_number_key field. - field :policy_number, type: Integer, encrypt: { - deterministic: false, - key_name_field: :policy_number_key - } - - embedded_in :patient - end - -.. note:: - If you are developing a Rails application, it is recommended to set - ``preload_models`` to ``true`` in ``mongoid.yml``. This will ensure that - Mongoid loads all models before the application starts, and the encryption - schema is configured before any data is read or written. - -Known Limitations -~~~~~~~~~~~~~~~~~ - -* MongoDB CSFLE has some limitations that are described on the - :manual:`CSFLE Limitations ` - page in the Server manual. These limitations also apply to Mongoid. -* Mongoid does not support encryption of ``embeds_many`` relations. -* If you use ``:key_name_field`` option, the field must be encrypted using - non-deterministic algorithm. To encrypt your field deterministically, you must - specify ``:key_id`` option instead. - -Working with Data -================= - -Automatic CSFLE usage is transparent in many situations. - -.. note:: - In code examples below we assume that there is a variable ``unencrypted_client`` - that is a ``MongoClient`` connected to the same database but without encryption. - We use this client to demonstrate what is actually persisted in the database. - -Documents can be created as usual, fields will be encrypted and decrypted -according to the configuration: - -.. code-block:: ruby - - Patient.create!( - category: 'ER', - passport_id: '123456', - blood_type: 'AB+', - ssn: 98765, - insurance: Insurance.new(insurer: 'TK', policy_number: 123456, policy_number_key: 'my_data_key') - ) - - # Fields are encrypted in the database - unencrypted_client['patients'].find.first - # => - # {"_id"=>BSON::ObjectId('6446a1d046ebfd701f9f4292'), - # "category"=>"ER", - # "passport_id"=>, - # "blood_type"=>, - # "ssn"=>, - # "insurance"=>{"_id"=>BSON::ObjectId('6446a1d046ebfd701f9f4293'), "insurer"=>"TK", "policy_number"=>}, "policy_number_key"=>"my_data_key"} - -Fields encrypted using a deterministic algorithm can be queried. Only exact match -queries are supported. For more details please consult `the server documentation -`_. - -.. code-block:: ruby - - # We can find documents by deterministically encrypted fields. - Patient.where(blood_type: "AB+").to_a - # => [#] - -Encryption Key Management -========================= - -Customer Master Keys -~~~~~~~~~~~~~~~~~~~~ - -Your Customer Master Key is the key you use to encrypt your Data Encryption Keys. -MongoDB automatically encrypts Data Encryption Keys using the specified CMK -during Data Encryption Key creation. - -The CMK is the most sensitive key in CSFLE. If your CMK is compromised, all of -your encrypted data can be decrypted. - -.. important:: - Ensure you store your Customer Master Key (CMK) on a remote KMS. - - To learn more about why you should use a remote KMS, see `Reasons to Use a Remote KMS. `_ - - To view a list of all supported KMS providers, see the `KMS Providers `_ page. - -MongoDB CSFLE supports the following Key Management System (KMS) providers: - * `Amazon Web Services KMS `_ - * `Azure Key Vault `_ - * `Google Cloud Platform KMS `_ - * Any KMIP Compliant Key Management System - * Local Key Provider (for testing only) - -Data Encryption Keys -~~~~~~~~~~~~~~~~~~~~ - -Data Encryption Keys can be created using the -``db:mongoid:encryption:create_data_key`` ``Rake`` task. By default they are -stored on the same cluster as the database. -However, it might be a good idea to store the keys separately. This can be -done by specifying a key vault client in ``mongoid.yml``: - -.. code-block:: yaml - - development: - clients: - key_vault: - uri: mongodb+srv://user:pass@anothercluster.mongodb.net/blog_development?retryWrites=true&w=majority - default: - uri: mongodb+srv://user:pass@yourcluster.mongodb.net/blog_development?retryWrites=true&w=majority - options: - auto_encryption_options: - key_vault_client: :key_vault # Client to connect to key vault - # ... - -Encryption Keys Rotation -~~~~~~~~~~~~~~~~~~~~~~~~ - -You can rotate encryption keys using the ``rewrap_many_data_key`` method -of the Ruby driver. This method automatically decrypts multiple data encryption -keys and re-encrypts them using a specified CMK. It then updates -the rotated keys in the key vault collection. This method allows you to rotate -encryption keys based on two optional arguments: - -* A filter used to specify which keys to rotate. If no data key matches the - given filter, no keys will be rotated. Omit the filter to rotate all keys in - your key vault collection. -* An object that represents a new CMK. Omit this object to rotate the data - keys using their current CMKs. - -Here is an example of rotating keys using AWS KMS: - -.. code-block:: ruby - - # Create a key vault client - key_vault_client = Mongo::Client.new('mongodb+srv://user:pass@yourcluster.mongodb.net') - # Or, if you declared the key value client in mongoid.yml, use it - key_vault_client = Mongoid.client(:key_vault) - - # Create the encryption object - encryption = Mongo::ClientEncryption.new( - key_vault_client, - key_vault_namespace: 'encryption.__keyVault', - kms_providers: { - aws: { - "accessKeyId": "", - "secretAccessKey": "" - } - } - ) - - encryption.rewrap_many_data_key( - {}, # We want to rewrap all keys - { - provider: 'aws', - master_key: { - region: 'us-east-2', - key: 'arn:aws:kms:us-east-2:...' - } - } - ) - -Adding Automatic Encryption To Existing Project -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -MongoDB automatic CSFLE supports encryption in place. You can enable encryption -for your existing database, and will still able to read unencrypted data. -All data written to the database will be encrypted. However, as soon as the -encryption is enabled, all query operations will use encrypted data: - -.. code-block:: ruby - - # We assume that there are two documents in the database, one created without - # encryption enabled, and one with encryption. - - # We can still read both. - Patient.all.to_a - # => - # [#, - # #] - - # But when we query, we can see only the latter one. - Patient.where(blood_type: 'AB+').to_a - # => [#] - -If you want to encrypt the existing database, it can be achieved by reading -and writing back all data, even without any changes. If you decide to do so, -please keep the following in mind: - -* Validate the integrity of existing data for consistent fidelity. CSFLE is - type sensitive - for example you cannot store integers in a field that is - declared as ``String``. -* For strings, make sure that empty values are always empty strings or just - not set, but not ``nil`` (CSFLE doesn't support native ``null``). -* This operation requires application downtime. diff --git a/source/tutorials/common-errors.txt b/source/tutorials/common-errors.txt deleted file mode 100644 index 226e3b0e..00000000 --- a/source/tutorials/common-errors.txt +++ /dev/null @@ -1,51 +0,0 @@ -.. _common-errors: - -************* -Common Errors -************* - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 2 - :class: singlecol - -Mongoid/Moped Authentication Error: failed with error 13 -======================================================== - -If you are encountering the following error: - -.. code-block:: ruby - - Moped::Errors::OperationFailure: The operation: #1, :w=>1} - @fields=nil> - failed with error 13: "not authorized for insert on mongose_development.people" - - See https://github.com/mongodb/mongo/blob/master/docs/errors.md - for details about this error. - from /.rbenv/versions/2.1.4/lib/ruby/gems/2.1.0/bundler/gems/moped-10abbf3eac37/lib/moped/operation/read.rb:50:in `block in execute' - from /.rbenv/versions/2.1.4/lib/ruby/gems/2.1.0/bundler/gems/moped-10abbf3eac37/lib/moped/node.rb:594:in `[]' - from /.rbenv/versions/2.1.4/lib/ruby/gems/2.1.0/bundler/gems/moped-10abbf3eac37/lib/moped/node.rb:594:in `block (2 levels) in flush' - from /.rbenv/versions/2.1.4/lib/ruby/gems/2.1.0/bundler/gems/moped-10abbf3eac37/lib/moped/node.rb:593:in `map' - from /.rbenv/versions/2.1.4/lib/ruby/gems/2.1.0/bundler/gems/moped-10abbf3eac37/lib/moped/node.rb:593:in `block in flush' - from /.rbenv/versions/2.1.4/lib/ruby/gems/2.1.0/bundler/gems/moped-10abbf3eac37/lib/moped/node.rb:617:in `block in logging' - from /.rbenv/versions/2.1.4/lib/ruby/gems/2.1.0/gems/activesupport-4.2.0/lib/active_support/notifications.rb:164:in `block in instrument' - from /.rbenv/versions/2.1.4/lib/ruby/gems/2.1.0/gems/activesupport-4.2.0/lib/active_support/notifications/instrumenter.rb:20:in `instrumen - -This error is caused by Moped, a Ruby driver that is no longer in use by -Mongoid. Upgrading to Mongoid 5+ should fix this issue. - -You can find more information about this issue here: -`MONGOID-4067 `_. - diff --git a/source/tutorials/documents.txt b/source/tutorials/documents.txt deleted file mode 100644 index 2bd87f30..00000000 --- a/source/tutorials/documents.txt +++ /dev/null @@ -1,18 +0,0 @@ -.. _documents: - -********* -Documents -********* - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 2 - :class: singlecol - -Documents are the core objects in Mongoid and any object that is to be persisted to the -database must include ``Mongoid::Document``. The representation of a Document in MongoDB -is a BSON object that is very similar to a Ruby hash or JSON object. Documents can be stored -in their own collections in the database, or can be embedded in other Documents n levels deep. diff --git a/source/tutorials/getting-started-rails6.txt b/source/tutorials/getting-started-rails6.txt deleted file mode 100644 index 84cd46c0..00000000 --- a/source/tutorials/getting-started-rails6.txt +++ /dev/null @@ -1,558 +0,0 @@ -.. _getting-started-6: - -************************* -Getting Started (Rails 6) -************************* - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 2 - :class: singlecol - -.. note:: - - This tutorial is for Ruby on Rails 6. If this is not the version you're using choose - the appropriate tutorial for your Rails version from the navigation menu. - -New Application -=============== - -This section shows how to create a new Ruby on Rails application using Mongoid -for data access. The application will be similar to the blog application -described in the `Ruby on Rails Getting Started -`_ -guide, however using Mongoid instead of ActiveRecord as the database adapter. - -The complete source code for this application can be found in the -`mongoid-demo GitHub repository -`_. - -.. note:: - - This guide assumes basic familiarity with Ruby on Rails. - To learn more about Ruby on Rails, please refer to its `Getting Started - guide `_ or - other Rails guides. - - -Install ``rails`` ------------------ - -We will use a Rails generator to create the application skeleton. -In order to do so, the first step is to install the ``rails`` gem: - -.. code-block:: sh - - gem install rails -v '~> {+rails-6-version+}' - - -Create New Application ----------------------- - -Use the ``rails`` command to create the application skeleton, as follows: - -.. code-block:: sh - - rails new blog --skip-active-record --skip-bundle - cd blog - -.. note:: - - You may receive a warning like this: - - .. code-block:: sh - - Could not find gem 'puma (~> 3.11)' in any of the gem sources listed in your Gemfile. - Run `bundle install` to install missing gems. - - Disregard it as we will be taking care of gem installation - in a moment. - -We pass ``--skip-active-record`` to request that ActiveRecord is not added -as a dependency, because we will be using Mongoid instead. Additionally -we pass ``--skip-bundle`` because we'll be modifying the ``Gemfile`` to -add the ``mongoid`` dependency. - -If you intend to test your application with RSpec, you can instruct the -generator to omit default Rails test setup by passing ``--skip-test`` -and ``--skip-system-test`` options: - -.. code-block:: sh - - rails new blog --skip-bundle --skip-active-record --skip-test --skip-system-test - cd blog - - -Create Git Repo ---------------- - -While not required, we recommend creating a Git repository for your application: - -.. code-block:: sh - - git init . - git add . - git commit - -Commit your changes as you are following this tutorial. - - -Add Mongoid ------------ - -1. Modify the ``Gemfile`` to add a reference to the -`mongoid `_ gem: - -.. code-block:: ruby - :caption: Gemfile - - gem 'mongoid' - -.. note:: - - Mongoid 7.0.5 or higher is required to use Rails 6.0. - -2. Install gem dependencies: - -.. code-block:: sh - - bundle install - -3. Generate the default Mongoid configuration: - -.. code-block:: sh - - bin/rails g mongoid:config - -This generator will create the ``config/mongoid.yml`` configuration file -(used to configure the connection to the MongoDB deployment) and the -``config/initializers/mongoid.rb`` initializer file (which may be used for -other Mongoid-related configuration). Note that as we are not using -ActiveRecord we will not have a ``database.yml`` file. - - -.. _run-locally: - -Run MongoDB Locally -------------------- - -The configuration created in the previous step is suitable when -a MongoDB server is running locally. If you do not already have a -local MongoDB server, `download and install MongoDB -`_. - -While the generated ``mongoid.yml`` will work without modifications, -we recommend reducing the server selection timeout for development. -With this change, the uncommented lines of ``mongoid.yml`` should look -like this: - -.. code-block:: none - - development: - clients: - default: - database: blog_development - hosts: - - localhost:27017 - options: - server_selection_timeout: 1 - - -.. _use-atlas: - -Use MongoDB Atlas ------------------ - -Instead of downloading, installing and running MongoDB locally, you can create -a free MongoDB Atlas account and create a `free MongoDB cluster in Atlas -`_. -Once the cluster is created, follow the instructions in `connect to the cluster -page `_ -to obtain the URI. Use the *Ruby driver 2.5 or later* format. - -Paste the URI into the ``config/mongoid.yml`` file, and comment out the -hosts that are defined. We recommend setting the server selection timeout to 5 -seconds for development environment when using Atlas. - -The uncommented contents of ``config/mongoid.yml`` should look like this: - -.. code-block:: yaml - - development: - clients: - default: - uri: mongodb+srv://user:pass@yourcluster.mongodb.net/blog_development?retryWrites=true&w=majority - options: - server_selection_timeout: 5 - - -Other Rails Dependencies ------------------------- - -If this is the first Rails application you are creating, you may need to -install Node.js on your computer. This can be done via your operating system -packages or by `downloading a binary `_. - -Next, if you do not have Yarn installed, `follow its installation instructions -`_. - -Finally, install webpacker: - -.. code-block:: sh - - rails webpacker:install - - -Run Application ---------------- - -You can now start the application server by running: - -.. code-block:: sh - - rails s - -Access the application by navigating to `localhost:3000 -`_. - - -Add Posts ---------- - -Using the standard Rails scaffolding, Mongoid can generate the necessary -model, controller and view files for our blog so that we can quickly begin -creating blog posts: - -.. code-block:: sh - - bin/rails g scaffold Post title:string body:text - -Navigate to `localhost:3000/posts `_ -to create posts and see the posts that have already been created. - -.. image:: ../img/rails-new-blog.png - :alt: Screenshot of the new blog - - -Add Comments ------------- - -To make our application more interactive, let's add the ability for users to -add comments to our posts. - -Create the ``Comment`` model: - -.. code-block:: sh - - bin/rails g scaffold Comment name:string message:string post:belongs_to - -Open the ``Post`` model file, ``app/models/post.rb``, and add a ``has_many`` -association for the comments: - -.. code-block:: ruby - :caption: app/models/post.rb - - class Post - include Mongoid::Document - - field :title, type: String - field :body, type: String - - has_many :comments, dependent: :destroy - end - -.. note:: - *The following is only required if using a version of Mongoid < 7.0.8 or 7.1.2 (see* - `MONGOID-4885 `_ *for details)* - - Open the ``Comment`` model file, ``app/models/comment.rb``, and change the - generated ``embedded_in`` association to ``belongs_to``: - - .. code-block:: ruby - :caption: app/models/comment.rb - - class Comment - include Mongoid::Document - - field :name, type: String - field :message, type: String - - belongs_to :post - end - -Open the post show view file, ``app/views/posts/show.html.erb``, and add -a section rendering existing comments and prompting to leave a new comment: - -.. code-block:: html - :caption: app/views/posts/show.html.erb - -
-
-

- <%= @post.comments.count %> Comments -

- <%= render @post.comments %> -
-
-

Leave a reply

- <%= render partial: 'comments/form', locals: { comment: @post.comments.build } %> -
-
-
- -Open the comment form file and change the type of field for ``:message`` -from ``text_field`` to ``text_area``, as well as the type of field for -``:post_id`` from ``text_field`` to ``hidden_field``. The result -should look like this: - -.. code-block:: html - :caption: app/views/comments/_form.html.erb - - <%= form_with(model: comment, local: true) do |form| %> - <% if comment.errors.any? %> -
-

<%= pluralize(comment.errors.count, "error") %> prohibited this comment from being saved:

- -
    - <% comment.errors.full_messages.each do |message| %> -
  • <%= message %>
  • - <% end %> -
-
- <% end %> - -
- <%= form.label :name %> - <%= form.text_field :name %> -
- -
- <%= form.label :message %> - <%= form.text_area :message %> -
- -
- <%= form.hidden_field :post_id %> -
- -
- <%= form.submit %> -
- <% end %> - -Create a partial for the comment view, ``app/views/comments/_comment.html.erb`` -with the following contents: - -.. code-block:: html - :caption: app/views/comments/_comment.html.erb - -

- <%= comment.name %>: - <%= comment.message %> -
- <%= link_to 'Delete', [comment], - method: :delete, - class: "button is-danger", - data: { confirm: 'Are you sure?' } %> -

- -You should now be able to leave comments for the posts: - -.. image:: ../img/rails-blog-new-comment.png - :alt: Screenshot of the blog with a new comment being added - - -Existing Application -==================== - -Follow these steps to switch an existing Ruby on Rails application to use -Mongoid instead of ActiveRecord. - -Dependencies ------------- - -Remove or comment out any RDBMS libraries like ``sqlite``, ``pg`` etc. -mentioned in ``Gemfile``, and add ``mongoid``: - -.. code-block:: ruby - :caption: Gemfile - - gem 'mongoid' - -.. note:: - - Mongoid 7.0.5 or higher is required to use Rails 6.0. - -Install gem dependencies: - -.. code-block:: sh - - bundle install - -Loaded Frameworks ------------------ - -Examine ``config/application.rb``. If it is requiring all components of Rails -via ``require 'rails/all'``, change it to require individual frameworks: - -.. code-block:: ruby - :caption: config/application.rb - - # Remove or comment out - #require "rails/all" - - # Add this require instead of "rails/all": - require "rails" - - # Pick the frameworks you want: - require "active_model/railtie" - require "active_job/railtie" - require "action_controller/railtie" - require "action_mailer/railtie" - # require "action_mailbox/engine" - # require "action_text/engine" - require "action_view/railtie" - require "action_cable/engine" - require "sprockets/railtie" - require "rails/test_unit/railtie" - - # Remove or comment out ActiveRecord and ActiveStorage: - # require "active_record/railtie" - # require "active_storage/engine" - -.. note:: - - At this time ActiveStorage requires ActiveRecord and is not usable with - Mongoid. - -ActiveRecord Configuration --------------------------- - -Review all configuration files (``config/application.rb``, -``config/environments/{development,production.test}.rb``) and remove or -comment out any references to ``config.active_record`` and -``config.active_storage``. - -Stop Spring ------------ - -If your application is using Spring, which is the default on Rails 6, -Spring must be stopped after changing dependencies or configuration. - -.. code-block:: sh - - ./bin/spring stop - -.. note:: - - Sometimes running ``./bin/spring stop`` claims to stop Spring, but does - not. Verify that all Spring processes are terminated before proceeding. - -.. note:: - - Sometimes Spring tries to load ActiveRecord even when the application - contains no ActiveRecord references. If this happens, add an ActiveRecord - adapter dependency such as ``sqlite3`` to your ``Gemfile`` so that - ActiveRecord may be completely loaded or remove Spring from your - application. - -Mongoid Configuration ---------------------- - -Generate the default Mongoid configuration: - -.. code-block:: sh - - bin/rails g mongoid:config - -This generator will create the ``config/mongoid.yml`` configuration file -(used to configure the connection to the MongoDB deployment) and the -``config/initializers/mongoid.rb`` initializer file (which may be used for -other Mongoid-related configuration). In general, it is recommended to use -``mongoid.yml`` for all Mongoid configuration. - -Review the sections :ref:`Run MongoDB Locally ` and -:ref:`Use MongoDB Atlas ` to decide how you would like to deploy -MongoDB, and adjust Mongoid configuration (``config/mongoid.yml``) to match. - -Adjust Models -------------- - -If your application already has models, these will need to be changed when -migrating from ActiveRecord to Mongoid. - -ActiveRecord models derive from ``ApplicationRecord`` and do not have -column definitions. Mongoid models generally have no superclass but must -include ``Mongoid::Document``, and usually define the fields explicitly -(but :ref:`dynamic fields ` may also be used instead of -explicit field definitions). - -For example, a bare-bones Post model may look like this in ActiveRecord: - -.. code-block:: ruby - :caption: app/models/post.rb - - class Post < ApplicationRecord - has_many :comments, dependent: :destroy - end - -The same model may look like this in Mongoid: - -.. code-block:: ruby - :caption: app/models/post.rb - - class Post - include Mongoid::Document - - field :title, type: String - field :body, type: String - - has_many :comments, dependent: :destroy - end - -Or like this with dynamic fields: - -.. code-block:: ruby - :caption: app/models/post.rb - - class Post - include Mongoid::Document - include Mongoid::Attributes::Dynamic - - has_many :comments, dependent: :destroy - end - -Mongoid does not utilize ActiveRecord migrations, since MongoDB does not -require a schema to be defined prior to storing data. - -Data Migration --------------- - -If you already have data in a relational database that you would like to -transfer to MongoDB, you will need to perform a data migration. As noted -above, no schema migration is necessary because MongoDB does not require -a predefined schema to store the data. - -The migration tools are often specific to the data being migrated because, -even though Mongoid supports a superset of ActiveRecord associations, -the way that model references are stored in collections differs between -Mongoid and ActiveRecord. With that said, MongoDB has -some resources on migrating from an RDBMS to MongoDB such as the -`RDBMS to MongoDB Migration Guide `_ and -`Modernization Guide `_. - - -Rails API ---------- - -The process for creating a Rails API application with Mongoid is the same -as when creating a regular application, with the only change being the -``--api`` parameter to ``rails new``. Migrating a Rails API application to -Mongoid follows the same process described above for regular Rails applications. - -A complete Rails API application similar to the one described in this tutorial -can be found in `the mongoid-demo GitHub repository -`_. diff --git a/source/tutorials/getting-started-rails7.txt b/source/tutorials/getting-started-rails7.txt deleted file mode 100644 index 87a0c07a..00000000 --- a/source/tutorials/getting-started-rails7.txt +++ /dev/null @@ -1,470 +0,0 @@ -.. _getting-started-7: - -************************* -Getting Started (Rails 7) -************************* - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 2 - :class: singlecol - -.. note:: - - This tutorial is for Ruby on Rails 7. If this is not the version you're using choose - the appropriate tutorial for your Rails version from the navigation menu. - -New Application -=============== - -This section demonstrates how to create a new Ruby on Rails application using the Mongoid ODM. -By replacing Rails' default `ActiveRecord `_ -adapter with MongoDB's ORM-like library for data access we will create an application similar to the -blog application described in the `Ruby on Rails Getting Started -`_ guide. - -The complete source code for this application can be found in the -`mongoid-demo GitHub repository -`_. - -.. note:: - - This guide assumes basic familiarity with Ruby on Rails. - To learn more about Ruby on Rails, please refer to its `Getting Started - guide `_ or - other Rails guides. - - -Install ``rails`` ------------------ - -We will use a Rails generator to create the application skeleton. -In order to do so, the first step is to install the ``rails`` gem: - -.. code-block:: sh - - gem install rails -v {+rails-7-version+} - - -Create New Application ----------------------- - -Use the ``rails`` command to create the application skeleton, as follows: - -.. code-block:: sh - - rails new blog --skip-active-record - cd blog - -We pass ``--skip-active-record`` to request that ActiveRecord is not added -as a dependency, because we will be using Mongoid instead. - -Optionally Skip Tests -````````````````````` - -If you intend to test your application with `RSpec `_, you can instruct the -generator to omit the default Rails test setup by passing ``--skip-test`` -and ``--skip-system-test`` options: - -.. code-block:: sh - - rails new blog --skip-active-record --skip-test --skip-system-test - cd blog - -Setup Mongoid -------------- - -1. Modify the ``Gemfile`` to add a reference to the -`mongoid `_ gem: - -.. code-block:: ruby - :caption: Gemfile - - gem 'mongoid' - -2. Install gem dependencies: - -.. code-block:: sh - - bundle install - -3. Generate the default `Mongoid configuration `_: - -.. code-block:: sh - - bin/rails g mongoid:config - -This generator will create the ``config/mongoid.yml`` configuration file -(used to configure the connection to the MongoDB deployment) and the -``config/initializers/mongoid.rb`` initializer file (which may be used for -other Mongoid-related configuration). Note that as we are not using -ActiveRecord we will not have a ``database.yml`` file. - - -.. _configure-self-managed: - -Configure for Self Managed MongoDB -`````````````````````````````````` - -The configuration created in the previous step is suitable when -a MongoDB server is running locally. If you do not already have a -local MongoDB server, `download and install MongoDB -`_. - -While the generated ``mongoid.yml`` will work without modifications, -we recommend reducing the server selection timeout for development. -With this change, the uncommented lines of ``mongoid.yml`` should look -like this: - -.. code-block:: yaml - - development: - clients: - default: - database: blog_development - hosts: - - localhost:27017 - options: - server_selection_timeout: 1 - - -.. _configure-atlas: - -Configure for MongoDB Atlas -``````````````````````````` - -Instead of downloading, installing and running MongoDB locally, you can create -a free MongoDB Atlas account and create a `free MongoDB cluster in Atlas -`_. -Once the cluster is created, follow the instructions in `connect to the cluster -page `_ -to obtain the URI. Use the *Ruby driver 2.5 or later* format. - -Paste the URI into the ``config/mongoid.yml`` file, and comment out the -hosts that are defined. We recommend setting the server selection timeout to 5 -seconds for development environment when using Atlas. - -The uncommented contents of ``config/mongoid.yml`` should look like this: - -.. code-block:: yaml - - development: - clients: - default: - uri: mongodb+srv://user:pass@yourcluster.mongodb.net/blog_development?retryWrites=true&w=majority - options: - server_selection_timeout: 5 - -Run Application ---------------- - -You can now start the application server by running: - -.. code-block:: sh - - bin/rails s - -Access the application by navigating to `localhost:3000 -`_. - - -Add Posts ---------- - -Using the standard Rails scaffolding, Mongoid can generate the necessary -model, controller and view files for our blog so that we can quickly begin -creating blog posts: - -.. code-block:: sh - - bin/rails g scaffold Post title:string body:text - -Navigate to `localhost:3000/posts `_ -to create posts and see the posts that have already been created. - -.. figure:: /img/rails-new-blog.png - :alt: Screenshot of the new blog - - -Add Comments ------------- - -To make our application more interactive, let's add the ability for users to -add comments to our posts. - -Create the ``Comment`` model: - -.. code-block:: sh - - bin/rails g scaffold Comment name:string message:string post:belongs_to - -Open the ``Post`` model file, ``app/models/post.rb``, and add a ``has_many`` -association for the comments: - -.. code-block:: ruby - :caption: app/models/post.rb - - class Post - include Mongoid::Document - include Mongoid::Timestamps - field :title, type: String - field :body, type: String - - has_many :comments, dependent: :destroy - end - -Open ``app/views/posts/show.html.erb`` and add -a section rendering existing comments and prompting to leave a new comment: - -.. code-block:: erb - :caption: app/views/posts/show.html.erb - -
-
-

- <%= @post.comments.count %> Comments -

- <%= render @post.comments %> -
-
-

Leave a reply

- <%= render partial: 'comments/form', locals: { comment: @post.comments.build } %> -
-
-
- -Open ``app/views/comments/_form.html.erb`` and change the type of field for ``:message`` -from ``text_field`` to ``text_area``, as well as the type of field for -``:post_id`` from ``text_field`` to ``hidden_field``. The result -should look like this: - -.. code-block:: erb - :caption: app/views/comments/_form.html.erb - - <%= form_with(model: comment, local: true) do |form| %> - <% if comment.errors.any? %> -
-

<%= pluralize(comment.errors.count, "error") %> prohibited this comment from being saved:

- -
    - <% comment.errors.full_messages.each do |message| %> -
  • <%= message %>
  • - <% end %> -
-
- <% end %> - -
- <%= form.label :name %> - <%= form.text_field :name %> -
- -
- <%= form.label :message %> - <%= form.text_area :message %> -
- -
- <%= form.hidden_field :post_id %> -
- -
- <%= form.submit %> -
- <% end %> - -Next replace ``app/view/comments/_comment.html.erb`` with the following contents: - -.. code-block:: erb - :caption: app/views/comments/_comment.html.erb - -

- <%= comment.name %>: - <%= comment.message %> - <%= link_to 'Delete', [comment], - data: { - "turbo-method": :delete, - "turbo-confirm": 'Are you sure?' - } %> -

- - -You should now be able to leave comments for the posts: - -.. figure:: /img/rails-blog-new-comment.png - :alt: Screenshot of the blog with a new comment being added - - -Existing Application -==================== - -Mongoid can be easily added to an existing Rails application and run alongside other ActiveRecord -adapters. If this is your use case, updating dependencies and populating the configuration file will -allow you to start using MongoDB within your application. - -To switch an existing Ruby on Rails application to use Mongoid instead of ActiveRecord additional -configuration changes will be required, as described below. - -Dependencies ------------- - -First, the ``mongoid`` gem will need to be added your ``Gemfile``. - -.. code-block:: ruby - :caption: Gemfile - - gem 'mongoid' - -If Mongoid will be the **only** database adapter, remove or comment out any RDBMS libraries -like ``sqlite`` or ``pg`` mentioned in the ``Gemfile``. - -Install gem dependencies: - -.. code-block:: sh - - bundle install - -Mongoid Configuration ---------------------- - -Generate the default Mongoid configuration: - -.. code-block:: sh - - bin/rails g mongoid:config - -This generator will create the ``config/mongoid.yml`` configuration file -(used to configure the connection to the MongoDB deployment) and the -``config/initializers/mongoid.rb`` initializer file (which may be used for -other Mongoid-related configuration). In general, it is recommended to use -``mongoid.yml`` for all Mongoid configuration. - -Review the sections :ref:`Configure for Self Managed MongoDB ` -and :ref:`Configure for MongoDB Atlas ` to decide how you -would like to deploy MongoDB, and adjust the Mongoid configuration -(``config/mongoid.yml``) to match. - -Loaded Frameworks ------------------ - -Examine ``config/application.rb``. If it is requiring all components of Rails -via ``require 'rails/all'``, change it to require individual frameworks. To verify the contents of -``rails/all`` for your version see the `Github Repository -`_: - -.. code-block:: ruby - :caption: config/application.rb - - # Remove or comment out - #require "rails/all" - - # Add the following instead of "rails/all": - require "rails" - - # require "active_record/railtie" rescue LoadError - # require "active_storage/engine" rescue LoadError - require "action_controller/railtie" rescue LoadError - require "action_view/railtie" rescue LoadError - require "action_mailer/railtie" rescue LoadError - require "active_job/railtie" rescue LoadError - require "action_cable/engine" rescue LoadError - # require "action_mailbox/engine" rescue LoadError - # require "action_text/engine" rescue LoadError - require "rails/test_unit/railtie" rescue LoadError - -.. warning:: - - Due to their reliance on ActiveRecord, `ActionText `_, - `ActiveStorage `_ and - `ActionMailbox `_ cannot be used - with Mongoid. - -ActiveRecord Configuration --------------------------- - -Review all configuration files (``config/application.rb``, -``config/environments/{development,production.test}.rb``) and remove or -comment out any references to ``config.active_record`` and -``config.active_storage``. - -Adjust Models -------------- - -If your application already has models, these will need to be changed when -migrating from ActiveRecord to Mongoid. - -ActiveRecord models derive from ``ApplicationRecord`` and do not have -column definitions. Mongoid models generally have no superclass but must -include ``Mongoid::Document``, and usually define the fields explicitly -(but :ref:`dynamic fields ` may also be used instead of -explicit field definitions). - -For example, a bare-bones Post model may look like this in ActiveRecord: - -.. code-block:: ruby - :caption: app/models/post.rb - - class Post < ApplicationRecord - has_many :comments, dependent: :destroy - end - -The same model may look like this in Mongoid: - -.. code-block:: ruby - :caption: app/models/post.rb - - class Post - include Mongoid::Document - - field :title, type: String - field :body, type: String - - has_many :comments, dependent: :destroy - end - -Or like this with dynamic fields: - -.. code-block:: ruby - :caption: app/models/post.rb - - class Post - include Mongoid::Document - include Mongoid::Attributes::Dynamic - - has_many :comments, dependent: :destroy - end - -Mongoid does not utilize ActiveRecord migrations, since MongoDB does not -require a schema to be defined prior to storing data. - -Data Migration --------------- - -If you already have data in a relational database that you would like to -transfer to MongoDB, you will need to perform a data migration. As noted -above, no schema migration is necessary because MongoDB does not require -a predefined schema to store the data. - -The migration tools are often specific to the data being migrated because, -even though Mongoid supports a superset of ActiveRecord associations, -the way that model references are stored in collections differs between -Mongoid and ActiveRecord. With that said, MongoDB has -some resources on migrating from an RDBMS to MongoDB such as the -`RDBMS to MongoDB Migration Guide `_ and -`Modernization Guide `_. - - -Rails API ---------- - -The process for creating a Rails API application with Mongoid is the same -as when creating a regular application, with the only change being the -``--api`` parameter to ``rails new``. Migrating a Rails API application to -Mongoid follows the same process described above for regular Rails applications. - -A complete Rails API application similar to the one described in this tutorial -can be found in `the mongoid-demo GitHub repository -`_. diff --git a/source/tutorials/getting-started-sinatra.txt b/source/tutorials/getting-started-sinatra.txt deleted file mode 100644 index 832dd0d6..00000000 --- a/source/tutorials/getting-started-sinatra.txt +++ /dev/null @@ -1,220 +0,0 @@ -.. _getting-started-sinatra: - -************************* -Getting Started (Sinatra) -************************* - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 2 - :class: singlecol - - -New Application -=============== - -This section shows how to create a new Sinatra application using Mongoid -for data access. The process is similar for other Ruby applications not -using Ruby on Rails. - -The complete source code for this application is available `in the -mongoid-demo GitHub repository -`_. - -Create Git Repo ---------------- - -While not required, we recommend creating a Git repository for your application: - -.. code-block:: sh - - git init blog - cd blog - -Commit your changes as you are following this tutorial. - - -Create Gemfile --------------- - -Create a file named ``Gemfile`` with the following contents: - -.. code-block:: ruby - - source 'https://rubygems.org' - - gem 'sinatra' - gem 'mongoid' - gem 'puma' - - -Install Dependencies --------------------- - -Run the following commands to install the dependencies: - -.. code-block:: sh - - gem install bundler - bundle install - -This command will generate a file named ``Gemfile.lock`` which we recommend -committing to your Git repository. - - -Run MongoDB Locally -------------------- - -To develop locally with MongoDB, `download and install MongoDB -`_. - -Once MongoDB is installed and running, create a file named ``config/mongoid.yml`` -pointing to your deployment. For example, if you launched a standalone -``mongod`` on the default port, the following contents would be appropriate: - -.. code-block:: none - - development: - clients: - default: - database: blog_development - hosts: - - localhost:27017 - options: - server_selection_timeout: 1 - - -Use MongoDB Atlas ------------------ - -Instead of downloading, installing and running MongoDB locally, you can create -a free MongoDB Atlas account and create a `free MongoDB cluster in Atlas -`_. -Once the cluster is created, follow the instructions in `connect to the cluster -page `_ -to obtain the URI. Use the *Ruby driver 2.5 or later* format. - -Create a file named ``config/mongoid.yml`` with the following -contents, replacing the URI with the actual URI for your cluster: - -.. code-block:: yaml - - development: - clients: - default: - uri: mongodb+srv://user:pass@yourcluster.mongodb.net/blog_development?retryWrites=true&w=majority - options: - server_selection_timeout: 5 - - - -Basic Application ------------------ - -Create a file named ``app.rb`` with the following contents. First, some -requires: - -.. code-block:: ruby - - require 'sinatra' - require 'mongoid' - -Load the Mongoid configuration file and configure Mongoid. This is done -automatically when Mongoid is used with Rails, but since we are using Mongoid -with Sinatra, we need to do this ourselves: - -.. code-block:: ruby - - Mongoid.load!(File.join(File.dirname(__FILE__), 'config', 'mongoid.yml')) - -Now we can define some models: - -.. code-block:: ruby - - class Post - include Mongoid::Document - - field :title, type: String - field :body, type: String - - has_many :comments - end - - class Comment - include Mongoid::Document - - field :name, type: String - field :message, type: String - - belongs_to :post - end - -... and add some routes: - -.. code-block:: ruby - - get '/posts' do - Post.all.to_json - end - - post '/posts' do - post = Post.create!(params[:post]) - post.to_json - end - - get '/posts/:post_id' do |post_id| - post = Post.find(post_id) - post.attributes.merge( - comments: post.comments, - ).to_json - end - - post '/posts/:post_id/comments' do |post_id| - post = Post.find(post_id) - comment = post.comments.create!(params[:comment]) - {}.to_json - end - - -Run Application -=============== - -Launch the application: - -.. code-block:: sh - - bundle exec ruby app.rb - -Try some requests via curl: - -.. code-block:: sh - - curl http://localhost:4567/posts - # => [] - - curl -d 'post[title]=hello&post[body]=hello+world' http://localhost:4567/posts - # => {"_id":{"$oid":"5d8151ec96fb4f0ed5a7a03f"},"body":"hello world","title":"hello"} - - curl http://localhost:4567/posts - # => [{"_id":{"_id":{"$oid":"5d8151ec96fb4f0ed5a7a03f"},"body":"hello world","title":"hello"}] - - curl -d 'comment[name]=David&comment[message]=I+like' http://localhost:4567/posts/5d8151ec96fb4f0ed5a7a03f/comments - # => {} - - curl http://localhost:4567/posts/5d8151ec96fb4f0ed5a7a03f - # => {"_id":{"$oid":"5d8151ec96fb4f0ed5a7a03f"},"title":"hello","body":"hello world","comments":[{"_id":{"$oid":"5d8157ac96fb4f20c5e45c4d"},"message":"I like","name":"David","post_id":{"$oid":"5d8151ec96fb4f0ed5a7a03f"}}]} - - -Existing Application -==================== - -To start using Mongoid in an existing Sinatra applications, the steps are -essentially the same as the one given above for a new application: - -1. Add the ``mongoid`` dependency to the ``Gemfile``. -2. Create a ``mongoid.yml`` configuration file. -3. Load the configuration file and configure Mongoid in the application. -4. Define Mongoid models. diff --git a/source/whats-new.txt b/source/whats-new.txt new file mode 100644 index 00000000..fda72e88 --- /dev/null +++ b/source/whats-new.txt @@ -0,0 +1,567 @@ +.. _mongoid-whats-new: + +========== +What's New +========== + +.. contents:: On this page + :local: + :backlinks: none + :depth: 1 + :class: singlecol + +Learn what's new in: + +* :ref:`Version 9.0 ` + +To view a list of releases and detailed release notes, see {+odm+} +:github:`Releases ` on GitHub. + +.. _mongoid-version-9.0: + +What's New in 9.0 +----------------- + +The 9.0 release includes the following new features, improvements, and +fixes: + +Railsmdb +~~~~~~~~ + +Alongside the release of {+odm+} v9.0, ``railsmdb``, a command-line utility +for creating, updating, managing, and maintaining Rails applications, is +generally available. + +``railsmdb`` makes it easier to work with MongoDB from the command line +through common generators that {+ror+} developers are already +familiar with. + +For example, you can use ``railsmdb`` to generate stubs for new {+odm+} models: + +.. code-block:: sh + + bin/railsmdb generate model person + +This will create a new model at ``app/models/person.rb``: + +.. code-block:: ruby + + class Person + include Mongoid::Document + include Mongoid::Timestamp + end + +You can specify the fields of the model: + +.. code-block:: sh + + bin/railsmdb generate model person name:string birth:date + +.. code-block:: ruby + + class Person + include {+odm+}::Document + include {+odm+}::Timestamp + field :name, type: String + field :birth, type: Date + end + +You can instruct the generator to make the new model a subclass of another, +by passing the ``--parent`` option: + +.. code-block:: sh + + bin/railsmdb generate model student --parent=person + +.. code-block:: ruby + + class Student < Person + include {+odm+}::Timestamp + end + +If you need to store your models in a different collection than can be +inferred from the model name, you can specify ``--collection``: + +.. code-block:: sh + + bin/railsmdb generate model course --collection=classes + +.. code-block:: ruby + + class Course + include {+odm+}::Document + include {+odm+}::Timestamp + store_in collection: 'classes' + end + +For more information see the :github:`mongoid-railsmdb repository +` on GitHub. + +Removal of Support for {+language+} 2.6 and J{+language+} 9.3 +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +{+odm+} v9.0 requires {+language+} 2.7 or newer or J{+language+} 9.4. +Earlier {+language+} and J{+language+} versions are not supported. + +Removal of Support for Rails 5 +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +{+odm+} v9.0 requires Rails 6.0 or newer. Earlier Rails versions are not +supported. + +Removal of Deprecated Class ``Mongoid::Errors::InvalidStorageParent`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The deprecated class ``{+odm+}::Errors::InvalidStorageParent`` has been +removed. + +``around_*`` Callbacks for Embedded Documents are Ignored +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +{+odm+} v8.x and older allow user to define ``around_*`` callbacks for embedded +documents. Starting in v9.0, these callbacks are ignored and will not be executed. +A warning will be printed to the console if such callbacks are defined. + +If you want to restore the old behavior, you can set +``Mongoid.around_embedded_document_callbacks`` to true in your application. + +.. note:: + + Enabling ``around_*`` callbacks for embedded documents is not recommended + as it may cause ``SystemStackError`` exceptions when a document has many + embedded documents. See `MONGOID-5658 + `__ for more details. + +``for_js`` Method is Deprecated +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The ``for_js`` method is deprecated and will be removed in {+odm+} v10.0. + +Removal of Deprecated Options +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +**Breaking change:** The following config options are removed in {+odm+} v9.0. +Please ensure you have removed all references to these from your app. +If you were using ``config.load_defaults 8.1`` before upgrading, you will not +experience any behavior change. Refer to earlier release notes for the meaning +of each option. + +- ``:use_activesupport_time_zone`` +- ``:broken_aggregables`` +- ``:broken_alias_handling`` +- ``:broken_and`` +- ``:broken_scoping`` +- ``:broken_updates`` +- ``:compare_time_by_ms`` +- ``:legacy_attributes`` +- ``:legacy_pluck_distinct`` +- ``:legacy_triple_equals`` +- ``:object_id_as_json_oid`` +- ``:overwrite_chained_operators`` + +In addition, support for ``config.load_defaults`` versions v7.5 and +prior has been dropped (you must use a minimum of version v8.0.) + +Removal of Deprecated Functionality +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +**Breaking change:** The following deprecated functionality is now removed: + +- The ``Mongoid::QueryCache`` module has been removed. Please replace + any usages 1-for-1 with ``Mongo::QueryCache``. The method + ``{+odm+}::QueryCache#clear_cache`` should be replaced with + ``Mongo::QueryCache#clear``. All other methods and submodules are + identically named. +- ``Object#blank_criteria?`` method is removed (previously deprecated). +- ``Document#as_json :compact`` option is removed. Please call ```#compact`` on the + returned ``Hash`` object instead. +- The deprecated class ``{+odm+}::Errors::InvalidStorageParent`` has + been removed. + +``touch`` Method Clears Changed State +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +In {+odm+} v8.x and older, the ``touch`` method leaves models in the +changed state: + +.. code-block:: ruby + + # v8.x behaviour + band = Band.create! + band.touch + band.changed? # => true + band.changes + # => {"updated_at"=>[2023-01-30 13:12:57.477191135 UTC, 2023-01-30 13:13:11.482975646 UTC]} + +Starting in v9.0, {+odm+} now correctly clears changed state after using +the ``touch`` method. + +.. code-block:: ruby + + # v9.0 behavior + band = Band.create! + band.touch + band.changed? # => false + band.changes # => {} + +Sandbox Mode for Rails Console +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +{+odm+} now supports Rails console sandbox mode. If the Rails console started +with ``--sandbox`` flag, {+odm+} starts a transaction on the ``:default`` client +before opening the console. This transaction won't be committed. Therefore, all +the commands executed in the console using the ``:default`` client won't +be persisted in the database. + +.. note:: + + If you execute commands in the sandbox mode *using any other client + than default*, these changes will be persisted as usual. + +New Transactions API +~~~~~~~~~~~~~~~~~~~~ + +{+odm+} 9.0 introduces new transactions API that is inspired by Active Record: + +.. code-block:: ruby + + Band.transaction do + Band.create(title: 'Led Zeppelin') + end + + band = Band.create(title: 'Deep Purple') + band.transaction do + band.active = false + band.save! + end + +To learn more, see the :ref:`mongoid-data-txn` guide. + +Embedded Documents Always Use Parent Persistence Context +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +{+odm+} v8.x and older allow user to specify persistence context for an +embedded document (using ``store_in`` macro). In {+odm+} v9.0 these settings are +ignored for embedded documents. An embedded document now always uses the persistence +context of its parent. + +Support for Passing Raw Values into Queries +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +When performing queries, it is now possible to skip {+odm+}'s type coercion logic +by using the ``{+odm+}::RawValue`` wrapper class. This can be useful when legacy +data in the database is of a different type than the field definition. + +.. code-block:: ruby + + class Person + include {+odm+}::Document + field :age, type: Integer + end + + # Query for the string "42", not the integer 42 + Person.where(age: {+odm+}::RawValue("42")) + +Raise ``AttributeNotLoaded`` Error When Accessing Fields Omitted from Query Projection +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +When attempting to access a field on a model instance which was +excluded with the ``only`` or ``without`` query projections methods +when the instance was loaded, {+odm+} will now raise a +``Mongoid::Errors::AttributeNotLoaded`` error. + +.. code-block:: ruby + + Band.only(:name).first.label + #=> raises {+odm+}::Errors::AttributeNotLoaded + + Band.without(:label).first.label = 'Sub Pop Records' + #=> raises {+odm+}::Errors::AttributeNotLoaded + +In earlier {+odm+} versions, the same conditions would raise an +``ActiveModel::MissingAttributeError``. Please check your code for +any {+odm+}-specific usages of this class, and change them to +``Mongoid::Errors::AttributeNotLoaded``. Note additionally that +``AttributeNotLoaded`` inherits from ``Mongoid::Errors::MongoidError``, +while ``ActiveModel::MissingAttributeError`` does not. + +Use Configured Time Zone to Typecast ``Date`` to ``Time`` in Queries +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +When querying for a Time field using a Date value, {+odm+} now correctly +considers ``Time.zone`` to perform type conversion. + +.. code-block:: ruby + + class Magazine + include {+odm+}::Document + + field :published_at, type: Time + end + + Time.zone = 'Asia/Tokyo' + + Magazine.gte(published_at: Date.parse('2022-09-26')) + #=> will return all results on or after Sept 26th, 2022 + # at 0:00 in Asia/Tokyo time zone. + +In prior {+odm+} versions, the above code would ignore the ``Time.zone`` +(irrespective of the now-removed ``:use_activesupport_time_zone`` +setting) and would always use the system time zone to perform the type +conversion. + +Note that in prior {+odm+} versions, typecasting ``Date`` to ``Time`` +during persistence operations was already correctly using time zone. + +``touch`` Method on Embedded Documents Correctly Handles ``touch: false`` Option +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +When the ``touch: false`` option is set on an ``embedded_in`` relation, +calling the ``touch`` method on an embedded child document will not +invoke ``touch`` on its parent document. + +.. code-block:: ruby + + class Address + include {+odm+}::Document + include {+odm+}::Timestamps + + embedded_in :mall, touch: false + end + + class Mall + include {+odm+}::Document + include {+odm+}::Timestamps + + embeds_many :addresses + end + + mall = Mall.create! + address = mall.addresses.create! + + address.touch + #=> updates address.updated_at but not mall.updated_at + +In addition, the ``touch`` method has been optimized to perform one +persistence operation per parent document, even when using multiple +levels of nested embedded documents. + +``embedded_in`` Associations Default to ``touch: true`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Updating an embedded sub-document will now automatically touch the +parent, unless you explicitly set ``touch: false`` on the relation: + +.. code-block:: ruby + + class Address + include {+odm+}::Document + include {+odm+}::Timestamps + + embedded_in :mall, touch: false + end + +For all other associations, the default remains ``touch: false``. + +Flipped Default for ``:replace`` Option in ``upsert`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +{+odm+} v8.1 added the ``:replace`` option to the ``upsert`` method. This +option was used to specify whether or not the existing document should be +updated or replaced. + +{+odm+} v9.0 flips the default of this flag from ``true`` to ``false``. +This means that, by default, {+odm+} v9.0 updates the existing document and +does not replace it. + +Immutability of the ``_id`` Field Enforced +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Before {+odm+} v9.0, mutating the ``_id`` field behaved inconsistently +depending on whether the document was top-level or embedded, and depending on +how the update was performed. In v9.0, changing the ``_id`` field will now +raise an exception when the document is saved, if the document had been +previously persisted. + +{+odm+} 9.0 also introduces a new feature flag, ``immutable_ids``, which +defaults to ``true``. + +.. code-block:: ruby + + {+odm+}::Config.immutable_ids = true + +When set to ``false``, the older, inconsistent behavior is restored. + +Support for Field Aliases on Index Options +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Support has been added to use aliased field names in the following options +of the ``index`` macro: ``partial_filter_expression``, ``weights``, +``wildcard_projection``. + +.. code-block:: ruby + + class Person + include {+odm+}::Document + field :a, as: :age + index({ age: 1 }, { partial_filter_expression: { age: { '$gte' => 20 } }) + end + +.. note:: + + The expansion of field name aliases in index options such as + ``partial_filter_expression`` is performed according to the behavior of MongoDB + server 6.0. Future server versions may change how they interpret these options, + and {+odm+}'s functionality may not support such changes. + +BSON 5 and ``BSON::Decimal128`` Fields +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +When BSON 4 or earlier is present, any field declared as ``BSON::Decimal128`` will +return a ``BSON::Decimal128`` value. When BSON 5 is present, however, any field +declared as ``BSON::Decimal128`` will return a ``BigDecimal`` value by default. + +.. code-block:: ruby + + class Model + include {+odm+}::Document + + field :decimal_field, type: BSON::Decimal128 + end + + # under BSON <= 4 + Model.first.decimal_field.class #=> BSON::Decimal128 + + # under BSON >= 5 + Model.first.decimal_field.class #=> BigDecimal + +If you need literal ``BSON::Decimal128`` values with BSON 5, you may instruct +{+odm+} to allow literal ``BSON::Decimal128`` fields: + +.. code-block:: ruby + + Model.first.decimal_field.class #=> BigDecimal + + {+odm+}.allow_bson5_decimal128 = true + Model.first.decimal_field.class #=> BSON::Decimal128 + +.. note:: + + The ``allow_bson5_decimal128`` option only has any effect under + BSON 5 and later. BSON 4 and earlier ignore the setting entirely. + +Search Index Management for MongoDB Atlas +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +When connected to MongoDB Atlas, {+odm+} now supports creating and removing +search indexes. You may do so programmatically, via the ``Mongoid::SearchIndexable`` +API: + +.. code-block:: ruby + + class SearchablePerson + include {+odm+}::Document + + search_index { ... } # define the search index here + end + + # create the declared search indexes; this returns immediately, but the + # search indexes may take several minutes before they are available. + SearchablePerson.create_search_indexes + + # query the available search indexes + SearchablePerson.search_indexes.each do |index| + # ... + end + + # remove all search indexes from the model's collection + SearchablePerson.remove_search_indexes + +If you are not connected to MongoDB Atlas, the search index definitions are +ignored. Trying to create, enumerate, or remove search indexes will result in +an error. + +There are also rake tasks available, for convenience: + +.. code-block:: bash + + # create search indexes for all models; waits for indexes to be created + # and shows progress on the terminal. + $ rake mongoid:db:create_search_indexes + + # as above, but returns immediately and lets the indexes be created in the + # background + $ rake WAIT_FOR_SEARCH_INDEXES=0 mongoid:db:create_search_indexes + + # removes search indexes from all models + $ rake mongoid:db:remove_search_indexes + +Removal of ``Time.configured`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +``Time.configured`` returned either the time object wrapping the configured +time zone, or the standard {+language+} ``Time`` class. This allowed you to query +a time value even if no time zone had been configured. + +{+odm+} now requires that you set a time zone if you intend to do +anything with time values (including using timestamps in your documents). +Any uses of ``Time.configured`` must be replaced with ``Time.zone``. + +.. code-block:: ruby + + # before: + puts Time.configured.now + + # after: + puts Time.zone.now + + # or, better for finding the current Time specifically: + puts Time.current + +If you do not set a time zone, you will see errors in your code related +to ``nil`` values. If you are using Rails, the default time zone is already +set to UTC. If you are not using Rails, you may set a time zone at the start +of your program like this: + +.. code-block:: ruby + + Time.zone = 'UTC' + +This will set the time zone to UTC. You can see all available time zone names +by running the following command: + +.. code-block:: bash + + $ ruby -ractive_support/values/time_zone \ + -e 'puts ActiveSupport::TimeZone::MAPPING.keys' + +Records Remember Persistence Context of Creation +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Consider the following code: + +.. code-block:: ruby + + record = Model.with(collection: 'other_collection') { Model.first } + record.update(field: 'value') + +Before {+odm+} v9.0, the preceding code silently fails to execute the update, +because the storage options (here, the specification of an alternate +collection for the model) would not be remembered by the record. Thus, the +record would be loaded from ``other_collection``, but when updated, would attempt +to look for and update the document in the default collection for Model. To +make this work, you would have had to specify the collection explicitly for +every update. + +As of {+odm+} v9.0, records that are created or loaded under explicit storage +options, will remember those options (including a named client, +a different database, or a different collection). + +If you need the legacy (pre-v9.0) behavior, you can enable it with the following +flag: + +.. code-block:: ruby + + Mongoid.legacy_persistence_context_behavior = true + +This flag defaults to false in {+odm+} v9.0. diff --git a/source/working-with-data.txt b/source/working-with-data.txt deleted file mode 100644 index 0380fa0b..00000000 --- a/source/working-with-data.txt +++ /dev/null @@ -1,37 +0,0 @@ -.. _working-with-data: - -***************** -Working With Data -***************** - -.. default-domain:: mongodb - -.. toctree:: - :titlesonly: - - CRUD Operations - Queries - Text Search - Aggregation Pipeline - MapReduce - Persistence Configuration - Nested Attributes - Callbacks - Sessions - Transactions - -Overview --------- - -See the following sections to learn more about working with data in Mongoid: - -- :ref:`CRUD Operations ` -- :ref:`Queries ` -- :ref:`Text Search ` -- :ref:`Aggregation Pipeline ` -- :ref:`Map/Reduce ` -- :ref:`Persistence Configuration ` -- :ref:`Nested Attributes ` -- :ref:`Callbacks ` -- :ref:`Sessions ` -- :ref:`Transactions ` \ No newline at end of file diff --git a/worker.sh b/worker.sh deleted file mode 100644 index c615eb59..00000000 --- a/worker.sh +++ /dev/null @@ -1 +0,0 @@ -"build-and-stage-next-gen"