graphoid 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/MIT-LICENSE +20 -0
- data/README.md +62 -0
- data/Rakefile +33 -0
- data/lib/graphoid/argument.rb +12 -0
- data/lib/graphoid/config.rb +19 -0
- data/lib/graphoid/definitions/filters.rb +57 -0
- data/lib/graphoid/definitions/inputs.rb +41 -0
- data/lib/graphoid/definitions/orders.rb +43 -0
- data/lib/graphoid/definitions/types.rb +119 -0
- data/lib/graphoid/drivers/active_record.rb +187 -0
- data/lib/graphoid/drivers/mongoid.rb +214 -0
- data/lib/graphoid/graphield.rb +32 -0
- data/lib/graphoid/grapho.rb +23 -0
- data/lib/graphoid/main.rb +21 -0
- data/lib/graphoid/mapper.rb +10 -0
- data/lib/graphoid/mutations/create.rb +49 -0
- data/lib/graphoid/mutations/delete.rb +49 -0
- data/lib/graphoid/mutations/processor.rb +31 -0
- data/lib/graphoid/mutations/structure.rb +9 -0
- data/lib/graphoid/mutations/update.rb +55 -0
- data/lib/graphoid/operators/attribute.rb +64 -0
- data/lib/graphoid/operators/inherited/belongs_to.rb +15 -0
- data/lib/graphoid/operators/inherited/embeds_many.rb +10 -0
- data/lib/graphoid/operators/inherited/embeds_one.rb +8 -0
- data/lib/graphoid/operators/inherited/has_many.rb +11 -0
- data/lib/graphoid/operators/inherited/has_one.rb +16 -0
- data/lib/graphoid/operators/inherited/many_to_many.rb +11 -0
- data/lib/graphoid/operators/relation.rb +70 -0
- data/lib/graphoid/queries/operation.rb +40 -0
- data/lib/graphoid/queries/processor.rb +45 -0
- data/lib/graphoid/queries/queries.rb +52 -0
- data/lib/graphoid/scalars.rb +87 -0
- data/lib/graphoid/utils.rb +39 -0
- data/lib/graphoid/version.rb +3 -0
- data/lib/graphoid.rb +36 -0
- metadata +120 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 570048f8ac7a5863e2649feabf58df51352c8736a8d94f8009ce666d26f21e67
|
4
|
+
data.tar.gz: 3168975860650e784303f5cc57851b4ed7106e360dc55af30077f02c4c0a6f1a
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 3e8eae2534153a462a4a705ddf43176146b5042d64d812f5dd62d246bba0dfece3f306539f6e19eba33329e81e7aabb95d07ceecd21e0ecca419e347c2314963
|
7
|
+
data.tar.gz: cc0450a2be3f570fca4178aaf969ebf93aa3c981faf2ce43bdf6e079193c1c62bf8e3e38d77eda5d6ae64e271f3c7bacd9bc735256497e15879fcf1a5cee7afb
|
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright 2018
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,62 @@
|
|
1
|
+
# Graphoid
|
2
|
+
This gem is used to generate a full GraphQL api using introspection of MongoId models.
|
3
|
+
|
4
|
+
## Dependency
|
5
|
+
This gem depends on the graphql gem for rails https://github.com/rmosolgo/graphql-ruby
|
6
|
+
So it is required to have it and install it using
|
7
|
+
```bash
|
8
|
+
rails generate graphql:install
|
9
|
+
```
|
10
|
+
|
11
|
+
## Usage
|
12
|
+
Require all the models in which you want to have basic find one, find many, create, update and delete actions on.
|
13
|
+
|
14
|
+
Create the file `config/initializers/Graphoid.rb`
|
15
|
+
|
16
|
+
And require the models like this:
|
17
|
+
|
18
|
+
```ruby
|
19
|
+
Graphoid.configure do |config|
|
20
|
+
config.driver = :mongoid
|
21
|
+
config.driver = :active_record
|
22
|
+
end
|
23
|
+
Graphoid.initialize
|
24
|
+
```
|
25
|
+
|
26
|
+
## Installation
|
27
|
+
Add this line to your application's Gemfile:
|
28
|
+
|
29
|
+
```ruby
|
30
|
+
gem 'graphoid'
|
31
|
+
```
|
32
|
+
|
33
|
+
And then execute:
|
34
|
+
```bash
|
35
|
+
$ bundle
|
36
|
+
```
|
37
|
+
|
38
|
+
Or install it yourself as:
|
39
|
+
```bash
|
40
|
+
$ gem install graphoid
|
41
|
+
```
|
42
|
+
|
43
|
+
Then you can determine which queries and mutations should be created in `app/graphql/types/query_type.rb`
|
44
|
+
|
45
|
+
```ruby
|
46
|
+
include Graphoid::Queries
|
47
|
+
include Graphoid::Mutations
|
48
|
+
```
|
49
|
+
|
50
|
+
And which mutations should be created in `app/graphql/types/mutation_type.rb`
|
51
|
+
|
52
|
+
```ruby
|
53
|
+
include Graphoid::Graphield
|
54
|
+
```
|
55
|
+
|
56
|
+
## Contributing
|
57
|
+
Figure out the driver
|
58
|
+
Functionality to sort top level models by association values
|
59
|
+
Filter by Array or Hash => The cases are failing, implementation correction needed.
|
60
|
+
|
61
|
+
## License
|
62
|
+
The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
|
data/Rakefile
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
begin
|
2
|
+
require 'bundler/setup'
|
3
|
+
rescue LoadError
|
4
|
+
puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
|
5
|
+
end
|
6
|
+
|
7
|
+
require 'rdoc/task'
|
8
|
+
|
9
|
+
RDoc::Task.new(:rdoc) do |rdoc|
|
10
|
+
rdoc.rdoc_dir = 'rdoc'
|
11
|
+
rdoc.title = 'Graphoid'
|
12
|
+
rdoc.options << '--line-numbers'
|
13
|
+
rdoc.rdoc_files.include('README.md')
|
14
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
15
|
+
end
|
16
|
+
|
17
|
+
|
18
|
+
|
19
|
+
|
20
|
+
|
21
|
+
|
22
|
+
require 'bundler/gem_tasks'
|
23
|
+
|
24
|
+
require 'rake/testtask'
|
25
|
+
|
26
|
+
Rake::TestTask.new(:test) do |t|
|
27
|
+
t.libs << 'test'
|
28
|
+
t.pattern = 'test/**/*_test.rb'
|
29
|
+
t.verbose = false
|
30
|
+
end
|
31
|
+
|
32
|
+
|
33
|
+
task default: :test
|
@@ -0,0 +1,12 @@
|
|
1
|
+
module Graphoid
|
2
|
+
module Argument
|
3
|
+
class << self
|
4
|
+
def query_many(field, filter, order, required = {})
|
5
|
+
field.argument :where, filter, required
|
6
|
+
field.argument :order, order, required
|
7
|
+
field.argument :limit, GraphQL::Types::Int, required
|
8
|
+
field.argument :skip, GraphQL::Types::Int, required
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Graphoid
|
2
|
+
class << self
|
3
|
+
attr_accessor :configuration
|
4
|
+
|
5
|
+
def configure
|
6
|
+
self.configuration ||= Configuration.new
|
7
|
+
yield(configuration)
|
8
|
+
Graphoid.initialize
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
class Configuration
|
13
|
+
attr_accessor :driver
|
14
|
+
|
15
|
+
def initialize
|
16
|
+
@driver = :active_record
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
module Graphoid
|
2
|
+
module Filters
|
3
|
+
|
4
|
+
LIST = {}
|
5
|
+
|
6
|
+
class << self
|
7
|
+
def generate(model)
|
8
|
+
LIST[model] ||= GraphQL::InputObjectType.define do
|
9
|
+
name("#{Utils.graphqlize(model.name)}Filter")
|
10
|
+
description("Generated model filter for #{model.name}")
|
11
|
+
|
12
|
+
Attribute.fields_of(model).each do |field|
|
13
|
+
type = Graphoid::Mapper.convert(field)
|
14
|
+
name = Utils.camelize(field.name)
|
15
|
+
|
16
|
+
argument name, type
|
17
|
+
|
18
|
+
m = LIST[model]
|
19
|
+
argument(:OR, -> { types[m] })
|
20
|
+
argument(:AND, -> { types[m] })
|
21
|
+
|
22
|
+
operators = ["lt", "lte", "gt", "gte", "contains", "not"]
|
23
|
+
operators.push("regex") if Graphoid.configuration.driver == :mongoid
|
24
|
+
|
25
|
+
operators.each do |suffix|
|
26
|
+
argument "#{name}_#{suffix}", type
|
27
|
+
end
|
28
|
+
|
29
|
+
["in", "nin"].each do |suffix|
|
30
|
+
argument "#{name}_#{suffix}", types[type]
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
Relation.relations_of(model).each do |name, relation|
|
35
|
+
relation_class = relation.class_name.safe_constantize
|
36
|
+
next unless relation_class
|
37
|
+
|
38
|
+
relation_filter = LIST[relation_class]
|
39
|
+
next unless relation_filter
|
40
|
+
|
41
|
+
relation_name = Utils.camelize(name)
|
42
|
+
|
43
|
+
if Relation.new(relation).many?
|
44
|
+
["some", "none", "every"].each do |suffix|
|
45
|
+
argument "#{relation_name}_#{suffix}", relation_filter
|
46
|
+
end
|
47
|
+
else
|
48
|
+
argument "#{relation_name}", relation_filter
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module Graphoid
|
2
|
+
module Inputs
|
3
|
+
LIST = {}
|
4
|
+
|
5
|
+
class << self
|
6
|
+
def generate model
|
7
|
+
LIST[model] ||= GraphQL::InputObjectType.define do
|
8
|
+
name = Utils.graphqlize(model.name)
|
9
|
+
name("#{name}Input")
|
10
|
+
description("Generated model input for #{name}")
|
11
|
+
|
12
|
+
Attribute.fields_of(model).each do |field|
|
13
|
+
unless field.name.start_with?("_")
|
14
|
+
type = Graphoid::Mapper.convert(field)
|
15
|
+
name = Utils.camelize(field.name)
|
16
|
+
|
17
|
+
argument(name, type)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
Relation.relations_of(model).each do |name, relation|
|
22
|
+
relation_class = relation.class_name.safe_constantize
|
23
|
+
next unless relation_class
|
24
|
+
|
25
|
+
relation_input = LIST[relation_class]
|
26
|
+
next unless relation_input
|
27
|
+
|
28
|
+
name = Utils.camelize(relation.name)
|
29
|
+
|
30
|
+
r = Relation.new(relation)
|
31
|
+
if r.many?
|
32
|
+
argument(name, -> { types[relation_input] })
|
33
|
+
else
|
34
|
+
argument(name, -> { relation_input })
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module Graphoid
|
2
|
+
module Orders
|
3
|
+
|
4
|
+
LIST = {}
|
5
|
+
@@enum_type = nil
|
6
|
+
|
7
|
+
class << self
|
8
|
+
def generate(model)
|
9
|
+
LIST[model] ||= GraphQL::InputObjectType.define do
|
10
|
+
name("#{Utils.graphqlize(model.name)}Order")
|
11
|
+
description("Generated model order for #{model.name}")
|
12
|
+
|
13
|
+
Attribute.fields_of(model).each do |field|
|
14
|
+
name = Utils.camelize(field.name)
|
15
|
+
argument(name, Orders.enum_type)
|
16
|
+
end
|
17
|
+
|
18
|
+
Relation.relations_of(model).each do |name, relation|
|
19
|
+
relation_class = relation.class_name.safe_constantize
|
20
|
+
next unless relation_class
|
21
|
+
|
22
|
+
relation_order = LIST[relation_class]
|
23
|
+
next unless relation_order
|
24
|
+
|
25
|
+
relation_name = Utils.camelize(name)
|
26
|
+
|
27
|
+
argument(relation_name, relation_order)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def enum_type
|
33
|
+
@@enum_type ||= GraphQL::EnumType.define do
|
34
|
+
name "SortType"
|
35
|
+
|
36
|
+
value "ASC", "Ascendent"
|
37
|
+
value "DESC", "Descendent"
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
@@ -0,0 +1,119 @@
|
|
1
|
+
module Graphoid
|
2
|
+
module Types
|
3
|
+
|
4
|
+
LIST = {}
|
5
|
+
ENUMS = {}
|
6
|
+
|
7
|
+
class << self
|
8
|
+
def generate(model)
|
9
|
+
Graphoid::Types::Meta ||= GraphQL::ObjectType.define do
|
10
|
+
name("Meta")
|
11
|
+
description("Meta Type")
|
12
|
+
field("count", types.Int)
|
13
|
+
end
|
14
|
+
|
15
|
+
LIST[model] ||= GraphQL::ObjectType.define do
|
16
|
+
name = Utils.graphqlize(model.name)
|
17
|
+
name("#{name}Type")
|
18
|
+
description("Generated model type for #{name}")
|
19
|
+
|
20
|
+
Attribute.fields_of(model).each do |_field|
|
21
|
+
type = Graphoid::Mapper.convert(_field)
|
22
|
+
name = Utils.camelize(_field.name)
|
23
|
+
field(name, type)
|
24
|
+
|
25
|
+
model.class_eval do
|
26
|
+
if _field.name.include?('_')
|
27
|
+
define_method :"#{Utils.camelize(_field.name)}" do
|
28
|
+
method_name = _field.name.to_s
|
29
|
+
self[method_name] || self.send(method_name)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
Relation.relations_of(model).each do |name, relation|
|
36
|
+
relation_class = relation.class_name.safe_constantize
|
37
|
+
|
38
|
+
message = "in model #{model.name}: skipping relation #{relation.class_name}"
|
39
|
+
unless relation_class
|
40
|
+
STDERR.puts "Graphoid: warning: #{message} because the model name is not valid" if ENV['DEBUG']
|
41
|
+
next
|
42
|
+
end
|
43
|
+
|
44
|
+
relation_type = LIST[relation_class]
|
45
|
+
unless relation_type
|
46
|
+
STDERR.puts "Graphoid: warning: #{message} because it was not found as a model" if ENV['DEBUG']
|
47
|
+
next
|
48
|
+
end
|
49
|
+
|
50
|
+
name = Utils.camelize(relation.name)
|
51
|
+
|
52
|
+
model.class_eval do
|
53
|
+
if relation.name.to_s.include?('_')
|
54
|
+
define_method :"#{name}" do
|
55
|
+
self.send(relation.name)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
if relation_type
|
61
|
+
filter = Graphoid::Filters::LIST[relation_class]
|
62
|
+
order = Graphoid::Orders::LIST[relation_class]
|
63
|
+
|
64
|
+
if Relation.new(relation).many?
|
65
|
+
plural_name = name.pluralize
|
66
|
+
|
67
|
+
field plural_name, types[relation_type] do
|
68
|
+
Graphoid::Argument.query_many(self, filter, order)
|
69
|
+
Graphoid::Types.resolve_many(self, relation_class, relation)
|
70
|
+
end
|
71
|
+
|
72
|
+
field "_#{plural_name}_meta", Graphoid::Types::Meta do
|
73
|
+
Graphoid::Argument.query_many(self, filter, order)
|
74
|
+
Graphoid::Types.resolve_many(self, relation_class, relation)
|
75
|
+
end
|
76
|
+
else
|
77
|
+
field name, relation_type do
|
78
|
+
argument :where, filter
|
79
|
+
Graphoid::Types.resolve_one(self, relation_class, relation)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
def resolve_one(field, model, association)
|
88
|
+
field.resolve -> (obj, args, ctx) do
|
89
|
+
filter = args["where"].to_h
|
90
|
+
result = obj.send(association.name)
|
91
|
+
result = Graphoid::Queries::Processor.execute(model.where({ id: result.id }), filter).first if filter.present? && result
|
92
|
+
result
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
def resolve_many(field, model, association)
|
97
|
+
field.resolve -> (obj, args, ctx) do
|
98
|
+
filter = args["where"].to_h
|
99
|
+
order = args["order"].to_h
|
100
|
+
limit = args["limit"]
|
101
|
+
skip = args["skip"]
|
102
|
+
|
103
|
+
result = obj.send(association.name)
|
104
|
+
result = Graphoid::Queries::Processor.execute(result, filter) if filter.present?
|
105
|
+
|
106
|
+
if order.present?
|
107
|
+
order = Graphoid::Queries::Processor.parse_order(obj.send(association.name), order)
|
108
|
+
result = result.order(order)
|
109
|
+
end
|
110
|
+
|
111
|
+
result = result.limit(limit) if limit.present?
|
112
|
+
result = result.skip(skip) if skip.present?
|
113
|
+
|
114
|
+
result
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
@@ -0,0 +1,187 @@
|
|
1
|
+
module Graphoid
|
2
|
+
module ActiveRecordDriver
|
3
|
+
class << self
|
4
|
+
def through?(type)
|
5
|
+
type == ActiveRecord::Reflection::ThroughReflection
|
6
|
+
end
|
7
|
+
|
8
|
+
def has_and_belongs_to_many?(type)
|
9
|
+
type == ActiveRecord::Reflection::HasAndBelongsToManyReflection
|
10
|
+
end
|
11
|
+
|
12
|
+
def has_many?(type)
|
13
|
+
type == ActiveRecord::Reflection::HasManyReflection
|
14
|
+
end
|
15
|
+
|
16
|
+
def belongs_to?(type)
|
17
|
+
type == ActiveRecord::Reflection::BelongsToReflection
|
18
|
+
end
|
19
|
+
|
20
|
+
def has_one?(type)
|
21
|
+
type == ActiveRecord::Reflection::HasOneReflection
|
22
|
+
end
|
23
|
+
|
24
|
+
def embeds_one?(type)
|
25
|
+
false
|
26
|
+
end
|
27
|
+
|
28
|
+
def embeds_many?(type)
|
29
|
+
false
|
30
|
+
end
|
31
|
+
|
32
|
+
def embedded_in?(type)
|
33
|
+
false
|
34
|
+
end
|
35
|
+
|
36
|
+
def types_map
|
37
|
+
{
|
38
|
+
binary: GraphQL::Types::Boolean,
|
39
|
+
boolean: GraphQL::Types::Boolean,
|
40
|
+
float: GraphQL::Types::Float,
|
41
|
+
integer: GraphQL::Types::Int,
|
42
|
+
string: GraphQL::Types::String,
|
43
|
+
|
44
|
+
datetime: Graphoid::Scalars::DateTime,
|
45
|
+
date: Graphoid::Scalars::DateTime,
|
46
|
+
time: Graphoid::Scalars::DateTime,
|
47
|
+
timestamp: Graphoid::Scalars::DateTime,
|
48
|
+
text: Graphoid::Scalars::Text,
|
49
|
+
bigint: Graphoid::Scalars::BigInt,
|
50
|
+
decimal: Graphoid::Scalars::Decimal
|
51
|
+
}
|
52
|
+
end
|
53
|
+
|
54
|
+
def class_of(relation)
|
55
|
+
{
|
56
|
+
ActiveRecord::Reflection::HasAndBelongsToManyReflection => ManyToMany,
|
57
|
+
ActiveRecord::Reflection::BelongsToReflection => BelongsTo,
|
58
|
+
ActiveRecord::Reflection::ThroughReflection => ManyToMany,
|
59
|
+
ActiveRecord::Reflection::HasManyReflection => HasMany,
|
60
|
+
ActiveRecord::Reflection::HasOneReflection => HasOne
|
61
|
+
}[relation.class] || Relation
|
62
|
+
end
|
63
|
+
|
64
|
+
def inverse_name_of(relation)
|
65
|
+
relation.inverse_of&.class_name&.underscore
|
66
|
+
end
|
67
|
+
|
68
|
+
def fields_of(model)
|
69
|
+
model.columns
|
70
|
+
end
|
71
|
+
|
72
|
+
def relations_of(model)
|
73
|
+
model.reflections
|
74
|
+
end
|
75
|
+
|
76
|
+
def skip(result, skip)
|
77
|
+
result.offset(skip)
|
78
|
+
end
|
79
|
+
|
80
|
+
def relation_type(relation)
|
81
|
+
relation.class
|
82
|
+
end
|
83
|
+
|
84
|
+
def eager_load(_selection, model)
|
85
|
+
model
|
86
|
+
end
|
87
|
+
|
88
|
+
def execute_and(scope, parsed)
|
89
|
+
scope.where(parsed)
|
90
|
+
end
|
91
|
+
|
92
|
+
def execute_or(scope, list)
|
93
|
+
list.map! do |object|
|
94
|
+
Graphoid::Queries::Processor.execute(scope, object)
|
95
|
+
end
|
96
|
+
list.reduce(:or)
|
97
|
+
end
|
98
|
+
|
99
|
+
def parse(attribute, value, operator)
|
100
|
+
field = attribute.name
|
101
|
+
case operator
|
102
|
+
when "not"
|
103
|
+
parsed = *["#{field} != ?", value]
|
104
|
+
parsed = *["#{field} not like ?", "#{value}"] if attribute.type == :string
|
105
|
+
parsed = *["#{field} is not null"] if value.nil?
|
106
|
+
when "contains", "regex"
|
107
|
+
parsed = *["#{field} like ?", "%#{value}%"]
|
108
|
+
when "gt", "gte", "lt", "lte", "not", "in", "nin"
|
109
|
+
operator = { gt: ">", gte: ">=", lt: "<", lte: "<=", in: "in", nin: "not in" }[operator.to_sym]
|
110
|
+
parsed = *["#{field} #{operator} (?)", value]
|
111
|
+
else
|
112
|
+
parsed = *["#{field} = ?", value]
|
113
|
+
end
|
114
|
+
parsed
|
115
|
+
end
|
116
|
+
|
117
|
+
# TODO: fix this as it is unused
|
118
|
+
def relate_through(scope, relation, value)
|
119
|
+
# if relation.has_one_through?
|
120
|
+
# ids = Graphoid::Queries::Processor.execute(relation.klass, value).to_a.map(&:id)
|
121
|
+
# through = relation.source.options[:through].to_s.camelize.constantize
|
122
|
+
# ids = through.where(id: ids)
|
123
|
+
# ids = Graphoid::Queries::Processor.execute(relation.klass, value).to_a.map(&:id)
|
124
|
+
# parsed = *["#{field.underscore}_id in (?)", ids]
|
125
|
+
# end
|
126
|
+
end
|
127
|
+
|
128
|
+
def relate_many(scope, relation, value, operator)
|
129
|
+
parsed = {}
|
130
|
+
field_name = relation.inverse_name || scope.name.underscore
|
131
|
+
target = Graphoid::Queries::Processor.execute(relation.klass, value).to_a
|
132
|
+
|
133
|
+
if relation.many_to_many?
|
134
|
+
field_name = field_name.to_s.singularize + "_ids"
|
135
|
+
ids = target.map(&(field_name.to_sym))
|
136
|
+
ids.flatten!.uniq!
|
137
|
+
else
|
138
|
+
field_name = :"#{field_name}_id"
|
139
|
+
ids = target.map(&(field_name))
|
140
|
+
end
|
141
|
+
|
142
|
+
if operator == "none"
|
143
|
+
parsed = *["id not in (?)", ids] if ids.present?
|
144
|
+
elsif operator == "some"
|
145
|
+
parsed = *["id in (?)", ids]
|
146
|
+
elsif operator == "every"
|
147
|
+
|
148
|
+
# the following process is a SQL division
|
149
|
+
# the amount of queries it executes is on per row
|
150
|
+
# it is the same than doing an iteration process
|
151
|
+
# that iteration process would work in mongoid too
|
152
|
+
|
153
|
+
# TODO: check and fix this query for many to many relations
|
154
|
+
|
155
|
+
plural_name = relation.name.pluralize
|
156
|
+
conditions = value.map do |_key, _value|
|
157
|
+
operation = Operation.new(relation.klass, _key, _value)
|
158
|
+
parsed = parse(operation.operand, operation.value, operation.operator)
|
159
|
+
val = parsed.last.is_a?(String) ? "'#{parsed.last}'" : parsed.last
|
160
|
+
parsed = parsed.first.sub("?", val)
|
161
|
+
" AND #{parsed}"
|
162
|
+
end.join
|
163
|
+
|
164
|
+
query = "
|
165
|
+
SELECT count(id) as total, #{field_name}
|
166
|
+
FROM #{plural_name} A
|
167
|
+
GROUP BY #{field_name}
|
168
|
+
HAVING total = (
|
169
|
+
SELECT count(id)
|
170
|
+
FROM #{plural_name} B
|
171
|
+
WHERE B.#{field_name} = A.#{field_name}
|
172
|
+
#{conditions}
|
173
|
+
)
|
174
|
+
"
|
175
|
+
result = ActiveRecord::Base.connection.execute(query)
|
176
|
+
ids = result.map{ |row| row["#{field_name}"] }
|
177
|
+
|
178
|
+
parsed = *["id in (?)", ids]
|
179
|
+
end
|
180
|
+
|
181
|
+
parsed
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
end
|
186
|
+
|
187
|
+
end
|