Docs Menu
Docs Home
/ / /
몽고이드
/

쿼리

이 페이지의 내용

  • 조건 구문
  • 필드 구문
  • MQL 구문
  • 기호 연산자 구문
  • 필드
  • 정의된 필드에 대한 쿼리
  • 원시 값 쿼리
  • 필드 별칭
  • 내장된 문서
  • 로직 연산
  • 연산자 조합
  • and 행동
  • any_of 행동
  • none_of 행동
  • not 행동
  • 증분 쿼리 구성
  • 전략 병합하기
  • 지원되는 연산자 메서드
  • 연산자 값 확장
  • 쿼리 메서드
  • elem_match
  • 프로젝션
  • only
  • without
  • 주문 중
  • 페이지 매김
  • limit
  • skip
  • batch_size
  • 검색 기준 _id
  • 기타 쿼리 메서드
  • 선행 로딩
  • 정규 표현식
  • 필드 관련 조건
  • 범위 지정
  • 명명 범위
  • 기본 범위
  • 런타임 기본 범위 재정의
  • 클래스 메서드
  • 쿼리 + 지속성
  • 쿼리 캐시
  • 쿼리 캐시 활성화 중
  • 쿼리 캐시 자동 활성화 중
  • 쿼리 캐시 수동 활성화 중
  • 다음 항목의 결과 캐싱 중: #first
  • 비동기 쿼리
  • 비동기 쿼리 실행 구성 중

Mongoid는 ActiveRecord에서 개발된 풍부한 쿼리 DSL을 제공합니다. 간단한 쿼리는 다음과 같습니다.

Band.where(name: "Depeche Mode")

다양한 Mongoid 기능을 활용할 수 있는 보다 복잡한 쿼리는 다음과 같습니다.

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

이 쿼리 메서드는 Mongoid::Criteria 객체를 반환합니다. 이 객체는 MQL(MongoDB query language, MongoDB 쿼리 언어)에 맞게 체인화되고 유연하게 평가되는 래퍼(wrapper)입니다. 이 쿼리는 그 결과 세트가 반복될 때 실행됩니다. 예시:

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

firstlast 등의 메서드는 개별 문서를 즉시 반환합니다. 그렇지 않은 경우에는 each 또는 map 등의 메서드로 Criteria(기준) 객체를 반복해서 서버에서 문서를 검색합니다. to_a을(를) 사용하면 문자 그대로 Criteria(기준) 객체를 배열로 변환해서 문서 배열을 반환하는 쿼리를 강제로 실행할 수 있습니다.

Criteria(기준) 인스턴스에서 쿼리 메서드를 호출하면 이 메서드가 기존 조건에 새 조건이 추가된 새 Criteria(기준) 인스턴스를 다음과 같이 반환합니다.

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>

Mongoid는 개별 조건을 지정하는 다음 세 가지 방법을 지원합니다.

  1. 필드 구문

  2. MQL 구문

  3. 기호 연산자 구문

모든 구문은 점 표기법을 사용하여 내장된 문서를 쿼리할 수 있도록 지원합니다. 쿼리 대상 필드가 모델 클래스에서 정의된 경우, 모든 구문은 필드 유형과 필드 별칭을 따릅니다.

이 섹션의 예시에서는 다음과 같은 모델 정의를 사용합니다.

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

가장 간단한 쿼리 구문은 기본 Ruby 해시를 사용합니다. 키는 기호 또는 문자열일 수 있으며 MongoDB 문서의 다음 필드 이름에 해당합니다.

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

MQL 연산자는 해시 구문을 사용하여 어떤 필드에도 지정할 수 있습니다.

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

다음과 같이 각 필드 이름에 필요한 기호의 메서드로 MQL 연산자를 지정할 수 있습니다.

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

필드에 쿼리하기 위해 모델 클래스 정의에 해당 필드를 추가할 필요는 없습니다. 단, 모델 클래스에 필드가 정의되어 있는 경우에는 Mongoid가 쿼리를 구성할 때 정의된 필드 유형과 일치하도록 쿼리 값을 강제 변환합니다.

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

Mongoid의 쿼리 유형 강제 동작을 무시하고 데이터베이스에서 원시 유형 값을 직접 쿼리하려면 쿼리 값을 클래스 Mongoid::RawValue에 래핑합니다. 이 기능은 레거시 데이터로 작업할 때 유용할 수 있습니다.

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

쿼리는 다음과 같은 스토리지 필드 이름필드 별칭을 고려합니다.

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

id 필드 및 _id 필드는 별칭이므로 둘 중 한 개를 쿼리에 사용할 수 있습니다.

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

내장된 문서의 필드 값을 일치시키려면 점 표기법을 사용하세요.

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>

참고

쿼리는 모든 조건이 내장된 문서를 참조하는 경우에도 항상 최상위 모델 인스턴스를 반환합니다.

Mongoid는 Criteria 객체에 대한 and, or, nor, not 로직 연산을 지원합니다. 이 메서드들은 한 개 이상의 조건 해시 또는 다른 Criteria 객체를 인수로 사용하는데, 이때 not에는 인수가 없는 버전이 추가로 있습니다.

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

모든 로직 연산 메서드는 이전 버전의 Mongoid와 호환될 수 있도록 매개변수 배열도 허용합니다. 이 배열은 기준을 획득하기 위해 평면화됩니다. 배열을 로직 연산으로 전달하는 기능은 더 이상 사용되지 않으며, 이후 버전의 Mongoid에서 제거될 수 있습니다.

다음 호출은 모두 동일한 쿼리 조건을 생성합니다.

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

Mongoid 부터 7.1 논리 연산자(and, or, nor ,not)가 ActiveRecord 의 와 동일한 의미 체계를 갖도록 변경되었습니다. . Mongoid 7 에서 동작하는 or 의 시맨틱을 구합니다.0 이전 버전에서는 아래에 설명된 any_of 를 사용하세요.

동일한 필드에 조건을 여러 번 지정하면 모든 조건이 다음 기준에 추가됩니다.

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, nornot은(는) 서로 유사하게 동작하며 not은(는) 아래 설명대로 다양한 쿼리 형태를 생성합니다.

and, ornor 로직 연산자를 사용하면 해당 점까지 구성된 기준과 그 인수대로 연산됩니다. where의 의미는 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"}

수신 기준의 각 필드에 이미 조건이 있는 경우를 제외하고, and 메서드는 기준의 최상위 수준에 간단한 새 조건을 추가합니다. 이 경우 해당 조건은 $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"}]}

Mongoid 7.1부터 동일한 필드에 대한 여러 기준을 and (으)로 지정하면 이렇게 지정된 기준이 전부 합쳐집니다. 한편 이전 버전의 Mongoid 조건에서는 사용된 and의 형식에 따라 필드 대상 조건이 동일한 필드에 대해 이전에 지정된 조건을 종종 대체하기도 했습니다.

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

ornor은(는) 수신자와 모든 인수를 피연산자로 사용해서 $or$nor MongoDB 연산자를 각각 생성합니다. 예시:

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

수신자의 유일한 조건이 또 하나의 or/nor인 경우에는 새 조건이 기존 목록에 추가됩니다.

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

지금까지 구성된 모든 조건을 그대로 유지하면서 Criteria(기준) 객체에 분리를 추가하려면 any_of을(를) 사용하세요.

any_of 인수로부터 구성된 분리를 기준의 기존 조건에 추가합니다. 예시:

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

이 조건은 가능하다면 최상위 레벨로 올립니다.

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

none_of 인수로부터 구성된 부정 분리("nor")를 기준의 기존 조건에 추가합니다. 예시:

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

not 인수 없이 메서드를 호출할 수 있습니다. 이 경우 지정된 다음 조건은 무효화됩니다. 한 개 이상의 해시 조건 또는 Criteria 객체를 사용하여 not을(를) 호출할 수도 있습니다. 이 경우 조건은 모두 무효화되어 기준에 추가됩니다.

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

참고

$not MongoDB 서버에서는 문자열 인수와 함께 사용할 수 없습니다. Mongoid는 $ne 연산자를 사용해서 이러한 부정을 실행합니다.

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

and와(과) 마찬가지로 not은 간단한 필드 기준의 개별 조건을 무효화합니다. 조건이 복잡하고 필드에 이미 조건이 지정되어 있는 경우, MongoDB 서버는 전역이 아닌 필드 단위로만 $not 연산자를 지원합니다. 따라서 Mongoid는 {'$and' => [{'$nor' => ...}]} 구문을 사용하여 $not을(를) 에뮬레이션합니다.

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

배열 또는 정규 표현식과 함께 not을(를) 사용할 경우 MongoDB 서버 설명서에 명시된 $not의 주의사항/제한사항에 유의하세요.

Mongoid는 쿼리에 조건을 추가할 때, 기본적으로 각 조건을 완전하면서도 쿼리에 있을 가능성이 있는 기타 조건과 무관한 것으로 간주합니다. 그 예로 in을(를) 두 번 호출하면 개별 $in 조건이 두 개 추가됩니다.

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

일부 연산자 메서드는 조건을 점진적으로 구성할 수 있도록 지원합니다. 이 경우 지원되는 연산자 중 한 개를 사용하는 필드에 조건을 추가하려고 할 때, 동일한 연산자를 사용하는 동일한 필드에 이미 조건이 있는 경우에는 지정된 병합 전략에 따라 연산자 표현식이 합쳐집니다.

Mongoid는 다음 세 가지 병합 전략을 제공합니다.

  • Override(재정의): 새 연산자 인스턴스가 동일한 연산자를 사용하여 동일한 필드의 기존 조건을 모두 대체합니다.

  • Intersect(교차): 동일한 필드에 동일한 연산자를 사용하는 조건이 이미 있는 경우, 기존 조건의 값이 새 조건의 값과 교차되고 그 결과가 연산자 값으로 저장됩니다.

  • Union(합집합): 동일한 필드에 동일한 연산자를 사용하는 조건이 이미 있는 경우, 새 조건의 값이 기존 조건의 값에 추가되고 그 결과가 연산자 값으로 저장됩니다.

다음 스니펫은 in을(를) 예시 연산자로 사용하여 모든 전략을 보여줍니다.

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>

전략은 Criteria 인스턴스에서 override, intersect 또는 union을 호출해서 요청합니다. 요청된 전략은 쿼리에서 호출되는 다음 조건 메서드에 적용됩니다. 호출된 다음 조건 메서드가 병합 전략을 지원하지 않으면 다음 예시와 같이 전략이 재설정됩니다.

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>

ne이(가) 병합 전략을 지원하지 않으므로 union 전략을 무시 및 재설정했습니다. 두 번째로 in을(를) 호출되었을 때는 어떤 전략도 활성화되지 않았습니다.

경고

병합 전략은 현재 이전 조건이 쿼리의 최상위 수준에 추가되었다고 가정하지만, 항상 그런 것은 아닙니다(조건이 $and 절 아래에 중첩될 수도 있음). 복잡한 기준이 있는 병합 전략을 사용하면 잘못된 쿼리가 구성될 수 있습니다. 이 오작동은 향후 수정될 예정입니다.

병합 전략을 지원하는 연산자 메서드는 다음과 같습니다.

  • all

  • in

  • nin

이후의 Mongoid 릴리스에서 메서드 세트를 확장할 수 있습니다. 앞으로 호환성을 확보하려면 다음 메서드 호출이 병합 전략을 지원하는 연산자인 경우에만 전략 메서드를 호출하세요.

현재 병합 전략은 지정된 메서드를 통해 조건이 추가된 경우에만 적용됩니다. 다음 예시에서는 두 번째 조건이 in이(가) 아닌 where을(를) 통해 추가되기 때문에 병합 전략이 적용되지 않습니다.

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

이 동작은 이후의 Mongoid 릴리스에서 변경될 수 있으므로 의존해서는 안 됩니다.

반면에 병합 전략을 지원하는 연산자 메서드가 호출되면 기존 쿼리가 어떻게 구성되었는지는 중요하지 않습니다. 다음 예시에서는 첫 번째 조건이 where을(를) 통해 추가되었지만 전략 메커니즘이 계속 적용됩니다.

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

병합 전략을 지원하는 연산자 메서드는 모두 Array을(를) 값 유형으로 사용합니다. Mongoid는 Array 호환 유형(예: Range)을 다음 연산자 메서드와 함께 사용할 때 확장합니다.

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>

이외에도 Mongoid는 다음 예시와 같이 배열에 속한 Array 외 값을 역사적으로 래핑했습니다.

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

이 매칭 프로그램은 배열 값 중 한 개가 모든 조건과 일치하는 배열 필드가 포함된 문서를 검색합니다. 예시:

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 포함 연결에서도 작동합니다.

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 MongoDB에 결합이 없기 때문에 비포함 연결에서는 작동하지 않습니다. 해당 조건은 비포함 연결의 대상인 컬렉션 대신 비포함 연결의 소스인 컬렉션에 추가됩니다.

elem_match 다음 예시와 같이 포함 연결과 함께 재귀적으로 사용할 수도 있습니다.

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는 두 가지 프로젝션 연산자, onlywithout을(를) 제공합니다.

only 메서드는 데이터베이스에서 지정된 필드만 검색합니다. 이 연산을 "프로젝션"이라고도 합니다.

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

로드되지 않은 속성을 참조하려고 하면 Mongoid::Errors::AttributeNotLoaded이(가) 발생합니다.

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

Mongoid가 현재 로드되지 않은 속성에 대한 쓰기를 허용하고는 있지만, 이러한 쓰기는 지속되지 않으므로(MONGOID-4701) 피해야 합니다.

only 포함 연결과 함께 사용할 수도 있습니다.

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

참고

4.2 이하의 서버 버전에서는 다음과 같이 동일한 쿼리에서 연결과 해당 연결의 필드를 둘 다 프로젝션할 수 있었습니다.

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

가장 최근의 프로젝션 사양이 이전의 프로젝션 사양보다 우선합니다. 그 예로 위 쿼리는 다음과 동일했습니다.

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

4.4 이상의 서버 버전에서는 동일한 쿼리에서 프로젝션에 연결과 해당 연결의 필드를 지정하는 것이 금지됩니다.

only 참조 연결(has_one, has_many, has_and_belongs_to_many)과 함께 지정할 수 있지만 현재는 참조 연결에 대해 무시됩니다. 참조 연결의 모든 필드가 로드됩니다(MONGOID-4704).

문서에 has_one 연결 또는 has_and_belongs_to_many 연결이 있는 경우, 해당 연결을 로드하려면 외부 키가 있는 필드를 only(으)로 로드한 속성의 목록에 포함시켜야 합니다. 예시:

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

only의 반대인 without을(를) 사용하면 지정된 필드가 생략됩니다.

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

Mongoid의 다양한 연산에는 _id 필드가 필요합니다. 따라서 without을(를) 통해 이 필드(와 그 id 별칭)를 생략할 수는 없습니다.

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>

Mongoid는 Criteria 객체에 order 메서드와 해당 메서드의 별칭인 order_by을(를) 제공하여 문서의 순서를 지정합니다. 이 메서드는 문서의 정렬 기준으로 사용할 필드와 각 필드에 오름차순 또는 내림차순을 사용할지 여부를 나타내는 해시를 사용합니다.

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>

방향은 오름차순과 내림차순 각각에 대해 정수인 1-1로, 기호인 :asc:desc로, 혹은 문자열인 "asc""desc"(으)로 지정할 수 있습니다.

이외에도 order은(는) 순서를 지정하는 2요소 배열 중 한 가지 배열을 허용합니다. 필드의 이름과 방향은 문자열 또는 기호일 수 있습니다.

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

순서를 제공하는 또 하나의 방법은 다음과 같이 기호에 #asc 메서드 및 #desc 메서드를 사용하는 것입니다.

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

인수는 SQL 구문을 사용하여 문자열로 제공할 수 있습니다.

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

마지막으로, order/order_by 대신 사용할 수 있는 asc 메서드 및 desc 메서드가 있습니다.

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

order 호출은 체인화할 수 있습니다. 이 경우 가장 오래된 호출은 중요도가 가장 높은 기준을 정의하고, 가장 최신인 호출은 중요도가 가장 낮은 기준을 정의합니다(Ruby 해시에서 키의 순서가 유지되기 때문).

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

order/order_by을(를) 사용하는 범위(기본 범위 포함)가 있는 경우에는 이로 인해 때때로 놀라운 결과가 발생하기도 합니다. 그 예로 다음 스니펫 밴드는 먼저 이름별로 정렬됩니다. 이는 기본 범위가 먼저 평가를 거침으로써 기본 범위의 순서가 쿼리에서 지정된 순서보다 우선적으로 적용되기 때문입니다.

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>

Mongoid는 Criteria에 페이지 매김 연산자인 limit, skip, batch_size을(를) 제공합니다.

limit 쿼리로 반환할 문서의 총 개수를 설정합니다.

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

skip (별칭: offset)(은)는 문서가 반환되기 전에 건너뛸 쿼리 결과의 수를 설정합니다. limit 값은 지정된 경우 문서를 건너뛴 후에 적용됩니다. 페이지 매김을 수행할 때 일관적인 결과를 얻으려면 skip을(를) 순서 지정과 함께 사용하는 것이 좋습니다.

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

대규모 쿼리를 실행하거나 Criteria#each 등의 열거자 메서드로 쿼리 결과를 반복할 경우, Mongoid는 자동으로 MongoDB getMore 명령을 사용하여 결과를 배치 형태로 로드합니다. 기본 batch_size는 1000이지만 명시적으로 설정할 수도 있습니다.

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

Mongoid는 Criteria 객체에 대해 find 메서드를 제공하여 _id 값을 기준으로 문서를 검색합니다.

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

find 메서드는 필요한 경우 _id 필드에 대해 쿼리되는 모델에서 선언된 유형으로 인수의 유형 변환을 수행합니다. _id 유형은 기본적으로 BSON::ObjectId에 해당합니다. 따라서 위 쿼리는 다음과 동일합니다.

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

참고

이 드라이버를 사용해서 컬렉션을 직접 쿼리할 경우에는 유형 변환이 자동으로 수행되지 않습니다.

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

find 메서드는 여러 개의 인수 또는 인수의 배열을 허용할 수 있습니다. 두 경우 모두 각각의 인수 또는 배열 요소가 _id 값으로 간주되고, 지정된 _id 값이 모두 포함된 문서가 배열 형태로 반환됩니다.

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

동일한 _id 값이 두 번 이상 제공될 경우 해당 문서는 한 번만 반환됩니다.

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

반환될 문서는 그 순서가 지정되지 않으며, 위 예시에 나온 대로 제공된 _id 값의 순서와 다른 순서로 반환될 수 있습니다.

데이터베이스에서 어떤 _id 값도 찾을 수 없는 경우 find의 동작은 raise_not_found_error 구성 옵션의 값에 따라 달라집니다. 옵션이 true로 설정된 경우 find_id를 찾을 수 없으면 Mongoid::Errors::DocumentNotFound를 발생시킵니다. 옵션이 false로 설정되어 있고 find에 찾을 _id 하나가 주어졌으며 일치하는 문서가 없는 경우 findnil을 반환합니다. 옵션이 false로 설정되어 있고 find에 찾을 ID 배열이 주어졌으며 일부가 발견되지 않은 경우 반환 값은 찾은 문서의 배열입니다(문서가 전혀 발견되지 않았다면 비어 있을 수 있음).

Mongoid의 기준에 유용한 메서드도 몇 가지 있습니다.

작업
예시

Criteria#count

필터와 일치하는 문서의 총 개수 또는 컬렉션 내 문서의 총 개수를 구하세요. 이렇게 하면 계수에 대해 항상 데이터베이스에서 충돌이 발생합니다.

Mongoid 7.2부터 count 메서드는 count_documents 드라이버 헬퍼를 사용하여 정확한 수를 구합니다. 이전까지는 count 드라이버 헬퍼가 사용되었는데, 이 헬퍼는 컬렉션 메타데이터를 사용했다는 점에서 반드시 정확하지만은 않았습니다(다만 결과를 더 빨리 반환했을 수는 있음). 메서드를 estimated_count 사용하여 컬렉션의 대략적인 문서 수를 빠르게 구하세요.

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

Criteria#estimated_count

컬렉션 메타데이터를 사용하여 컬렉션의 대략적인 문서 수를 구하세요. 이 estimated_count 메서드는 쿼리 조건을 허용하지 않습니다. 어떤 조건이든 지정된다면 해당 메서드가 상승합니다 Mongoid::Errors::InvalidEstimatedCountCriteria. 모델이 기본 범위를 정의하는 경우, 범위가 지정되지 않은 모델에 대해 estimated_count을(를) 호출해야 합니다.

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

단일 필드에 대한 고유 값 목록을 가져오세요. 이렇게 하면 고유 값에 대해 항상 데이터베이스에서 충돌이 발생합니다.

이 메서드는 점 표기법을 허용하므로 포함 연결에서 필드 참조를 허용합니다.

이 메서드는 내장된 문서에서 정의된 항목이 포함된 :ref:`field aliases <field-aliases>`를 준수합니다.

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

이 기준에 일치하는 모든 문서를 반복하세요.

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

Criteria#exists?

일치하는 문서가 있는지 확인하세요. 개수가 1개 이상이면 true를 반환합니다.

#exists? 이제 인수 유형을 다음과 같이 여러 개로 사용할 수 있습니다.

  • Hash: 조건의 해시입니다.

  • Object: 검색할 _id입니다.

  • false/nil: 항상 false를 반환합니다.

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

지정된 기준에 맞는 다섯 번째 문서를 가져오세요.

이 메서드는 정렬을 실행하지 않을 경우 _id를 기준으로 한 정렬을 자동으로 추가합니다.

Band.fifth

Criteria#fifth!

지정된 기준에 맞는 다섯 번째 문서를 가져오세요. 문서가 아무것도 없다면 오류를 제기하세요.

이 메서드는 정렬을 실행하지 않을 경우 _id를 기준으로 한 정렬을 자동으로 추가합니다.

Band.fifth!

Criteria#find_by

제공된 속성을 기준으로 문서를 찾으세요. 찾을 수 없다면 오류를 제기하거나, 구성 옵션의 값에 따라 nil을 raise_not_found_error 반환하세요.

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

Criteria#find_or_create_by

제공된 속성을 기준으로 문서를 찾으세요. 찾을 수 없다면 새 영구 문서를 생성해서 반환하세요. 이 메서드의 인수로 제공된 속성이 ``create_with``의 모든 집합을 재정의합니다.

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

find_or_create_by 어떤 범위에서도 사용할 수 있습니다. 다만 이 경우에는 해당 범위에서 지정한 기준과 find_or_create_by(으)로 지정된 기준이 합쳐집니다. 다음 항목은 밴드 세 개를 생성합니다.

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

제공된 속성을 기준으로 문서를 찾으세요. 찾을 수 없다면 새 문서를 반환하세요.

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

Criteria#first|last

제공된 기준에 따라 단일 문서를 찾으세요. limit 인수를 전달하여 문서 목록을 가져오세요. 이 메서드는 _id를 기준으로 한 정렬을 자동으로 추가합니다. 이는 성능 문제를 유발할 수 있습니다. 따라서 정렬이 바람직하지 않은 경우에는 Criteria#take를 대신 사용해도 됩니다.

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

Criteria#first!|last!

제공된 기준에 따라 단일 문서를 찾으세요. 아무것도 찾을 수 없다면 오류를 제기하세요. 이 메서드는 정렬을 실행하지 않을 경우 _id를 기준으로 한 정렬을 자동으로 추가합니다. 이는 성능 문제를 유발할 수 있습니다. 따라서 정렬이 바람직하지 않은 경우에는 Criteria#take!를 대신 사용해도 됩니다.

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

Criteria#first_or_create

제공된 속성을 기준으로 첫 번째 문서를 찾으세요. 찾을 수 없다면 새 영구 문서를 생성해서 반환하세요.

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

Criteria#first_or_create!

제공된 속성을 기준으로 첫 번째 문서를 찾으세요. 찾을 수 없다면 다음 항목을 사용하여 새 영구 문서를 생성해서 반환하세요. create!.

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

Criteria#first_or_initialize

제공된 속성을 기준으로 첫 번째 문서를 찾으세요. 찾을 수 없다면 새 문서를 반환하세요.

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

Criteria#for_js

선택에 따라 지정된 변수를 평가 범위에 추가하여 제공된 JavaScript 표현식에 해당하는 문서를 찾으세요. 범위 인수는 MongoDB 4.2 이하에서 지원됩니다. Prefer $expr 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

지정된 기준에 맞는 네 번째 문서를 가져오세요.

이 메서드는 정렬을 실행하지 않을 경우 _id를 기준으로 한 정렬을 자동으로 추가합니다.

Band.fourth

Criteria#fourth!

지정된 기준에 맞는 네 번째 문서를 가져오세요. 문서가 아무것도 없다면 오류를 제기하세요.

이 메서드는 정렬을 실행하지 않을 경우 _id를 기준으로 한 정렬을 자동으로 추가합니다.

Band.fourth!

Criteria#length|size

개수가 동일하더라도 데이터베이스에 대한 후속 호출을 캐시합니다.

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

Criteria#pick

한 문서에서 제공된 필드에 대한 값을 구하세요. 미설정 필드와 무존재 필드에 대해서는 nil을 반환합니다.

이 메서드는 문서에 정렬을 적용하지 않으므로, 첫 번째 문서의 값을 반드시 반환하지는 않습니다.

이 메서드는 점 표기법을 허용하므로 포함 연결에서 필드 참조를 허용합니다.

이 메서드는 내장된 문서에서 정의된 항목이 포함된 :ref:`field aliases <field-aliases>`를 준수합니다.

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

제공된 필드의 값을 모두 구하세요. 미설정 필드와 무존재 필드에 대해서는 nil을 반환합니다.

이 메서드는 점 표기법을 허용하므로 포함 연결에서 필드 참조를 허용합니다.

이 메서드는 내장된 문서에서 정의된 항목이 포함된 :ref:`field aliases <field-aliases>`를 준수합니다.

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

기준을 위한 읽기 설정을 설정합니다.

Band.all.read(mode: :primary)

Criteria#second

지정된 기준에 맞는 두 번째 문서를 가져오세요.

이 메서드는 정렬을 실행하지 않을 경우 _id를 기준으로 한 정렬을 자동으로 추가합니다.

Band.second

Criteria#second!

지정된 기준에 맞는 두 번째 문서를 가져오세요. 문서가 아무것도 없다면 오류를 제기하세요.

이 메서드는 정렬을 실행하지 않을 경우 _id를 기준으로 한 정렬을 자동으로 추가합니다.

Band.second!

Criteria#second_to_last

지정된 기준에 맞는 끝에서 두 번째 문서를 가져오세요.

이 메서드는 정렬을 실행하지 않을 경우 _id를 기준으로 한 정렬을 자동으로 추가합니다.

Band.second_to_last

Criteria#second_to_last!

지정된 기준에 맞는 끝에서 두 번째 문서를 가져오세요. 문서가 아무것도 없다면 오류를 제기하세요.

이 메서드는 정렬을 실행하지 않을 경우 _id를 기준으로 한 정렬을 자동으로 추가합니다.

Band.second_to_last!

Criteria#take

데이터베이스에서 n개 문서의 목록을 가져오세요. 매개변수가 제공되지 않았다면 문서를 1개만 가져오세요.

이 메서드는 문서에 정렬을 적용하지 않으므로, #first 및 #last 이외의 다른 문서를 반환할 수 있습니다.

Band.take
Band.take(5)

Criteria#take!

데이터베이스에서 문서를 한 개 가져오세요. 문서가 아무것도 없다면 오류를 제기하세요.

이 메서드는 문서에 정렬을 적용하지 않으므로, #first 및 #last 이외의 다른 문서를 반환할 수 있습니다.

Band.take!

Criteria#tally

제공된 필드에 대한 값과 계수의 매핑을 가져오세요.

이 메서드는 점 표기법을 허용하므로 포함 연결에서 필드 참조를 허용합니다.

이 메서드는 내장된 문서에서 정의된 항목이 포함된 :ref:`field aliases <field-aliases>`를 준수합니다.

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

지정된 기준에 맞는 세 번째 문서를 가져오세요.

이 메서드는 정렬을 실행하지 않을 경우 _id를 기준으로 한 정렬을 자동으로 추가합니다.

Band.third

Criteria#third!

지정된 기준에 맞는 세 번째 문서를 가져오세요. 문서가 아무것도 없다면 오류를 제기하세요.

이 메서드는 정렬을 실행하지 않을 경우 _id를 기준으로 한 정렬을 자동으로 추가합니다.

Band.third!

Criteria#third_to_last

지정된 기준에 맞는 끝에서 세 번째 문서를 가져오세요.

이 메서드는 정렬을 실행하지 않을 경우 _id를 기준으로 한 정렬을 자동으로 추가합니다.

Band.third_to_last

Criteria#third_to_last!

지정된 기준에 맞는 끝에서 번째 문서를 가져오세요. 문서가 아무것도 없다면 오류를 제기하세요.

이 메서드는 정렬을 실행하지 않을 경우 _id를 기준으로 한 정렬을 자동으로 추가합니다.

Band.third_to_last!

Mongoid는 연결 액세스 권한이 있는 문서를 반복할 때 n+1 문제가 발생하지 않도록 연결에서 문서를 선행 로드하는 기능을 제공합니다. 선행 로드는 다형성 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 및 Mongoid를 사용하면 정규 표현식으로 문서를 쿼리할 수 있습니다.

제공되는 모델 정의는 다음과 같습니다.

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

... 간단한 Ruby 정규 표현식을 사용하여 자연스럽게 쿼리할 수 있습니다.

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

BSON::Regexp::Raw 객체를 명시적으로 구성하여 PCRE 구문을 사용해 쿼리할 수도 있습니다.

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

조건이 모델에서 정의된 필드를 사용할 경우, 이 조건에 지정된 값은 해당 필드의 규칙(있는 경우)에 따라 변환됩니다. 그 예로 다음 모델 정의는 Time 필드, Date 필드 및 암시적 Object 필드를 포함하지 않는 동시에, deregistered_at(이)라는 필드를 의도적으로 정의하지 않습니다.

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

Date 값과 Time 값을 각각 사용하는 born_on 필드 및 registered_at 필드에 대한 쿼리는 간단합니다.

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}

단, 가능한 모든 시나리오에서 Date 인스턴스를 제공할 때는 동작이 달라집니다.

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}

유형이 Timeregistered_at 필드를 사용하는 경우, 이 날짜는 현지 시간(구성된 시간대에 따름)에 맞게 해석되었습니다. 유형이 Dateborn_on 필드를 사용하는 경우, 이 날짜는 UTC에 맞게 해석되었습니다. 유형 없이 정의된(이에 따라 암시적으로 Object(으)로 정의됨) voted_at 필드를 사용하는 경우, 이 날짜는 구성된 쿼리에서 수정되지 않은 상태로 사용되었습니다. 무존재 필드인 deregistered_at을(를) 사용하는 경우에는 이 날짜가 UTC에 맞게 해석되면서 Date 필드를 쿼리하는 동작과 일치하는 시간으로 변환되었습니다.

범위는 더 많은 비즈니스 도메인 스타일 구문으로 공통 기준을 재사용할 수 있는 편리한 방법을 제공합니다.

명명 범위는 제공된 이름을 기준으로 참조되는 클래스 로드에서 정의된 기준일 뿐입니다. 이 범위는 일반 기준과 마찬가지로 지연적이고 연쇄적입니다.

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.

명명 범위는 매개변수를 허용하거나 기능을 확장하기 위한 절차 및 차단을 사용할 수 있습니다.

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.

Mongoid는 기본적으로 다음 예시와 같이 기존 클래스 메서드를 섀도잉하는 범위를 지정할 수 있습니다.

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

범위가 기존 클래스 메서드를 덮어쓸 때 Mongoid가 오류를 제기하게 하려면 scope_overwrite_exception 구성 옵션true(으)로 설정하세요.

기본 범위는 대부분의 쿼리에 동일한 조건을 적용하고 이 조건을 기본값으로 지정하려는 경우에 유용할 수 있습니다. 기본 범위는 기준 객체를 반환하는 절차입니다.

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

기본 범위를 지정하면 값이 단순 리터럴인 경우, 새 모델의 필드가 기본 범위에서 지정된 값으로 초기화되기도 합니다.

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>

필드 정의와 기본 범위 양쪽에 기본값이 제공되는 경우에는 기본 범위의 값이 우선적으로 적용됩니다.

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>

기본 범위는 앞서 설명한 대로 새 모델의 필드를 초기화합니다. 그러므로 점선 키와 단순 리터럴 값으로 기본 범위를 정의하는 것은 가능한 일이지만, 권장되지 않습니다.

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

Mongoid 8을 사용하면 Mongoid에서 점선 키를 사용할 수 있으며, 문서 생성 시 범위가 속성의 점선 키로 추가됩니다.

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

한편 Mongoid는 쿼리 실행 시 내장된 문서를 검색합니다.

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

해결 방법은 기본 범위를 복잡한 쿼리로 정의하는 것입니다.

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

인라인이거나 차단을 사용할 수 있는 unscoped을(를) 사용하면 기본 범위를 적용하지 않도록 Mongoid에 지시할 수 있습니다.

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

나중에 기본 범위를 명시적으로 다시 적용하여 항상 해당 범위에 있도록 Mongoid에게 지시할 수도 있습니다.

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

연결의 일부인 모델에 대해 기본 범위를 사용하려는 경우, 범위 지정을 다시 적용하려면 해당 연결을 다시 로드해야 합니다. 범위가 지정된 연결 내에서 문서의 가시성에 영향을 줄 수 있는 연결 내 문서의 값을 변경할 경우에는 이 점을 명심해야 합니다.

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

참고

기본 범위는 적용되고 나면 더 이상 다른 쿼리 조건과 구별되지 않습니다. 이 때문에 특히 or 연산자 및 nor 연산자를 사용할 때 예상치 못한 동작이 발생할 수 있습니다.

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>

마지막 예시에서는 두 조건(active: truetouring: true)이 $and와(과) 합쳐질 것으로 예상할 수 있습니다. 하지만 Band 클래스에는 이미 범위가 적용되어 있으므로 해당 클래스가 or의 분리 브랜치 중 하나가 됩니다.

다음과 같이 with_scope 메서드를 사용하여 런타임에서 차단의 기본 범위를 변경할 수 있습니다.

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>

참고

with_scope 호출이 중첩된 경우, 중첩된 with_scope 블록이 완성되면 Mongoid 7이 상위 범위 대신 현재 범위를 nil로 설정합니다. Mongoid 8은 현재 범위를 올바른 상위 범위로 설정합니다. Mongoid 7.4 이상에서 Mongoid 8 동작을 가져오려면 Mongoid.broken_scoping 글로벌 옵션을 false로 설정하세요.

기준 객체를 반환하는 모델에 대한 클래스 메서드 역시 범위처럼 처리되며 체인화할 수도 있습니다.

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

Mongoid는 사용자가 복수 문서 삽입, 업데이트 및 삭제를 명시적으로 수행하려는 경우, 낮은 용량으로 기준을 벗어난 지속성 연산을 지원합니다.

경고

order, limit, offset, batch_size 등등 기준 순서 지정 조건 및 페이지 매김 조건은 다음 연산에서 무시됩니다.

작업
예시

Criteria#create

새 영구 문서를 생성하세요.

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

Criteria#create!

새 영구 문서를 생성하고 유효성 검사 실패 시 예외를 제기하세요.

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

Criteria#build|new

새(저장되지 않음) 문서를 생성하세요.

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

Criteria#update

첫 번째로 일치하는 문서의 속성을 업데이트하세요.

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

Criteria#update_all

일치하는 모든 문서의 속성을 업데이트하세요.

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

Criteria#add_to_set

일치하는 모든 문서에 대해 $addToSet을(를) 실행하세요.

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

Criteria#bit

일치하는 모든 문서에 대해 $bit을(를) 실행하세요.

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

Criteria#inc

일치하는 모든 문서에 대해 $inc을(를) 실행하세요.

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

Criteria#pop

일치하는 모든 문서에 대해 $pop을(를) 실행하세요.

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

Criteria#pull

일치하는 모든 문서에 대해 $pull을(를) 실행하세요.

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

Criteria#pull_all

일치하는 모든 문서에 대해 $pullAll을(를) 실행하세요.

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

Criteria#push

일치하는 모든 문서에 대해 $push을(를) 실행하세요.

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

Criteria#push_all

일치하는 모든 문서에 대해 $each(으)로 $push을(를) 실행하세요.

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

Criteria#rename

일치하는 모든 문서에 대해 $rename을(를) 실행하세요.

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

Criteria#set

일치하는 모든 문서에 대해 $set을(를) 실행하세요.

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

Criteria#unset

일치하는 모든 문서에 대해 $unset을(를) 실행하세요.

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

Criteria#delete

데이터베이스에서 일치하는 모든 문서를 삭제합니다.

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

Criteria#destroy

데이터베이스에서 일치하는 모든 문서를 삭제하는 동시에 모두에 대해 콜백을 실행합니다. 이렇게 하면 모든 문서가 메모리로 로드되어 연산에 많은 비용이 소요될 수 있습니다.

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

Ruby MongoDB 드라이버 버전 2.14 이상은 쿼리 캐싱 기능을 제공합니다. 쿼리 캐시는 활성화되었을 때 이전에 실행된 검색 쿼리 및 집계 쿼리의 결과를 저장했다가, 나중에 해당 쿼리를 다시 실행하지 않고 이전의 쿼리 결과를 재사용합니다. 이에 따라 애플리케이션 성능이 향상되고 데이터베이스 부하가 감소합니다.

드라이버의 쿼리 캐시 동작에 대한 자세한 내용은 드라이버 쿼리 캐시 설명서를 참조하세요.

이 섹션의 나머지 부분에서는 2.14.0 이상의 드라이버를 사용한다고 가정합니다.

쿼리 캐시는 드라이버의 네임스페이스 또는 Mongoid의 네임스페이스를 사용하여 활성화할 수 있습니다.

MongoDB Ruby Driver는 미들웨어를 제공하여 Rack 웹 요청 및 ActiveJob 작업 실행에 필요한 쿼리 캐시를 자동으로 활성화합니다. 관련 지침은 구성 페이지의 Query Cache Rack Middleware(쿼리 캐시 Rack 미들웨어) 섹션을 참조하세요.

쿼리 캐시 미들웨어는 웹 요청 및/또는 작업 외부에서 실행되는 코드에는 적용되지 않습니다.

코드 세그먼트에 대해 쿼리 캐시를 수동으로 활성화하려면 다음 기능을 사용하세요.

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

쿼리 캐시를 명시적으로 활성화하거나 비활성화할 수도 있지만, 앞서 설명한 차단 형식을 사용하는 것이 좋습니다.

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

모델 클래스에서 first 메서드를 호출하면 기본 쿼리의 _id 필드를 기준으로 오름차순 정렬이 적용됩니다. 이로 인해 쿼리 캐싱 도중 예상치 못한 동작이 발생할 수 있습니다.

그 예로 모델 클래스에서 all을(를) 호출한 다음 first을(를) 호출하면, 두 번째 쿼리가 첫 번째 쿼리에서 캐싱된 결과를 사용할 수 있습니다. 그러나 두 번째 쿼리에 적용되는 정렬 때문에 두 메서드 모두 데이터베이스를 쿼리하고 각자의 결과를 별도로 캐싱합니다.

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

캐싱된 결과를 사용하려면 모델 클래스에서 all.to_a.first을(를) 호출하세요.

Mongoid를 사용하면 백그라운드에서 데이터베이스 쿼리를 비동기적으로 실행할 수 있습니다. 이는 다른 컬렉션에서 문서를 가져와야 할 때 유용할 수 있습니다.

비동기 쿼리를 예약하려면 Criteria에서 load_async 메서드를 호출하세요.

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

위 예시에서는 비동기 실행을 위해 쿼리 3개를 예약합니다. 이 쿼리들의 결과는 나중에 평소처럼 액세스할 수 있습니다.

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

쿼리가 비동기 실행으로 예약되어 있더라도 호출자의 스레드에서 이 쿼리를 동기적으로 실행할 수 있습니다. 가능한 시나리오는 쿼리 결과에 액세스하는 시기에 따라 다음 3가지로 나뉩니다.

  1. 예약된 비동기 작업이 이미 실행된 경우에는 그 결과가 반환됩니다.

  2. 작업이 시작되었으나 아직 완료되지 않은 경우, 해당 작업이 완료될 때까지 호출자의 스레드가 차단됩니다.

  3. 작업이 아직 시작되지 않은 경우에는 실행 대기열에서 해당 작업이 제거되고 호출자의 스레드에서 쿼리가 동기적으로 실행됩니다.

참고

load_async 메서드가 Criteria 객체를 반환하더라도 쿼리 결과에 액세스하는 것 외에는 이 객체에 대해 어떤 연산도 수행해서는 안 됩니다. 이 쿼리는 load_async 호출 이후 바로 실행되도록 예약되었습니다. 따라서 Criteria 객체에 대한 이후의 변경사항이 적용되지 않을 수 있습니다.

비동기 쿼리는 기본적으로 비활성화되어 있습니다. 비동기 쿼리가 비활성화되면 load_async이(가) 현재 스레드에서 즉시 쿼리를 실행하고, 필요한 경우 차단합니다. 따라서 이 경우 기준에 대해 load_async을(를) 호출하는 것은 to_a을(를) 호출하여 쿼리를 강제로 실행하는 것과 거의 같습니다.

비동기 쿼리 실행을 활성화하려면 다음 구성 옵션을 설정해야 합니다.

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

돌아가기

CRUD 작업