CloudSesame 0.4.6 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|