CloudSesame 0.1.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.
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