/
findable.rb
244 lines (231 loc) · 7.94 KB
/
findable.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
# frozen_string_literal: true
# rubocop:todo all
module Mongoid
# This module defines the finder methods that hang off the document at the
# class level.
module Findable
extend Forwardable
def_delegators :with_default_scope, *(
Criteria::Queryable::Selectable.forwardables +
Criteria::Queryable::Optional.forwardables
)
# These are methods defined on the criteria that should also be accessible
# directly from the class level.
def_delegators :with_default_scope,
:aggregates,
:avg,
:create_with,
:distinct,
:each,
:each_with_index,
:extras,
:fifth,
:fifth!,
:find_one_and_delete,
:find_one_and_replace,
:find_one_and_update,
:find_or_create_by,
:find_or_create_by!,
:find_or_initialize_by,
:first!,
:first_or_create,
:first_or_create!,
:first_or_initialize,
:for_js,
:fourth,
:fourth!,
:geo_near,
:includes,
:last!,
:map_reduce,
:max,
:min,
:none,
:pick,
:pluck,
:read,
:second,
:second!,
:second_to_last,
:second_to_last!,
:sum,
:take,
:take!,
:tally,
:text_search,
:third,
:third!,
:third_to_last,
:third_to_last!,
:update,
:update_all,
# Returns a count of records in the database.
# If you want to specify conditions use where.
#
# @example Get the count of matching documents.
# Person.count
# Person.where(title: "Sir").count
#
# @return [ Integer ] The number of matching documents.
def count
with_default_scope.count
end
# Returns an estimated count of records in the database.
#
# @example Get the count of matching documents.
# Person.estimated_count
#
# @return [ Integer ] The number of matching documents.
def estimated_count
with_default_scope.estimated_count
end
# Returns true if count is zero
#
# @example Are there no saved documents for this model?
# Person.empty?
#
# @return [ true | false ] If the collection is empty.
def empty?
count == 0
end
# Returns true if there are on document in database based on the
# provided arguments.
#
# @example Do any documents exist for the conditions?
# Person.exists?
#
# @example Do any documents exist for given _id.
# Person.exists?(BSON::ObjectId(...))
#
# @example Do any documents exist for given conditions.
# Person.exists?(name: "...")
#
# @param [ Hash | Object | false ] id_or_conditions an _id to
# search for, a hash of conditions, nil or false.
#
# @return [ true | false ] If any documents exist for the conditions.
# Always false if passed nil or false.
def exists?(id_or_conditions = :none)
with_default_scope.exists?(id_or_conditions)
end
# Finds a +Document+ or multiple documents by their _id values.
#
# If a single non-Array argument is given, this argument is interpreted
# as the _id value of a document to find. If there is a matching document
# in the database, this document is returned; otherwise, if the
# +raise_not_found_error+ Mongoid configuration option is truthy
# (which is the default), +Errors::DocumentNotFound+ is raised, and if
# +raise_not_found_error+ is falsy, +find+ returns +nil+.
#
# If multiple arguments are given, or an Array argument is given, the
# array is flattened and each array element is interpreted as the _id
# value of the document to find. Mongoid then attempts to retrieve all
# documents with the provided _id values. The return value is an array
# of found documents. Each document appears one time in the returned array,
# even if its _id is given multiple times in the argument to +find+.
# If the +raise_not_found_error+ Mongoid configuration option is truthy,
# +Errors::DocumentNotFound+ exception is raised if any of the specified
# _ids were not found in the database. If the +raise_not_found_error+
# Mongoid configuration option is falsy, only those documents which are
# found are returned; if no documents are found, the return value is an
# empty array.
#
# Note that MongoDB does not allow the _id field to be an array.
#
# The argument undergoes customary Mongoid type conversions based on
# the type declared for the _id field. By default the _id field is a
# +BSON::ObjectId+; this allows strings to be passed to +find+ and the
# strings will be transparently converted to +BSON::ObjectId+ instances
# during query construction.
#
# If this method is given a block, it delegates to +Enumerable#find+ and
# returns the first document of those found by the current Crieria object
# for which the block returns a truthy value. If both a block and ids are
# given, the block is ignored and the documents for the given ids are
# returned. If a block and a Proc are given, the method delegates to
# +Enumerable#find+ and uses the proc as the default.
#
# The +find+ method takes into account the default scope defined on the
# model class, if any.
#
# @note Each argument can be an individual id, an array of ids or
# a nested array. Each array will be flattened.
#
# @param [ [ Object | Array<Object> ]... ] *args The id(s) to find.
#
# @return [ Document | Array<Document> | nil ] A document or matching documents.
#
# @raise Errors::DocumentNotFound If not all documents are found and
# the +raise_not_found_error+ Mongoid configuration option is truthy.
def find(*args, &block)
empty_or_proc = args.empty? || (args.length == 1 && args.first.is_a?(Proc))
if block_given? && empty_or_proc
with_default_scope.find(*args, &block)
else
with_default_scope.find(*args)
end
end
# Find the first +Document+ given the conditions.
# If a matching Document is not found and
# Mongoid.raise_not_found_error is true it raises
# Mongoid::Errors::DocumentNotFound, return null nil elsewise.
#
# @example Find the document by attribute other than id
# Person.find_by(:username => "superuser")
#
# @param [ Hash ] attrs The attributes to check.
#
# @raise [ Errors::DocumentNotFound ] If no document found
# and Mongoid.raise_not_found_error is true.
#
# @return [ Document | nil ] A matching document.
def find_by(attrs = {})
result = where(attrs).find_first
if result.nil? && Mongoid.raise_not_found_error
raise(Errors::DocumentNotFound.new(self, attrs))
end
yield(result) if result && block_given?
result
end
# Find the first +Document+ given the conditions, or raises
# Mongoid::Errors::DocumentNotFound
#
# @example Find the document by attribute other than id
# Person.find_by(:username => "superuser")
#
# @param [ Hash ] attrs The attributes to check.
#
# @raise [ Errors::DocumentNotFound ] If no document found.
#
# @return [ Document ] A matching document.
def find_by!(attrs = {})
result = where(attrs).find_first
raise(Errors::DocumentNotFound.new(self, attrs)) unless result
yield(result) if result && block_given?
result
end
# Find the first +Document+ given the conditions.
#
# @example Find the first document.
# Person.first
#
# @param [ Integer ] limit The number of documents to return.
#
# @return [ Document ] The first matching document.
def first(limit = nil)
with_default_scope.first(limit)
end
alias :one :first
# Find the last +Document+ given the conditions.
#
# @example Find the last document.
# Person.last
#
# @param [ Integer ] limit The number of documents to return.
#
# @return [ Document ] The last matching document.
def last(limit = nil)
with_default_scope.last(limit)
end
end
end