CloudSesame 0.4.6 → 0.5.0
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 +4 -4
- data/Gemfile.lock +4 -1
- data/cloud_sesame.gemspec +2 -2
- data/lib/cloud_sesame/domain/base.rb +40 -30
- data/lib/cloud_sesame/query/ast/field_array.rb +4 -5
- data/lib/cloud_sesame/query/ast/literal.rb +11 -12
- data/lib/cloud_sesame/query/ast/multi_expression_operator.rb +4 -9
- data/lib/cloud_sesame/query/ast/near.rb +3 -1
- data/lib/cloud_sesame/query/ast/not.rb +3 -1
- data/lib/cloud_sesame/query/ast/root.rb +1 -1
- data/lib/cloud_sesame/query/ast/single_expression_operator.rb +3 -3
- data/lib/cloud_sesame/query/builder.rb +20 -12
- data/lib/cloud_sesame/query/domain/block.rb +65 -0
- data/lib/cloud_sesame/query/domain/chaining_block.rb +44 -0
- data/lib/cloud_sesame/query/domain/literal.rb +39 -0
- data/lib/cloud_sesame/query/dsl/block_methods.rb +6 -27
- data/lib/cloud_sesame/query/dsl/field_accessors.rb +24 -0
- data/lib/cloud_sesame/query/dsl/field_array_methods.rb +5 -9
- data/lib/cloud_sesame/query/dsl/filter_query_methods.rb +3 -3
- data/lib/cloud_sesame/query/dsl/operator_methods.rb +8 -15
- data/lib/cloud_sesame/query/dsl/range_methods.rb +4 -4
- data/lib/cloud_sesame/query/dsl/response_methods.rb +1 -1
- data/lib/cloud_sesame/query/dsl/scope_accessors.rb +28 -0
- data/lib/cloud_sesame/query/error/scope_not_defined.rb +8 -0
- data/lib/cloud_sesame/query/scope.rb +21 -0
- data/lib/cloud_sesame.rb +14 -7
- data/spec/cloud_sesame/query/ast/single_expression_operator_spec.rb +1 -1
- data/spec/cloud_sesame/query/dsl/block_methods_spec.rb +54 -70
- data/spec/cloud_sesame/query/dsl/{field_methods_spec.rb → field_accessors_spec.rb} +1 -1
- data/spec/cloud_sesame_spec.rb +35 -22
- metadata +30 -34
- data/lib/cloud_sesame/query/ast/block_chaining_relation.rb +0 -30
- data/lib/cloud_sesame/query/dsl/base.rb +0 -27
- data/lib/cloud_sesame/query/dsl/block_chaining_methods.rb +0 -26
- data/lib/cloud_sesame/query/dsl/field_methods.rb +0 -16
- data/lib/cloud_sesame/query/dsl/scope_methods.rb +0 -33
- data/spec/cloud_sesame/query/ast/block_chaining_relation_spec.rb +0 -44
- data/spec/cloud_sesame/query/dsl/base_spec.rb +0 -31
- data/spec/cloud_sesame/query/dsl/block_chaining_methods_spec.rb +0 -64
@@ -5,7 +5,7 @@ module CloudSesame
|
|
5
5
|
|
6
6
|
def included?(field, value = nil)
|
7
7
|
!!(
|
8
|
-
(field_options =
|
8
|
+
(field_options = _context[:fields][field.to_sym]) &&
|
9
9
|
(applied = field_options[:applied]) &&
|
10
10
|
(
|
11
11
|
(!value && applied.values.any?) ||
|
@@ -19,7 +19,7 @@ module CloudSesame
|
|
19
19
|
|
20
20
|
def excluded?(field, value = nil)
|
21
21
|
!!(
|
22
|
-
(field_options =
|
22
|
+
(field_options = _context[:fields][field.to_sym]) &&
|
23
23
|
(applied = field_options[:applied]) &&
|
24
24
|
(
|
25
25
|
(!value && !applied.values.all?) ||
|
@@ -33,7 +33,7 @@ module CloudSesame
|
|
33
33
|
|
34
34
|
def applied_filters
|
35
35
|
applied = {}
|
36
|
-
|
36
|
+
_context[:fields].each do |field, options|
|
37
37
|
if options && options[:applied] &&
|
38
38
|
!(values = options[:applied].select { |k, v| v }.keys).empty?
|
39
39
|
applied[field] = values
|
@@ -6,7 +6,7 @@ module CloudSesame
|
|
6
6
|
# NEAR: creates a single NEAR node
|
7
7
|
# =======================================
|
8
8
|
def near(value, options = {})
|
9
|
-
|
9
|
+
_build_operator AST::Near, options, value
|
10
10
|
end
|
11
11
|
|
12
12
|
alias_method :sloppy, :near
|
@@ -14,7 +14,7 @@ module CloudSesame
|
|
14
14
|
# PREFIX: creates a single PREFIX node
|
15
15
|
# =======================================
|
16
16
|
def prefix(value, options = {})
|
17
|
-
|
17
|
+
_build_operator AST::Prefix, options, value
|
18
18
|
end
|
19
19
|
|
20
20
|
alias_method :start_with, :prefix
|
@@ -23,28 +23,21 @@ module CloudSesame
|
|
23
23
|
# PHRASE: creates a single PHRASE node
|
24
24
|
# =======================================
|
25
25
|
def phrase(value, options = {})
|
26
|
-
|
26
|
+
_build_operator AST::Phrase, options, value
|
27
27
|
end
|
28
28
|
|
29
29
|
# TERM: creates a single TERM node
|
30
30
|
# =======================================
|
31
31
|
def term(value, options = {})
|
32
|
-
|
32
|
+
_build_operator AST::Term, options, value
|
33
33
|
end
|
34
34
|
|
35
35
|
private
|
36
36
|
|
37
|
-
def
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
def create_literal(klass, options, value)
|
42
|
-
(node = klass.new dsl_context, options) << fieldless_literal(value)
|
43
|
-
return node
|
44
|
-
end
|
45
|
-
|
46
|
-
def fieldless_literal(value)
|
47
|
-
AST::Literal.new nil, value
|
37
|
+
def _build_operator(klass, options, value)
|
38
|
+
node = klass.new _context, options
|
39
|
+
node << AST::Literal.new(nil, value)
|
40
|
+
node
|
48
41
|
end
|
49
42
|
|
50
43
|
end
|
@@ -4,19 +4,19 @@ module CloudSesame
|
|
4
4
|
module RangeMethods
|
5
5
|
|
6
6
|
def gt(input)
|
7
|
-
AST::RangeValue.new.gt
|
7
|
+
AST::RangeValue.new.gt input
|
8
8
|
end
|
9
9
|
|
10
10
|
def gte(input)
|
11
|
-
AST::RangeValue.new.gte
|
11
|
+
AST::RangeValue.new.gte input
|
12
12
|
end
|
13
13
|
|
14
14
|
def lt(input)
|
15
|
-
AST::RangeValue.new.lt
|
15
|
+
AST::RangeValue.new.lt input
|
16
16
|
end
|
17
17
|
|
18
18
|
def lte(input)
|
19
|
-
AST::RangeValue.new.lte
|
19
|
+
AST::RangeValue.new.lte input
|
20
20
|
end
|
21
21
|
|
22
22
|
end
|
@@ -29,7 +29,7 @@ module CloudSesame
|
|
29
29
|
def search
|
30
30
|
compiled = request.compile
|
31
31
|
raise Error::MissingQuery.new("Query or FilterQuery can not be empty!") if !compiled[:query] || compiled[:query].empty?
|
32
|
-
@response = searchable.cloudsearch.client.search compiled
|
32
|
+
@response = @searchable.cloudsearch.client.search compiled
|
33
33
|
end
|
34
34
|
|
35
35
|
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module CloudSesame
|
2
|
+
module Query
|
3
|
+
module DSL
|
4
|
+
module ScopeAccessors
|
5
|
+
|
6
|
+
def scopes(name = nil, *args)
|
7
|
+
defined_scopes = _scope.context[:scopes]
|
8
|
+
return _return if name.nil?
|
9
|
+
if defined_scopes && (block = defined_scopes[name.to_sym])
|
10
|
+
instance_exec *args, &block
|
11
|
+
_return
|
12
|
+
else
|
13
|
+
raise Error::ScopeNotDefined
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def method_missing(name, *args, &block)
|
20
|
+
scopes name, *args
|
21
|
+
rescue Error::ScopeNotDefined
|
22
|
+
super
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module CloudSesame
|
2
|
+
module Query
|
3
|
+
class Scope
|
4
|
+
|
5
|
+
attr_reader :_scope, :_return
|
6
|
+
|
7
|
+
def initialize(_scope, _return)
|
8
|
+
@_scope, @_return = _scope, _return
|
9
|
+
end
|
10
|
+
|
11
|
+
def _context
|
12
|
+
_scope.context
|
13
|
+
end
|
14
|
+
|
15
|
+
def <<(object)
|
16
|
+
_scope << object
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
data/lib/cloud_sesame.rb
CHANGED
@@ -14,14 +14,13 @@ require 'cloud_sesame/config/credential'
|
|
14
14
|
require 'cloud_sesame/query/error/invalid_syntax'
|
15
15
|
require 'cloud_sesame/query/error/missing_operator_symbol'
|
16
16
|
require 'cloud_sesame/query/error/missing_query'
|
17
|
+
require 'cloud_sesame/query/error/scope_not_defined'
|
17
18
|
|
18
19
|
# Query DSL Methods
|
19
20
|
# ===============================================
|
20
|
-
require 'cloud_sesame/query/dsl/base'
|
21
21
|
require 'cloud_sesame/query/dsl/block_methods'
|
22
|
-
require 'cloud_sesame/query/dsl/block_chaining_methods'
|
23
22
|
require 'cloud_sesame/query/dsl/field_array_methods'
|
24
|
-
require 'cloud_sesame/query/dsl/
|
23
|
+
require 'cloud_sesame/query/dsl/field_accessors'
|
25
24
|
require 'cloud_sesame/query/dsl/filter_query_methods'
|
26
25
|
require 'cloud_sesame/query/dsl/operator_methods'
|
27
26
|
require 'cloud_sesame/query/dsl/page_methods'
|
@@ -29,15 +28,20 @@ require 'cloud_sesame/query/dsl/query_methods'
|
|
29
28
|
require 'cloud_sesame/query/dsl/range_methods'
|
30
29
|
require 'cloud_sesame/query/dsl/response_methods'
|
31
30
|
require 'cloud_sesame/query/dsl/return_methods'
|
32
|
-
require 'cloud_sesame/query/dsl/
|
31
|
+
require 'cloud_sesame/query/dsl/scope_accessors'
|
33
32
|
require 'cloud_sesame/query/dsl/sort_methods'
|
34
33
|
|
34
|
+
# Query Query Domain Objects
|
35
|
+
# ===============================================
|
36
|
+
require 'cloud_sesame/query/domain/block'
|
37
|
+
require 'cloud_sesame/query/domain/chaining_block'
|
38
|
+
require 'cloud_sesame/query/domain/literal'
|
39
|
+
|
35
40
|
# Query Query Filter Query AST Tree
|
36
41
|
# ===============================================
|
37
42
|
require 'cloud_sesame/query/ast/operator'
|
38
43
|
require 'cloud_sesame/query/ast/multi_expression_operator'
|
39
44
|
require 'cloud_sesame/query/ast/single_expression_operator'
|
40
|
-
require 'cloud_sesame/query/ast/block_chaining_relation'
|
41
45
|
require 'cloud_sesame/query/ast/field_array'
|
42
46
|
require 'cloud_sesame/query/ast/and'
|
43
47
|
require 'cloud_sesame/query/ast/or'
|
@@ -71,6 +75,7 @@ require 'cloud_sesame/query/node/return'
|
|
71
75
|
# Query Builder Interface
|
72
76
|
# ===============================================
|
73
77
|
require 'cloud_sesame/query/builder'
|
78
|
+
require 'cloud_sesame/query/scope'
|
74
79
|
|
75
80
|
# Domain Objects
|
76
81
|
# ===============================================
|
@@ -95,13 +100,15 @@ module CloudSesame
|
|
95
100
|
|
96
101
|
def define_cloudsearch(&block)
|
97
102
|
if block_given?
|
98
|
-
Domain::Base
|
103
|
+
Domain::Base.definitions[self] = block
|
104
|
+
cloudsearch._caller = block.binding.eval "self"
|
99
105
|
cloudsearch.instance_eval &block
|
106
|
+
cloudsearch._caller = nil
|
100
107
|
end
|
101
108
|
end
|
102
109
|
|
103
110
|
def load_definition_from(klass)
|
104
|
-
if (definition = Domain::Base
|
111
|
+
if (definition = Domain::Base.definitions[self])
|
105
112
|
cloudsearch.instance_eval &definition
|
106
113
|
end
|
107
114
|
end
|
@@ -53,7 +53,7 @@ module CloudSesame
|
|
53
53
|
describe '#compile' do
|
54
54
|
before {
|
55
55
|
subject.child = OpenStruct.new compile: ""
|
56
|
-
allow(subject.child).to receive(:compile)
|
56
|
+
allow(subject.child).to receive(:compile).and_return(" ")
|
57
57
|
}
|
58
58
|
it 'should compile it\'s child and detailed set to false' do
|
59
59
|
expect(subject.child).to receive(:compile).with(SingleExpressionOperator::DETAILED)
|
@@ -5,7 +5,9 @@ module CloudSesame
|
|
5
5
|
module DSL
|
6
6
|
describe BlockMethods do
|
7
7
|
|
8
|
-
# Setup
|
8
|
+
# Setup
|
9
|
+
# =================================================
|
10
|
+
|
9
11
|
class Product
|
10
12
|
include CloudSesame
|
11
13
|
define_cloudsearch {}
|
@@ -13,99 +15,81 @@ module CloudSesame
|
|
13
15
|
|
14
16
|
subject(:cloudsearch) { Product.cloudsearch.builder }
|
15
17
|
|
16
|
-
#
|
17
|
-
#
|
18
|
-
|
18
|
+
# Block Style Clause
|
19
|
+
# =================================================
|
20
|
+
shared_examples 'block styled clause' do
|
21
|
+
|
22
|
+
let(:node) { klass1.new({}) }
|
23
|
+
let(:root) { subject.request.filter_query.root }
|
24
|
+
|
25
|
+
let(:empty_block) { Proc.new { } }
|
26
|
+
let(:nested_block) { Proc.new { send(method, empty_block) } }
|
27
|
+
|
28
|
+
let(:clause_call) { subject.send(clause1, &empty_block) }
|
19
29
|
|
20
|
-
it
|
21
|
-
expect(
|
22
|
-
subject.
|
30
|
+
it "should create an multi-expression node" do
|
31
|
+
expect(klass1).to receive(:new).once
|
32
|
+
subject.send(clause1)
|
23
33
|
end
|
24
34
|
|
25
35
|
context 'when given a block' do
|
36
|
+
before { allow(klass1).to receive(:new).and_return(node) }
|
26
37
|
|
27
|
-
context 'when called from cloudsearch' do
|
28
|
-
it 'should
|
29
|
-
|
30
|
-
|
31
|
-
expect(subject.request.filter_query.root.children).to include(node)
|
38
|
+
context 'when called from cloudsearch build' do
|
39
|
+
it 'should add to root\'s children' do
|
40
|
+
expect{ clause_call }.to change{ root.children.size }.by(1)
|
41
|
+
expect(root.children).to include(node)
|
32
42
|
end
|
33
|
-
it 'should return
|
34
|
-
expect(
|
35
|
-
end
|
36
|
-
it 'should use self as the dsl scope in the block' do
|
37
|
-
block = ->(scope, node) { expect(scope).to eq node }
|
38
|
-
subject.and { block.call(dsl_scope, self) }
|
43
|
+
it 'should return the build itself' do
|
44
|
+
expect(clause_call).to eq(subject)
|
39
45
|
end
|
40
46
|
end
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
expect(parent.children).to include(child)
|
47
|
+
context 'when called within a nested block' do
|
48
|
+
it 'should add to scope\'s children' do
|
49
|
+
subject.send(clause2) do
|
50
|
+
child = send(clause1, &empty_block)
|
51
|
+
expect(_scope.children).to include(child)
|
52
|
+
end
|
48
53
|
end
|
49
54
|
it 'should return the node it self' do
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
expect(return_node).to eq node
|
55
|
+
subject.send(clause2) {
|
56
|
+
child = send(clause1, &empty_block)
|
57
|
+
expect(child).to eq(node)
|
58
|
+
}
|
55
59
|
end
|
56
60
|
end
|
57
|
-
|
58
61
|
end
|
59
62
|
|
60
63
|
context 'when not given a block' do
|
61
|
-
it 'should
|
62
|
-
expect(subject.
|
64
|
+
it 'should return a chaining block domain object' do
|
65
|
+
expect(subject.send(clause1)).to be_kind_of(Domain::ChainingBlock)
|
63
66
|
end
|
64
|
-
it 'should
|
65
|
-
expect{ subject.
|
67
|
+
it 'should NOT add any children to the scope node' do
|
68
|
+
expect{ subject.send(clause1) }.to_not change{ root.children.size }
|
66
69
|
end
|
67
70
|
end
|
71
|
+
end
|
72
|
+
|
68
73
|
|
74
|
+
# AND
|
75
|
+
# =======================================================
|
76
|
+
describe '#and' do
|
77
|
+
it_behaves_like 'block styled clause' do
|
78
|
+
let(:klass1) { AST::And }
|
79
|
+
let(:klass2) { AST::Or }
|
80
|
+
let(:clause1) { :and! }
|
81
|
+
let(:clause2) { :or! }
|
82
|
+
end
|
69
83
|
end
|
70
84
|
|
71
85
|
# OR
|
72
86
|
# =======================================================
|
73
87
|
describe '#or' do
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
context 'when called from cloudsearch' do
|
81
|
-
it 'should become a child of root node' do
|
82
|
-
node = nil
|
83
|
-
subject.or { node = self; }
|
84
|
-
expect(subject.request.filter_query.root.children).to include(node)
|
85
|
-
end
|
86
|
-
it 'should return cloudsearch' do
|
87
|
-
expect(subject.or {}).to eq subject
|
88
|
-
end
|
89
|
-
it 'should use self as the dsl scope in the block' do
|
90
|
-
block = ->(scope, node) { expect(scope).to eq node }
|
91
|
-
subject.and { block.call(dsl_scope, self) }
|
92
|
-
end
|
93
|
-
end
|
94
|
-
|
95
|
-
context 'when called from inside a block' do
|
96
|
-
it 'should become a child of the dsl_scope' do
|
97
|
-
parent = nil
|
98
|
-
child = nil
|
99
|
-
subject.and { parent = self; child = or! {} }
|
100
|
-
expect(parent.children).to include(child)
|
101
|
-
end
|
102
|
-
it 'should return the node it self' do
|
103
|
-
node = AST::Or.new({})
|
104
|
-
return_node = nil
|
105
|
-
allow(AST::Or).to receive(:new).and_return(node)
|
106
|
-
subject.and { return_node = or! {} }
|
107
|
-
expect(return_node).to eq node
|
108
|
-
end
|
88
|
+
it_behaves_like 'block styled clause' do
|
89
|
+
let(:klass1) { AST::Or }
|
90
|
+
let(:klass2) { AST::And }
|
91
|
+
let(:clause1) { :or! }
|
92
|
+
let(:clause2) { :and! }
|
109
93
|
end
|
110
94
|
end
|
111
95
|
|
data/spec/cloud_sesame_spec.rb
CHANGED
@@ -25,6 +25,10 @@
|
|
25
25
|
# class Product
|
26
26
|
# include CloudSesame
|
27
27
|
|
28
|
+
# def self.greeting
|
29
|
+
# "hello world!"
|
30
|
+
# end
|
31
|
+
|
28
32
|
# define_cloudsearch do
|
29
33
|
# # Product CloudSesame Config
|
30
34
|
# config.endpoint = ENV['AWS_ENDPOINT']
|
@@ -62,26 +66,31 @@
|
|
62
66
|
# end
|
63
67
|
|
64
68
|
|
65
|
-
#
|
66
|
-
#
|
67
|
-
#
|
68
|
-
#
|
69
|
-
#
|
69
|
+
# @tags = [1, 2]
|
70
|
+
# n = 10_000
|
71
|
+
# q = nil
|
72
|
+
# result = RubyProf.profile do
|
73
|
+
# n.times do
|
74
|
+
# q = Product.cloudsearch.query("black jacket").sort(price: :asc).page(1).size(1000)
|
75
|
+
# .price { gt 100 }
|
76
|
+
# .and {
|
70
77
|
# or! {
|
71
|
-
# tags
|
78
|
+
# tags *@tags
|
79
|
+
# tags
|
80
|
+
# tags nil
|
72
81
|
# and! {
|
73
82
|
# tags.not "3", "4"
|
74
83
|
# }
|
75
84
|
# and!.not {
|
76
85
|
# tags.start_with "5", "6"
|
77
|
-
# tags.not.start_with
|
78
|
-
# tags.not.near
|
86
|
+
# tags.not.start_with("7")
|
87
|
+
# tags.not.near("8", distance: 7)
|
79
88
|
# tags start_with("9"), near("10")
|
80
|
-
# tags term
|
81
|
-
# tags phrase "12"
|
89
|
+
# tags term("11", boost: 2)
|
90
|
+
# tags.not phrase "12"
|
82
91
|
# }
|
83
92
|
# or!.not {
|
84
|
-
# price
|
93
|
+
# price(25..100)
|
85
94
|
# price 100...200
|
86
95
|
# price gte(200).lt(300)
|
87
96
|
# price gte(300)
|
@@ -93,30 +102,34 @@
|
|
93
102
|
# }
|
94
103
|
# }
|
95
104
|
# }
|
96
|
-
#
|
105
|
+
# q.applied_filters
|
97
106
|
|
98
|
-
#
|
99
|
-
#
|
100
|
-
#
|
101
|
-
#
|
107
|
+
# end
|
108
|
+
# end
|
109
|
+
# printer = RubyProf::FlatPrinter.new(result)
|
110
|
+
# printer.print(STDOUT, {})
|
102
111
|
|
103
112
|
# binding.pry
|
104
113
|
|
105
|
-
# # class
|
106
|
-
|
114
|
+
# # class ProductController
|
115
|
+
|
116
|
+
# # def load_user
|
107
117
|
# # @name = "scott"
|
108
118
|
# # end
|
119
|
+
|
109
120
|
# # def greeting
|
110
121
|
# # "hello world!"
|
111
122
|
# # end
|
112
|
-
|
113
|
-
# #
|
123
|
+
|
124
|
+
# # def search
|
125
|
+
# # load_user
|
114
126
|
# # q = Product.cloudsearch.and {
|
115
127
|
# # binding.pry
|
116
128
|
# # }
|
117
129
|
# # end
|
130
|
+
|
118
131
|
# # end
|
119
132
|
|
120
|
-
# # test =
|
121
|
-
# # test.
|
133
|
+
# # test = ProductController.new
|
134
|
+
# # test.search
|
122
135
|
# end
|