cql-model 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.ruby-version +1 -0
- data/Gemfile +17 -0
- data/Gemfile.lock +56 -0
- data/README.md +24 -0
- data/Rakefile +54 -0
- data/VERSION +1 -0
- data/cql-model.gemspec +85 -0
- data/cql_model.rconf +11 -0
- data/lib/cql/model.rb +49 -0
- data/lib/cql/model/class_methods.rb +178 -0
- data/lib/cql/model/instance_methods.rb +50 -0
- data/lib/cql/model/query.rb +107 -0
- data/lib/cql/model/query/comparison_expression.rb +126 -0
- data/lib/cql/model/query/expression.rb +5 -0
- data/lib/cql/model/query/insert_statement.rb +51 -0
- data/lib/cql/model/query/mutation_statement.rb +48 -0
- data/lib/cql/model/query/select_statement.rb +143 -0
- data/lib/cql/model/query/statement.rb +44 -0
- data/lib/cql/model/query/update_expression.rb +104 -0
- data/lib/cql/model/query/update_statement.rb +91 -0
- metadata +227 -0
@@ -0,0 +1,50 @@
|
|
1
|
+
module Cql::Model::InstanceMethods
|
2
|
+
# Instantiate a new instance of this model. Do not validate the contents of
|
3
|
+
# cql_properties; it may contain properties that aren't declared by this model, that have
|
4
|
+
# a missing CQL column, or an improper name/value for their column type.
|
5
|
+
#
|
6
|
+
# @param [Hash] cql_properties typed hash of all properties associated with this model
|
7
|
+
def initialize(cql_properties=nil)
|
8
|
+
@cql_properties = cql_properties || {}
|
9
|
+
end
|
10
|
+
|
11
|
+
# Read a property. Property names are column names, and can therefore take any data type
|
12
|
+
# that a column name can take (integer, UUID, etc).
|
13
|
+
#
|
14
|
+
# @param [Object] name
|
15
|
+
# @return [Object] the value of the specified column, or nil
|
16
|
+
def [](name)
|
17
|
+
@cql_properties[name]
|
18
|
+
end
|
19
|
+
|
20
|
+
# Start an INSERT CQL statement to update model
|
21
|
+
# @see Cql::Model::Query::InsertStatement
|
22
|
+
#
|
23
|
+
# @param [Hash] values Hash of column values indexed by column name, optional
|
24
|
+
# @return [Cql::Model::Query::InsertStatement] a query object to customize (ttl, timestamp etc) or execute
|
25
|
+
#
|
26
|
+
# @example
|
27
|
+
# joe.update(:age => 35).execute
|
28
|
+
# joe.update.ttl(3600).execute
|
29
|
+
# joe.update(:age => 36).ttl(7200).consistency('ONE').execute
|
30
|
+
def update(values={})
|
31
|
+
key_vals = self.class.primary_key.inject({}) { |h, k| h[k] = @cql_properties[k]; h }
|
32
|
+
Cql::Model::Query::UpdateStatement.new(self.class).update(values.merge(key_vals))
|
33
|
+
end
|
34
|
+
|
35
|
+
# Start an UPDATE CQL statement to update all models with given key
|
36
|
+
# This can update multiple models if the key is part of a composite key
|
37
|
+
# Updating all models with given (different) key values can be done using the '.update' class method
|
38
|
+
# @see Cql::Model::Query::UpdateStatement
|
39
|
+
#
|
40
|
+
# @param [Symbol|String] key Name of key used to select models to be updated
|
41
|
+
# @param [Hash] values Hash of column values indexed by column names, optional
|
42
|
+
# @return [Cql::Model::Query::UpdateStatement] a query object to customize (ttl, timestamp etc) or execute
|
43
|
+
#
|
44
|
+
# @example
|
45
|
+
# joe.update_all_by(:name, :age => 25).execute # Set all joe's age to 25
|
46
|
+
# joe.update_all_by(:name).ttl(3600).execute # Set all joe's TTL to one hour
|
47
|
+
def update_all_by(key, values={})
|
48
|
+
Cql::Model::Query::UpdateStatement.new(self.class).update(values.merge({ key => @cql_properties[key.to_s] }))
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,107 @@
|
|
1
|
+
module Cql::Model::Query
|
2
|
+
# CQL single quote character.
|
3
|
+
SQ = "'"
|
4
|
+
|
5
|
+
# CQL single-quote escape sequence.
|
6
|
+
SQSQ = "''"
|
7
|
+
|
8
|
+
# CQL double-quote character.
|
9
|
+
DQ = '"'
|
10
|
+
|
11
|
+
# CQL double-quote escape.
|
12
|
+
DQDQ = '""'
|
13
|
+
|
14
|
+
# Valid CQL identifier (can be used as a column name without double-quoting)
|
15
|
+
IDENTIFIER = /[a-z][a-z0-9_]*/i
|
16
|
+
|
17
|
+
module_function
|
18
|
+
|
19
|
+
# Transform a list of symbols or strings into CQL column names. Performs no safety checks!!
|
20
|
+
def cql_column_names(list)
|
21
|
+
if list.empty?
|
22
|
+
'*'
|
23
|
+
else
|
24
|
+
list.join(', ')
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
# Transform a Ruby object into its CQL identifier representation.
|
29
|
+
#
|
30
|
+
# @TODO more docs
|
31
|
+
#
|
32
|
+
def cql_identifier(value)
|
33
|
+
# TODO UUID, Time, ...
|
34
|
+
case value
|
35
|
+
when Symbol, String
|
36
|
+
if value =~ IDENTIFIER
|
37
|
+
value.to_s
|
38
|
+
else
|
39
|
+
"#{DQ}#{value.gsub(DQ, DQDQ)}#{DQ}"
|
40
|
+
end
|
41
|
+
when Numeric, TrueClass, FalseClass
|
42
|
+
"#{DQ}#{cql_value(value)}#{DQ}"
|
43
|
+
else
|
44
|
+
raise Cql::Model::SyntaxError, "Cannot convert #{value.class} to a CQL identifier"
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
# Transform a Ruby object into its CQL literal value representation. A literal value is anything
|
49
|
+
# that can appear in a CQL statement as a key or column value (but not column NAME; see
|
50
|
+
# #cql_identifier to convert values to column names).
|
51
|
+
#
|
52
|
+
# @param value [String,Numeric,Boolean,Array,Set,Array,#map]
|
53
|
+
# @return [String] the CQL equivalent of value
|
54
|
+
#
|
55
|
+
# When used as a key or column value, CQL supports the following kinds of literal value:
|
56
|
+
# * unquoted identifier (treated as a string value)
|
57
|
+
# * string literal
|
58
|
+
# * integer number
|
59
|
+
# * UUID
|
60
|
+
# * floating-point number
|
61
|
+
# * boolean true/false
|
62
|
+
#
|
63
|
+
# When used as a column name, any value that is not a valid identifier MUST BE ENCLOSED IN
|
64
|
+
# DOUBLE QUOTES. This method does not handle the double-quote escaping; see #cql_identifier
|
65
|
+
# for that.
|
66
|
+
#
|
67
|
+
# @see #cql_identifier
|
68
|
+
# @see http://www.datastax.com/docs/1.1/references/cql/cql_lexicon#keywords-and-identifiers
|
69
|
+
def cql_value(value, context=nil)
|
70
|
+
# TODO UUID, Time, ...
|
71
|
+
case value
|
72
|
+
when String
|
73
|
+
"#{SQ}#{value.gsub(SQ, SQSQ)}#{SQ}"
|
74
|
+
when Numeric, TrueClass, FalseClass
|
75
|
+
value.to_s
|
76
|
+
when Set
|
77
|
+
raise SyntaxError, "Set notation is not allowed outside UPDATE statements" unless (context == :update)
|
78
|
+
'{' + value.map { |v| cql_value(v) }.join(', ') + '}'
|
79
|
+
else
|
80
|
+
if value.respond_to?(:map)
|
81
|
+
if value.respond_to?(:each_pair)
|
82
|
+
# Pairwise map -- CQL map literal
|
83
|
+
'{' + value.map { |k, v| "#{cql_value(k)}: #{cql_value(v)}" }.join(', ') + '}'
|
84
|
+
else
|
85
|
+
# Single map -- CQL list (for UPDATE) or set (for WHERE...IN) literal
|
86
|
+
case context
|
87
|
+
when :update
|
88
|
+
'[' + value.map { |v| cql_value(v) }.join(', ') + ']'
|
89
|
+
else
|
90
|
+
'(' + value.map { |v| cql_value(v) }.join(', ') + ')'
|
91
|
+
end
|
92
|
+
end
|
93
|
+
else
|
94
|
+
raise Cql::Model::SyntaxError, "Cannot convert #{value.class} to a CQL value"
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
require 'cql/model/query/expression'
|
101
|
+
require 'cql/model/query/comparison_expression'
|
102
|
+
require 'cql/model/query/update_expression'
|
103
|
+
require 'cql/model/query/statement'
|
104
|
+
require 'cql/model/query/mutation_statement'
|
105
|
+
require 'cql/model/query/select_statement'
|
106
|
+
require 'cql/model/query/insert_statement'
|
107
|
+
require 'cql/model/query/update_statement'
|
@@ -0,0 +1,126 @@
|
|
1
|
+
require 'set'
|
2
|
+
|
3
|
+
module Cql::Model::Query
|
4
|
+
# @TODO docs
|
5
|
+
class ComparisonExpression < Expression
|
6
|
+
# Operators allowed in a where-clause lambda
|
7
|
+
OPERATORS = {
|
8
|
+
:== => '=',
|
9
|
+
:'!=' => '!=',
|
10
|
+
:'>' => '>',
|
11
|
+
:'<' => '<',
|
12
|
+
:'>=' => '>=',
|
13
|
+
:'<=' => '<=',
|
14
|
+
:'in' => 'IN',
|
15
|
+
}.freeze
|
16
|
+
|
17
|
+
# Methods used to escape CQL column names that aren't valid CQL identifiers
|
18
|
+
TYPECASTS = [
|
19
|
+
:ascii,
|
20
|
+
:bigint,
|
21
|
+
:blob,
|
22
|
+
:boolean,
|
23
|
+
:counter,
|
24
|
+
:decimal,
|
25
|
+
:double,
|
26
|
+
:float,
|
27
|
+
:int,
|
28
|
+
:text,
|
29
|
+
:timestamp,
|
30
|
+
:uuid,
|
31
|
+
:timeuuid,
|
32
|
+
:varchar,
|
33
|
+
:varint
|
34
|
+
].freeze
|
35
|
+
|
36
|
+
# @TODO docs
|
37
|
+
def initialize(*params, &block)
|
38
|
+
@left = nil
|
39
|
+
@operator = nil
|
40
|
+
@right = nil
|
41
|
+
|
42
|
+
instance_exec(*params, &block) if block
|
43
|
+
end
|
44
|
+
|
45
|
+
# @TODO docs
|
46
|
+
def to_s
|
47
|
+
__build__
|
48
|
+
end
|
49
|
+
|
50
|
+
# @TODO docs
|
51
|
+
def inspect
|
52
|
+
__build__
|
53
|
+
end
|
54
|
+
|
55
|
+
# This is where the magic happens. Ensure all of our operators are overloaded so they call
|
56
|
+
# #apply and contribute to the CQL expression that will be built.
|
57
|
+
OPERATORS.keys.each do |op|
|
58
|
+
define_method(op) do |*args|
|
59
|
+
__apply__(op, args)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
TYPECASTS.each do |func|
|
64
|
+
define_method(func) do |*args|
|
65
|
+
__apply__(func, args)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
# @TODO docs
|
70
|
+
def method_missing(token, *args)
|
71
|
+
__apply__(token, args)
|
72
|
+
end
|
73
|
+
|
74
|
+
private
|
75
|
+
|
76
|
+
# @TODO docs
|
77
|
+
def __apply__(token, args)
|
78
|
+
if @left.nil?
|
79
|
+
if args.empty?
|
80
|
+
# A well-behaved CQL identifier (column name that is a valid Ruby method name)
|
81
|
+
@left = token
|
82
|
+
elsif args.length == 1
|
83
|
+
# A CQL typecast (column name that is an integer, float, etc and must be wrapped in a decorator)
|
84
|
+
@left = args.first
|
85
|
+
else
|
86
|
+
::Kernel.raise ::Cql::Model::SyntaxError.new(
|
87
|
+
"Unacceptable token '#{token}'; expected a CQL identifier or typecast")
|
88
|
+
end
|
89
|
+
elsif @operator.nil?
|
90
|
+
# Looking for an operator + right operand
|
91
|
+
if OPERATORS.keys.include?(token)
|
92
|
+
@operator = token
|
93
|
+
|
94
|
+
if (args.size > 1) || (token == :in)
|
95
|
+
@right = args
|
96
|
+
else
|
97
|
+
@right = args.first
|
98
|
+
end
|
99
|
+
else
|
100
|
+
::Kernel.raise ::Cql::Model::SyntaxError.new(
|
101
|
+
"Unacceptable token '#{token}'; expected a CQL-compatible operator")
|
102
|
+
end
|
103
|
+
else
|
104
|
+
::Kernel.raise ::Cql::Model::SyntaxError.new(
|
105
|
+
"Unacceptable token '#{token}'; the expression is " +
|
106
|
+
"already complete")
|
107
|
+
end
|
108
|
+
|
109
|
+
self
|
110
|
+
end
|
111
|
+
|
112
|
+
# @TODO docs
|
113
|
+
def __build__
|
114
|
+
if @left.nil? || @operator.nil? || @right.nil?
|
115
|
+
::Kernel.raise ::Cql::Model::SyntaxError.new(
|
116
|
+
"Cannot build a CQL expression; the Ruby expression is incomplete " +
|
117
|
+
"(#{@left.inspect}, #{@operator.inspect}, #{@right.inspect})")
|
118
|
+
else
|
119
|
+
left = ::Cql::Model::Query.cql_identifier(@left)
|
120
|
+
op = OPERATORS[@operator]
|
121
|
+
right = ::Cql::Model::Query.cql_value(@right)
|
122
|
+
"#{left} #{op} #{right}"
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
module Cql::Model::Query
|
2
|
+
|
3
|
+
# INSERT statement DSL
|
4
|
+
# << An INSERT writes one or more columns to a record in a Cassandra column family. No results are returned.
|
5
|
+
# The first column name in the INSERT list must be the name of the column family key >>
|
6
|
+
# (from: http://www.datastax.com/docs/1.1/references/cql/INSERT)
|
7
|
+
#
|
8
|
+
# Ex:
|
9
|
+
# Model.create(:key => 'val', :col1 => 'value', :col2 => 42) # Simple insert
|
10
|
+
# Model.create(:key => 'val', :key2 => 64, :col1 => 'value', :col2 => 42) # Composite keys
|
11
|
+
# Model.create(:key => 'val', :col => 'value').ttl(3600) # TTL in seconds
|
12
|
+
# Model.create(:key => 'val', :col => 'value').timestamp(1366057256324) # Milliseconds since epoch timestamp
|
13
|
+
# Model.create(:key => 'val', :col => 'value').timestamp('2013-04-15 13:21:48') # ISO 8601 timestamp
|
14
|
+
# Model.create(:key => 'val', :col => 'value').consistency('ONE') # Custom consistency (default is 'LOCAL_QUORUM')
|
15
|
+
# Model.create(:key => 'val', :col => 'value').ttl(3600).timestamp(1366057256324).consistency('ONE') # Multiple options
|
16
|
+
class InsertStatement < MutationStatement
|
17
|
+
|
18
|
+
# Specify names and values to insert.
|
19
|
+
#
|
20
|
+
# @param [Hash] values Hash of column values indexed by column name
|
21
|
+
def insert(values)
|
22
|
+
raise ArgumentError, "Cannot specify INSERT values twice" unless @values.nil?
|
23
|
+
@values = values
|
24
|
+
self
|
25
|
+
end
|
26
|
+
|
27
|
+
alias create insert
|
28
|
+
|
29
|
+
# Build a string representation of this CQL statement, suitable for execution by a CQL client.
|
30
|
+
# Do not validate the statement for completeness; Cassnadra will raise an error if a key
|
31
|
+
# component is missing.
|
32
|
+
#
|
33
|
+
# @return [String] a CQL INSERT statement with suitable constraints and options
|
34
|
+
def to_s
|
35
|
+
keys = @klass.primary_key.inject([]) { |h, k| h << [k, @values.delete(k)]; h }
|
36
|
+
if keys.any? { |k| k[1].nil? }
|
37
|
+
raise Cql::Model::MissingKey.new("Missing primary key(s) in INSERT statement: #{keys.select { |k| k[1].nil? }.map(&:first).map(&:inspect).join(', ')}")
|
38
|
+
end
|
39
|
+
s = "INSERT INTO #{@klass.table_name} (#{keys.map { |k| k[0] }.join(', ')}, #{@values.keys.join(', ')})"
|
40
|
+
s << " VALUES (#{keys.map { |k| ::Cql::Model::Query.cql_value(k[1]) }.join(', ')}, #{@values.values.map { |v| ::Cql::Model::Query.cql_value(v) }.join(', ')})"
|
41
|
+
options = []
|
42
|
+
options << "CONSISTENCY #{@consistency || @klass.write_consistency}"
|
43
|
+
options << "TIMESTAMP #{@timestamp}" unless @timestamp.nil?
|
44
|
+
options << "TTL #{@ttl}" unless @ttl.nil?
|
45
|
+
s << " USING #{options.join(' AND ')}"
|
46
|
+
s << ';'
|
47
|
+
|
48
|
+
s
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
module Cql::Model::Query
|
2
|
+
|
3
|
+
# Common parent to InsertStatement and UpdateStatment
|
4
|
+
# provide helpers for managing common DSL settings
|
5
|
+
class MutationStatement < Statement
|
6
|
+
|
7
|
+
# Instantiate statement
|
8
|
+
#
|
9
|
+
# @param [Class] klass
|
10
|
+
# @param [Cql::Client] CQL client used to execute statement
|
11
|
+
def initialize(klass, client=nil)
|
12
|
+
super(klass, client)
|
13
|
+
@values = nil
|
14
|
+
@ttl = nil
|
15
|
+
@timestamp = nil
|
16
|
+
end
|
17
|
+
|
18
|
+
# DSL for setting TTL value
|
19
|
+
#
|
20
|
+
# @param [Fixnum] ttl_value TTL value in seconds
|
21
|
+
def ttl(ttl_value)
|
22
|
+
raise ArgumentError, "Cannot specify TTL twice" unless @ttl.nil?
|
23
|
+
@ttl = ttl_value
|
24
|
+
self
|
25
|
+
end
|
26
|
+
|
27
|
+
# DSL for setting timestamp value
|
28
|
+
#
|
29
|
+
# @param [Fixnum|String] timestamp_value (number of milliseconds since epoch or ISO 8601 date time value)
|
30
|
+
def timestamp(timestamp_value)
|
31
|
+
raise ArgumentError, "Cannot specify timestamp twice" unless @timestamp.nil?
|
32
|
+
@timestamp = timestamp_value
|
33
|
+
self
|
34
|
+
end
|
35
|
+
|
36
|
+
# Execute this statement on the CQL client connection
|
37
|
+
# INSERT statements do not return a result
|
38
|
+
#
|
39
|
+
# @return [true] always returns true
|
40
|
+
def execute
|
41
|
+
@client.execute(to_s)
|
42
|
+
true
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
48
|
+
|
@@ -0,0 +1,143 @@
|
|
1
|
+
module Cql::Model::Query
|
2
|
+
|
3
|
+
# SELECT statement DSL
|
4
|
+
# << A SELECT expression reads one or more records from a Cassandra column family and returns a result-set of rows.
|
5
|
+
# Each row consists of a row key and a collection of columns corresponding to the query. >>
|
6
|
+
# (from: http://www.datastax.com/docs/1.1/references/cql/SELECT)
|
7
|
+
#
|
8
|
+
# Ex:
|
9
|
+
# Model.select(:col1, :col2)
|
10
|
+
# Model.select(:col1, :col2).where { name == 'Joe' }
|
11
|
+
# Model.select(:col1, :col2).where { name == 'Joe' }.and { age.in(33,34,35) }
|
12
|
+
# Model.select(:col1, :col2).where { name == 'Joe' }.and { age.in(33,34,35) }.order_by(:age).desc
|
13
|
+
class SelectStatement < Statement
|
14
|
+
|
15
|
+
# Instantiate statement
|
16
|
+
#
|
17
|
+
# @param [Class] klass Model class
|
18
|
+
# @param [Cql::Client] CQL client used to execute statement
|
19
|
+
def initialize(klass, client=nil)
|
20
|
+
super(klass, client)
|
21
|
+
@columns = nil
|
22
|
+
@where = []
|
23
|
+
@order = ''
|
24
|
+
@limit = nil
|
25
|
+
end
|
26
|
+
|
27
|
+
# Create or append to the WHERE clause for this statement. The block that you pass will define the constraint
|
28
|
+
# and any where() parameters will be forwarded to the block as yield parameters. This allows late binding of
|
29
|
+
# variables in the WHERE clause, e.g. for prepared statements.
|
30
|
+
#
|
31
|
+
# @param [Object] *params parameters to be forwarded to the block
|
32
|
+
# @yield the block will be evaluated in the context of a ComparisonExpression to capture a CQL expression that will be appended to the WHERE clause
|
33
|
+
#
|
34
|
+
# @return [SelectStatement] always returns self
|
35
|
+
#
|
36
|
+
# @example find people named Joe
|
37
|
+
# where { name == "Joe" }
|
38
|
+
#
|
39
|
+
# @example find people older than 33 who are named Joe or Fred
|
40
|
+
# where { age > 33 }.and { name.in("Joe", "Fred") }
|
41
|
+
#
|
42
|
+
# @example find people older than 33
|
43
|
+
# where { age > 33 }
|
44
|
+
#
|
45
|
+
# @example find by a late-bound search term (e.g. for a named scope)
|
46
|
+
# where(age_chosen_by_user) { |chosen| age > chosen }
|
47
|
+
#
|
48
|
+
# @example find by a column name that is not a valid Ruby identifier
|
49
|
+
# where { timestamp(12345) == "logged in" }
|
50
|
+
#
|
51
|
+
# @see ComparisonExpression
|
52
|
+
# @see Cql::Model::ClassMethods#scope
|
53
|
+
def where(*params, &block)
|
54
|
+
@where << ComparisonExpression.new(*params, &block)
|
55
|
+
self
|
56
|
+
end
|
57
|
+
|
58
|
+
alias and where
|
59
|
+
|
60
|
+
# Specify the order in which result rows should be returned.
|
61
|
+
#
|
62
|
+
# @param [Array] *params glob of column names (symbols, strings, or Ruby values that correspond to valid column names)
|
63
|
+
# @return [SelectStatement] always returns self
|
64
|
+
def order(*columns)
|
65
|
+
raise ArgumentError, "Cannot specify ORDER BY twice" unless @order.empty?
|
66
|
+
@order = ::Cql::Model::Query.cql_column_names(columns)
|
67
|
+
self
|
68
|
+
end
|
69
|
+
|
70
|
+
alias order_by order
|
71
|
+
|
72
|
+
# @TODO docs
|
73
|
+
def asc
|
74
|
+
raise ArgumentError, "Cannot specify ASC / DESC twice" if @order =~ /ASC|DESC$/
|
75
|
+
raise ArgumentError, "Must specify ORDER BY before ASC" if @order.empty?
|
76
|
+
@order << " ASC"
|
77
|
+
self
|
78
|
+
end
|
79
|
+
|
80
|
+
# @TODO docs
|
81
|
+
def desc
|
82
|
+
raise ArgumentError, "Cannot specify ASC / DESC twice" if @order =~ /ASC|DESC$/
|
83
|
+
raise ArgumentError, "Must specify ORDER BY before DESC" if @order.empty?
|
84
|
+
@order << " DESC"
|
85
|
+
self
|
86
|
+
end
|
87
|
+
|
88
|
+
# @TODO docs
|
89
|
+
def limit(lim)
|
90
|
+
raise ArgumentError, "Cannot specify LIMIT twice" unless @limit.nil?
|
91
|
+
@limit = lim
|
92
|
+
self
|
93
|
+
end
|
94
|
+
|
95
|
+
# @TODO docs
|
96
|
+
def select(*columns)
|
97
|
+
raise ArgumentError, "Cannot specify SELECT column names twice" unless @columns.nil?
|
98
|
+
@columns = ::Cql::Model::Query.cql_column_names(columns)
|
99
|
+
self
|
100
|
+
end
|
101
|
+
|
102
|
+
# @return [String] a CQL SELECT statement with suitable constraints and options
|
103
|
+
def to_s
|
104
|
+
s = "SELECT #{@columns || '*'} FROM #{@klass.table_name}"
|
105
|
+
|
106
|
+
s << " USING CONSISTENCY " << (@consistency || @klass.write_consistency)
|
107
|
+
|
108
|
+
unless @where.empty?
|
109
|
+
s << " WHERE " << @where.map { |w| w.to_s }.join(' AND ')
|
110
|
+
end
|
111
|
+
|
112
|
+
s << " ORDER BY " << @order unless @order.empty?
|
113
|
+
s << " LIMIT #{@limit}" unless @limit.nil?
|
114
|
+
s << ';'
|
115
|
+
end
|
116
|
+
|
117
|
+
# Execute this SELECT statement on the CQL client connection and yield each row of the
|
118
|
+
# result set as a raw-data Hash.
|
119
|
+
#
|
120
|
+
# @yield each row of the result set
|
121
|
+
# @yieldparam [Hash] row a Ruby Hash representing the column names and values for a given row
|
122
|
+
# @return [true] always returns true
|
123
|
+
def execute(&block)
|
124
|
+
@client.execute(to_s).each_row(&block).size
|
125
|
+
|
126
|
+
true
|
127
|
+
end
|
128
|
+
|
129
|
+
alias each_row execute
|
130
|
+
|
131
|
+
# Execute this SELECT statement on the CQL client connection and yield each row of the
|
132
|
+
# as an instance of CQL::Model.
|
133
|
+
#
|
134
|
+
# @yield each row of the result set
|
135
|
+
# @yieldparam [Hash] row a Ruby Hash representing the column names and values for a given row
|
136
|
+
# @return [true] always returns true
|
137
|
+
def each(&block)
|
138
|
+
each_row do |row|
|
139
|
+
block.call(@klass.new(row))
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|