Menu Docs
Página inicial do Docs
/ / /
Mongoid
/

Consultas

Nesta página

  • Sintaxe de condição
  • Sintaxe de campo
  • Sintaxe MQL
  • Sintaxe do operador de símbolos
  • Campos
  • Executar queries em campos definidos
  • Queries por valores brutos
  • Aliases de campo
  • Documentos incorporados
  • Operações lógicas
  • Combinações do operador
  • and Comportamento
  • any_of Comportamento
  • none_of Comportamento
  • not Comportamento
  • Construção de queries incrementais
  • Estratégias de fusão
  • Métodos de operador suportados
  • Expansão de valor do operador
  • Métodos de query
  • elem_match
  • Projeção
  • only
  • without
  • Encomenda
  • Paginação
  • limit
  • skip
  • batch_size
  • Encontrando por _id
  • Métodos de queries adicionais
  • Carregamento ansioso
  • Expressões regulares
  • Condições nos campos
  • Escopo
  • Escopos nomeados
  • Escopos padrão
  • Substituição de escopo padrão de tempo de execução
  • Métodos de classe
  • queries + persistência
  • Cache de consulta
  • Habilitando cache de query
  • Habilitando o cache de query automaticamente
  • Habilitando cache de query manualmente
  • Armazenando em cache o resultado de #first
  • queries assíncronas
  • Configuração da execução de query assíncrona

Mongoid fornece uma query DSL rica inspirada no ActiveRecord. Uma query trivial tem a seguinte aparência:

Band.where(name: "Depeche Mode")

Uma query mais complexa utilizando várias características Mongoid pode ser como segue:

Band.
where(:founded.gte => "1980-01-01").
in(name: [ "Tool", "Deftones" ]).
union.
in(name: [ "Melvins" ])

Os métodos de query retornam objetos Mongoid::Criteria, que são encadeáveis e são invólucros avaliados de forma preguiçosa para a linguagem de query do MongoDB (MQL). As queries são executadas quando seus conjuntos de resultados são iterados. Por exemplo:

# Construct a Criteria object:
Band.where(name: 'Deftones')
# => #<Mongoid::Criteria
# selector: {"name"=>"Deftones"}
# options: {}
# class: Band
# embedded: false>
# Evaluate the query and get matching documents:
Band.where(name: 'Deftones').to_a
# => [#<Band _id: 5ebdeddfe1b83265a376a760, name: "Deftones", description: nil>]

Métodos como first e last devolver os documentos individuais imediatamente. Caso contrário, a iteração de um objeto Criteria com métodos como each ou map recupera os documentos do servidor. to_a pode ser usado para forçar a execução de uma query que retorna uma array de documentos, literalmente convertendo um objeto Criteria em uma array.

Quando um método de query é chamado em uma instância de Critérios, o método retorna uma nova instância de Critérios com as novas condições adicionadas às condições existentes:

scope = Band.where(:founded.gte => "1980-01-01")
# => #<Mongoid::Criteria
# selector: {"founded"=>{"$gte"=>"1980-01-01"}}
# options: {}
# class: Band
# embedded: false>
scope.where(:founded.lte => "2020-01-01")
# => #<Mongoid::Criteria
# selector: {"founded"=>{"$gte"=>"1980-01-01", "$lte"=>"2020-01-01"}}
# options: {}
# class: Band
# embedded: false>
scope
# => #<Mongoid::Criteria
# selector: {"founded"=>{"$gte"=>"1980-01-01"}}
# options: {}
# class: Band
# embedded: false>

O Mongoid oferece suporte a três maneiras de especificar condições individuais:

  1. Sintaxe de campo.

  2. Sintaxe MQL.

  3. Sintaxe do operador de símbolos.

Todas as sintaxes suportam a query de documentos incorporados usando a notação de pontos. Todas as sintaxes respeitam os tipos de campo, se o campo que está sendo consultado estiver definido na classe do modelo e nos aliases de campo.

Os exemplos nesta seção usam a seguinte definição de modelo:

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

A sintaxe de query mais simples utiliza os hash Ruby básicos. As chaves podem ser símbolos ou strings e correspondem a nomes de campos em documentos MongoDB:

Band.where(name: "Depeche Mode")
# => #<Mongoid::Criteria
# selector: {"name"=>"Depeche Mode"}
# options: {}
# class: Band
# embedded: false>
# Equivalent to:
Band.where("name" => "Depeche Mode")

É possível especificar um operador de MQL em qualquer campo usando a sintaxe de hash:

Band.where(founded: {'$gt' => 1980})
# => #<Mongoid::Criteria
# selector: {"founded"=>{"$gt"=>1980}}
# options: {}
# class: Band
# embedded: false>
# Equivalent to:
Band.where('founded' => {'$gt' => 1980})

Os operadores MQL podem ser especificados como métodos em símbolos para o respectivo nome de campo, como segue:

Band.where(:founded.gt => 1980)
# => #<Mongoid::Criteria
# selector: {"founded"=>{"$gt"=>1980}}
# options: {}
# class: Band
# embedded: false>

Para consultar um campo, não é necessário adicionar o campo à definição da classe de modelo. No entanto, se um campo for definido na classe de modelo, o Mongoid forçará os valores de query a corresponderem aos tipos de campo definidos ao construir a query:

Band.where(name: 2020, founded: "2020")
# => #<Mongoid::Criteria
# selector: {"name"=>"2020", "founded"=>2020}
# options: {}
# class: Band
# embedded: false>

Se você quiser ignorar o comportamento de coerção de tipo de query do Mongoid e consultar diretamente o valor de tipo bruto no banco de dados, encapsule o valor de query em classe Mongoid::RawValue. Isso pode ser útil ao trabalhar com dados legados.

Band.where(founded: Mongoid::RawValue("2020"))
# => #<Mongoid::Criteria
# selector: {"founded"=>"2020"}
# options: {}
# class: Band
# embedded: false>

As queries levam em conta os nomes de campo de armazenamento e os aliases de campo:

Band.where(name: 'Astral Projection')
# => #<Mongoid::Criteria
# selector: {"n"=>"Astral Projection"}
# options: {}
# class: Band
# embedded: false>

Como os campos id e _id são aliases, qualquer um pode ser utilizado para queries:

Band.where(id: '5ebdeddfe1b83265a376a760')
# => #<Mongoid::Criteria
# selector: {"_id"=>BSON::ObjectId('5ebdeddfe1b83265a376a760')}
# options: {}
# class: Band
# embedded: false>

Para corresponder aos valores de campos de documentos incorporados, use a notação de ponto:

Band.where('manager.name' => 'Smith')
# => #<Mongoid::Criteria
# selector: {"manager.name"=>"Smith"}
# options: {}
# class: Band
# embedded: false>
Band.where(:'manager.name'.ne => 'Smith')
# => #<Mongoid::Criteria
# selector: {"manager.name"=>{"$ne"=>"Smith"}}
# options: {}
# class: Band
# embedded: false>

Observação

As queries sempre retornam instâncias de modelo de nível superior, mesmo que todas as condições estejam referenciando documentos incorporados.

O Mongoid suporta operações lógicas and, or, nor e not em objetos Criteria . Esses métodos tomam um ou mais hash de condições ou outro objeto Criteria como seus argumentos, com not adicionalmente tendo uma versão livre de argumentos.

# 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')

Para compatibilidade com versões anteriores do Mongoid, todos os métodos de operação lógica também aceitam arrays de parâmetros, que serão achatadas para obter os critérios. Passar arrays para operações lógicas é obsoleto e pode ser removido em uma versão futura do Mongoid.

As chamadas a seguir produzem as mesmas condições de query:

# 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:
# => #<Mongoid::Criteria
# selector: {"name"=>"SUN Project", "member_count"=>2}
# options: {}
# class: Band
# embedded: false>

A partir do Mongoid 7.1, os operadores lógicos (and, or, nor e not) foram alterados para ter a mesma semântica que os do ActiveRecord. Para obter a semântica de or como ela se comportou no Mongoid 7.0 e versões anteriores, use any_of , descrito abaixo.

Quando as condições são especificadas no mesmo campo várias vezes, todas as condições são adicionadas aos critérios:

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 e not comportam-se da mesma forma, com not produzindo diferentes formas de query como descrito abaixo.

Quando andoperadores lógicos, or e nor são usados, eles operam sobre os critérios construídos até aquele ponto e seu argumento. where tem o mesmo significado que and:

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

O método and adicionará novas condições simples ao nível superior dos critérios, a menos que os critérios de recebimento já tenham uma condição nos respectivos campos, caso em que as condições serão combinadas com $and.

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

A partir do Mongoid 7.1, especificar vários critérios no mesmo campo com and combina todos os critérios assim especificados, enquanto em versões anteriores de condições Mongoid em um campo às vezes substituía condições previamente especificadas no mesmo campo, dependendo de qual forma de and foi usada.

or/nor Behavior _----------------------

or e nor produzem $or e $nor operadores MongoDB, respectivamente, usando o receptor e todos os argumentos como operandos. Por exemplo:

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

Se a única condição no receptor for outra or/nor, as novas condições são adicionadas à lista existente:

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 para adicionar uma disjunção a um objeto Criteria e, ao mesmo tempo, manter todas as condições criadas até o momento.

any_of adiciona uma disjunção construída de seus argumentos às condições existentes nos critérios. Por exemplo:

Band.where(label: /Trust/).any_of({name: 'Astral Projection'}, {name: /Best/})
# => {"label"=>/Trust/, "$or"=>[{"name"=>"Astral Projection"}, {"name"=>/Best/}]}

As condições são elevadas ao nível superior, se possível:

Band.where(label: /Trust/).any_of({name: 'Astral Projection'})
# => {"label"=>/Trust/, "name"=>"Astral Projection"}

none_of adiciona uma disjunção negativa ("nor") construída a partir de seus argumentos para as condições existentes nos critérios. Por exemplo:

Band.where(label: /Trust/).none_of({name: 'Astral Projection'}, {name: /Best/})
# => {"label"=>/Trust/, "$nor"=>[{"name"=>"Astral Projection"}, {"name"=>/Best/}]}

not método pode ser chamado sem argumentos, caso em que ele negará a próxima condição que é especificada. not também pode ser chamado com uma ou mais condições de hash ou objetos Criteria , que serão todos negados e adicionados aos critérios.

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

Observação

$not no servidor MongoDB não pode ser usado com um argumento de string. O Mongoid usa o operador $ne para obter essa negação:

# 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/}}

Da mesma forma que and, not negará condições individuais para critérios de campo simples. Para condições complexas e quando um campo já tem uma condição definida nele, uma vez que o servidor MongoDB só suporta o operador $not em uma base por campo em vez de globalmente, o Mongoid emula $not usando uma construção {'$and' => [{'$nor' => ...}]} :

# 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')
# => #<Mongoid::Criteria
# selector: {"$and"=>[{"$nor"=>[{"name"=>{"$ne"=>"Astral Projection"}}]}]}
# options: {}
# class: Band
# embedded: false>

Se utilizar o not com arrays ou expressões regulares, note as advertências/limitações do $not estacionadas na documentação do servidor MongoDB.

Por padrão, quando as condições são adicionadas a uma query, o Mongoid considera cada condição completa e independente de quaisquer outras condições potencialmente presentes na query. Por exemplo, chamar in duas vezes adiciona duas condições $in separadas:

Band.in(name: ['a']).in(name: ['b'])
=> #<Mongoid::Criteria
selector: {"name"=>{"$in"=>["a"]}, "$and"=>[{"name"=>{"$in"=>["b"]}}]}
options: {}
class: Band
embedded: false>

Alguns métodos de operador permitem criar a condição de forma incremental. Nesse caso, quando uma condição em um campo que usa um dos operadores suportados estiver sendo adicionada, se já houver uma condição no mesmo campo usando o mesmo operador, as expressões do operador serão combinadas de acordo com a estratégia de mesclagem especificada.

O Mongoid oferece três estratégias de mesclagem:

  • Substituir: a nova instância do operador substitui quaisquer condições existentes no mesmo campo usando o mesmo operador.

  • Interseção: se já houver uma condição usando o mesmo operador no mesmo campo, os valores da condição existente serão cruzados com os valores da nova condição e o resultado será armazenado como o valor do operador.

  • Unidade: se já houver uma condição usando o mesmo operador no mesmo campo, os valores da nova condição serão adicionados aos valores da condição existente e o resultado será armazenado como o valor do operador.

O seguinte trecho demonstra todas as estratégias, utilizando in como o operador de exemplo:

Band.in(name: ['a']).override.in(name: ['b'])
=> #<Mongoid::Criteria
selector: {"name"=>{"$in"=>["b"]}}
options: {}
class: Band
embedded: false>
Band.in(name: ['a', 'b']).intersect.in(name: ['b', 'c'])
=> #<Mongoid::Criteria
selector: {"name"=>{"$in"=>["b"]}}
options: {}
class: Band
embedded: false>
Band.in(name: ['a']).union.in(name: ['b'])
=> #<Mongoid::Criteria
selector: {"name"=>{"$in"=>["a", "b"]}}
options: {}
class: Band
embedded: false>

A estratégia é solicitada ligando para override, intersect ou union em uma instância do Criteria. A estratégia solicitada se aplica ao próximo método de condição chamado na query. Se o próximo método de condição chamado não for compatível com estratégias de mesclagem, a estratégia será redefinida, conforme mostrado no exemplo a seguir:

Band.in(name: ['a']).union.ne(name: 'c').in(name: ['b'])
=> #<Mongoid::Criteria
selector: {"name"=>{"$in"=>["a"], "$ne"=>"c"}, "$and"=>[{"name"=>{"$in"=>["b"]}}]}
options: {}
class: Band
embedded: false>

Como ne não oferece suporte a estratégias de mesclagem, a estratégia union foi ignorada e redefinida e, quando in foi invocado pela segunda vez, não havia nenhuma estratégia ativa.

Aviso

Atualmente, as estratégias de mesclagem pressupõem que as condições anteriores tenham sido adicionadas ao nível superior da query, mas esse nem sempre é o caso (as condições podem estar aninhadas em uma cláusula $and). Usar estratégias de mesclagem com critérios complexos pode fazer com que queries incorretas sejam construídas. O plano é que esse mau comportamento seja corrigido no futuro.

Os métodos de operador a seguir oferecem suporte a estratégias de mesclagem:

  • all

  • in

  • nin

O conjunto de métodos pode ser expandido em futuras versões do Mongoid. Para compatibilidade futura, invoque um método estratégico somente quando a próxima chamada de método for um operador que ofereça suporte a estratégias de mesclagem.

Observe que, no momento, as estratégias de mesclagem são aplicadas somente quando as condições são adicionadas por meio dos métodos designados. No exemplo a seguir, a estratégia de mesclagem não é aplicada porque a segunda condição é adicionada por meio de where e não por meio de in:

Band.in(foo: ['a']).union.where(foo: {'$in' => 'b'})
=> #<Mongoid::Criteria
selector: {"foo"=>{"$in"=>["a"]}, "$and"=>[{"foo"=>{"$in"=>"b"}}]}
options: {}
class: Band
embedded: false>

Esse comportamento pode mudar em uma versão futura do Mongoid e não deve ser considerado.

Por outro lado, não importa como a query existente foi criada quando um método de operador de suporte à estratégia de mesclagem é invocado. No exemplo a seguir, a primeira condição foi adicionada por meio where mas o mecanismo de estratégia ainda se aplica:

Band.where(foo: {'$in' => ['a']}).union.in(foo: ['b'])
=> #<Mongoid::Criteria
selector: {"foo"=>{"$in"=>["a", "b"]}}
options: {}
class: Band
embedded: false>

Todos os métodos de operador que suportam estratégias de mesclagem usam Array como seu tipo de valor. O Mongoid expande os tipos compatíveis com Array, como um Range, quando são utilizados com estes métodos de operador:

Band.in(year: 1950..1960)
=> #<Mongoid::Criteria
selector: {"year"=>{"$in"=>[1950, 1951, 1952, 1953, 1954, 1955, 1956, 1957, 1958, 1959, 1960]}}
options: {}
class: Band
embedded: false>

Além disso, o Mongoid historicamente envolveu valores nãoArray em arrays, como demonstra o exemplo a seguir:

Band.in(year: 1950)
=> #<Mongoid::Criteria
selector: {"year"=>{"$in"=>[1950]}}
options: {}
class: Band
embedded: false>

Esse correspondente localiza documentos com campos de array em que um dos valores de array corresponde a todas as condições. Por exemplo:

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 também funciona com associações incorporadas:

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 não funciona com associações não incorporadas porque o MongoDB não tem participações - as condições seriam adicionadas à coleção que é a origem de uma associação não incorporada em vez da coleção do alvo da associação.c

elem_match também pode ser usado com associações recursivamente incorporadas, como mostra o exemplo a seguir:

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]

Mongoid fornece dois operadores de projeção: only e without.

O método only recupera somente os campos especificados do banco de dados. Essa operação às vezes é chamada de "projeção".

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

Tentar referenciar atributos que não foram carregados resulta em Mongoid::Errors::AttributeNotLoaded.

band.label
#=> raises Mongoid::Errors::AttributeNotLoaded

Embora o Mongoid atualmente permita a gravação em atributos que não foram carregados, essas gravações não serão mantidas (MONGOID-4701) e, portanto, devem ser evitadas.

only também pode ser usado com associações incorporadas:

band = Band.only(:name, 'tours.year').last
# => #<Band _id: 5c59afb1026d7c034dba46ac, name: "Aerosmith">
band.tours.first
# => #<Tour _id: 5c59afdf026d7c034dba46af, city: nil, year: 1995>

Observação

As versões 4.2 e inferiores do servidor permitiram projetar uma associação e os campos da associação na mesma query, da seguinte forma:

band = Band.only(:tours, 'tours.year').last

A especificação de projeção mais recente substitui a anterior. Por exemplo, a query acima era equivalente a:

band = Band.only('tours.year').last

Versões de servidor 4.4 e superior proíbem a especificação de uma associação e seus campos em projeção na mesma query.

only pode ser especificado com associações referenciadas (has_one, has_many, has_and_belongs_to_many), mas é atualmente ignorado para associações referenciadas - todos os campos de associações referenciadas serão carregados (MONGOID-4704).

Observe que, se um documento tiver has_one ou has_and_belongs_to_many associações, os campos com chaves estrangeiras deverão ser incluídos na lista de atributos carregados com only para que essas associações sejam carregadas. Por exemplo:

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
# => [#<Manager _id: 5c5dc2f0026d7c1730969843, band_ids: [BSON::ObjectId('5c5dc2f0026d7c1730969842')]>]

O oposto de only, without faz com que os campos especificados sejam omitidos:

Band.without(:name)
# =>
# #<Mongoid::Criteria
# selector: {}
# options: {:fields=>{"name"=>0}}
# class: Band
# embedded: false>

Como o Mongoid requer o campo _id para várias operações, ele (bem como seu alias id ) não pode ser omitido via without:

Band.without(:name, :id)
# =>
# #<Mongoid::Criteria
# selector: {}
# options: {:fields=>{"name"=>0}}
# class: Band
# embedded: false>
Band.without(:name, :_id)
# =>
# #<Mongoid::Criteria
# selector: {}
# options: {:fields=>{"name"=>0}}
# class: Band
# embedded: false>

O Mongoid fornece o método order em objetos Criteria e seu alias, order_by, para especificar a ordem dos documentos. Esses métodos usam um hash indicando por quais campos ordenar os documentos e se devem ser usados em ordem crescente ou decrescente para cada campo.

Band.order(name: 1)
# => #<Mongoid::Criteria
# selector: {}
# options: {:sort=>{"name"=>1}}
# class: Band
# embedded: false>
Band.order_by(name: -1, description: 1)
# => #<Mongoid::Criteria
# selector: {}
# options: {:sort=>{"name"=>-1, "description"=>1}}
# class: Band
# embedded: false>
Band.order_by(name: :desc, description: 'asc')
# => #<Mongoid::Criteria
# selector: {}
# options: {:sort=>{"name"=>-1, "description"=>1}}
# class: Band
# embedded: false>

A direção pode ser especificada como inteiros 1 e -1 para ascendente e descendente, respectivamente, ou como símbolos :asc e :desc, ou como strings "asc" e "desc".

Como alternativa, order aceita uma array de arrays de dois elementos especificando a ordem. Os nomes e as direções dos campos podem ser cadeias de caracteres ou símbolos.

Band.order([['name', 'desc'], ['description', 'asc']])
Band.order([[:name, :desc], [:description, :asc]])

Outra maneira de fornecer o pedido é utilizar os métodos #asc e #desc em símbolos, como segue:

Band.order(:name.desc, :description.asc)

Os argumentos podem ser fornecidos como uma string usando a sintaxe SQL:

Band.order('name desc, description asc')

Finalmente, existem asc e desc métodos que podem ser usados em vez de order/order_by:

Band.asc('name').desc('description')
# => #<Mongoid::Criteria
selector: {}
options: {:sort=>{"name"=>1, "description"=>-1}}
class: Band
embedded: false>

order podem ser encadeadas e, nesse caso, as chamadas mais antigas definem os critérios mais significativos e as mais recentes definem os menos significativos (já que em Ruby os hashes mantêm a ordem de suas chaves):

Band.order('name desc').order('description asc')
# => #<Mongoid::Criteria
selector: {}
options: {:sort=>{"name"=>-1, "description"=>1}}
class: Band
embedded: false>

Às vezes, isso pode levar a resultados surpreendentes se houver escopos, incluindo o escopo padrão, que usam order/order_by. Por exemplo, nas seguintes bandas de trecho são ordenadas por nome primeiro porque a ordem no escopo padrão tem precedência sobre a ordem fornecida na query, devido ao escopo padrão sendo avaliado primeiro:

class Band
include Mongoid::Document
field :name, type: String
field :year, type: Integer
default_scope -> { order(name: :asc) }
end
Band.order(year: :desc)
# => #<Mongoid::Criteria
selector: {}
options: {:sort=>{"name"=>1, "year"=>-1}}
class: Band
embedded: false>

O Mongoid fornece os operadores de paginação limit, skip e batch_size em Criteria.

limit Define o número total de documentos a serem devolvidos por uma query:

Band.limit(5)
# =>
# #<Mongoid::Criteria
# selector: {}
# options: {:limit=>5}
# class: Band
# embedded: false>

skip (alias: offset) define o número de resultados da query a ignorar antes de retornar documentos. O valor limit, se especificado, será aplicado após os documentos serem ignorados. Ao realizar a paginação, recomenda-se que skip seja combinado com a ordenação para garantir resultados consistentes.

Band.skip(10)
# =>
# #<Mongoid::Criteria
# selector: {}
# options: {:skip=>10}
# class: Band
# embedded: false>

Ao executar queries grandes ou ao iterar sobre os resultados das queries com um método enumerador como Criteria#each, o Mongoid usa automaticamente o comando getMore do MongoDB para carregar os resultados em lotes. O batch_size padrão é 1000, mas você pode defini-lo explicitamente:

Band.batch_size(500)
# =>
# #<Mongoid::Criteria
# selector: {}
# options: {:batch_size=>500}
# class: Band
# embedded: false>

O Mongoid fornece o método find em objetos Criteria para encontrar documentos por seus valores _id:

Band.find('5f0e41d92c97a64a26aabd10')
# => #<Band _id: 5f0e41d92c97a64a26aabd10, name: "Juno Reactor">

O método find realiza uma conversão de tipo, se necessário, do argumento para o tipo declarado no modelo que está sendo consultado para o campo _id. Por padrão, o tipo _id é BSON::ObjectId, portanto a query acima é equivalente a:

Band.find(BSON::ObjectId.from_string('5f0e41d92c97a64a26aabd10'))
# => #<Band _id: 5f0e41d92c97a64a26aabd10, name: "Juno Reactor">

Observação

Ao fazer query de coleções diretamente usando o driver, a conversão de tipos não é executada automaticamente:

Band.collection.find(_id: BSON::ObjectId.from_string('5f0e41d92c97a64a26aabd10')).first
# => {"_id"=>BSON::ObjectId('5f0e41d92c97a64a26aabd10'), "name"=>"Juno Reactor"}
Band.collection.find(_id: '5f0e41d92c97a64a26aabd10').first
# => nil

O método find pode aceitar vários argumentos ou uma array de argumentos. Em ambos os casos, cada um dos argumentos ou elementos de array é considerado um valor _id , e os documentos com todos os valores de _id especificados são retornados em uma array:

Band.find('5f0e41d92c97a64a26aabd10', '5f0e41b02c97a64a26aabd0e')
# => [#<Band _id: 5f0e41b02c97a64a26aabd0e, name: "SUN Project", description: nil, likes: nil>,
#<Band _id: 5f0e41d92c97a64a26aabd10, name: "Juno Reactor", description: nil, likes: nil>]
Band.find(['5f0e41d92c97a64a26aabd10', '5f0e41b02c97a64a26aabd0e'])
# => [#<Band _id: 5f0e41b02c97a64a26aabd0e, name: "SUN Project", description: nil, likes: nil>,
#<Band _id: 5f0e41d92c97a64a26aabd10, name: "Juno Reactor", description: nil, likes: nil>]

Se o mesmo valor de _id for fornecido mais de uma vez, o documento correspondente será retornado apenas uma vez:

Band.find('5f0e41b02c97a64a26aabd0e', '5f0e41b02c97a64a26aabd0e')
# => [#<Band _id: 5f0e41b02c97a64a26aabd0e, name: "SUN Project", description: nil, likes: nil>]

Os documentos retornados não são ordenados e podem ser retornados em uma ordem diferente da ordem dos valores _id fornecidos, conforme ilustrado nos exemplos acima.

Se qualquer um dos valores _id não for encontrado no banco de dados, o comportamento de find dependerá do valor da opção de configuração raise_not_found_error. Se a opção estiver definida como true, find gera Mongoid::Errors::DocumentNotFound se algum dos _ids não for encontrado. Se a opção estiver definida como false e find receber um único _id para localizar e não houver nenhum documento correspondente, find retornará nil. Se a opção estiver definida como false e find receber uma array de ids para localizar e alguns não forem encontrados, o valor de retorno será uma array de documentos que foram encontrados (que podem estar vazios se nenhum documento for encontrado).

O Mongoid também possui alguns métodos úteis sobre critérios.

(operação)
Exemplo

Criteria#count

Obtenha o número total de documentos correspondentes a um filtro ou o número total de documentos em uma coleção. Observe que isso sempre atingirá o banco de dados para a contagem.

A partir do Mongoid 7.2, o count o método usa o count_documents ajudante de driver para obter a contagem precisa. anteriormente o count foi usado um driver auxiliar que usava metadados de coleção e, portanto, não era necessariamente preciso (mas pode ter retornado o resultado mais rapidamente). Usar estimated_count método para obter rapidamente um número aproximado de documentos na coleção.

Band.count
Band.where(name: "Photek").count

Criteria#estimated_count

Obtenha um número aproximado de documentos na coleção usando os metadados da coleção. O estimated_count o método não aceita condições de query; se alguma for fornecida, ele aumentará Mongoid::Errors::InvalidEstimatedCountCriteria. Se um modelo definir um escopo padrão, estimated_count deve ser chamado no modelo sem escopo.

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

Obtenha uma lista de valores distintos para um único campo. Observe que isso sempre atingirá o banco de dados para os valores distintos.

Este método aceita a notação de ponto, permitindo assim referenciar campos em associações incorporadas.

Este método respeita :ref:'field aliases <field-aliases>', incluindo os definidos em documentos incorporados.

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

Iterar sobre todos os documentos correspondentes nos critérios.

Band.where(members: 1).each do |band|
p band.name
end

Criteria#exists?

Determinar se existem documentos correspondentes. Retornará verdadeiro se houver 1 ou mais.

#exists? agora leva vários tipos de argumentos:

  • Hash: Um hash de condições.

  • Object: Um _id para pesquisar.

  • false/nil: Sempre retorna falso.

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

Obter o quinto documento para os critérios fornecidos.

Este método adiciona automaticamente uma classificação em _id se nenhuma classificação for fornecida.

Band.fifth

Criteria#fifth!

Obter o quinto documento para os critérios fornecidos ou gere um erro se não existir nenhum.

Este método adiciona automaticamente uma classificação em _id se nenhuma classificação for fornecida.

Band.fifth!

Criteria#find_by

Encontre um documento pelos atributos fornecidos. Se não for encontrado, gere um erro ou retorne nulo dependendo do valor da opção de configuração raise_not_found_error opção de configuração.

Band.find_by(name: "Photek")
Band.find_by(name: "Tool") do |band|
band.impressions += 1
end

Criteria#find_or_create_by

Encontre um documento pelos set fornecidos e, se não for encontrado, crie e retorne um documento recém-persistente. Observe que os conjuntos fornecidos nos argumentos desse método substituirão qualquer conjunto em "create_with".

Band.find_or_create_by(name: "Photek")
Band.where(:likes.gt => 10).find_or_create_by(name: "Photek")

find_or_create_by pode ser usado em qualquer escopo, mas neste caso os critérios fornecidos pelo escopo e por find_or_create_by são combinados. O seguinte cria três bandas:

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

Encontre um documento pelos atributos fornecidos e, se não encontrado, devolva um novo.

Band.find_or_initialize_by(name: "Photek")
Band.where(:likes.gt => 10).find_or_initialize_by(name: "Photek")

Criteria#first|last

Encontra um único documento de acordo com os critérios fornecidos. Obtenha uma lista de documentos passando um argumento de limite. Este método adiciona automaticamente uma classificação em _id. Isso pode causar problemas de desempenho; portanto, se a classificação não for desejável, o Critério #take poderá ser usado em seu lugar.

Band.first
Band.where(:members.with_size => 3).first
Band.where(:members.with_size => 3).last
Band.first(2)

Criteria#first!|last!

Encontra um único documento de acordo com os critérios fornecidos ou gera um erro se nenhum for encontrado. Este método adiciona automaticamente uma classificação em _id se nenhuma classificação for fornecida. Isso pode causar problemas de desempenho, portanto, se a classificação for indesejável, Critérios#take! podem ser usados.

Band.first!
Band.where(:members.with_size => 3).first!
Band.where(:members.with_size => 3).last!

Criteria#first_or_create

Encontre o primeiro documento pelos atributos fornecidos e, se não for encontrado, crie e retorne um novo documento persistente.

Band.where(name: "Photek").first_or_create

Criteria#first_or_create!

Encontre o primeiro documento pelos atributos fornecidos e, se não for encontrado, crie e retorne um novo documento persistente usando create!.

Band.where(name: "Photek").first_or_create!

Criteria#first_or_initialize

Encontre o primeiro documento pelos atributos fornecidos e, se não encontrado, devolva um novo.

Band.where(name: "Photek").first_or_initialize

Criteria#for_js

Encontre documentos para uma expressão JavaScript fornecida; como opção, use as variáveis especificadas adicionadas ao escopo da avaliação. O argumento do escopo é aceito no MongoDB 4.2 e versões inferiores. Preferir $expr terminado for_js.

# All MongoDB versions
Band.for_js("this.name = 'Tool'")
# MongoDB 4.2 and lower
Band.for_js("this.name = param", param: "Tool")

Criteria#fourth

Obtenha o quarto documento para os critérios fornecidos.

Este método adiciona automaticamente uma classificação em _id se nenhuma classificação for fornecida.

Band.fourth

Criteria#fourth!

Obtenha o quarto documento para os critérios fornecidos ou gere um erro se não existir nenhum.

Este método adiciona automaticamente uma classificação em _id se nenhuma classificação for fornecida.

Band.fourth!

Criteria#length|size

Igual à contagem, mas armazena em cache as chamadas subsequentes ao banco de dados

Band.length
Band.where(name: "FKA Twigs").size

Criteria#pick

Obtenha os valores de um documento para os campos fornecidos. Retorna zero para campos não definidos e para campos inexistentes.

Esse método não aplica uma classificação aos documentos, portanto, não necessariamente retornará os valores do primeiro documento.

Este método aceita a notação de ponto, permitindo assim referenciar campos em associações incorporadas.

Este método respeita :ref:'field aliases <field-aliases>', incluindo os definidos em documentos incorporados.

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

Obtenha todos os valores para o campo fornecido. Retorna nulo para campos não definidos e para campos inexistentes.

Este método aceita a notação de ponto, permitindo assim referenciar campos em associações incorporadas.

Este método respeita :ref:'field aliases <field-aliases>', incluindo os definidos em documentos incorporados.

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

Define a preferência de leitura para os critérios.

Band.all.read(mode: :primary)

Criteria#second

Obtenha o segundo documento para os critérios fornecidos.

Este método adiciona automaticamente uma classificação em _id se nenhuma classificação for fornecida.

Band.second

Criteria#second!

Obtenha o segundo documento para os critérios fornecidos ou gere um erro se não existir nenhum.

Este método adiciona automaticamente uma classificação em _id se nenhuma classificação for fornecida.

Band.second!

Criteria#second_to_last

Obtenha o penúltimo documento de acordo com os critérios fornecidos.

Este método adiciona automaticamente uma classificação em _id se nenhuma classificação for fornecida.

Band.second_to_last

Criteria#second_to_last!

Obtenha o penúltimo documento de acordo com os critérios fornecidos ou gere um erro se não existir nenhum.

Este método adiciona automaticamente uma classificação em _id se nenhuma classificação for fornecida.

Band.second_to_last!

Criteria#take

Obtenha uma lista de n documentos do banco de dados ou apenas um se nenhum parâmetro for fornecido.

Este método não aplica uma classificação aos documentos, portanto pode retornar documentos diferentes de #first e #last.

Band.take
Band.take(5)

Criteria#take!

Obtenha um documento do banco de dados ou gere um erro se não existir nenhum.

Este método não aplica uma classificação aos documentos, portanto pode retornar documentos diferentes de #first e #last.

Band.take!

Criteria#tally

Obtenha um mapeamento de valores para contagens para o campo fornecido.

Este método aceita a notação de ponto, permitindo assim referenciar campos em associações incorporadas.

Este método respeita :ref:'field aliases <field-aliases>', incluindo os definidos em documentos incorporados.

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

Obtenha o terceiro documento para os critérios fornecidos.

Este método adiciona automaticamente uma classificação em _id se nenhuma classificação for fornecida.

Band.third

Criteria#third!

Obtenha o terceiro documento para os critérios fornecidos ou gere um erro se não existir nenhum.

Este método adiciona automaticamente uma classificação em _id se nenhuma classificação for fornecida.

Band.third!

Criteria#third_to_last

Obtenha o penúltimo documento de acordo com os critérios fornecidos.

Este método adiciona automaticamente uma classificação em _id se nenhuma classificação for fornecida.

Band.third_to_last

Criteria#third_to_last!

Obtenha o penúltimo documento para os critérios fornecidos ou gere um erro se nenhum existir.

Este método adiciona automaticamente uma classificação em _id se nenhuma classificação for fornecida.

Band.third_to_last!

O Mongoid fornece uma facilidade para carregar documentos de associações para evitar o problema n+1 ao iterar sobre documentos com acesso de associação. A carga ávida é suportada em todas as associações, com exceção das associações polimórficas belongs_to.

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

MongoDB e Mongoid permitem fazer query de documentos por expressões regulares.

Dadas as seguintes definições de modelo:

class Band
include Mongoid::Document
field :name, type: String
field :description, type: String
end
Band.create!(name: 'Sun Project', description: "Sun\nProject")

...podemos fazer queries utilizando expressões regulares Ruby de forma natural:

Band.where(name: /project/i).first
# => #<Band _id: 5dc9f7d5ce4ef34893354323, name: "Sun Project", description: "Sun\nProject">

Também é possível fazer query usando a sintaxe PCRE construindo BSON::Regexp::Raw objetos explicitamente:

Band.where(description: /\AProject/).first
# => #<Band _id: 5dc9f7d5ce4ef34893354323, name: "Sun Project", description: "Sun\nProject">
Band.where(description: BSON::Regexp::Raw.new('^Project')).first
# => nil
Band.where(description: BSON::Regexp::Raw.new('^Project', 'm')).first
# => #<Band _id: 5dc9f7d5ce4ef34893354323, name: "Sun Project", description: "Sun\nProject">

Quando uma condição utiliza um campo definido no modelo, o valor que está sendo especificado na condição é convertido de acordo com as regras do campo, se houver. Por exemplo, considere a seguinte definição de modelo que contém um campo Time , um campo Date e um campo Object implícito, e também intencionalmente não define um campo chamado deregistered_at:

class Voter
include Mongoid::Document
field :born_on, type: Date
field :registered_at, type: Time
field :voted_at
end

As queries nos campos born_on e registered_at utilizando valores Date e Time, respectivamente, são diretas:

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}

Mas observe as diferenças no comportamento ao fornecer uma instância do Date em todos os cenários possíveis:

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}

Ao usar o campo registered_at que é do tipo Time, a data foi interpretada como sendo na hora local (de acordo com o zona configurada). Ao utilizar o campo born_on que é do tipo Date, a data foi interpretada para estar em UTC. Ao usar o campo voted_at que foi definido sem um tipo (portanto, implicitamente como um Object), a data foi usada sem modificações na query construída. Ao usar um campo inexistente deregistered_at a data foi interpretada como sendo em UTC e convertida em uma hora, correspondendo ao comportamento de fazer query de um campo Date .

Os escopos fornecem uma maneira conveniente de reutilizar critérios comuns com mais sintaxe de estilo de domínio comercial.

Os escopos nomeados são simplesmente critérios definidos na carga de classe que são referenciados por um nome fornecido. Assim como os critérios normais, eles são preguiçosos e acorrentados.

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.

Escopos nomeados podem levar procs e blocos para aceitar parâmetros ou estender a funcionalidade.

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.

Por padrão, o Mongoid permite definir um escopo que sombrearia um método de classe existente, como mostra o exemplo a seguir:

class Product
include Mongoid::Document
def self.fresh
true
end
scope :fresh, ->{ where(fresh: true) }
end

Para que o Mongoid gere um erro quando um escopo substituiria um método de classe existente, defina a opção de configuração scope_overwrite_exception como true.

Os escopos padrão podem ser úteis quando você se encontra aplicando os mesmos critérios à maioria das queries e deseja especificar esses critérios como padrão. Escopos padrão são procs que retornam objetos de critérios.

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

Especificar um escopo padrão também inicializa os campos de novos modelos para os valores fornecidos no escopo padrão, se os valores forem literais simples:

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 # => #<Band _id: 5c3f7452ce4ef378295ca5f5, name: nil, active: true, num_tours: nil>

Observe que, se um valor padrão for fornecido na definição de campo e no escopo padrão, o valor no escopo padrão terá precedência:

class Band
include Mongoid::Document
field :name, type: String
field :active, type: Boolean, default: true
default_scope ->{ where(active: false) }
end
Band.new # => #<Band _id: 5c3f74ddce4ef3791abbb088, name: nil, active: false>

Como um escopo padrão inicializa campos em novos modelos conforme descrito acima, definir um escopo padrão com uma chave pontilhada e um valor literal simples, embora possível, não é recomendado:

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
# => [ #<Band _id: 632de4ad3282a404bee1877c, tags: {"foo"=>"bar"}> ]

O Mongoid 8 permite que chaves pontilhadas sejam usadas no Mongoid e, ao criar um documento, o escopo é adicionado como uma chave pontilhada nos atributos:

Band.new.attribute
# => {"_id"=>BSON::ObjectId('632de97d3282a404bee1877d'), "tags.foo"=>"bar"}

Por outro lado, ao fazer query, o Mongoid procura um documento incorporado:

Band.create!
# => Created document: {"_id"=>BSON::ObjectId('632de48f3282a404bee1877b'), "tags.foo"=>"bar"}
Band.where
# => #<Mongoid::Criteria
selector: {"tags.foo"=>"bar"}
options: {}
class: Band
embedded: false>
# This looks for something like: { tags: { "foo" => "bar" } }
Band.count
# => 0

Uma solução alternativa é definir o escopo padrão como uma query complexa:

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

Você pode dizer ao Mongoid que não aplique o escopo padrão usando unscoped, que pode ser embutido ou ter um bloco.

Band.unscoped.where(name: "Depeche Mode")
Band.unscoped do
Band.where(name: "Depeche Mode")
end

Você também pode dizer ao Mongoid para aplicar explicitamente o escopo padrão novamente mais tarde para garantir que ele esteja sempre lá.

Band.unscoped.where(name: "Depeche Mode").scoped

Se estiver usando um escopo padrão em um modelo que faz parte de uma associação, será necessário recarregar a associação para que o escopo seja reaplicado. É importante observar que, se você alterar um valor de um documento na associação, isso afetará sua visibilidade dentro da associação com escopo.

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 # []

Observação

Depois que o escopo padrão for aplicado, ele não será mais distinguido de outras condições de query. Isso pode levar a um comportamento surpreendente ao usar os operadores or e nor em particular:

class Band
include Mongoid::Document
field :name
field :active
field :touring
default_scope ->{ where(active: true) }
end
Band.where(name: 'Infected Mushroom')
# =>
# #<Mongoid::Criteria
# selector: {"active"=>true, "name"=>"Infected Mushroom"}
# options: {}
# class: Band
# embedded: false>
Band.where(name: 'Infected Mushroom').or(touring: true)
# =>
# #<Mongoid::Criteria
# selector: {"$or"=>[{"active"=>true, "name"=>"Infected Mushroom"}, {"touring"=>true}]}
# options: {}
# class: Band
# embedded: false>
Band.or(touring: true)
# =>
# #<Mongoid::Criteria
# selector: {"$or"=>[{"active"=>true}, {"touring"=>true}]}
# options: {}
# class: Band
# embedded: false>

No último exemplo, você pode esperar que as duas condições (active: true e touring: true) sejam combinadas com um $and, mas como a classe Band já tem o escopo aplicado a ela, ela se torna uma das ramificações de disjunção do or.

Você pode usar o método with_scope para alterar o escopo padrão em um bloco no tempo de execução:

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
# =>
# #<Mongoid::Criteria
# selector: {"country"=>"England"}
# options: {}
# class: Band
# embedded: false>

Observação

Se as chamadas with_scope forem aninhadas, quando o bloqueio with_scope aninhado concluir o Mongoid 7 define o escopo atual como nulo em vez do escopo pai. O Mongoid 8 definirá o escopo atual para o escopo pai correto. Para obter o comportamento Mongoid 8 no Mongoid 7.4 e superior, defina a opção global Mongoid.broken_scoping como falsa.

Métodos de classe em modelos que retornam objetos de critérios também são tratados como escopos, e também podem ser encadeados.

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

O Mongoid suporta operações de persistência fora dos critérios em uma capacidade leve para quando você deseja realizar inserções, atualizações e exclusões de vários documentos de forma expressiva.

Aviso

As condições de pedido e paginação dos critérios, incluindo order, limit, offset e batch_size, serão ignoradas nas seguintes operações.

(operação)
Exemplo

Criteria#create

Crie um documento recentemente persistido.

Band.where(name: "Photek").create

Criteria#create!

Crie um documento recentemente persistido e gere uma exceção em caso de falha de validação.

Band.where(name: "Photek").create!

Criteria#build|new

Crie um novo documento (não salvo).

Band.where(name: "Photek").build
Band.where(name: "Photek").new

Criteria#update

Atualize os atributos do primeiro documento correspondente.

Band.where(name: "Photek").update(label: "Mute")

Criteria#update_all

Atualizar atributos de todos os documentos correspondentes.

Band.where(members: 2).update_all(label: "Mute")

Criteria#add_to_set

Executar um $addToSet em todos os documentos correspondentes.

Band.where(name: "Photek").add_to_set(label: "Mute")

Criteria#bit

Execute um $bit em todos os documentos correspondentes.

Band.where(name: "Photek").bit(likes: { and: 14, or: 4 })

Criteria#inc

Executar um $inc em todos os documentos correspondentes.

Band.where(name: "Photek").inc(likes: 123)

Criteria#pop

Executar um $pop em todos os documentos correspondentes.

Band.where(name: "Photek").pop(members: -1)
Band.where(name: "Photek").pop(members: 1)

Criteria#pull

Executar um $pull em todos os documentos correspondentes.

Band.where(name: "Tool").pull(members: "Maynard")

Criteria#pull_all

Execute um $pullAll em todos os documentos correspondentes.

Band.where(name: "Tool").
pull_all(:members, [ "Maynard", "Danny" ])

Criteria#push

Execute um $push em todos os documentos correspondentes.

Band.where(name: "Tool").push(members: "Maynard")

Criteria#push_all

Execute uma $push com $each em todos os documentos correspondentes.

Band.where(name: "Tool").
push_all(members: [ "Maynard", "Danny" ])

Criteria#rename

Executar um $rename em todos os documentos correspondentes.

Band.where(name: "Tool").rename(name: :title)

Criteria#set

Executar um $set em todos os documentos correspondentes.

Band.where(name: "Tool").set(likes: 10000)

Criteria#unset

Executar um $unset em todos os documentos correspondentes.

Band.where(name: "Tool").unset(:likes)

Criteria#delete

Exclui todos os documentos correspondentes no banco de dados.

Band.where(label: "Mute").delete

Criteria#destroy

Exclui todos os documentos correspondentes no banco de dados enquanto executa callbacks para todos. Isso carrega todos os documentos na memória e pode ser uma operação cara.

Band.where(label: "Mute").destroy

As versões 2.14 e superiores do driver Ruby MongoDB fornecem funcionalidade de cache de consulta. Quando ativado, o cache de query salva os resultados das querys de busca e agregação executadas anteriormente e os reutiliza no futuro, em vez de realizar as querys novamente, aumentando o desempenho do aplicativo e reduzindo a carga do banco de dados.

Consulte a documentação do cache de consulta do driver para obter detalhes sobre o comportamento do cache de query de driver.

O restante desta seção presume que o driver 2.14.0 ou posterior está sendo usado.

O cache de query pode ser habilitado usando o namespace do driver ou o namespace do Mongoid.

O MongoDB Ruby Driver fornece middleware para ativar automaticamente o cache de query para solicitações web do Rack e execuções de tarefas do ActiveJob. Consulte a seção Query Cache Rack Middleware na página de configuração para obter instruções.

Observe que o Query Cache Middleware não se aplica ao código executado fora de solicitações da web e/ou trabalhos.

Para ativar o cache de query manualmente para um segmento de código, use:

Mongo::QueryCache.cache do
# ...
end

O Cache de Query também pode ser ativado e desativado explicitamente, embora seja recomendável usar o formulário de bloqueio descrito acima:

begin
Mongo::QueryCache.enabled = true
# ...
ensure
Mongo::QueryCache.enabled = false
end

A chamada do método first em uma classe de modelo impõe uma classificação crescente pelo campo _id na query subjacente. Isso pode produzir um comportamento inesperado com o cache de query.

Por exemplo, ao chamar all em uma classe de modelo e depois first, seria de se esperar que a segunda query usasse os resultados em cache da primeira. No entanto, devido à classificação imposta na segunda query, ambos os métodos farão a query do banco de dados e armazenarão separadamente seus resultados em cache.

Band.all.to_a
#=> Queries the database and caches the results
Band.first
#=> Queries the database again because of the sort

Para usar os resultados em cache, ligue para all.to_a.first na classe de modelo.

O Mongoid permite executar queries de banco de dados de forma assíncrona em segundo plano. Isso pode ser benéfico quando houver necessidade de obter documentos de diferentes coleções.

Para agendar uma query assíncrona, chame o método load_async em um Criteria:

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

No exemplo acima, três queries serão agendadas para execução assíncrona. Os resultados das queries podem ser acessados como de costume:

<ul>
<%- @active_bands.each do -%>
<li><%= band.name %></li>
<%- end -%>
</ul>

Mesmo se uma query estiver programada para execução assíncrona, ela poderá ser executada de forma sincronizada na conversa do chamador. Existem três cenários possíveis dependendo de quando os resultados da query estiverem sendo acessados:

  1. Se a tarefa assíncrona agendada já tiver sido executada, os resultados serão retornados.

  2. Se a tarefa tiver sido iniciada, mas ainda não tiver sido concluída, o tópico do chamador bloqueará até que a tarefa seja concluída.

  3. Se a tarefa ainda não tiver sido iniciada, ela será removida da fila de execução e a query será executada de forma síncrona na thread do chamador.

Observação

Embora o método load_async devolva um objeto Criteria, você não deve realizar nenhuma operação neste objeto, exceto acessar os resultados da query. A query está programada para execução imediatamente após ligar para load_async, portanto, alterações posteriores no objeto Criteria podem não ser aplicadas.

As queries assíncronas são desativadas por padrão. Quando as queries assíncronas estiverem desativadas, load_async executará uma query imediatamente na thread atual, bloqueando conforme necessário. Portanto, chamando load_async nos critérios, nesse caso, é praticamente o equivalente de chamar to_a para forçar a execução da query.

Para habilitar a execução de query assíncrona, as seguintes opções de configuração devem ser definidas:

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

Voltar

Operações CRUD