active_aggregate 0.0.2 → 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 653177242ed87c2c3965933b6a7932644528af9ad7a5a861d950c0e5b3bd7078
4
- data.tar.gz: fcb515097fb8001c7cc314e7bca0131b1ab83ea155a3d69300537b77126bff9a
3
+ metadata.gz: 4ea7dac3e2a7ec25fe7a0626f180c744e56997e950097def32c6d5ffa3d834bc
4
+ data.tar.gz: 6960dd0b37fada8e6ea18b6b1513cf285a865472349403f8411bf8a7559654a5
5
5
  SHA512:
6
- metadata.gz: f086dc4d9a449ba36d25d775e85c52b2bd61299a4cedfe104b3a715917cd9e0037affc4410bfa1c2b609d8000a5410c279fe4d0dd1a15034373c0301429e17a3
7
- data.tar.gz: f6d84f33cf8c04003f3436df053fd5e86932c1edb7e04217768e5863b42f693fc1a9110e53b9939c1360b2483f9130c7f7b16de37e4df8ebd7720cfaa5efe2d6
6
+ metadata.gz: 728e4ebf029dfe740e67d3caa5bb8eb1ba720a02c7af329baee1285a6574c6ac560b1499c105056203bd57f9d87de59eb09d70bc16b61df8a89b978b16608fa2
7
+ data.tar.gz: e13acc34d877094783c344a34674263d1a629d25e9e9eab73febb02dd93ec5b0596284b01afc923d98ff5075b8e187ac495e33c6f6ce9588404dd276a647dd01
data/README.md CHANGED
@@ -1,10 +1,109 @@
1
1
  # active_aggregate
2
2
 
3
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
4
+ - A toolkit for building queries like ActiveRelation. Rich support for more flexible merge conditions, states
5
5
 
6
6
  ## Getting started
7
7
 
8
8
  ```ruby
9
9
  gem install active_aggregate
10
10
  ```
11
+
12
+ ## Requirements
13
+ - mongoid >= 5.0.1
14
+
15
+ ## Usage
16
+
17
+ ```ruby
18
+ # models/user.rb
19
+ class User
20
+ include Mongoid::Document
21
+
22
+ belongs_to :school
23
+ belongs_to :branch
24
+
25
+ scope :active, -> { where(status: :active) }
26
+ scope :by_status, ->(status) { where(status: status) }
27
+ end
28
+
29
+ class Query
30
+ include ActiveAggregate::Concern
31
+ end
32
+
33
+ class UserQuery < Query
34
+ define_for User
35
+
36
+ # you can use `criteria.active` instead of `User.active`
37
+ scope :load_active_user_names, criteria: User.active,
38
+ project: {
39
+ id: '$_id',
40
+ name: {
41
+ '$concat': [
42
+ '$first_name',
43
+ ' ',
44
+ '$last_name',
45
+ ]
46
+ }
47
+ }
48
+
49
+ scope :not_deleted, criteria: User.where(deleted_at: nil)
50
+ scope :load_user_ids,
51
+ ->(status:, school_id_branch_ids:) do
52
+ where(status: status).pipeline(
53
+ '$match': {
54
+ 'school_id_branch_id': {
55
+ '$in': school_id_branch_ids,
56
+ }
57
+ }
58
+ )
59
+
60
+ # it avaiable to use like
61
+ # query_criteria(User.by_status(status)).pipeline([
62
+ # '$match': {
63
+ # 'school_id_branch_id': {
64
+ # '$in': school_id_branch_ids,
65
+ # }
66
+ # }
67
+ # ])
68
+
69
+ end,
70
+ project: {
71
+ id: '$_id',
72
+ school_id_branch_id: {
73
+ '$concatArrays': [
74
+ ['$school_id'],
75
+ ['$branch_id'],
76
+ ]
77
+ }
78
+ }
79
+ end
80
+
81
+ # another way
82
+ # class Query
83
+ # include ActiveAggregate::Concern
84
+
85
+ # # On children class it will remove [suffix] at end of class name to get model name then you can skip call define_for each all of Query class
86
+ # # [suffix] have default value is Query
87
+ # with_suffix
88
+ # # with_suffix suffix: :Query
89
+ # end
90
+
91
+ # class UserQuery < Query
92
+ # end
93
+
94
+
95
+ UserQuery.not_deleted.load_user_ids.where(:created_at.lt => Time.current)
96
+ ```
97
+
98
+ - `scope` support define for:
99
+ - criteria: as `Mongoid::Criteria` it will place at first of pipeline if given, by default it is default scope
100
+ - group: as object, can be merge throw all queries.
101
+ - project: as object, will be replace throw merge ActiveAggregate::Relation
102
+ - sort, limit: as object, will be replace throw merge ActiveAggregate::Relation.
103
+ - pipeline: as Array will place end of pre-pipeline if given, merge with previous pipeline by concat 2 array
104
+ `scope` will generate pipeline to use with aggregate with states order by:
105
+ - State 1 is `$match` use`criteria` selector if selector present
106
+ - state 2 is `$group` if `group` given
107
+ - state 3 is `$project` if `project` given
108
+ - state 3 is `$limit` if `limit` given
109
+ - pipeline will be place from here
@@ -2,28 +2,22 @@ module ActiveAggregate::Concern
2
2
  extend ActiveSupport::Concern
3
3
 
4
4
  class_methods do
5
- attr_reader :model, :criteria
6
-
7
5
  delegate :query, :query_criteria, :group, :project, :pipeline, :group_by, :to_a,
8
- :where, :in, :any_off, :all_of,
6
+ :where, :in, :where_in, :any_off, :all_of, :limit, :sort,
9
7
  to: :all
10
8
 
11
- def define_for(model)
12
- @model = model
13
- @criteria = model.all
14
- end
15
-
16
9
  def scope(name, *options)
17
- raise TypeError, 'set defined scope for model first' unless model
10
+ required_model!
18
11
  scope_name = name.to_sym
19
- scopes[scope_name] = ActiveAggregate::Relation.new(self, *options)
12
+ scopes[scope_name] = ActiveAggregate::Relation.new(self, *options).tap { |relation| relation.cacheable = false }
20
13
  singleton_class.send(:define_method, scope_name) do |*args|
14
+ scopes[scope_name] = ActiveAggregate::Relation.new(self, *options)
21
15
  return scopes[scope_name].generate(*args)
22
16
  end
23
17
  end
24
18
 
25
19
  def all
26
- raise TypeError, 'set defined scope for model first' unless model
20
+ required_model!
27
21
  ActiveAggregate::Relation.new(self).generate
28
22
  end
29
23
 
@@ -51,5 +45,40 @@ module ActiveAggregate::Concern
51
45
  def scopes
52
46
  @scopes ||= {}
53
47
  end
48
+
49
+ def model
50
+ @model ||= load_model
51
+ end
52
+
53
+ def criteria
54
+ @criteria ||= load_model.all
55
+ end
56
+
57
+ private
58
+
59
+ def define_for(model)
60
+ @criteria = model.all
61
+ @model = model
62
+ end
63
+
64
+ def with_suffix(suffix: :Query)
65
+ singleton_class.send(:define_method, :suffix) do
66
+ suffix
67
+ end
68
+ end
69
+
70
+ def load_model
71
+ @model || define_for_model_by_remove_suffix
72
+ end
73
+
74
+ def define_for_model_by_remove_suffix
75
+ return if !defined?(suffix) || suffix.nil?
76
+ model_name = name[0..(name.length - suffix.length - 1)]
77
+ define_for(model_name.constantize)
78
+ end
79
+
80
+ def required_model!
81
+ raise TypeError, 'set defined scope for model first' unless model
82
+ end
54
83
  end
55
84
  end
@@ -2,9 +2,11 @@ class ActiveAggregate::Relation
2
2
  include ActiveSupport::Concern
3
3
 
4
4
  attr_reader :scope_class, :body
5
+ attr_accessor :cacheable
5
6
 
6
7
  def initialize(scope_class, body = nil, options = {})
7
8
  @scope_class = scope_class
9
+ @cacheable = true
8
10
  if body.respond_to?(:call)
9
11
  init_default_value(options)
10
12
  @body = body
@@ -15,9 +17,9 @@ class ActiveAggregate::Relation
15
17
 
16
18
  delegate :scope?, :scope_names, :model, to: :scope_class
17
19
  delegate :selector, to: :criteria
18
- delegate :to_a, :count, :first, to: :aggregate
20
+ delegate :count, :first, to: :aggregate
19
21
  delegate :select, :find, :last, :group_by, :each_with_object, :each,
20
- :map, :reduce, :reject,
22
+ :map, :reduce, :reject, :to_json,
21
23
  to: :to_a
22
24
 
23
25
  def query(options)
@@ -80,10 +82,10 @@ class ActiveAggregate::Relation
80
82
  def generate_execute_pipeline(select_all: false, aggregate: {})
81
83
  merge(aggregate) if aggregate.present?
82
84
  [].tap do |execute_pipeline|
83
- execute_pipeline << { '$match': @criteria.selector } if @criteria
85
+ execute_pipeline << { '$match': selector } if selector.present?
84
86
  execute_pipeline << { '$group': @group } if @group.present?
85
87
  execute_pipeline << { '$sort': @sort } if @sort.present?
86
- execute_pipeline << { '$project': project_selector } if select_all || @project.present?
88
+ execute_pipeline << { '$project': generate_project } if select_all || @project.present?
87
89
  execute_pipeline << { '$limit': @limit } if @limit.present?
88
90
  execute_pipeline.push(*@pipeline)
89
91
  end
@@ -107,6 +109,13 @@ class ActiveAggregate::Relation
107
109
  model.collection.aggregate(generate_execute_pipeline(options), *args)
108
110
  end
109
111
 
112
+ def to_a
113
+ return @as_array if cacheable && @as_array
114
+ aggregate.to_a.tap { |array| @as_array ||= array if cacheable }
115
+ end
116
+
117
+ alias load to_a
118
+
110
119
  def add_pipeline(*stages)
111
120
  @pipeline.push(*stages.flatten)
112
121
  end
@@ -136,6 +145,20 @@ class ActiveAggregate::Relation
136
145
  query_criteria(model.in(*args))
137
146
  end
138
147
 
148
+ def pluck(*fields)
149
+ fields = Array.wrap(fields).map(&:to_s)
150
+ to_a.each_with_object([]) do |doc, result|
151
+ record = if fields.size == 1
152
+ doc[fields.first]
153
+ else
154
+ doc.slice(*fields).values
155
+ end
156
+ result << record unless record.nil?
157
+ end
158
+ end
159
+
160
+ alias where_in in
161
+
139
162
  def any_of(*args)
140
163
  query_criteria(model.any_of(*args))
141
164
  end
@@ -164,7 +187,7 @@ class ActiveAggregate::Relation
164
187
 
165
188
  private
166
189
 
167
- def init_default_value(criteria: nil, pipeline: [], group: nil, project: nil, sort: nil, limit: nil)
190
+ def init_default_value(criteria: model.all, pipeline: [], group: nil, project: nil, sort: nil, limit: nil)
168
191
  @criteria = format_criteria(criteria)
169
192
  @pipeline = pipeline.present? ? pipeline : []
170
193
  @group = group.present? ? group : {}
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: active_aggregate
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.2
4
+ version: 0.0.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Phan Quang Tien
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-11-25 00:00:00.000000000 Z
11
+ date: 2020-05-08 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: mongoid
@@ -25,7 +25,7 @@ dependencies:
25
25
  - !ruby/object:Gem::Version
26
26
  version: 5.0.1
27
27
  description: A toolkit for building queries like ActiveRelation. Rich support for
28
- more flexible merge conditons, states
28
+ more flexible merge conditions, states
29
29
  email:
30
30
  executables: []
31
31
  extensions: []
@@ -56,5 +56,5 @@ rubygems_version: 3.0.6
56
56
  signing_key:
57
57
  specification_version: 4
58
58
  summary: active_aggregate is a little helper support to queries by mongoDB aggregate
59
- more easily
59
+ more easily.
60
60
  test_files: []