forked from mongodb/mongoid
/
enumerable.rb
118 lines (106 loc) · 3.21 KB
/
enumerable.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
# encoding: utf-8
module Mongoid #:nodoc:
module Contexts #:nodoc:
class Enumerable
include Paging
attr_reader :criteria
delegate :first, :last, :to => :execute
delegate :documents, :options, :selector, :to => :criteria
# Return aggregation counts of the grouped documents. This will count by
# the first field provided in the fields array.
#
# Returns:
#
# A +Hash+ with field values as keys, count as values
def aggregate
counts = {}
group.each_pair { |key, value| counts[key] = value.size }
counts
end
# Gets the number of documents in the array. Delegates to size.
def count
@count ||= documents.size
end
# Groups the documents by the first field supplied in the field options.
#
# Returns:
#
# A +Hash+ with field values as keys, arrays of documents as values.
def group
field = options[:fields].first
documents.group_by { |doc| doc.send(field) }
end
# Enumerable implementation of execute. Returns matching documents for
# the selector, and adds options if supplied.
#
# Returns:
#
# An +Array+ of documents that matched the selector.
def execute(paginating = false)
limit(documents.select { |document| document.matches?(selector) })
end
# Create the new enumerable context. This will need the selector and
# options from a +Criteria+ and a documents array that is the underlying
# array of embedded documents from a has many association.
#
# Example:
#
# <tt>Mongoid::Contexts::Enumerable.new(criteria)</tt>
def initialize(criteria)
@criteria = criteria
if criteria.klass.hereditary
criteria.in(:_type => criteria.klass._types)
end
end
# Get the largest value for the field in all the documents.
#
# Returns:
#
# The numerical largest value.
def max(field)
determine(field, :>=)
end
# Get the smallest value for the field in all the documents.
#
# Returns:
#
# The numerical smallest value.
def min(field)
determine(field, :<=)
end
# Get one document.
#
# Returns:
#
# The first document in the +Array+
alias :one :first
# Get the sum of the field values for all the documents.
#
# Returns:
#
# The numerical sum of all the document field values.
def sum(field)
sum = documents.inject(nil) do |memo, doc|
value = doc.send(field)
memo ? memo += value : value
end
end
protected
# If the field exists, perform the comparison and set if true.
def determine(field, operator)
matching = documents.inject(nil) do |memo, doc|
value = doc.send(field)
(memo && memo.send(operator, value)) ? memo : value
end
end
# Limits the result set if skip and limit options.
def limit(documents)
skip, limit = options[:skip], options[:limit]
if skip && limit
return documents.slice(skip, limit)
end
documents
end
end
end
end