active_aggregate 0.0.2 → 0.0.3

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 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: []