CloudSesame 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (74) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +4 -0
  3. data/.rspec +2 -0
  4. data/Gemfile +3 -0
  5. data/Gemfile.lock +76 -0
  6. data/Guardfile +70 -0
  7. data/cloud_sesame.gemspec +25 -0
  8. data/lib/abstract_object.rb +67 -0
  9. data/lib/cloud_sesame.rb +77 -0
  10. data/lib/cloud_sesame/config/credential.rb +8 -0
  11. data/lib/cloud_sesame/domain/base.rb +70 -0
  12. data/lib/cloud_sesame/domain/client.rb +28 -0
  13. data/lib/cloud_sesame/domain/config.rb +8 -0
  14. data/lib/cloud_sesame/domain/context.rb +35 -0
  15. data/lib/cloud_sesame/query/ast/and.rb +9 -0
  16. data/lib/cloud_sesame/query/ast/compound_array.rb +73 -0
  17. data/lib/cloud_sesame/query/ast/leaf.rb +8 -0
  18. data/lib/cloud_sesame/query/ast/literal.rb +27 -0
  19. data/lib/cloud_sesame/query/ast/multi_branch.rb +27 -0
  20. data/lib/cloud_sesame/query/ast/not.rb +13 -0
  21. data/lib/cloud_sesame/query/ast/operator.rb +24 -0
  22. data/lib/cloud_sesame/query/ast/or.rb +9 -0
  23. data/lib/cloud_sesame/query/ast/prefix_literal.rb +17 -0
  24. data/lib/cloud_sesame/query/ast/root.rb +24 -0
  25. data/lib/cloud_sesame/query/ast/single_branch.rb +24 -0
  26. data/lib/cloud_sesame/query/ast/value.rb +38 -0
  27. data/lib/cloud_sesame/query/builder.rb +78 -0
  28. data/lib/cloud_sesame/query/dsl.rb +62 -0
  29. data/lib/cloud_sesame/query/dsl/and.rb +19 -0
  30. data/lib/cloud_sesame/query/dsl/base.rb +23 -0
  31. data/lib/cloud_sesame/query/dsl/filter_query.rb +47 -0
  32. data/lib/cloud_sesame/query/dsl/literal.rb +41 -0
  33. data/lib/cloud_sesame/query/dsl/or.rb +19 -0
  34. data/lib/cloud_sesame/query/dsl/range.rb +41 -0
  35. data/lib/cloud_sesame/query/dsl/scope.rb +24 -0
  36. data/lib/cloud_sesame/query/error/missing_operator_symbol.rb +8 -0
  37. data/lib/cloud_sesame/query/error/missing_query.rb +8 -0
  38. data/lib/cloud_sesame/query/node/abstract.rb +14 -0
  39. data/lib/cloud_sesame/query/node/facet.rb +14 -0
  40. data/lib/cloud_sesame/query/node/filter_query.rb +17 -0
  41. data/lib/cloud_sesame/query/node/page.rb +27 -0
  42. data/lib/cloud_sesame/query/node/query.rb +29 -0
  43. data/lib/cloud_sesame/query/node/query_options.rb +37 -0
  44. data/lib/cloud_sesame/query/node/query_options_field.rb +20 -0
  45. data/lib/cloud_sesame/query/node/query_parser.rb +27 -0
  46. data/lib/cloud_sesame/query/node/request.rb +71 -0
  47. data/lib/cloud_sesame/query/node/sort.rb +37 -0
  48. data/spec/abstract_object_spec.rb +103 -0
  49. data/spec/cloud_sesame/domain/base_spec.rb +27 -0
  50. data/spec/cloud_sesame/domain/context_spec.rb +24 -0
  51. data/spec/cloud_sesame/query/ast/and_spec.rb +13 -0
  52. data/spec/cloud_sesame/query/ast/literal_spec.rb +37 -0
  53. data/spec/cloud_sesame/query/ast/multi_branch_spec.rb +65 -0
  54. data/spec/cloud_sesame/query/ast/operator_spec.rb +30 -0
  55. data/spec/cloud_sesame/query/ast/or_spec.rb +13 -0
  56. data/spec/cloud_sesame/query/ast/root_spec.rb +43 -0
  57. data/spec/cloud_sesame/query/ast/value_spec.rb +40 -0
  58. data/spec/cloud_sesame/query/builder_spec.rb +12 -0
  59. data/spec/cloud_sesame/query/dsl/base_spec.rb +31 -0
  60. data/spec/cloud_sesame/query/dsl/filter_query_spec.rb +158 -0
  61. data/spec/cloud_sesame/query/dsl_spec.rb +96 -0
  62. data/spec/cloud_sesame/query/node/abstract_spec.rb +19 -0
  63. data/spec/cloud_sesame/query/node/facet_spec.rb +42 -0
  64. data/spec/cloud_sesame/query/node/filter_query_spec.rb +29 -0
  65. data/spec/cloud_sesame/query/node/page_spec.rb +58 -0
  66. data/spec/cloud_sesame/query/node/query_options_field_spec.rb +27 -0
  67. data/spec/cloud_sesame/query/node/query_options_spec.rb +56 -0
  68. data/spec/cloud_sesame/query/node/query_parser_spec.rb +37 -0
  69. data/spec/cloud_sesame/query/node/query_spec.rb +46 -0
  70. data/spec/cloud_sesame/query/node/request_spec.rb +71 -0
  71. data/spec/cloud_sesame/query/node/sort_spec.rb +76 -0
  72. data/spec/cloud_sesame_spec.rb +58 -0
  73. data/spec/spec_helper.rb +12 -0
  74. metadata +218 -0
@@ -0,0 +1,8 @@
1
+ module CloudSesame
2
+ module Domain
3
+ class Config < CloudSesame::Config::Credential
4
+ accept :sigv4_region, as: [:region]
5
+ accept :endpoint, as: [:search_endpoint]
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,35 @@
1
+ module CloudSesame
2
+ module Domain
3
+ class Context
4
+
5
+ attr_reader :table
6
+
7
+ def initialize(table = {})
8
+ @table = table
9
+ end
10
+
11
+ def [](key, find_or_create = false)
12
+ table[key] ||= default_value(find_or_create)
13
+ end
14
+
15
+ def []=(key, value)
16
+ table[key] = value
17
+ end
18
+
19
+ def each(&block)
20
+ table.each &block
21
+ end
22
+
23
+ def map(&block)
24
+ table.map &block
25
+ end
26
+
27
+ private
28
+
29
+ def default_value(data)
30
+ data != true ? data : Context.new if data
31
+ end
32
+
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,9 @@
1
+ module CloudSesame
2
+ module Query
3
+ module AST
4
+ class And < Operator
5
+ self.symbol = :and
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,73 @@
1
+ module CloudSesame
2
+ module Query
3
+ module AST
4
+ class CompoundArray < Array
5
+ # include Range
6
+
7
+ attr_accessor :scope, :parent, :literal
8
+ attr_reader :field
9
+
10
+ def field=(field)
11
+ self.parent = nil
12
+ self.literal = AST::Literal
13
+ @field = field
14
+ end
15
+
16
+ def set_scope(scope)
17
+ self.scope = scope
18
+ return self
19
+ end
20
+
21
+ # SINGLE BRANCH OPERATOR
22
+ # =======================================
23
+ def not(*values)
24
+ self.parent = AST::Not
25
+ self.literal = AST::Literal
26
+ insert_children(values)
27
+ return self
28
+ end
29
+
30
+ alias_method :is_not, :not
31
+
32
+ # PREFIX LITERAL
33
+ # =======================================
34
+ def prefix(*values)
35
+ self.literal = AST::PrefixLiteral
36
+ insert_children(values)
37
+ return self
38
+ end
39
+
40
+ alias_method :start_with, :prefix
41
+ alias_method :begin_with, :prefix
42
+
43
+ # RANGE LITERAL
44
+ # =======================================
45
+ # def range
46
+ # self.literal = AST::PrefixLiteral
47
+ # insert_children(values)
48
+ # return self
49
+ # end
50
+
51
+ # alias_method :start_with, :prefix
52
+
53
+ def insert_children(values = [])
54
+ values.each do |value|
55
+ if parent
56
+ self << (node = parent.new scope.context)
57
+ node.child = literal.new(field, value, options)
58
+ else
59
+ self << literal.new(field, value, options)
60
+ end
61
+ end
62
+ end
63
+
64
+ private
65
+
66
+ def options
67
+ scope.context[:fields][field]
68
+ end
69
+
70
+ end
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,8 @@
1
+ module CloudSesame
2
+ module Query
3
+ module AST
4
+ class Leaf
5
+ end
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,27 @@
1
+ module CloudSesame
2
+ module Query
3
+ module AST
4
+ class Literal < SingleBranch
5
+
6
+ attr_accessor :field
7
+ attr_reader :value, :options
8
+
9
+ def initialize(field, value, options = {})
10
+ @field = field
11
+ @value = Value.new(value)
12
+ @options = options
13
+ (options[:included] ||= []) << value
14
+ end
15
+
16
+ def value=(value)
17
+ @value = Value.new(value)
18
+ end
19
+
20
+ def compile
21
+ "#{ field }:#{ value.compile }"
22
+ end
23
+
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,27 @@
1
+ module CloudSesame
2
+ module Query
3
+ module AST
4
+ class MultiBranch
5
+ include DSL::Base
6
+ include DSL::FilterQuery
7
+ include DSL::Scope
8
+
9
+ attr_reader :context
10
+
11
+ def initialize(context, &block)
12
+ @context = context
13
+ instance_eval &block if block_given?
14
+ end
15
+
16
+ def children
17
+ @children ||= CompoundArray.new.set_scope self
18
+ end
19
+
20
+ def compile_children
21
+ children.map(&:compile).join(' ')
22
+ end
23
+
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,13 @@
1
+ module CloudSesame
2
+ module Query
3
+ module AST
4
+ class Not < SingleBranch
5
+
6
+ def compile
7
+ "(not #{ child.compile })"
8
+ end
9
+
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,24 @@
1
+ module CloudSesame
2
+ module Query
3
+ module AST
4
+ class Operator < MultiBranch
5
+
6
+ # Operator Symbol Writer
7
+ def self.symbol=(symbol)
8
+ @symbol = symbol
9
+ end
10
+
11
+ # Operator Symbol Getter
12
+ def self.symbol
13
+ @symbol
14
+ end
15
+
16
+ def compile
17
+ raise Error::MissingOperatorSymbol if self.class.symbol.nil?
18
+ "(#{ self.class.symbol } #{ compile_children })" unless children.empty?
19
+ end
20
+
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,9 @@
1
+ module CloudSesame
2
+ module Query
3
+ module AST
4
+ class Or < Operator
5
+ self.symbol = :or
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,17 @@
1
+ module CloudSesame
2
+ module Query
3
+ module AST
4
+ class PrefixLiteral < Literal
5
+
6
+ def compile
7
+ "(prefix field=#{ escape(field) } #{ value.compile })"
8
+ end
9
+
10
+ def escape(data = "")
11
+ "'#{ data.to_s.gsub(/\'/) { "\\'" } }'"
12
+ end
13
+
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,24 @@
1
+ module CloudSesame
2
+ module Query
3
+ module AST
4
+ class Root < MultiBranch
5
+
6
+ def compile
7
+ if children.size > 1
8
+ join_by_default_operator.compile
9
+ else
10
+ compile_children
11
+ end
12
+ end
13
+
14
+ private
15
+
16
+ def join_by_default_operator
17
+ (default = And.new context).children.concat children
18
+ default
19
+ end
20
+
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,24 @@
1
+ module CloudSesame
2
+ module Query
3
+ module AST
4
+ class SingleBranch
5
+
6
+ attr_accessor :child
7
+ attr_reader :context
8
+
9
+ def initialize(context, &block)
10
+ @context = context
11
+ instance_eval &block if block_given?
12
+ end
13
+
14
+ def child=(object)
15
+ if object.kind_of? Literal
16
+ (object.options[:excluded] ||= []) << object.options[:included].delete(object.value.data)
17
+ end
18
+ @child = object
19
+ end
20
+
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,38 @@
1
+ module CloudSesame
2
+ module Query
3
+ module AST
4
+ class Value < Leaf
5
+
6
+ attr_accessor :data
7
+
8
+ def initialize(data)
9
+ @data = data
10
+ end
11
+
12
+ def compile
13
+ format(data)
14
+ end
15
+
16
+ private
17
+
18
+ def format(data)
19
+ range?(data) || digits?(data) ? data : escape(data)
20
+ end
21
+
22
+ def range?(data)
23
+ data =~ /^[\[\{].*[\]\}]$/
24
+ end
25
+
26
+ def digits?(data)
27
+ data.to_s =~ /^\d+(.\d+)?$/
28
+ end
29
+
30
+ # "hello" => "'hello'"
31
+ def escape(data = "")
32
+ "'#{ data.to_s.gsub(/\'/) { "\\'" } }'"
33
+ end
34
+
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,78 @@
1
+ module CloudSesame
2
+ module Query
3
+ class Builder
4
+ include DSL::Base
5
+ include DSL::FilterQuery
6
+
7
+ attr_accessor :context, :searchable_class
8
+
9
+ def initialize(default_context, searchable_class)
10
+ @context = default_context
11
+ @searchable_class = searchable_class
12
+ end
13
+
14
+ def request
15
+ @request ||= Node::Request.new context.dup
16
+ end
17
+
18
+ def reset
19
+ @request = nil
20
+ end
21
+
22
+ def inspect
23
+ "#<CloudSesame::Query::Builder:#{ object_id } #{ request.compile }>"
24
+ end
25
+
26
+ # CHAINABLE METHODS
27
+ # =========================================
28
+
29
+ def query(string)
30
+ request.query.query = string
31
+ return self
32
+ end
33
+
34
+ def terms(*terms)
35
+ request.query.terms.concat terms
36
+ return self
37
+ end
38
+
39
+ def exclude_terms(*terms)
40
+ request.query.terms.concat terms.map { |t| "-#{ t }" }
41
+ return self
42
+ end
43
+
44
+ def page(value)
45
+ request.page.page = value.to_i
46
+ return self
47
+ end
48
+
49
+ def size(value)
50
+ request.page.size = value.to_i
51
+ return self
52
+ end
53
+
54
+ def sort(hash = {})
55
+ hash.each { |key, value| request.sort[key] = value }
56
+ return self
57
+ end
58
+
59
+ # ENDING METHODS
60
+ # =========================================
61
+
62
+ def search
63
+ compiled = request.compile
64
+ raise Error::MissingQuery.new("Query can not be empty!") unless compiled[:query] && !compiled[:query].empty?
65
+ result = searchable_class.cloudsearch.client.search compiled
66
+ reset
67
+ result
68
+ end
69
+
70
+ private
71
+
72
+ def method_scope
73
+ request.filter_query.root
74
+ end
75
+
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,62 @@
1
+ # module CloudSesame
2
+ # module Query
3
+ # module DSL
4
+
5
+ # # CLAUSE: AND
6
+ # # =========================================
7
+ # def and(&block)
8
+ # filter_query.children << AST::And.new(context, &block)
9
+ # return filter_query
10
+ # end
11
+
12
+ # alias_method :all, :and
13
+ # alias_method :and!, :and
14
+ # alias_method :+, :and
15
+
16
+ # # CLAUSE: OR
17
+ # # =========================================
18
+ # def or(&block)
19
+ # filter_query.children << AST::Or.new(context, &block)
20
+ # return filter_query
21
+ # end
22
+
23
+ # alias_method :any, :or
24
+ # alias_method :or!, :or
25
+
26
+ # # CLAUSE: LITERAL
27
+ # # =========================================
28
+ # def literal(field, value, options = {})
29
+ # node = AST::Literal.new(field, value, options)
30
+ # filter_query.children << node
31
+ # node
32
+ # end
33
+
34
+ # def prefix(literals)
35
+ # literals.each { |literal| literal.options[:prefix] = true }
36
+ # return filter_query
37
+ # end
38
+
39
+ # private
40
+
41
+ # def filter_query
42
+ # self
43
+ # end
44
+
45
+ # def scopes
46
+ # filter_query.context[:scopes]
47
+ # end
48
+
49
+ # def method_missing(field, *values, &block)
50
+ # if context[:fields] && (options = context[:fields][field])
51
+ # values.map { |value| literal(field, value, options) }
52
+ # elsif scope && (callback = scope[field])
53
+ # filter_query.instance_exec *values, &callback
54
+ # return filter_query
55
+ # else
56
+ # super
57
+ # end
58
+ # end
59
+
60
+ # end
61
+ # end
62
+ # end