dse-driver 1.0.1

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.
data/lib/dse/graph.rb ADDED
@@ -0,0 +1,18 @@
1
+ # encoding: utf-8
2
+
3
+ #--
4
+ # Copyright (C) 2016 DataStax Inc.
5
+ #
6
+ # This software can be used solely with DataStax Enterprise. Please consult the license at
7
+ # http://www.datastax.com/terms/datastax-dse-driver-license-terms
8
+ #++
9
+
10
+ require 'dse/graph/edge'
11
+ require 'dse/graph/options'
12
+ require 'dse/graph/path'
13
+ require 'dse/graph/result'
14
+ require 'dse/graph/result_set'
15
+ require 'dse/graph/statement'
16
+ require 'dse/graph/vertex'
17
+ require 'dse/graph/vertex_property'
18
+ require 'dse/graph/duration'
@@ -0,0 +1,131 @@
1
+ # encoding: utf-8
2
+
3
+ #--
4
+ # Copyright (C) 2016 DataStax Inc.
5
+ #
6
+ # This software can be used solely with DataStax Enterprise. Please consult the license at
7
+ # http://www.datastax.com/terms/datastax-dse-driver-license-terms
8
+ #++
9
+
10
+ module Dse
11
+ module Graph
12
+ # Represents a duration of time, corresponding to the Duration datatype in DSE Graph. In DSE Graph,
13
+ # this type is represented by the
14
+ # {https://docs.oracle.com/javase/8/docs/api/java/time/Duration.html Java 8 Duration} type.
15
+ class Duration
16
+ # Days in duration of time. May be negative. Is internally coerced to an integer,
17
+ # so a value being assigned need not be an `Integer` itself.
18
+ # @return [Integer] days in duration of time.
19
+ attr_reader :days
20
+
21
+ # Hours in duration of time. May be negative. Is internally coerced to an integer,
22
+ # so a value being assigned need not be an `Integer` itself.
23
+ # @return [Integer] hours in duration of time.
24
+ attr_reader :hours
25
+
26
+ # Minutes in duration of time. May be negative. Is internally coerced to an integer,
27
+ # so a value being assigned need not be an `Integer` itself.
28
+ # @return [Integer] minutes in duration of time.
29
+ attr_reader :minutes
30
+
31
+ # Seconds in duration of time. May be negative. Is internally coerced to an float,
32
+ # so a value being assigned need not be an `Float` itself.
33
+ # @return [Float] seconds in duration of time.
34
+ attr_reader :seconds
35
+
36
+ # @private
37
+ # We expect a string of the form PnDTnHnMn.nS, where n's are positive or negative integers, and where
38
+ # components may be missing (e.g. PT7.8S is valid)
39
+ PAT = /^P((?<days>-?\d+)D)?T((?<hours>-?\d+)H)?((?<minutes>-?\d+)M)?((?<seconds>-?[0-9.]+)S)?$/
40
+
41
+ # Create a {Duration} object. All arguments are internally coerced to desired types.
42
+ # @param days [Integer] number of days in the time-frame. May be negative.
43
+ # @param hours [Integer] number of hours in the time-frame. May be negative.
44
+ # @param minutes [Integer] number of minutes in the time-frame. May be negative.
45
+ # @param seconds [Float] number of seconds in the time-frame. May be negative.
46
+ def initialize(days, hours, minutes, seconds)
47
+ @days = days.to_i
48
+ @hours = hours.to_i
49
+ @minutes = minutes.to_i
50
+ @seconds = seconds.to_f
51
+ end
52
+
53
+ def days=(days)
54
+ @days = days.to_i
55
+ end
56
+
57
+ def hours=(hours)
58
+ @hours = hours.to_i
59
+ end
60
+
61
+ def minutes=(minutes)
62
+ @minutes = minutes.to_i
63
+ end
64
+
65
+ def seconds=(seconds)
66
+ @seconds = seconds.to_f
67
+ end
68
+
69
+ # Parse a duration string from DSE Graph and construct a {Duration} object
70
+ # @param duration [String] duration string from DSE Graph.
71
+ # @raise [ArgumentError] if the duration string fails to parse.
72
+ def self.parse(duration)
73
+ parse_result = PAT.match(duration.to_s)
74
+ raise(ArgumentError,
75
+ "Failed to parse '#{duration}': expected format PnDTnHnMn.nS with integer n" \
76
+ ' and optionally missing duration components') unless parse_result
77
+ Duration.new(parse_result[:days], parse_result[:hours], parse_result[:minutes], parse_result[:seconds])
78
+ end
79
+
80
+ # A string formatted as `PnDTnHnMn.nS`, where `n` is a number that goes with the character code following it.
81
+ #
82
+ # D - days<br>
83
+ # H - hours<br>
84
+ # M - minutes<br>
85
+ # S - seconds<br>
86
+ #
87
+ # @example a duration of 1 day, 2 hours, 3 minutes, 4.5 seconds
88
+ # P1DT2H3M4.5S
89
+ # @return [String] this {Duration} as a string formatted `PnDTnHnMn.nS`.
90
+ #
91
+ # @see https://docs.oracle.com/javase/8/docs/api/java/time/Duration.html#parse-java.lang.CharSequence-
92
+ # Java 8 Duration#parse
93
+ def to_s
94
+ # Construct a string of the form PnDTnHnMn.nS
95
+ "P#{@days}DT#{@hours}H#{@minutes}M#{@seconds}S"
96
+ end
97
+
98
+ # @private
99
+ def eql?(other)
100
+ other.is_a?(Duration) && as_seconds == other.as_seconds
101
+ end
102
+ alias == eql?
103
+
104
+ # @return [Float] this {Duration} object converted to seconds
105
+ def as_seconds
106
+ @seconds + @minutes * 60 + @hours * 3600 + @days * 86400
107
+ end
108
+
109
+ # @private
110
+ def hash
111
+ @hash ||= begin
112
+ h = 17
113
+ h = 31 * h + @days.hash
114
+ h = 31 * h + @hours.hash
115
+ h = 31 * h + @minutes.hash
116
+ h = 31 * h + @seconds.hash
117
+ h
118
+ end
119
+ end
120
+
121
+ # @private
122
+ def inspect
123
+ "#<Duration:0x#{object_id.to_s(16)} " \
124
+ "@days=#{@days.inspect}, " \
125
+ "@hours=#{@hours.inspect}, " \
126
+ "@minutes=#{@minutes.inspect}, " \
127
+ "@seconds=#{@seconds.inspect}>"
128
+ end
129
+ end
130
+ end
131
+ end
@@ -0,0 +1,74 @@
1
+ # encoding: utf-8
2
+
3
+ #--
4
+ # Copyright (C) 2016 DataStax Inc.
5
+ #
6
+ # This software can be used solely with DataStax Enterprise. Please consult the license at
7
+ # http://www.datastax.com/terms/datastax-dse-driver-license-terms
8
+ #++
9
+
10
+ module Dse
11
+ module Graph
12
+ # Edge represents an edge in DSE graph. An edge connects two vertices.
13
+ class Edge
14
+ include Cassandra::Util
15
+
16
+ # @return [Hash] id of this edge
17
+ attr_reader :id
18
+ # @return [String] label of this edge
19
+ attr_reader :label
20
+ # @return [Hash<String, String>] properties of this edge
21
+ attr_reader :properties
22
+ # @return [String] id of the "to" vertex of the edge
23
+ attr_reader :in_v
24
+ # @return [String] label of the "to" vertex of the edge
25
+ attr_reader :in_v_label
26
+ # @return [String] id of the "from" vertex of the edge
27
+ attr_reader :out_v
28
+ # @return [String] label of the "from" vertex of the edge
29
+ attr_reader :out_v_label
30
+
31
+ # @private
32
+ def initialize(id, label, properties, in_v, in_v_label, out_v, out_v_label)
33
+ @id = id
34
+ @label = label
35
+ @properties = properties
36
+ @in_v = in_v
37
+ @in_v_label = in_v_label
38
+ @out_v = out_v
39
+ @out_v_label = out_v_label
40
+ end
41
+
42
+ # @private
43
+ def [](key)
44
+ @properties[key]
45
+ end
46
+
47
+ # @private
48
+ def eql?(other)
49
+ # id's are unique among graph objects, so we only need to compare id's to test for equality.
50
+ other.is_a?(Edge) && \
51
+ @id == other.id
52
+ end
53
+ alias == eql?
54
+
55
+ # @private
56
+ def hash
57
+ # id's are unique among graph objects, so we only need to hash on the id for safely adding to a hash/set.
58
+ @hash ||= 31 * 17 + @id.hash
59
+ end
60
+
61
+ # @private
62
+ def inspect
63
+ "#<Dse::Graph::Edge:0x#{object_id.to_s(16)} " \
64
+ "@id=#{@id.inspect}, " \
65
+ "@label=#{@label.inspect}, " \
66
+ "@properties=#{@properties.inspect}, " \
67
+ "@in_v=#{@in_v.inspect}, " \
68
+ "@in_v_label=#{@in_v_label.inspect}, " \
69
+ "@out_v=#{@out_v.inspect}, " \
70
+ "@out_v_label=#{@out_v_label.inspect}>"
71
+ end
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,194 @@
1
+ # encoding: utf-8
2
+
3
+ #--
4
+ # Copyright (C) 2016 DataStax Inc.
5
+ #
6
+ # This software can be used solely with DataStax Enterprise. Please consult the license at
7
+ # http://www.datastax.com/terms/datastax-dse-driver-license-terms
8
+ #++
9
+
10
+ module Dse
11
+ module Graph
12
+ # @!parse
13
+ # class Options
14
+ # # @return [String] name of the targeted graph; required unless the statement is a system query.
15
+ # attr_accessor :graph_name
16
+ # # @return [String] graph traversal source (default "g")
17
+ # attr_accessor :graph_source
18
+ # # @return [String] language used in the graph statement (default "gremlin-groovy")
19
+ # attr_accessor :graph_language
20
+ # # @return [Cassandra::CONSISTENCIES] read consistency level for graph statement.
21
+ # # Overrides the standard statement consistency level. Defaults to ONE in the server,
22
+ # # but the default may be configured differently.
23
+ # attr_accessor :graph_read_consistency
24
+ # # @return [Cassandra::CONSISTENCIES] write consistency level for graph statement.
25
+ # # Overrides the standard statement consistency level. Defaults to QUORUM in the server,
26
+ # # but the default may be configured differently.
27
+ # attr_accessor :graph_write_consistency
28
+ # end
29
+
30
+ # Options for DSE Graph queries
31
+ class Options
32
+ # @return [Numeric] the timeout for graph requests
33
+ attr_reader :timeout
34
+
35
+ # @private
36
+ DEFAULT_GRAPH_OPTIONS = {
37
+ 'graph-source' => 'g',
38
+ 'graph-language' => 'gremlin-groovy'
39
+ }.freeze
40
+
41
+ # @private
42
+ OPTION_NAMES = [
43
+ :graph_name,
44
+ :graph_source,
45
+ :graph_language,
46
+ :graph_read_consistency,
47
+ :graph_write_consistency
48
+ ].freeze
49
+
50
+ # Create an Options object.
51
+ # @param options [Hash] optional hash containing graph options. Keys are option name symbols
52
+ # (e.g. `:graph_name`). Unset options will inherit from the defaults.
53
+ def initialize(options = {})
54
+ # Filter the given options to only those we care about.
55
+ @real_options = {}
56
+ return unless options
57
+
58
+ options.each do |k, v|
59
+ set(k, v) if OPTION_NAMES.include?(k)
60
+ end
61
+
62
+ set_timeout(options[:timeout])
63
+ end
64
+
65
+ OPTION_NAMES.each do |attr|
66
+ define_method(attr.to_s) do
67
+ @real_options[stringify(attr)]
68
+ end
69
+
70
+ define_method("#{attr}=") do |value|
71
+ @real_options[stringify(attr)] = value
72
+ end
73
+ end
74
+
75
+ # @private
76
+ def timeout=(val)
77
+ set_timeout(val)
78
+ val
79
+ end
80
+
81
+ # @private
82
+ # rubocop:disable Style/AccessorMethodName
83
+ def set_timeout(new_timeout)
84
+ @timeout = new_timeout
85
+ if @timeout
86
+ @real_options['request-timeout'] = [@timeout * 1000].pack('Q>')
87
+ else
88
+ @real_options.delete('request-timeout')
89
+ end
90
+ nil
91
+ end
92
+
93
+ # Set an option in this {Options} object. This is primarily used to set "expert" options that
94
+ # are not part of the public api and thus may change over time.
95
+ # @param key [String, Symbol] option to set.
96
+ # @param value [String] value to set for the option.
97
+ # @return [Options] self, thus allowing method chaining.
98
+ def set(key, value)
99
+ string_key = stringify(key)
100
+ if string_key == 'timeout'
101
+ set_timeout(value)
102
+ elsif value
103
+ @real_options[stringify(key)] = value
104
+ end
105
+ self
106
+ end
107
+
108
+ # Delete an option from this {Options} object.
109
+ # @param key [String, Symbol] option to delete.
110
+ # @return nil
111
+ def delete(key)
112
+ string_key = stringify(key)
113
+ if string_key == 'timeout'
114
+ @timeout = nil
115
+ @real_options.delete('request-timeout')
116
+ else
117
+ @real_options.delete(string_key)
118
+ end
119
+ nil
120
+ end
121
+
122
+ # Merge another {Options} object with this one to produce a new merged {Options} object.
123
+ # The "other" object's values take precedence over this one.
124
+ # @param other [Options] Options object to merge with this one.
125
+ # @return [Options] new Options object with the merged options.
126
+ def merge(other)
127
+ # Just return our-self (no need to copy) if we're merging in nothing.
128
+ return self if other.nil?
129
+
130
+ # This is fairly efficient, but manipulates the guts of an Options object.
131
+ result = Options.new
132
+ result.instance_variable_set(:@real_options,
133
+ @real_options.merge(other.instance_variable_get(:@real_options)))
134
+ other_timeout = other.instance_variable_get(:@timeout)
135
+ result.instance_variable_set(:@timeout,
136
+ other_timeout ? other_timeout : @timeout)
137
+ result
138
+ end
139
+
140
+ # Clear the options within this {Options} object.
141
+ def clear
142
+ @real_options.clear
143
+ @timeout = nil
144
+ end
145
+
146
+ # @private
147
+ def stringify(attr)
148
+ attr.to_s.tr('_', '-')
149
+ end
150
+
151
+ # @private
152
+ def merge!(other)
153
+ result = merge(other)
154
+ @real_options = result.instance_variable_get(:@real_options)
155
+ @timeout = result.instance_variable_get(:@timeout)
156
+ self
157
+ end
158
+
159
+ # @return whether or not this options object is configured for the analytics graph source.
160
+ def analytics?
161
+ @real_options['graph-source'] == 'a'
162
+ end
163
+
164
+ # @private
165
+ def as_payload
166
+ # Merge in real options with defaults to get a final payload
167
+ DEFAULT_GRAPH_OPTIONS.merge(@real_options)
168
+ end
169
+
170
+ # @private
171
+ def eql?(other)
172
+ other.is_a?(Options) && \
173
+ @real_options == other.instance_variable_get(:@real_options)
174
+ end
175
+ alias == eql?
176
+
177
+ # @private
178
+ def hash
179
+ @hash ||= 31 * 17 + @real_options.hash
180
+ end
181
+
182
+ # @private
183
+ def inspect
184
+ "#<Dse::Graph::Options:0x#{object_id.to_s(16)} " \
185
+ "@graph_name=#{@real_options['graph-name'].inspect}, " \
186
+ "@graph_source=#{@real_options['graph-source'].inspect}, " \
187
+ "@graph_language=#{@real_options['graph-language'].inspect}, " \
188
+ "@graph_read_consistency=#{@real_options['graph-read-consistency'].inspect}, " \
189
+ "@graph_write_consistency=#{@real_options['graph-write-consistency'].inspect}, " \
190
+ "@timeout=#{@timeout.inspect}>"
191
+ end
192
+ end
193
+ end
194
+ end
@@ -0,0 +1,54 @@
1
+ # encoding: utf-8
2
+
3
+ #--
4
+ # Copyright (C) 2016 DataStax Inc.
5
+ #
6
+ # This software can be used solely with DataStax Enterprise. Please consult the license at
7
+ # http://www.datastax.com/terms/datastax-dse-driver-license-terms
8
+ #++
9
+
10
+ module Dse
11
+ module Graph
12
+ # Path represents a path connecting a set of vertices.
13
+ class Path
14
+ # @return [Array<Array>] labels in the path
15
+ attr_reader :labels
16
+ # @return [Array<Vertex, Edge, Result>] objects in the path, coerced to domain objects that
17
+ # we recognize, or a {Result} otherwise
18
+ attr_reader :objects
19
+
20
+ # @private
21
+ def initialize(labels, objects)
22
+ @labels = labels
23
+ @objects = objects.map do |o|
24
+ Result.new(o).cast
25
+ end
26
+ end
27
+
28
+ # @private
29
+ def eql?(other)
30
+ other.is_a?(Path) && \
31
+ @labels == other.labels && \
32
+ @objects == other.objects
33
+ end
34
+ alias == eql?
35
+
36
+ # @private
37
+ def hash
38
+ @hash ||= begin
39
+ h = 17
40
+ h = 31 * h + @labels.hash
41
+ h = 31 * h + @objects.hash
42
+ h
43
+ end
44
+ end
45
+
46
+ # @private
47
+ def inspect
48
+ "#<Dse::Graph::Path:0x#{object_id.to_s(16)} " \
49
+ "@labels=#{@labels.inspect}, " \
50
+ "@objects=#{@objects.inspect}>"
51
+ end
52
+ end
53
+ end
54
+ end