mongo_ql 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/.gitignore +9 -0
- data/.rubocop.yml +173 -0
- data/LICENSE.txt +21 -0
- data/README.md +1 -0
- data/Untitled-1 +4 -0
- data/bin/console +7 -0
- data/bin/setup +5 -0
- data/design_specs.md +80 -0
- data/lib/mongo_ql/binary_operators.rb +30 -0
- data/lib/mongo_ql/collection_operators.rb +64 -0
- data/lib/mongo_ql/date_operators.rb +25 -0
- data/lib/mongo_ql/expression/binary.rb +17 -0
- data/lib/mongo_ql/expression/date_note.rb +19 -0
- data/lib/mongo_ql/expression/field_node.rb +23 -0
- data/lib/mongo_ql/expression/method_call.rb +22 -0
- data/lib/mongo_ql/expression/unary.rb +16 -0
- data/lib/mongo_ql/expression/value_node.rb +15 -0
- data/lib/mongo_ql/expression.rb +68 -0
- data/lib/mongo_ql/string_operators.rb +20 -0
- data/lib/mongo_ql/unary_operators.rb +17 -0
- data/lib/mongo_ql/version.rb +5 -0
- data/lib/mongo_ql.rb +13 -0
- data/mongo_ql.gemspec +21 -0
- metadata +66 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 54ebbab82b00ce95966996491caf76cea95683f8f88be3676f15655c836ffa6b
|
4
|
+
data.tar.gz: 4e9bd70e840f6f2c118fcba06e8d086ceca67d42bfa8ecd82d3cf8412dd9191e
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 591efcb215b9769e6be3e4cdbf0d493a6c3800042fec75a649852afd0901b25916ad063516a3f46af05b9a44bdb62eb5cd719f754624d7b15135e85177e3e7d3
|
7
|
+
data.tar.gz: 586002d366b3d6f264ee8e6621297679febed34b869e6a9c94d4e9698d64a58a07d663b275d313682bef6d7ef16edf1187bd535503312e67acc53abdaea17264
|
data/.gitignore
ADDED
data/.rubocop.yml
ADDED
@@ -0,0 +1,173 @@
|
|
1
|
+
AllCops:
|
2
|
+
TargetRubyVersion: 2.6
|
3
|
+
# RuboCop has a bunch of cops enabled by default. This setting tells RuboCop
|
4
|
+
# to ignore them, so only the ones explicitly set in this file are enabled.
|
5
|
+
DisabledByDefault: true
|
6
|
+
Exclude:
|
7
|
+
- 'bin/**/*'
|
8
|
+
- 'Rakefile'
|
9
|
+
|
10
|
+
# Security
|
11
|
+
Security:
|
12
|
+
Enabled: true
|
13
|
+
|
14
|
+
Security/YAMLLoad:
|
15
|
+
Enabled: false
|
16
|
+
|
17
|
+
# Prefer &&/|| over and/or.
|
18
|
+
Style/AndOr:
|
19
|
+
Enabled: true
|
20
|
+
|
21
|
+
# Do not use braces for hash literals when they are the last argument of a
|
22
|
+
# method call.
|
23
|
+
Style/BracesAroundHashParameters:
|
24
|
+
Enabled: true
|
25
|
+
EnforcedStyle: context_dependent
|
26
|
+
|
27
|
+
# Align `when` with `case`.
|
28
|
+
Layout/CaseIndentation:
|
29
|
+
Enabled: true
|
30
|
+
|
31
|
+
# Align comments with method definitions.
|
32
|
+
Layout/CommentIndentation:
|
33
|
+
Enabled: true
|
34
|
+
|
35
|
+
Layout/ElseAlignment:
|
36
|
+
Enabled: true
|
37
|
+
|
38
|
+
Layout/EmptyLineAfterMagicComment:
|
39
|
+
Enabled: false
|
40
|
+
|
41
|
+
# In a regular class definition, no empty lines around the body.
|
42
|
+
Layout/EmptyLinesAroundClassBody:
|
43
|
+
Enabled: true
|
44
|
+
|
45
|
+
# In a regular method definition, no empty lines around the body.
|
46
|
+
Layout/EmptyLinesAroundMethodBody:
|
47
|
+
Enabled: true
|
48
|
+
|
49
|
+
# In a regular module definition, no empty lines around the body.
|
50
|
+
Layout/EmptyLinesAroundModuleBody:
|
51
|
+
Enabled: false
|
52
|
+
|
53
|
+
Layout/IndentFirstArgument:
|
54
|
+
Enabled: true
|
55
|
+
|
56
|
+
# Use Ruby >= 1.9 syntax for hashes. Prefer { a: :b } over { :a => :b }.
|
57
|
+
Style/HashSyntax:
|
58
|
+
Enabled: true
|
59
|
+
|
60
|
+
# Method definitions after `private` or `protected` isolated calls need one
|
61
|
+
# extra level of indentation.
|
62
|
+
Layout/IndentationConsistency:
|
63
|
+
Enabled: true
|
64
|
+
EnforcedStyle: indented_internal_methods
|
65
|
+
|
66
|
+
# Two spaces, no tabs (for indentation).
|
67
|
+
Layout/IndentationWidth:
|
68
|
+
Enabled: true
|
69
|
+
|
70
|
+
Layout/LeadingCommentSpace:
|
71
|
+
Enabled: true
|
72
|
+
|
73
|
+
Layout/SpaceAfterColon:
|
74
|
+
Enabled: true
|
75
|
+
|
76
|
+
Layout/SpaceAfterComma:
|
77
|
+
Enabled: true
|
78
|
+
|
79
|
+
Layout/SpaceAroundEqualsInParameterDefault:
|
80
|
+
Enabled: true
|
81
|
+
|
82
|
+
Layout/SpaceAroundKeyword:
|
83
|
+
Enabled: true
|
84
|
+
|
85
|
+
Layout/SpaceAroundOperators:
|
86
|
+
Enabled: true
|
87
|
+
|
88
|
+
Layout/SpaceBeforeComma:
|
89
|
+
Enabled: true
|
90
|
+
|
91
|
+
Layout/SpaceBeforeFirstArg:
|
92
|
+
Enabled: true
|
93
|
+
|
94
|
+
Style/DefWithParentheses:
|
95
|
+
Enabled: true
|
96
|
+
|
97
|
+
# Defining a method with parameters needs parentheses.
|
98
|
+
Style/MethodDefParentheses:
|
99
|
+
Enabled: true
|
100
|
+
|
101
|
+
Style/FrozenStringLiteralComment:
|
102
|
+
Enabled: true
|
103
|
+
EnforcedStyle: always
|
104
|
+
Exclude:
|
105
|
+
- 'actionview/test/**/*.builder'
|
106
|
+
- 'actionview/test/**/*.ruby'
|
107
|
+
- 'actionpack/test/**/*.builder'
|
108
|
+
- 'actionpack/test/**/*.ruby'
|
109
|
+
- 'activestorage/db/migrate/**/*.rb'
|
110
|
+
- 'db/migrate/**/*.rb'
|
111
|
+
- 'db/*.rb'
|
112
|
+
|
113
|
+
# Use `foo {}` not `foo{}`.
|
114
|
+
Layout/SpaceBeforeBlockBraces:
|
115
|
+
Enabled: true
|
116
|
+
|
117
|
+
# Use `foo { bar }` not `foo {bar}`.
|
118
|
+
Layout/SpaceInsideBlockBraces:
|
119
|
+
Enabled: true
|
120
|
+
|
121
|
+
# Use `{ a: 1 }` not `{a:1}`.
|
122
|
+
Layout/SpaceInsideHashLiteralBraces:
|
123
|
+
Enabled: false
|
124
|
+
|
125
|
+
Layout/SpaceInsideParens:
|
126
|
+
Enabled: true
|
127
|
+
|
128
|
+
# Check quotes usage according to lint rule below.
|
129
|
+
Style/StringLiterals:
|
130
|
+
Enabled: true
|
131
|
+
EnforcedStyle: double_quotes
|
132
|
+
|
133
|
+
# Detect hard tabs, no hard tabs.
|
134
|
+
Layout/Tab:
|
135
|
+
Enabled: true
|
136
|
+
|
137
|
+
# Blank lines should not have any spaces.
|
138
|
+
Layout/TrailingBlankLines:
|
139
|
+
Enabled: false
|
140
|
+
|
141
|
+
# No trailing whitespace.
|
142
|
+
Layout/TrailingWhitespace:
|
143
|
+
Enabled: true
|
144
|
+
|
145
|
+
# Use quotes for string literals when they are enough.
|
146
|
+
Style/UnneededPercentQ:
|
147
|
+
Enabled: true
|
148
|
+
|
149
|
+
Lint:
|
150
|
+
Enabled: true
|
151
|
+
|
152
|
+
Lint/UselessAccessModifier:
|
153
|
+
Enabled: false
|
154
|
+
|
155
|
+
Lint/UnusedMethodArgument:
|
156
|
+
Enabled: false
|
157
|
+
|
158
|
+
Style/RedundantReturn:
|
159
|
+
Enabled: true
|
160
|
+
AllowMultipleReturnValues: true
|
161
|
+
|
162
|
+
Style/Semicolon:
|
163
|
+
Enabled: true
|
164
|
+
AllowAsExpressionSeparator: true
|
165
|
+
|
166
|
+
Naming/AccessorMethodName:
|
167
|
+
Enabled: true
|
168
|
+
|
169
|
+
Naming/ConstantName:
|
170
|
+
Enabled: true
|
171
|
+
|
172
|
+
Naming/FileName:
|
173
|
+
Enabled: true
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2019 KUBERCLOUD SOLUTIONS INC.
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
# mongo_ql
|
data/Untitled-1
ADDED
data/bin/console
ADDED
data/bin/setup
ADDED
data/design_specs.md
ADDED
@@ -0,0 +1,80 @@
|
|
1
|
+
|
2
|
+
# Query DSL
|
3
|
+
```ruby
|
4
|
+
Order.where { tax != 0 }
|
5
|
+
#=> Order.where(tax: { "$ne": 0 })
|
6
|
+
Order.where { total >= 100 }
|
7
|
+
#=> Order.where(total: { "$gte": 100 })
|
8
|
+
Order.where { (total >= 100) & (total_tax < 15) }
|
9
|
+
#=> Order.where({ "$and": [{ total: { "$gte": 100 }}, { total_tax: { "$lt": 15 }}]})
|
10
|
+
Order.where { tax > (shipping / 2) }
|
11
|
+
#=> Order.where(tax: { "$gt": { "$divide": [ "$shipping", 2]}})
|
12
|
+
Order.where { total >= If(currency == "CAD", 100, 80) }
|
13
|
+
#=> Order.where(total: { "$cond": { if: { "$eq": ["$currency", "CAD"]}, then: 100, else: 80 }})
|
14
|
+
```
|
15
|
+
|
16
|
+
# Aggregation Pipeline DSL
|
17
|
+
```ruby
|
18
|
+
Order.all.mongo_ql do
|
19
|
+
join Customer,
|
20
|
+
:customer_id => _id,
|
21
|
+
:as => customers
|
22
|
+
|
23
|
+
join Shipping, :as => shippings do
|
24
|
+
match order_id == doc._id,
|
25
|
+
status == :shipped
|
26
|
+
end
|
27
|
+
|
28
|
+
match province == "ON"
|
29
|
+
|
30
|
+
project :_id,
|
31
|
+
:total,
|
32
|
+
:customer => customers.name,
|
33
|
+
:tax => total * tax_rate
|
34
|
+
|
35
|
+
group customer,
|
36
|
+
:total => total.sum,
|
37
|
+
:total_tax => tax.sum * 5
|
38
|
+
end
|
39
|
+
|
40
|
+
# The above aggregation is equivalent to the following mognodb pipeline
|
41
|
+
Order.collection.pipeline([
|
42
|
+
{ "$lookup": {
|
43
|
+
from: "customers",
|
44
|
+
localField: "$customer_id",
|
45
|
+
foreignField: "$_id",
|
46
|
+
as: "customers"
|
47
|
+
}},
|
48
|
+
{ "$unwind": {
|
49
|
+
path: "customers"
|
50
|
+
}},
|
51
|
+
{ "$lookup": {
|
52
|
+
from: "shippings",
|
53
|
+
as: "shippings",
|
54
|
+
let: { doc_id: "$_id" },
|
55
|
+
pipeline: [{
|
56
|
+
"$match": {
|
57
|
+
order_id: { "$eq": "$$dock_id" },
|
58
|
+
status: :shipped
|
59
|
+
}
|
60
|
+
}]
|
61
|
+
}},
|
62
|
+
{ "$unwind": {
|
63
|
+
path: "customers"
|
64
|
+
}},
|
65
|
+
{ "$match": {
|
66
|
+
province: { "$eq": "ON" }
|
67
|
+
}},
|
68
|
+
{ "$project": {
|
69
|
+
_id: 1,
|
70
|
+
total: 1,
|
71
|
+
customer: "$customers.name",
|
72
|
+
tax: { "$multiply": ["$total", "$tax_rate"] }
|
73
|
+
}},
|
74
|
+
{ "$group": {
|
75
|
+
_id: "$customer",
|
76
|
+
total: { "$sum": "$total" },
|
77
|
+
total_tax: { "$multiply": [{ "$sum": "$tax" }, 5] }
|
78
|
+
}}
|
79
|
+
])
|
80
|
+
```
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module MongoQL
|
4
|
+
module BinaryOperators
|
5
|
+
BINARY_OPS = {
|
6
|
+
"+": "$add",
|
7
|
+
"-": "$subtract",
|
8
|
+
"*": "$multiply",
|
9
|
+
"/": "$divide",
|
10
|
+
">": "$gt",
|
11
|
+
"<": "$lt",
|
12
|
+
">=": "$gte",
|
13
|
+
"<=": "$lte",
|
14
|
+
"!=": "$ne",
|
15
|
+
"==": "$eq",
|
16
|
+
"&": "$and",
|
17
|
+
"|": "$or",
|
18
|
+
"%": "$mod",
|
19
|
+
"**": "$pow"
|
20
|
+
}.freeze
|
21
|
+
|
22
|
+
BINARY_OPS.keys.each do |op|
|
23
|
+
class_eval <<~RUBY
|
24
|
+
def #{op}(right_node)
|
25
|
+
Expression::Binary.new(BINARY_OPS[__method__], self, right_node)
|
26
|
+
end
|
27
|
+
RUBY
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module MongoQL
|
4
|
+
module CollectionOperators
|
5
|
+
AGGREGATE_OPS = {
|
6
|
+
"max": "$max",
|
7
|
+
"min": "$min",
|
8
|
+
"first": "$first",
|
9
|
+
"last": "$last",
|
10
|
+
"sum": "$sum",
|
11
|
+
"avg": "$avg",
|
12
|
+
"size": "$size",
|
13
|
+
"push": "$push"
|
14
|
+
}.freeze
|
15
|
+
|
16
|
+
AGGREGATE_OPS.keys.each do |op|
|
17
|
+
class_eval <<~RUBY
|
18
|
+
def #{op}
|
19
|
+
Expression::MethodCall.new(AGGREGATE_OPS[__method__], self)
|
20
|
+
end
|
21
|
+
RUBY
|
22
|
+
end
|
23
|
+
|
24
|
+
def filter(&block)
|
25
|
+
Expression::MethodCall.new "$filter", self, ast_template: -> (target, **_args) {
|
26
|
+
{
|
27
|
+
"input" => target,
|
28
|
+
"as" => "item",
|
29
|
+
"cond" => block.call(Expression::FieldNode.new("$item")).to_ast
|
30
|
+
}
|
31
|
+
}
|
32
|
+
end
|
33
|
+
|
34
|
+
def map(&block)
|
35
|
+
Expression::MethodCall.new "$map", self, ast_template: -> (target, **_args) {
|
36
|
+
{
|
37
|
+
"input" => target,
|
38
|
+
"as" => "item",
|
39
|
+
"in" => block.call(Expression::FieldNode.new("$item")).to_ast
|
40
|
+
}
|
41
|
+
}
|
42
|
+
end
|
43
|
+
|
44
|
+
def reduce(initial_value, &block)
|
45
|
+
Expression::MethodCall.new "$reduce", self, ast_template: -> (target, **_args) {
|
46
|
+
{
|
47
|
+
"input" => target,
|
48
|
+
"initialValue" => to_expression(initial_value).to_ast,
|
49
|
+
"in" => to_expression(block.call(Expression::FieldNode.new("$value"), Expression::FieldNode.new("$this"))).to_ast
|
50
|
+
}
|
51
|
+
}
|
52
|
+
end
|
53
|
+
|
54
|
+
def contains(ele)
|
55
|
+
Expression::MethodCall.new "$in", self, ast_template: -> (target, **_args) {
|
56
|
+
[to_expression(ele).to_ast, target]
|
57
|
+
}
|
58
|
+
end
|
59
|
+
alias_method :includes, :contains
|
60
|
+
alias_method :include, :contains
|
61
|
+
alias_method :include?, :contains
|
62
|
+
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module MongoQL
|
4
|
+
module DateOperators
|
5
|
+
DATE_OPERATORS = {
|
6
|
+
"year": "$year",
|
7
|
+
"week": "$week",
|
8
|
+
"month": "$month",
|
9
|
+
"day_of_month": "$dayOfMonth",
|
10
|
+
"day_of_week": "$dayOfWeek",
|
11
|
+
"day_of_year": "$dayOfYear",
|
12
|
+
"iso_day_of_week": "$isoDayOfWeek",
|
13
|
+
"iso_week": "$isoWeek",
|
14
|
+
"iso_week_Year": "$isoWeekYear"
|
15
|
+
}.freeze
|
16
|
+
|
17
|
+
DATE_OPERATORS.keys.each do |op|
|
18
|
+
class_eval <<~RUBY
|
19
|
+
def #{op}
|
20
|
+
Expression::MethodCall.new DATE_OPERATORS[__method__], self
|
21
|
+
end
|
22
|
+
RUBY
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module MongoQL
|
4
|
+
class Expression::Binary < Expression
|
5
|
+
attr_accessor :operator, :left_node, :right_node
|
6
|
+
|
7
|
+
def initialize(operator, left, right)
|
8
|
+
@operator = operator
|
9
|
+
@left_node = to_expression(left)
|
10
|
+
@right_node = to_expression(right)
|
11
|
+
end
|
12
|
+
|
13
|
+
def to_ast
|
14
|
+
{ operator => [left_node.to_ast, right_node.to_ast] }
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "../date_operators"
|
4
|
+
|
5
|
+
module MongoQL
|
6
|
+
class Expression::DateNode < Expression
|
7
|
+
include DateOperators
|
8
|
+
|
9
|
+
attr_accessor :expression
|
10
|
+
|
11
|
+
def initialize(expression)
|
12
|
+
@expression = expression
|
13
|
+
end
|
14
|
+
|
15
|
+
def to_ast
|
16
|
+
expression.to_ast
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module MongoQL
|
4
|
+
class Expression::FieldNode < Expression
|
5
|
+
attr_accessor :field_name
|
6
|
+
|
7
|
+
def initialize(name)
|
8
|
+
@field_name = name
|
9
|
+
end
|
10
|
+
|
11
|
+
def method_missing(method_name, *args, &block)
|
12
|
+
Expression::FieldNode.new("#{field_name}.#{method_name}")
|
13
|
+
end
|
14
|
+
|
15
|
+
def f(field)
|
16
|
+
Expression::FieldNode.new("#{field_name}.#{field}")
|
17
|
+
end
|
18
|
+
|
19
|
+
def to_ast
|
20
|
+
"$#{field_name}"
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module MongoQL
|
4
|
+
class Expression::MethodCall < Expression
|
5
|
+
attr_accessor :method, :target, :ast_template, :args
|
6
|
+
|
7
|
+
def initialize(method, target, ast_template: nil, **args)
|
8
|
+
@target = to_expression(target)
|
9
|
+
@method = method
|
10
|
+
@ast_template = ast_template
|
11
|
+
@args = args
|
12
|
+
end
|
13
|
+
|
14
|
+
def to_ast
|
15
|
+
if ast_template
|
16
|
+
{ method => ast_template.call(target.to_ast, **args) }
|
17
|
+
else
|
18
|
+
{ method => target.to_ast }
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module MongoQL
|
4
|
+
class Expression::Unary < Expression
|
5
|
+
attr_accessor :operator, :right_node
|
6
|
+
|
7
|
+
def initialize(operator, right)
|
8
|
+
@operator = operator
|
9
|
+
@right_node = to_expression(right)
|
10
|
+
end
|
11
|
+
|
12
|
+
def to_ast
|
13
|
+
{ operator => right_node.to_ast }
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "binary_operators"
|
4
|
+
require_relative "unary_operators"
|
5
|
+
require_relative "collection_operators"
|
6
|
+
require_relative "string_operators"
|
7
|
+
|
8
|
+
module MongoQL
|
9
|
+
class Expression
|
10
|
+
include BinaryOperators
|
11
|
+
include UnaryOperators
|
12
|
+
include CollectionOperators
|
13
|
+
include StringOperators
|
14
|
+
|
15
|
+
FORMATING_OPS = {
|
16
|
+
"to_object_id": "$toObjectId",
|
17
|
+
"to_id": "$toObjectId",
|
18
|
+
"to_s": "$toString",
|
19
|
+
"to_string": "$toString",
|
20
|
+
"to_int": "$toInt",
|
21
|
+
"to_long": "$toLong",
|
22
|
+
"to_bool": "$toBool",
|
23
|
+
"to_date": "$toDate",
|
24
|
+
"to_decimal": "$toDecimal",
|
25
|
+
"to_double": "$toDouble",
|
26
|
+
"downcase": "$toLower",
|
27
|
+
"to_lower": "$toLower",
|
28
|
+
"upcase": "$toUpper",
|
29
|
+
"to_upper": "$toUpper"
|
30
|
+
}.freeze
|
31
|
+
|
32
|
+
FORMATING_OPS.keys.each do |op|
|
33
|
+
class_eval <<~RUBY
|
34
|
+
def #{op}
|
35
|
+
Expression::MethodCall.new(FORMATING_OPS[__method__], self)
|
36
|
+
end
|
37
|
+
RUBY
|
38
|
+
end
|
39
|
+
|
40
|
+
def type
|
41
|
+
Expression::MethodCall.new "$type", self
|
42
|
+
end
|
43
|
+
|
44
|
+
def if_null(default_val)
|
45
|
+
Expression::MethodCall.new "$ifNull", self, ast_template: -> (target, **_args) {
|
46
|
+
[target, to_expression(default_val).to_ast]
|
47
|
+
}
|
48
|
+
end
|
49
|
+
alias_method :default, :if_null
|
50
|
+
|
51
|
+
def as_date
|
52
|
+
Expression::DateNode.new(self)
|
53
|
+
end
|
54
|
+
|
55
|
+
def to_ast
|
56
|
+
raise NotImplementedError, "#{self.class.name} must implement to_ast"
|
57
|
+
end
|
58
|
+
|
59
|
+
protected
|
60
|
+
def to_expression(val)
|
61
|
+
if val.is_a?(Expression)
|
62
|
+
val
|
63
|
+
else
|
64
|
+
Expression::ValueNode.new(val)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module MongoQL
|
4
|
+
module StringOperators
|
5
|
+
def substr(start, length)
|
6
|
+
Expression::MethodCall.new "$substr", self, ast_template: -> (target, **_args) {
|
7
|
+
[target, to_expression(start).to_ast, to_expression(length).to_ast]
|
8
|
+
}
|
9
|
+
end
|
10
|
+
|
11
|
+
def trim(chars)
|
12
|
+
Expression::MethodCall.new "$trim", self, ast_template: -> (target, **_args) {
|
13
|
+
{
|
14
|
+
"input" => target,
|
15
|
+
"chars" => to_expression(chars).to_ast
|
16
|
+
}
|
17
|
+
}
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module MongoQL
|
4
|
+
module UnaryOperators
|
5
|
+
UNARY_OPS = {
|
6
|
+
"!": "$not"
|
7
|
+
}.freeze
|
8
|
+
|
9
|
+
UNARY_OPS.keys.each do |op|
|
10
|
+
class_eval <<~RUBY
|
11
|
+
def #{op}
|
12
|
+
Expression::Unary.new(UNARY_OPS[__method__], self)
|
13
|
+
end
|
14
|
+
RUBY
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
data/lib/mongo_ql.rb
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module MongoQL
|
4
|
+
end
|
5
|
+
|
6
|
+
require_relative "mongo_ql/version"
|
7
|
+
require_relative "mongo_ql/expression"
|
8
|
+
require_relative "mongo_ql/expression/date_note"
|
9
|
+
require_relative "mongo_ql/expression/field_node"
|
10
|
+
require_relative "mongo_ql/expression/value_node"
|
11
|
+
require_relative "mongo_ql/expression/method_call"
|
12
|
+
require_relative "mongo_ql/expression/binary"
|
13
|
+
require_relative "mongo_ql/expression/unary"
|
data/mongo_ql.gemspec
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
lib = File.expand_path("lib", __dir__)
|
4
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
5
|
+
require "mongo_ql/version"
|
6
|
+
|
7
|
+
Gem::Specification.new do |spec|
|
8
|
+
spec.name = "mongo_ql"
|
9
|
+
spec.version = MongoQL::VERSION
|
10
|
+
spec.authors = ["Xizheng Ding"]
|
11
|
+
spec.email = ["dingxizheng@gamil.com"]
|
12
|
+
|
13
|
+
spec.summary = "A DSL for building mongo query in a more natual way"
|
14
|
+
spec.homepage = "https://github.com/dingxizheng/mongo_ql"
|
15
|
+
spec.license = "MIT"
|
16
|
+
|
17
|
+
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
18
|
+
spec.bindir = "exe"
|
19
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
20
|
+
spec.require_paths = ["lib"]
|
21
|
+
end
|
metadata
ADDED
@@ -0,0 +1,66 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: mongo_ql
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Xizheng Ding
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2019-10-09 00:00:00.000000000 Z
|
12
|
+
dependencies: []
|
13
|
+
description:
|
14
|
+
email:
|
15
|
+
- dingxizheng@gamil.com
|
16
|
+
executables: []
|
17
|
+
extensions: []
|
18
|
+
extra_rdoc_files: []
|
19
|
+
files:
|
20
|
+
- ".gitignore"
|
21
|
+
- ".rubocop.yml"
|
22
|
+
- LICENSE.txt
|
23
|
+
- README.md
|
24
|
+
- Untitled-1
|
25
|
+
- bin/console
|
26
|
+
- bin/setup
|
27
|
+
- design_specs.md
|
28
|
+
- lib/mongo_ql.rb
|
29
|
+
- lib/mongo_ql/binary_operators.rb
|
30
|
+
- lib/mongo_ql/collection_operators.rb
|
31
|
+
- lib/mongo_ql/date_operators.rb
|
32
|
+
- lib/mongo_ql/expression.rb
|
33
|
+
- lib/mongo_ql/expression/binary.rb
|
34
|
+
- lib/mongo_ql/expression/date_note.rb
|
35
|
+
- lib/mongo_ql/expression/field_node.rb
|
36
|
+
- lib/mongo_ql/expression/method_call.rb
|
37
|
+
- lib/mongo_ql/expression/unary.rb
|
38
|
+
- lib/mongo_ql/expression/value_node.rb
|
39
|
+
- lib/mongo_ql/string_operators.rb
|
40
|
+
- lib/mongo_ql/unary_operators.rb
|
41
|
+
- lib/mongo_ql/version.rb
|
42
|
+
- mongo_ql.gemspec
|
43
|
+
homepage: https://github.com/dingxizheng/mongo_ql
|
44
|
+
licenses:
|
45
|
+
- MIT
|
46
|
+
metadata: {}
|
47
|
+
post_install_message:
|
48
|
+
rdoc_options: []
|
49
|
+
require_paths:
|
50
|
+
- lib
|
51
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
52
|
+
requirements:
|
53
|
+
- - ">="
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
version: '0'
|
56
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
57
|
+
requirements:
|
58
|
+
- - ">="
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
version: '0'
|
61
|
+
requirements: []
|
62
|
+
rubygems_version: 3.0.3
|
63
|
+
signing_key:
|
64
|
+
specification_version: 4
|
65
|
+
summary: A DSL for building mongo query in a more natual way
|
66
|
+
test_files: []
|