mongodoc 0.2.2 → 0.2.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/Rakefile +21 -0
- data/TODO +6 -1
- data/VERSION +1 -1
- data/features/finders.feature +1 -1
- data/features/mongodoc_base.feature +11 -2
- data/features/{named_scopes.feature → scopes.feature} +0 -0
- data/features/step_definitions/document_steps.rb +15 -4
- data/features/step_definitions/documents.rb +3 -3
- data/features/step_definitions/query_steps.rb +17 -14
- data/features/step_definitions/{named_scope_steps.rb → scope_steps.rb} +0 -0
- data/features/using_criteria.feature +22 -43
- data/lib/mongodoc.rb +1 -1
- data/lib/mongodoc/associations/collection_proxy.rb +3 -1
- data/lib/mongodoc/associations/document_proxy.rb +4 -1
- data/lib/mongodoc/associations/hash_proxy.rb +3 -1
- data/lib/mongodoc/associations/proxy_base.rb +6 -4
- data/lib/mongodoc/attributes.rb +6 -6
- data/lib/mongodoc/contexts.rb +24 -0
- data/lib/mongodoc/contexts/enumerable.rb +132 -0
- data/lib/mongodoc/contexts/mongo.rb +215 -0
- data/lib/mongodoc/criteria.rb +36 -479
- data/lib/mongodoc/document.rb +3 -2
- data/lib/mongodoc/finders.rb +31 -11
- data/lib/mongodoc/matchers.rb +35 -0
- data/lib/mongodoc/scope.rb +64 -0
- data/lib/mongoid/contexts/paging.rb +42 -0
- data/lib/mongoid/criteria.rb +264 -0
- data/lib/mongoid/criterion/complex.rb +21 -0
- data/lib/mongoid/criterion/exclusion.rb +65 -0
- data/lib/mongoid/criterion/inclusion.rb +92 -0
- data/lib/mongoid/criterion/optional.rb +136 -0
- data/lib/mongoid/extensions/hash/criteria_helpers.rb +20 -0
- data/lib/mongoid/extensions/symbol/inflections.rb +36 -0
- data/lib/mongoid/matchers/all.rb +11 -0
- data/lib/mongoid/matchers/default.rb +26 -0
- data/lib/mongoid/matchers/exists.rb +13 -0
- data/lib/mongoid/matchers/gt.rb +11 -0
- data/lib/mongoid/matchers/gte.rb +11 -0
- data/lib/mongoid/matchers/in.rb +11 -0
- data/lib/mongoid/matchers/lt.rb +11 -0
- data/lib/mongoid/matchers/lte.rb +11 -0
- data/lib/mongoid/matchers/ne.rb +11 -0
- data/lib/mongoid/matchers/nin.rb +11 -0
- data/lib/mongoid/matchers/size.rb +11 -0
- data/mongodoc.gemspec +39 -9
- data/spec/attributes_spec.rb +16 -2
- data/spec/contexts/enumerable_spec.rb +335 -0
- data/spec/contexts/mongo_spec.rb +148 -0
- data/spec/contexts_spec.rb +28 -0
- data/spec/criteria_spec.rb +15 -766
- data/spec/finders_spec.rb +28 -36
- data/spec/matchers_spec.rb +342 -0
- data/spec/scope_spec.rb +79 -0
- metadata +40 -10
- data/features/step_definitions/criteria_steps.rb +0 -42
- data/lib/mongodoc/named_scope.rb +0 -68
- data/spec/named_scope_spec.rb +0 -82
data/Rakefile
CHANGED
@@ -81,6 +81,27 @@ namespace :mongo do
|
|
81
81
|
end
|
82
82
|
end
|
83
83
|
|
84
|
+
namespace :mongoid do
|
85
|
+
desc 'Sync criteria from Mongoid'
|
86
|
+
task :sync do
|
87
|
+
require 'pathname'
|
88
|
+
|
89
|
+
src_dir = Pathname.new('../durran-mongoid/lib/mongoid')
|
90
|
+
dest_dir = Pathname.new('lib/mongoid')
|
91
|
+
dest_dir.mkpath
|
92
|
+
%w(criteria.rb contexts/paging.rb criterion extensions/symbol/inflections.rb extensions/hash/criteria_helpers.rb matchers).each do |f|
|
93
|
+
src = src_dir + f
|
94
|
+
if src.directory?
|
95
|
+
FileUtils.cp_r(src, dest_dir)
|
96
|
+
else
|
97
|
+
dest = dest_dir + f
|
98
|
+
dest.dirname.mkpath
|
99
|
+
FileUtils.cp(src, dest)
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
84
105
|
namespace :bench do
|
85
106
|
desc 'Run benchmark for MongoDoc'
|
86
107
|
task 'mongodoc' do
|
data/TODO
CHANGED
@@ -1,10 +1,15 @@
|
|
1
|
-
As of 2010-02-
|
1
|
+
As of 2010-02-23
|
2
2
|
|
3
3
|
Associations
|
4
4
|
------------
|
5
5
|
|
6
6
|
Pull associations out of attributes and refactor
|
7
7
|
|
8
|
+
Criteria
|
9
|
+
--------
|
10
|
+
|
11
|
+
Make critieria module, include into Document, CriteriaProxy, Association?
|
12
|
+
|
8
13
|
Documentation
|
9
14
|
-------------
|
10
15
|
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.2.
|
1
|
+
0.2.4
|
data/features/finders.feature
CHANGED
@@ -109,11 +109,20 @@ Feature: MongoDoc::Base
|
|
109
109
|
Then the Place collection should have 1 document
|
110
110
|
And the document 'hashrocket' roundtrips
|
111
111
|
|
112
|
-
Scenario:
|
112
|
+
Scenario: Class criteria
|
113
113
|
Given an empty Contact document collection
|
114
114
|
And a Contact document named 'hashrocket' :
|
115
115
|
| Name | Type |
|
116
116
|
| Hashrocket | company |
|
117
117
|
And I save the last document
|
118
|
-
When I query contacts with
|
118
|
+
When I query contacts with criteria where('type' => 'company')
|
119
|
+
Then the size of the last return value is 1
|
120
|
+
|
121
|
+
Scenario: Finder
|
122
|
+
Given an empty Contact document collection
|
123
|
+
And a Contact document named 'hashrocket' :
|
124
|
+
| Name | Type |
|
125
|
+
| Hashrocket | company |
|
126
|
+
And I save the document 'hashrocket'
|
127
|
+
When I find a contact using the id of 'hashrocket'
|
119
128
|
Then the size of the last return value is 1
|
File without changes
|
@@ -42,7 +42,7 @@ end
|
|
42
42
|
|
43
43
|
Given /^I set the id on the document '(.*)' to (.*)$/ do |doc_name, value|
|
44
44
|
doc = instance_variable_get("@#{doc_name}")
|
45
|
-
doc._id = Mongo::ObjectID.
|
45
|
+
doc._id = Mongo::ObjectID.from_string("%024x" % value.to_i(16))
|
46
46
|
end
|
47
47
|
|
48
48
|
Given /^'(.+)' has one (.+?) as (.+?) \(identified by '(.+)'\):$/ do |doc_name, class_name, assoc_name, var_name, table|
|
@@ -79,10 +79,21 @@ When /^I update the document '(.*)' with the hash named '(.*)'$/ do |doc_name, h
|
|
79
79
|
@last_return = doc.update_attributes(attrs)
|
80
80
|
end
|
81
81
|
|
82
|
-
When /^I query (.*) with
|
82
|
+
When /^I query (.*) with criteria (.*)$/ do |doc, criteria_text|
|
83
|
+
klass = doc.singularize.camelize
|
84
|
+
@query = @last_return = eval("#{klass}.criteria.#{criteria_text}")
|
85
|
+
end
|
86
|
+
|
87
|
+
When /^I query (.*) with the '(.*)' id$/ do |doc, name|
|
83
88
|
klass = doc.singularize.camelize.constantize
|
84
|
-
|
85
|
-
@last_return = klass.
|
89
|
+
doc = instance_variable_get("@#{name}")
|
90
|
+
@query = @last_return = klass.criteria.id(doc.id).entries
|
91
|
+
end
|
92
|
+
|
93
|
+
When /^I find a (.*) using the id of '(.*)'$/ do |type, doc_name|
|
94
|
+
klass = type.camelize.constantize
|
95
|
+
doc = instance_variable_get("@#{doc_name}")
|
96
|
+
@last_return = klass.find(doc.id)
|
86
97
|
end
|
87
98
|
|
88
99
|
When /^'(.+)' is the first (.+?) of '(.+)'$/ do |var_name, single_assoc, doc_name|
|
@@ -24,7 +24,7 @@ class Contact
|
|
24
24
|
key :interests
|
25
25
|
has_many :addresses
|
26
26
|
|
27
|
-
|
28
|
-
|
29
|
-
|
27
|
+
scope :rubyists, any_in(:interests => ['ruby'])
|
28
|
+
scope :contract_work, any_in(:interests => ['contract work'])
|
29
|
+
scope :in_state, lambda {|state| where('addresses.state' => state)}
|
30
30
|
end
|
@@ -6,19 +6,23 @@ def query(klass_name = nil)
|
|
6
6
|
@query ||= klass(klass_name).criteria
|
7
7
|
end
|
8
8
|
|
9
|
-
Then /^the
|
10
|
-
|
11
|
-
query.
|
9
|
+
Then /^the query result is equal to the document '(.*)'$/ do |name|
|
10
|
+
doc = instance_variable_get("@#{name}")
|
11
|
+
query.should == doc
|
12
12
|
end
|
13
13
|
|
14
14
|
Then /^one of the query results is the document '(.*)'$/ do |name|
|
15
|
-
|
16
|
-
query.any? {|
|
15
|
+
doc = instance_variable_get("@#{name}")
|
16
|
+
query.any? {|d| d == doc}.should be_true
|
17
17
|
end
|
18
18
|
|
19
|
-
Then /^the
|
20
|
-
|
21
|
-
|
19
|
+
Then /^the query result with "(.*)" == "(.*)" has a count of (.*)$/ do |key, value, count|
|
20
|
+
query.find {|r| r.has_key?(key) and r[key] == value }['count'].should == count.to_i
|
21
|
+
end
|
22
|
+
|
23
|
+
Then /^the query result with "([^\"]*)" == "([^\"]*)" has the document '(.*)'$/ do |key, value, name|
|
24
|
+
doc = instance_variable_get("@#{name}")
|
25
|
+
query.find {|r| r.has_key?(key) and r[key] == value }['group'].should include(doc)
|
22
26
|
end
|
23
27
|
|
24
28
|
Then /^the query result has (.*) documents*$/ do |count|
|
@@ -29,14 +33,13 @@ Then /^the query result has (.*) documents*$/ do |count|
|
|
29
33
|
end
|
30
34
|
end
|
31
35
|
|
32
|
-
Then /^the
|
33
|
-
|
36
|
+
Then /^the (first|last) query result is the document '(.*)'$/ do |position, name|
|
37
|
+
doc = instance_variable_get("@#{name}")
|
38
|
+
query.entries.send(position).should == doc
|
34
39
|
end
|
35
40
|
|
36
|
-
Then /^the
|
37
|
-
|
38
|
-
result = query.group
|
39
|
-
result.find {|r| r.has_key?(key) and r[key] == value }['group'].should include(object)
|
41
|
+
Then /^the size of the query result is (.*)$/ do |count|
|
42
|
+
query.to_a.size.should == count.to_i
|
40
43
|
end
|
41
44
|
|
42
45
|
Then /^the query result is the document '(.*)'$/ do |name|
|
File without changes
|
@@ -74,76 +74,55 @@ Feature: MongoDoc::Base
|
|
74
74
|
And I save the document 'jax'
|
75
75
|
|
76
76
|
Scenario: Counting results
|
77
|
-
When I query contacts with
|
77
|
+
When I query contacts with criteria all('interests' => ['ruby', 'rails', 'employment'])
|
78
78
|
Then the query result has 2 documents
|
79
79
|
|
80
80
|
Scenario: Finding contacts with interests in ruby and rails
|
81
|
-
When I query contacts with
|
82
|
-
Then the query
|
83
|
-
And one of the query results is the document 'rocketeer'
|
81
|
+
When I query contacts with criteria all('interests' => ['ruby', 'rails', 'employment'])
|
82
|
+
Then one of the query results is the document 'rocketeer'
|
84
83
|
|
85
|
-
Scenario: Finding contacts with interests in restaurants
|
86
|
-
When I query contacts with
|
87
|
-
Then the query
|
88
|
-
And one of the query results is the document 'contractor'
|
84
|
+
Scenario: Finding contacts with interests in restaurants or hotels
|
85
|
+
When I query contacts with criteria in('interests' => ['restaurants', 'hotels'])
|
86
|
+
Then one of the query results is the document 'contractor'
|
89
87
|
|
90
88
|
Scenario: Aggregating Places
|
91
|
-
When I query places
|
92
|
-
|
93
|
-
Then the aggregate query result with "type" == "hotel" has a count of 2
|
89
|
+
When I query places with criteria only('type').where('address.state' => 'FL').aggregate
|
90
|
+
Then the query result with "type" == "hotel" has a count of 2
|
94
91
|
|
95
92
|
Scenario: Excluding places in Neptune Beach
|
96
|
-
When I query places
|
97
|
-
|
98
|
-
Then the aggregate query result with "type" == "hotel" has a count of 1
|
93
|
+
When I query places with criteria only('type').where('address.city' => 'Neptune Beach').aggregate
|
94
|
+
Then the query result with "type" == "hotel" has a count of 1
|
99
95
|
|
100
96
|
Scenario: Using extras to limit results
|
101
|
-
When I query contacts with
|
102
|
-
And I set the query extras limit on contacts to 1
|
97
|
+
When I query contacts with criteria all('interests' => ['ruby', 'rails', 'employment']).limit(1)
|
103
98
|
Then the size of the query result is 1
|
104
99
|
|
105
100
|
Scenario: Finding the first result
|
106
|
-
When I query contacts with
|
107
|
-
Then the
|
101
|
+
When I query contacts with criteria all('interests' => ['ruby', 'rails', 'employment']).first
|
102
|
+
Then the query result is equal to the document 'hashrocket'
|
108
103
|
|
109
104
|
Scenario: Grouping places by type
|
110
|
-
When I query places
|
111
|
-
|
112
|
-
Then the group query result with "type" == "hotel" has the document 'one_ocean'
|
105
|
+
When I query places with criteria only('type').where('type' => 'hotel').group
|
106
|
+
Then the query result with "type" == "hotel" has the document 'one_ocean'
|
113
107
|
|
114
108
|
Scenario: Selecting contacts with in operator
|
115
|
-
When I query contacts with in
|
109
|
+
When I query contacts with criteria in('interests' => ['ruby', 'rails', 'employment'])
|
116
110
|
Then the query result has 3 documents
|
117
111
|
|
118
112
|
Scenario: Selecting a contact with the id operator
|
119
113
|
When I query contacts with the 'hashrocket' id
|
120
114
|
Then the query result has 1 documents
|
121
|
-
And the
|
122
|
-
|
123
|
-
Scenario: Finding the last result
|
124
|
-
When I query contacts with every "{'interests' => ['ruby', 'rails', 'employment']}"
|
125
|
-
Then the last query result is equal to the document 'rocketeer'
|
126
|
-
|
127
|
-
Scenario: Using limit on results
|
128
|
-
When I query contacts with every "{'interests' => ['ruby', 'rails', 'employment']}"
|
129
|
-
And I set the query on contacts to limit 1
|
130
|
-
Then the size of the query result is 1
|
115
|
+
And the query result is the document 'hashrocket'
|
131
116
|
|
132
117
|
Scenario: Selecting contacts with not in operator
|
133
|
-
When I query contacts with
|
118
|
+
When I query contacts with criteria not_in('interests' => ['contract work', 'employment'])
|
134
119
|
Then the query result has 0 documents
|
135
120
|
|
136
121
|
Scenario: Ordering contacts
|
137
|
-
When I query contacts with in
|
138
|
-
|
139
|
-
|
140
|
-
Then the last query result is equal to the document 'rocketeer'
|
122
|
+
When I query contacts with criteria in('interests' => ['ruby', 'rails']).order_by([[:name, :asc]]).entries
|
123
|
+
Then the first query result is the document 'contractor'
|
124
|
+
And the last query result is the document 'rocketeer'
|
141
125
|
|
142
126
|
Scenario: Using skip on results
|
143
|
-
When I query contacts with
|
144
|
-
And I set the query on contacts to skip 1
|
127
|
+
When I query contacts with criteria all('interests' => ['ruby', 'rails']).skip(1)
|
145
128
|
Then the size of the query result is 2
|
146
|
-
|
147
|
-
Scenario: All
|
148
|
-
When I query contacts with 'all'
|
149
|
-
Then the size of the query result is 3
|
data/lib/mongodoc.rb
CHANGED
@@ -2,7 +2,9 @@
|
|
2
2
|
# http://github.com/sandro
|
3
3
|
module MongoDoc
|
4
4
|
module Associations
|
5
|
-
class CollectionProxy
|
5
|
+
class CollectionProxy
|
6
|
+
include ProxyBase
|
7
|
+
|
6
8
|
# List of array methods (that are not in +Object+) that need to be
|
7
9
|
# delegated to +collection+.
|
8
10
|
ARRAY_METHODS = (Array.instance_methods - Object.instance_methods).map { |n| n.to_s }
|
@@ -1,9 +1,12 @@
|
|
1
1
|
module MongoDoc
|
2
2
|
module Associations
|
3
|
-
class DocumentProxy
|
3
|
+
class DocumentProxy
|
4
|
+
include ProxyBase
|
4
5
|
|
5
6
|
attr_reader :document
|
6
7
|
|
8
|
+
delegate :to_bson, :id, :to => :document
|
9
|
+
|
7
10
|
def _root=(root)
|
8
11
|
@_root = root
|
9
12
|
document._root = root if is_document?(document)
|
@@ -2,7 +2,9 @@ module MongoDoc
|
|
2
2
|
class InvalidEmbeddedHashKey < RuntimeError; end
|
3
3
|
|
4
4
|
module Associations
|
5
|
-
class HashProxy
|
5
|
+
class HashProxy
|
6
|
+
include ProxyBase
|
7
|
+
|
6
8
|
HASH_METHODS = (Hash.instance_methods - Object.instance_methods).map { |n| n.to_s }
|
7
9
|
|
8
10
|
MUST_DEFINE = %w[to_a inspect to_bson ==]
|
@@ -1,9 +1,11 @@
|
|
1
1
|
module MongoDoc
|
2
2
|
module Associations
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
3
|
+
module ProxyBase
|
4
|
+
def self.included(klass)
|
5
|
+
klass.class_eval do
|
6
|
+
attr_reader :assoc_name, :assoc_class, :_parent, :_root
|
7
|
+
end
|
8
|
+
end
|
7
9
|
|
8
10
|
def _parent=(parent)
|
9
11
|
@_parent = parent
|
data/lib/mongodoc/attributes.rb
CHANGED
@@ -51,7 +51,7 @@ module MongoDoc
|
|
51
51
|
def has_one(*args)
|
52
52
|
options = args.extract_options!
|
53
53
|
assoc_class = if class_name = options.delete(:class_name)
|
54
|
-
|
54
|
+
self.class_from_name(class_name)
|
55
55
|
end
|
56
56
|
|
57
57
|
args.each do |name|
|
@@ -75,7 +75,7 @@ module MongoDoc
|
|
75
75
|
def has_many(*args)
|
76
76
|
options = args.extract_options!
|
77
77
|
assoc_class = if class_name = options.delete(:class_name)
|
78
|
-
|
78
|
+
self.class_from_name(class_name)
|
79
79
|
end
|
80
80
|
|
81
81
|
args.each do |name|
|
@@ -84,7 +84,7 @@ module MongoDoc
|
|
84
84
|
define_method("#{name}") do
|
85
85
|
association = instance_variable_get("@#{name}")
|
86
86
|
unless association
|
87
|
-
association = Associations::CollectionProxy.new(:root => _root || self, :parent => self, :assoc_name => name, :assoc_class => assoc_class || self.class.
|
87
|
+
association = Associations::CollectionProxy.new(:root => _root || self, :parent => self, :assoc_name => name, :assoc_class => assoc_class || self.class.class_from_name(name))
|
88
88
|
instance_variable_set("@#{name}", association)
|
89
89
|
end
|
90
90
|
association
|
@@ -105,7 +105,7 @@ module MongoDoc
|
|
105
105
|
def has_hash(*args)
|
106
106
|
options = args.extract_options!
|
107
107
|
assoc_class = if class_name = options.delete(:class_name)
|
108
|
-
|
108
|
+
self.class_from_name(class_name)
|
109
109
|
end
|
110
110
|
|
111
111
|
args.each do |name|
|
@@ -114,7 +114,7 @@ module MongoDoc
|
|
114
114
|
define_method("#{name}") do
|
115
115
|
association = instance_variable_get("@#{name}")
|
116
116
|
unless association
|
117
|
-
association = Associations::HashProxy.new(:root => _root || self, :parent => self, :assoc_name => name, :assoc_class => assoc_class || self.class.
|
117
|
+
association = Associations::HashProxy.new(:root => _root || self, :parent => self, :assoc_name => name, :assoc_class => assoc_class || self.class.class_from_name(name))
|
118
118
|
instance_variable_set("@#{name}", association)
|
119
119
|
end
|
120
120
|
association
|
@@ -133,7 +133,7 @@ module MongoDoc
|
|
133
133
|
end
|
134
134
|
|
135
135
|
def type_name_with_module(type_name)
|
136
|
-
(/^::/ =~ type_name) ? type_name : "#{
|
136
|
+
(/^::/ =~ type_name) ? type_name : "#{parent}::#{type_name}"
|
137
137
|
end
|
138
138
|
end
|
139
139
|
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require "mongoid/contexts/paging"
|
3
|
+
require "mongodoc/contexts/enumerable"
|
4
|
+
require "mongodoc/contexts/mongo"
|
5
|
+
|
6
|
+
module Mongoid
|
7
|
+
module Contexts
|
8
|
+
# Determines the context to be used for this criteria. If the class is an
|
9
|
+
# embedded document, then the context will be the array in the has_many
|
10
|
+
# association it is in. If the class is a root, then the database itself
|
11
|
+
# will be the context.
|
12
|
+
#
|
13
|
+
# Example:
|
14
|
+
#
|
15
|
+
# <tt>Contexts.context_for(criteria)</tt>
|
16
|
+
def self.context_for(criteria)
|
17
|
+
if criteria.klass.respond_to?(:collection)
|
18
|
+
return MongoDoc::Contexts::Mongo.new(criteria)
|
19
|
+
end
|
20
|
+
return MongoDoc::Contexts::Enumerable.new(criteria)
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,132 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
module MongoDoc #:nodoc:
|
3
|
+
module Contexts #:nodoc:
|
4
|
+
class Enumerable
|
5
|
+
include Mongoid::Contexts::Paging
|
6
|
+
attr_reader :criteria
|
7
|
+
|
8
|
+
delegate :first, :last, :to => :execute
|
9
|
+
delegate :documents, :options, :selector, :to => :criteria
|
10
|
+
|
11
|
+
# Return aggregation counts of the grouped documents. This will count by
|
12
|
+
# the first field provided in the fields array.
|
13
|
+
#
|
14
|
+
# Returns:
|
15
|
+
#
|
16
|
+
# A +Hash+ with field values as keys, count as values
|
17
|
+
def aggregate
|
18
|
+
counts = {}
|
19
|
+
group.each_pair { |key, value| counts[key] = value.size }
|
20
|
+
counts
|
21
|
+
end
|
22
|
+
|
23
|
+
# Gets the number of documents in the array. Delegates to size.
|
24
|
+
def count
|
25
|
+
@count ||= documents.size
|
26
|
+
end
|
27
|
+
|
28
|
+
# Groups the documents by the first field supplied in the field options.
|
29
|
+
#
|
30
|
+
# Returns:
|
31
|
+
#
|
32
|
+
# A +Hash+ with field values as keys, arrays of documents as values.
|
33
|
+
def group
|
34
|
+
field = options[:fields].first
|
35
|
+
documents.group_by { |doc| doc.send(field) }
|
36
|
+
end
|
37
|
+
|
38
|
+
# Enumerable implementation of execute. Returns matching documents for
|
39
|
+
# the selector, and adds options if supplied.
|
40
|
+
#
|
41
|
+
# Returns:
|
42
|
+
#
|
43
|
+
# An +Array+ of documents that matched the selector.
|
44
|
+
def execute(paginating = false)
|
45
|
+
limit(documents.select { |document| document.matches?(selector) })
|
46
|
+
end
|
47
|
+
|
48
|
+
# Return documents based on an id search. Will handle if a single id has
|
49
|
+
# been passed or mulitple ids.
|
50
|
+
#
|
51
|
+
# Example:
|
52
|
+
#
|
53
|
+
# context.id_criteria([1, 2, 3])
|
54
|
+
#
|
55
|
+
# Returns:
|
56
|
+
#
|
57
|
+
# The single or multiple documents.
|
58
|
+
def id_criteria(params)
|
59
|
+
criteria.id(params)
|
60
|
+
result = params.is_a?(String) ? one : criteria.entries
|
61
|
+
return result
|
62
|
+
end
|
63
|
+
|
64
|
+
# Create the new enumerable context. This will need the selector and
|
65
|
+
# options from a +Criteria+ and a documents array that is the underlying
|
66
|
+
# array of embedded documents from a has many association.
|
67
|
+
#
|
68
|
+
# Example:
|
69
|
+
#
|
70
|
+
# <tt>MongoDoc::Contexts::Enumerable.new(criteria)</tt>
|
71
|
+
def initialize(criteria)
|
72
|
+
@criteria = criteria
|
73
|
+
end
|
74
|
+
|
75
|
+
# Get the largest value for the field in all the documents.
|
76
|
+
#
|
77
|
+
# Returns:
|
78
|
+
#
|
79
|
+
# The numerical largest value.
|
80
|
+
def max(field)
|
81
|
+
determine(field, :>=)
|
82
|
+
end
|
83
|
+
|
84
|
+
# Get the smallest value for the field in all the documents.
|
85
|
+
#
|
86
|
+
# Returns:
|
87
|
+
#
|
88
|
+
# The numerical smallest value.
|
89
|
+
def min(field)
|
90
|
+
determine(field, :<=)
|
91
|
+
end
|
92
|
+
|
93
|
+
# Get one document.
|
94
|
+
#
|
95
|
+
# Returns:
|
96
|
+
#
|
97
|
+
# The first document in the +Array+
|
98
|
+
alias :one :first
|
99
|
+
|
100
|
+
# Get the sum of the field values for all the documents.
|
101
|
+
#
|
102
|
+
# Returns:
|
103
|
+
#
|
104
|
+
# The numerical sum of all the document field values.
|
105
|
+
def sum(field)
|
106
|
+
sum = documents.inject(nil) do |memo, doc|
|
107
|
+
value = doc.send(field)
|
108
|
+
memo ? memo += value : value
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
protected
|
113
|
+
# If the field exists, perform the comparison and set if true.
|
114
|
+
def determine(field, operator)
|
115
|
+
matching = documents.inject(nil) do |memo, doc|
|
116
|
+
value = doc.send(field)
|
117
|
+
(memo && memo.send(operator, value)) ? memo : value
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
# Limits the result set if skip and limit options.
|
122
|
+
def limit(documents)
|
123
|
+
skip, limit = options[:skip], options[:limit]
|
124
|
+
if skip && limit
|
125
|
+
return documents.slice(skip, limit)
|
126
|
+
end
|
127
|
+
documents
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|