active_aggregate 0.0.2
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.
- checksums.yaml +7 -0
- data/README.md +10 -0
- data/lib/active_aggregate/concern.rb +55 -0
- data/lib/active_aggregate/relation.rb +184 -0
- metadata +60 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 653177242ed87c2c3965933b6a7932644528af9ad7a5a861d950c0e5b3bd7078
|
4
|
+
data.tar.gz: fcb515097fb8001c7cc314e7bca0131b1ab83ea155a3d69300537b77126bff9a
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: f086dc4d9a449ba36d25d775e85c52b2bd61299a4cedfe104b3a715917cd9e0037affc4410bfa1c2b609d8000a5410c279fe4d0dd1a15034373c0301429e17a3
|
7
|
+
data.tar.gz: f6d84f33cf8c04003f3436df053fd5e86932c1edb7e04217768e5863b42f693fc1a9110e53b9939c1360b2483f9130c7f7b16de37e4df8ebd7720cfaa5efe2d6
|
data/README.md
ADDED
@@ -0,0 +1,10 @@
|
|
1
|
+
# active_aggregate
|
2
|
+
|
3
|
+
- active_aggregate is a little helper support to queries by mongoDB aggregate more easily.
|
4
|
+
- A toolkit for building queries like ActiveRelation. Rich support for more flexible merge conditons, states
|
5
|
+
|
6
|
+
## Getting started
|
7
|
+
|
8
|
+
```ruby
|
9
|
+
gem install active_aggregate
|
10
|
+
```
|
@@ -0,0 +1,55 @@
|
|
1
|
+
module ActiveAggregate::Concern
|
2
|
+
extend ActiveSupport::Concern
|
3
|
+
|
4
|
+
class_methods do
|
5
|
+
attr_reader :model, :criteria
|
6
|
+
|
7
|
+
delegate :query, :query_criteria, :group, :project, :pipeline, :group_by, :to_a,
|
8
|
+
:where, :in, :any_off, :all_of,
|
9
|
+
to: :all
|
10
|
+
|
11
|
+
def define_for(model)
|
12
|
+
@model = model
|
13
|
+
@criteria = model.all
|
14
|
+
end
|
15
|
+
|
16
|
+
def scope(name, *options)
|
17
|
+
raise TypeError, 'set defined scope for model first' unless model
|
18
|
+
scope_name = name.to_sym
|
19
|
+
scopes[scope_name] = ActiveAggregate::Relation.new(self, *options)
|
20
|
+
singleton_class.send(:define_method, scope_name) do |*args|
|
21
|
+
return scopes[scope_name].generate(*args)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def all
|
26
|
+
raise TypeError, 'set defined scope for model first' unless model
|
27
|
+
ActiveAggregate::Relation.new(self).generate
|
28
|
+
end
|
29
|
+
|
30
|
+
def scope_names
|
31
|
+
scopes.keys
|
32
|
+
end
|
33
|
+
|
34
|
+
def scope?(scope_name)
|
35
|
+
scopes.key?(scope_name.to_sym)
|
36
|
+
end
|
37
|
+
|
38
|
+
def respond_to_missing?(method_name, include_private = false)
|
39
|
+
scope?(method_name) || super
|
40
|
+
end
|
41
|
+
|
42
|
+
def method_missing(scope_name, *args, &block)
|
43
|
+
name = scope_name.to_sym
|
44
|
+
if scope?(name)
|
45
|
+
merge(public_send(name, *args, &block))
|
46
|
+
else
|
47
|
+
super
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def scopes
|
52
|
+
@scopes ||= {}
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,184 @@
|
|
1
|
+
class ActiveAggregate::Relation
|
2
|
+
include ActiveSupport::Concern
|
3
|
+
|
4
|
+
attr_reader :scope_class, :body
|
5
|
+
|
6
|
+
def initialize(scope_class, body = nil, options = {})
|
7
|
+
@scope_class = scope_class
|
8
|
+
if body.respond_to?(:call)
|
9
|
+
init_default_value(options)
|
10
|
+
@body = body
|
11
|
+
else
|
12
|
+
init_default_value(body.nil? ? options : body)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
delegate :scope?, :scope_names, :model, to: :scope_class
|
17
|
+
delegate :selector, to: :criteria
|
18
|
+
delegate :to_a, :count, :first, to: :aggregate
|
19
|
+
delegate :select, :find, :last, :group_by, :each_with_object, :each,
|
20
|
+
:map, :reduce, :reject,
|
21
|
+
to: :to_a
|
22
|
+
|
23
|
+
def query(options)
|
24
|
+
case options
|
25
|
+
when Hash
|
26
|
+
merge(self.class.new(scope_class, options))
|
27
|
+
when self.class
|
28
|
+
merge(options)
|
29
|
+
else
|
30
|
+
self
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def query_criteria(options)
|
35
|
+
query(criteria: options)
|
36
|
+
end
|
37
|
+
|
38
|
+
def group(options)
|
39
|
+
query(group: options)
|
40
|
+
end
|
41
|
+
|
42
|
+
def pipeline(options)
|
43
|
+
query(pipeline: options)
|
44
|
+
end
|
45
|
+
|
46
|
+
def project(options)
|
47
|
+
query(project: options)
|
48
|
+
end
|
49
|
+
|
50
|
+
def sort(options)
|
51
|
+
@sort = options
|
52
|
+
self
|
53
|
+
end
|
54
|
+
|
55
|
+
def limit(options)
|
56
|
+
@limit = options
|
57
|
+
self
|
58
|
+
end
|
59
|
+
|
60
|
+
def expose
|
61
|
+
{
|
62
|
+
pipeline: @pipeline,
|
63
|
+
criteria: @criteria,
|
64
|
+
group: @group,
|
65
|
+
project: @project,
|
66
|
+
sort: @sort,
|
67
|
+
limit: @limit
|
68
|
+
}
|
69
|
+
end
|
70
|
+
|
71
|
+
def generate_project
|
72
|
+
return @project if @project.present?
|
73
|
+
selector.keys.each_with_object({}) { |con, hashes| hashes[con] = "$#{con}" }
|
74
|
+
end
|
75
|
+
|
76
|
+
def selector
|
77
|
+
@criteria ? @criteria.selector : {}
|
78
|
+
end
|
79
|
+
|
80
|
+
def generate_execute_pipeline(select_all: false, aggregate: {})
|
81
|
+
merge(aggregate) if aggregate.present?
|
82
|
+
[].tap do |execute_pipeline|
|
83
|
+
execute_pipeline << { '$match': @criteria.selector } if @criteria
|
84
|
+
execute_pipeline << { '$group': @group } if @group.present?
|
85
|
+
execute_pipeline << { '$sort': @sort } if @sort.present?
|
86
|
+
execute_pipeline << { '$project': project_selector } if select_all || @project.present?
|
87
|
+
execute_pipeline << { '$limit': @limit } if @limit.present?
|
88
|
+
execute_pipeline.push(*@pipeline)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
def generate(*args)
|
93
|
+
return self if body.nil?
|
94
|
+
result = scope_class.instance_exec(*args, &body)
|
95
|
+
case result
|
96
|
+
when Hash
|
97
|
+
init_default_value(merge_exposed(result))
|
98
|
+
self
|
99
|
+
when self.class
|
100
|
+
merge(result)
|
101
|
+
else
|
102
|
+
self
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
def aggregate(options = {}, *args)
|
107
|
+
model.collection.aggregate(generate_execute_pipeline(options), *args)
|
108
|
+
end
|
109
|
+
|
110
|
+
def add_pipeline(*stages)
|
111
|
+
@pipeline.push(*stages.flatten)
|
112
|
+
end
|
113
|
+
|
114
|
+
def override(*stages)
|
115
|
+
@pipeline = stages.flatten
|
116
|
+
end
|
117
|
+
|
118
|
+
def respond_to_missing?(method_name, include_private = false)
|
119
|
+
scope?(scope_name) || super
|
120
|
+
end
|
121
|
+
|
122
|
+
def method_missing(scope_name, *args, &block)
|
123
|
+
name = scope_name.to_sym
|
124
|
+
if scope?(scope_name)
|
125
|
+
merge(scope_class.public_send(name, *args, &block))
|
126
|
+
else
|
127
|
+
super
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
def where(*args)
|
132
|
+
query_criteria(model.where(*args))
|
133
|
+
end
|
134
|
+
|
135
|
+
def in(*args)
|
136
|
+
query_criteria(model.in(*args))
|
137
|
+
end
|
138
|
+
|
139
|
+
def any_of(*args)
|
140
|
+
query_criteria(model.any_of(*args))
|
141
|
+
end
|
142
|
+
|
143
|
+
def all_of(*args)
|
144
|
+
query_criteria(model.all_of(*args))
|
145
|
+
end
|
146
|
+
|
147
|
+
def merge(relation)
|
148
|
+
if !relation.is_a?(ActiveAggregate::Relation) || relation.scope_class != scope_class
|
149
|
+
raise TypeError, "#relation much be an ActiveAggregate::Relation of #{scope_class}"
|
150
|
+
end
|
151
|
+
self.class.new(scope_class, merge_exposed(relation.expose))
|
152
|
+
end
|
153
|
+
|
154
|
+
def merge_exposed(exposed)
|
155
|
+
{
|
156
|
+
pipeline: @pipeline + (exposed[:pipeline] || []),
|
157
|
+
criteria: [@criteria, format_criteria(exposed[:criteria])].compact.reduce(&:merge),
|
158
|
+
group: [@group, exposed[:group]].compact.reduce(&:deep_merge),
|
159
|
+
project: [@project, exposed[:project]].compact.reduce(&:deep_merge),
|
160
|
+
sort: exposed[:sort] || nil,
|
161
|
+
limit: exposed[:limit] || nil
|
162
|
+
}
|
163
|
+
end
|
164
|
+
|
165
|
+
private
|
166
|
+
|
167
|
+
def init_default_value(criteria: nil, pipeline: [], group: nil, project: nil, sort: nil, limit: nil)
|
168
|
+
@criteria = format_criteria(criteria)
|
169
|
+
@pipeline = pipeline.present? ? pipeline : []
|
170
|
+
@group = group.present? ? group : {}
|
171
|
+
@project = project.present? ? project : {}
|
172
|
+
@sort = sort
|
173
|
+
@limit = limit
|
174
|
+
end
|
175
|
+
|
176
|
+
def format_criteria(criteria)
|
177
|
+
case criteria
|
178
|
+
when Mongoid::Criteria
|
179
|
+
criteria
|
180
|
+
when self.class
|
181
|
+
format_criteria(criteria.expose[:criteria])
|
182
|
+
end
|
183
|
+
end
|
184
|
+
end
|
metadata
ADDED
@@ -0,0 +1,60 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: active_aggregate
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.2
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Phan Quang Tien
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2019-11-25 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: mongoid
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 5.0.1
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 5.0.1
|
27
|
+
description: A toolkit for building queries like ActiveRelation. Rich support for
|
28
|
+
more flexible merge conditons, states
|
29
|
+
email:
|
30
|
+
executables: []
|
31
|
+
extensions: []
|
32
|
+
extra_rdoc_files: []
|
33
|
+
files:
|
34
|
+
- README.md
|
35
|
+
- lib/active_aggregate/concern.rb
|
36
|
+
- lib/active_aggregate/relation.rb
|
37
|
+
homepage: https://github.com/phantien133/active_aggregate
|
38
|
+
licenses: []
|
39
|
+
metadata: {}
|
40
|
+
post_install_message:
|
41
|
+
rdoc_options: []
|
42
|
+
require_paths:
|
43
|
+
- lib
|
44
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
45
|
+
requirements:
|
46
|
+
- - ">="
|
47
|
+
- !ruby/object:Gem::Version
|
48
|
+
version: 2.2.3
|
49
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
50
|
+
requirements:
|
51
|
+
- - ">="
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '0'
|
54
|
+
requirements: []
|
55
|
+
rubygems_version: 3.0.6
|
56
|
+
signing_key:
|
57
|
+
specification_version: 4
|
58
|
+
summary: active_aggregate is a little helper support to queries by mongoDB aggregate
|
59
|
+
more easily
|
60
|
+
test_files: []
|