riak-yz-query 0.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.
- checksums.yaml +7 -0
- data/.gitignore +17 -0
- data/Gemfile +6 -0
- data/LICENSE.txt +22 -0
- data/README.md +104 -0
- data/Rakefile +11 -0
- data/lib/riak-yz-query.rb +1 -0
- data/lib/riak/yz_query.rb +8 -0
- data/lib/riak/yz_query/bucket_extension.rb +16 -0
- data/lib/riak/yz_query/order_clause.rb +41 -0
- data/lib/riak/yz_query/query_builder.rb +80 -0
- data/lib/riak/yz_query/version.rb +5 -0
- data/lib/riak/yz_query/where_clause.rb +77 -0
- data/riak-yz-query.gemspec +30 -0
- data/test/helper.rb +32 -0
- data/test/integration_test.rb +71 -0
- data/test/query_builder_test.rb +78 -0
- data/test/where_clause_test.rb +36 -0
- metadata +149 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 14bc1032d645d5a7493cc47b6135318baba3480a
|
4
|
+
data.tar.gz: dde740329c41482fb288c07679fe79803765f367
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: e46c72a4d779115c680e554ae20b8eb40d0e46c9fb4f0738825dc3ae06dc2ed3ebd6103aa5e53dcfd33d28eece4c80fd4f2a95024ab403ca9104fd0adcd0591e
|
7
|
+
data.tar.gz: 1636294e3fcde489e96d753fbc3742512ad24731f512751f938ef80bb66650c26ad458a114123a96c916d23f5ccf1021827eb26b9118c98056f93d19bd227661
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2013 Bryce Kerley
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,104 @@
|
|
1
|
+
# Riak::YzQuery
|
2
|
+
|
3
|
+
***EXPERIMENTAL! DO NOT USE IN PRODUCTION! THERE ARE SECURITY HOLES!***
|
4
|
+
|
5
|
+
Riak 2.0 will feature [Yokozuna](https://github.com/basho/yokozuna/), a
|
6
|
+
distributed search system built on Apache Solr. `Riak::YzQuery` aims to provide
|
7
|
+
[Arel-style](http://guides.rubyonrails.org/active_record_querying.html#limit-and-offset)
|
8
|
+
querying for Yokozuna.
|
9
|
+
|
10
|
+
## Installation
|
11
|
+
|
12
|
+
Add this line to your application's Gemfile:
|
13
|
+
|
14
|
+
gem 'riak-yz-query'
|
15
|
+
|
16
|
+
And then execute:
|
17
|
+
|
18
|
+
$ bundle
|
19
|
+
|
20
|
+
Or install it yourself as:
|
21
|
+
|
22
|
+
$ gem install riak-yz-query
|
23
|
+
|
24
|
+
## Usage
|
25
|
+
|
26
|
+
You've already got a Riak bucket full of JSON documents as such:
|
27
|
+
|
28
|
+
```json
|
29
|
+
{
|
30
|
+
name_t: "Bryce Kerley",
|
31
|
+
title_t: "Cosmopolitan Space Emperor",
|
32
|
+
created_dt: "2007-12-13T23:59:59Z"
|
33
|
+
}
|
34
|
+
```
|
35
|
+
|
36
|
+
To create a blank query, pull it from the `Riak::Bucket`:
|
37
|
+
|
38
|
+
```ruby
|
39
|
+
q = @bucket.query
|
40
|
+
```
|
41
|
+
|
42
|
+
Let's look for Bryce:
|
43
|
+
|
44
|
+
```ruby
|
45
|
+
q = @bucket.query.where name_t: 'Bryce'
|
46
|
+
q.keys #=> ['Hswdh9zli0CDcnPSKmEGeIcd6tU']
|
47
|
+
q.values #=> {"Hswdh9zli0CDcnPSKmEGeIcd6tU"=> #<Riak::RObject {user,Hswdh...}>}
|
48
|
+
```
|
49
|
+
|
50
|
+
In the above example, `q` is a `Riak::YzQuery::QueryBuilder` instance. As the
|
51
|
+
name implies, and much like Active Record, you can build up more complex queries
|
52
|
+
by chaining constraints.
|
53
|
+
|
54
|
+
```ruby
|
55
|
+
q = @bucket.query.where name_t: 'Andrew'
|
56
|
+
q.keys #=> ["PtgA5YsxWpSg7RzTY2eJVJ81hDQ", "OL1quOfOKiYEmxYsqvjf9cyRmH3"]
|
57
|
+
q2 = q.where name_t: 'Stone'
|
58
|
+
q2.keys #=> ["OL1quOfOKiYEmxYsqvjf9cyRmH3"]
|
59
|
+
```
|
60
|
+
|
61
|
+
`where` clauses are ANDed together. To OR together two values, use a string:
|
62
|
+
|
63
|
+
```ruby
|
64
|
+
q = @bucket.query.where 'name_t:Andrew OR name_t:Drew'
|
65
|
+
q.keys #=> ["Z0tsBudxTQp50pBlTNeBw6CtwZx", "OL1quOfOKiYEmxYsqvjf9cyRmH3", "PtgA5YsxWpSg7RzTY2eJVJ81hDQ"]
|
66
|
+
```
|
67
|
+
|
68
|
+
If you don't want to concatenate the string yourself, use an array.
|
69
|
+
|
70
|
+
|
71
|
+
***WARNING: THIS DOES NOT ADEQUATELY ESCAPE THINGS!*** Your application
|
72
|
+
could host the first Yokozuna-injection exploit!
|
73
|
+
|
74
|
+
```ruby
|
75
|
+
q = @bucket.query.where ['name_t:? OR name_t:?', 'Drew', 'Andrew']
|
76
|
+
q.keys #=> ["Z0tsBudxTQp50pBlTNeBw6CtwZx", "OL1quOfOKiYEmxYsqvjf9cyRmH3", "PtgA5YsxWpSg7RzTY2eJVJ81hDQ"]
|
77
|
+
```
|
78
|
+
|
79
|
+
You can sort:
|
80
|
+
|
81
|
+
```ruby
|
82
|
+
q = @bucket.query.where(name_t: 'Andrew').order(created_dt: 'asc').keys
|
83
|
+
#=> ["OL1quOfOKiYEmxYsqvjf9cyRmH3", "PtgA5YsxWpSg7RzTY2eJVJ81hDQ"]
|
84
|
+
q = @bucket.query.where(name_t: 'Andrew').order('created_dt desc').keys
|
85
|
+
#=> ["PtgA5YsxWpSg7RzTY2eJVJ81hDQ", "OL1quOfOKiYEmxYsqvjf9cyRmH3"]
|
86
|
+
```
|
87
|
+
|
88
|
+
And you can paginate:
|
89
|
+
|
90
|
+
```ruby
|
91
|
+
q = @bucket.query.
|
92
|
+
where(name_t: '*e*').
|
93
|
+
order(created_dt: 'asc').
|
94
|
+
limit(5).
|
95
|
+
offset(5)
|
96
|
+
```
|
97
|
+
|
98
|
+
## Contributing
|
99
|
+
|
100
|
+
1. Fork it
|
101
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
102
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
103
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
104
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "riak/yz_query"
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module Riak
|
2
|
+
module YzQuery
|
3
|
+
class OrderClause
|
4
|
+
def initialize(clause)
|
5
|
+
@clause = clause
|
6
|
+
end
|
7
|
+
|
8
|
+
def to_yz_query
|
9
|
+
return nil if @clause.empty?
|
10
|
+
|
11
|
+
case @clause
|
12
|
+
when Hash
|
13
|
+
build_clause_hash
|
14
|
+
when Array
|
15
|
+
build_clause_array
|
16
|
+
when String
|
17
|
+
@clause
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def consume(new_clause)
|
22
|
+
new_clause_query = self.class.new(new_clause)
|
23
|
+
return new_clause_query if @clause.empty?
|
24
|
+
return self.class.new "#{to_yz_query}, #{new_clause.to_yz_query}"
|
25
|
+
end
|
26
|
+
|
27
|
+
def build_clause_hash
|
28
|
+
@clause.map do |k,v|
|
29
|
+
v = v.to_s.downcase
|
30
|
+
raise ArgumentError.new "Couldn't use #{v.inspect} in an order clause" unless %w{asc desc}.include? v
|
31
|
+
|
32
|
+
"#{k} #{v}"
|
33
|
+
end.join ', '
|
34
|
+
end
|
35
|
+
|
36
|
+
def build_clause_array
|
37
|
+
@clause.join ', '
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
module Riak
|
2
|
+
module YzQuery
|
3
|
+
class QueryBuilder
|
4
|
+
def initialize(bucket)
|
5
|
+
@bucket = bucket
|
6
|
+
end
|
7
|
+
|
8
|
+
def initialize_from_opts(opts)
|
9
|
+
@bucket = opts[:bucket]
|
10
|
+
@where_clauses = opts[:where_clauses]
|
11
|
+
@order_clauses = opts[:order_clauses]
|
12
|
+
@limit = opts[:limit]
|
13
|
+
@offset = opts[:offset]
|
14
|
+
end
|
15
|
+
|
16
|
+
def where(opts)
|
17
|
+
chain where_clauses: where_clauses.consume(opts)
|
18
|
+
end
|
19
|
+
|
20
|
+
def order(opts)
|
21
|
+
chain order_clauses: order_clauses.consume(opts)
|
22
|
+
end
|
23
|
+
|
24
|
+
def limit(lim)
|
25
|
+
chain limit: lim
|
26
|
+
end
|
27
|
+
|
28
|
+
def offset(off)
|
29
|
+
chain offset: off
|
30
|
+
end
|
31
|
+
|
32
|
+
def keys
|
33
|
+
@keys ||= results['docs'].map{|d| d['_yz_rk']}
|
34
|
+
end
|
35
|
+
|
36
|
+
def values
|
37
|
+
@bucket.get_many keys
|
38
|
+
end
|
39
|
+
|
40
|
+
def to_yz_query
|
41
|
+
where_clauses.to_yz_query
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
def chain(opts)
|
46
|
+
new_options = {
|
47
|
+
bucket: @bucket,
|
48
|
+
where_clauses: where_clauses,
|
49
|
+
order_clauses: order_clauses,
|
50
|
+
limit: @limit,
|
51
|
+
offset: @offset
|
52
|
+
}.merge opts
|
53
|
+
|
54
|
+
chain = self.class.allocate
|
55
|
+
chain.initialize_from_opts new_options
|
56
|
+
|
57
|
+
return chain
|
58
|
+
end
|
59
|
+
|
60
|
+
def results
|
61
|
+
opts = {}
|
62
|
+
opts[:rows] = @limit if @limit
|
63
|
+
opts[:start] = @offset if @offset
|
64
|
+
if order = order_clauses.to_yz_query
|
65
|
+
opts[:sort] = order
|
66
|
+
end
|
67
|
+
|
68
|
+
@results ||= @bucket.client.search @bucket.name, to_yz_query, opts
|
69
|
+
end
|
70
|
+
|
71
|
+
def where_clauses
|
72
|
+
@where_clauses ||= WhereClause.new Hash.new
|
73
|
+
end
|
74
|
+
|
75
|
+
def order_clauses
|
76
|
+
@order_clauses ||= OrderClause.new Hash.new
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
require 'time'
|
2
|
+
|
3
|
+
module Riak
|
4
|
+
module YzQuery
|
5
|
+
class WhereClause
|
6
|
+
def initialize(clause)
|
7
|
+
@clause = clause
|
8
|
+
end
|
9
|
+
|
10
|
+
def to_yz_query
|
11
|
+
case @clause
|
12
|
+
when Hash
|
13
|
+
build_clause_hash
|
14
|
+
when Array
|
15
|
+
escape_clause_array
|
16
|
+
when String
|
17
|
+
@clause
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def consume(new_clause)
|
22
|
+
if @clause.is_a? Hash and new_clause.is_a? Hash
|
23
|
+
return self.class.new(@clause.merge new_clause)
|
24
|
+
end
|
25
|
+
|
26
|
+
if @clause.empty?
|
27
|
+
return self.class.new new_clause
|
28
|
+
end
|
29
|
+
|
30
|
+
new_clause_query = self.class.new(new_clause).to_yz_query
|
31
|
+
|
32
|
+
return self.class.new "(#{to_yz_query}) AND #{new_clause_query}"
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
def build_clause_hash
|
37
|
+
@clause.map do |k,v|
|
38
|
+
"#{k}:#{escape v}"
|
39
|
+
end.join ' AND '
|
40
|
+
end
|
41
|
+
|
42
|
+
def escape_clause_array
|
43
|
+
working = @clause.first.dup
|
44
|
+
remaining = @clause[1..-1].dup
|
45
|
+
|
46
|
+
while remaining.length > 0
|
47
|
+
working['?'] = escape remaining.shift
|
48
|
+
end
|
49
|
+
|
50
|
+
working
|
51
|
+
end
|
52
|
+
|
53
|
+
def escape(candidate)
|
54
|
+
case candidate
|
55
|
+
when Range
|
56
|
+
return "[#{escape(candidate.begin)} TO #{escape(candidate.end)}]"
|
57
|
+
when Time
|
58
|
+
return candidate.iso8601
|
59
|
+
else
|
60
|
+
escape_string candidate.to_s
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def escape_string(str)
|
65
|
+
if (str.include? ' ' or str.include? '"') and str.include? '*'
|
66
|
+
raise ArgumentError.new "Couldn't figure out how to escape #{str.inspect}"
|
67
|
+
end
|
68
|
+
|
69
|
+
if str.include? '*'
|
70
|
+
return str
|
71
|
+
end
|
72
|
+
|
73
|
+
return %Q{"#{str}"}
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'riak/yz_query/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "riak-yz-query"
|
8
|
+
spec.version = Riak::YzQuery::VERSION
|
9
|
+
spec.authors = ["Bryce Kerley"]
|
10
|
+
spec.email = ["bkerley@brycekerley.net"]
|
11
|
+
spec.description = %q{Arel-style queries for Riak Yokozuna}
|
12
|
+
spec.summary = %q{Arel-style queries for Riak Yokozuna}
|
13
|
+
spec.homepage = "https://github.com/bkerley/riak-yz-query"
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files`.split($/)
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.required_ruby_version = '> 2.0'
|
22
|
+
|
23
|
+
spec.add_development_dependency "bundler", "~> 1.3"
|
24
|
+
spec.add_development_dependency "rake"
|
25
|
+
spec.add_development_dependency "minitest", "~> 4.7"
|
26
|
+
spec.add_development_dependency "shoulda-context", "~> 1.1.5"
|
27
|
+
spec.add_development_dependency "mocha", "~> 0.14.0"
|
28
|
+
|
29
|
+
spec.add_dependency 'riak-client'
|
30
|
+
end
|
data/test/helper.rb
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'bundler/setup'
|
3
|
+
require 'minitest/autorun'
|
4
|
+
|
5
|
+
require 'shoulda/context'
|
6
|
+
require 'mocha/setup'
|
7
|
+
|
8
|
+
require 'riak'
|
9
|
+
require 'riak-yz-query'
|
10
|
+
|
11
|
+
class TestCase < MiniTest::Unit::TestCase
|
12
|
+
include ShouldaContextLoadable
|
13
|
+
|
14
|
+
def client
|
15
|
+
@client ||= Riak::Client.new pb_port: 10017
|
16
|
+
end
|
17
|
+
|
18
|
+
def bucket_name
|
19
|
+
return @bucket_name if defined? @bucket_name
|
20
|
+
@bucket_name = "riak-yz-query-#{rand(36**10).to_s(36)}"
|
21
|
+
$stderr.puts "Using bucket name #{@bucket_name.inspect}"
|
22
|
+
end
|
23
|
+
|
24
|
+
def bucket
|
25
|
+
return @bucket if defined? @bucket
|
26
|
+
|
27
|
+
@bucket = client.bucket bucket_name
|
28
|
+
end
|
29
|
+
|
30
|
+
|
31
|
+
end
|
32
|
+
|
@@ -0,0 +1,71 @@
|
|
1
|
+
require 'helper'
|
2
|
+
require 'time'
|
3
|
+
|
4
|
+
class IntegrationTest < TestCase
|
5
|
+
context 'riak-yz-query with some data' do
|
6
|
+
setup do
|
7
|
+
@bucket = client.bucket 'user'
|
8
|
+
end
|
9
|
+
|
10
|
+
should 'perform single-term queries' do
|
11
|
+
q = @bucket.query.where(name_t: '*drew*')
|
12
|
+
['Z0tsBudxTQp50pBlTNeBw6CtwZx',
|
13
|
+
'PtgA5YsxWpSg7RzTY2eJVJ81hDQ',
|
14
|
+
'OL1quOfOKiYEmxYsqvjf9cyRmH3'].each do |k|
|
15
|
+
assert_includes q.keys, k
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
should 'perform multi-term AND queries' do
|
20
|
+
q = @bucket.query.where(name_t: '*drew*', title_t: '*engineer*')
|
21
|
+
['Z0tsBudxTQp50pBlTNeBw6CtwZx',
|
22
|
+
'PtgA5YsxWpSg7RzTY2eJVJ81hDQ'].each do |k|
|
23
|
+
assert_includes q.keys, k
|
24
|
+
end
|
25
|
+
['OL1quOfOKiYEmxYsqvjf9cyRmH3'].each do |k|
|
26
|
+
refute_includes q.keys, k
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
should 'perform multi-term OR queries' do
|
31
|
+
q = @bucket.query.where(['name_t:? OR name_t:?', 'Drew', 'Bryce'])
|
32
|
+
['Z0tsBudxTQp50pBlTNeBw6CtwZx',
|
33
|
+
'Hswdh9zli0CDcnPSKmEGeIcd6tU'].each do |k|
|
34
|
+
assert_includes q.keys, k
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
should 'perform range queries with times' do
|
39
|
+
open = Time.parse '2007-12-10T23:59:59Z'
|
40
|
+
close = Time.parse '2007-12-20T23:59:59Z'
|
41
|
+
q = @bucket.query.where created_dt: (open..close)
|
42
|
+
|
43
|
+
assert_includes q.keys, 'Hswdh9zli0CDcnPSKmEGeIcd6tU'
|
44
|
+
end
|
45
|
+
|
46
|
+
should 'perform multi-term queries with ranges' do
|
47
|
+
open = Time.parse '2007-12-10T23:59:59Z'
|
48
|
+
close = Time.parse '2007-12-20T23:59:59Z'
|
49
|
+
q = @bucket.query.where created_dt: (open..close)
|
50
|
+
q_merge = q.where name_t: 'Bryce'
|
51
|
+
|
52
|
+
assert_includes q.keys, 'Hswdh9zli0CDcnPSKmEGeIcd6tU'
|
53
|
+
end
|
54
|
+
|
55
|
+
should 'perform single-term queries with pagination controls' do
|
56
|
+
q = @bucket.query.
|
57
|
+
where(name_t: '*e*').
|
58
|
+
order(created_dt: 'asc').
|
59
|
+
limit(5).
|
60
|
+
offset(5)
|
61
|
+
|
62
|
+
assert_equal(['RvOzMXHJrDG6HbqQaZGrq50qzP',
|
63
|
+
'AGB7aYJ31jvMTaaolur9Hqp2gyF',
|
64
|
+
'LYNcKRvQiUNaKW1zTEDdzZytrQp',
|
65
|
+
'Hswdh9zli0CDcnPSKmEGeIcd6tU',
|
66
|
+
'6l5cpsxsyDb9mDHEtcS9U6mNCbO'
|
67
|
+
],
|
68
|
+
q.keys)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
class QueryBuilderTest < TestCase
|
4
|
+
context "a QueryBuilder" do
|
5
|
+
setup do
|
6
|
+
@bucket = client.bucket 'user'
|
7
|
+
end
|
8
|
+
|
9
|
+
should "chain from 'where'" do
|
10
|
+
q = @bucket.query
|
11
|
+
q_where = q.where(asdf: 'asdf')
|
12
|
+
refute_equal q, q_where
|
13
|
+
assert_instance_of Riak::YzQuery::QueryBuilder, q_where
|
14
|
+
end
|
15
|
+
|
16
|
+
should "convert to a yokozuna query" do
|
17
|
+
q = @bucket.query.where(asdf: 'jkl')
|
18
|
+
assert_equal 'asdf:"jkl"', q.to_yz_query
|
19
|
+
end
|
20
|
+
|
21
|
+
should "query on #keys" do
|
22
|
+
q = @bucket.query
|
23
|
+
client.
|
24
|
+
expects(:search).
|
25
|
+
with(@bucket.name, 'asdf:"jkl"', {}).
|
26
|
+
returns({
|
27
|
+
'docs' => [
|
28
|
+
'score' => 1.0,
|
29
|
+
'_yz_rk' => 'jkl',
|
30
|
+
],
|
31
|
+
'max_score' => 1.0,
|
32
|
+
'num_found' => 1
|
33
|
+
})
|
34
|
+
|
35
|
+
keys = q.where(asdf: 'jkl').keys
|
36
|
+
assert_equal ['jkl'], keys
|
37
|
+
end
|
38
|
+
|
39
|
+
context 'where clauses' do
|
40
|
+
should 'support hashes' do
|
41
|
+
assert_produces 'asdf:"jkl"', {asdf: 'jkl'}
|
42
|
+
assert_produces 'asdf:"jkl"', {'asdf' => 'jkl'}
|
43
|
+
end
|
44
|
+
should 'support strings' do
|
45
|
+
assert_produces 'asdf:jkl', 'asdf:jkl'
|
46
|
+
end
|
47
|
+
should 'support arrays with interpolation' do
|
48
|
+
assert_produces 'asdf:"jkl"', ['asdf:?', 'jkl']
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
context 'limit and offset' do
|
53
|
+
should "pass limit and offset through" do
|
54
|
+
q = @bucket.query
|
55
|
+
client.
|
56
|
+
expects(:search).
|
57
|
+
with(@bucket.name, 'asdf:"jkl"', {rows: 4, start: 5}).
|
58
|
+
returns({
|
59
|
+
'docs' => [
|
60
|
+
'score' => 1.0,
|
61
|
+
'_yz_rk' => 'jkl',
|
62
|
+
],
|
63
|
+
'max_score' => 1.0,
|
64
|
+
'num_found' => 1
|
65
|
+
})
|
66
|
+
|
67
|
+
keys = q.where(asdf: 'jkl').limit(4).offset(5).keys
|
68
|
+
assert_equal ['jkl'], keys
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
def assert_produces(desired_query, *clause)
|
74
|
+
assert_equal(desired_query,
|
75
|
+
@bucket.query.where(*clause).to_yz_query,
|
76
|
+
"Clause #{clause.inspect} did not produce expected query #{desired_query.inspect}")
|
77
|
+
end
|
78
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
class WhereClauseTest < TestCase
|
4
|
+
WhereClause = Riak::YzQuery::WhereClause
|
5
|
+
|
6
|
+
context 'a WhereClause' do
|
7
|
+
should 'merge a hash and a hash' do
|
8
|
+
c = WhereClause.new asdf: 'jkl'
|
9
|
+
c_merge = c.consume qwe: 'rty'
|
10
|
+
assert_equal 'asdf:"jkl" AND qwe:"rty"', c_merge.to_yz_query
|
11
|
+
end
|
12
|
+
|
13
|
+
should 'merge a hash and a string' do
|
14
|
+
c = WhereClause.new asdf: 'jkl'
|
15
|
+
c_merge = c.consume 'qwe:rty'
|
16
|
+
assert_equal '(asdf:"jkl") AND qwe:rty', c_merge.to_yz_query
|
17
|
+
end
|
18
|
+
|
19
|
+
should 'merge a string and a hash' do
|
20
|
+
c = WhereClause.new 'qwe:rty'
|
21
|
+
c_merge = c.consume asdf: 'jkl'
|
22
|
+
assert_equal '(qwe:rty) AND asdf:"jkl"', c_merge.to_yz_query
|
23
|
+
end
|
24
|
+
|
25
|
+
should 'merge a string and a string' do
|
26
|
+
c = WhereClause.new "bryce:hello"
|
27
|
+
c_merge = c.consume "hello:to_u"
|
28
|
+
assert_equal '(bryce:hello) AND hello:to_u', c_merge.to_yz_query
|
29
|
+
end
|
30
|
+
|
31
|
+
should 'escape an array into a string' do
|
32
|
+
c = WhereClause.new ["bryce:? OR bryce:?", 'hi', 'hello']
|
33
|
+
assert_equal 'bryce:"hi" OR bryce:"hello"', c.to_yz_query
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
metadata
ADDED
@@ -0,0 +1,149 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: riak-yz-query
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Bryce Kerley
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2013-09-09 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ~>
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.3'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ~>
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.3'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - '>='
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - '>='
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: minitest
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ~>
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '4.7'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ~>
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '4.7'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: shoulda-context
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ~>
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: 1.1.5
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ~>
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: 1.1.5
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: mocha
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ~>
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: 0.14.0
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ~>
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: 0.14.0
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: riak-client
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - '>='
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :runtime
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - '>='
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
description: Arel-style queries for Riak Yokozuna
|
98
|
+
email:
|
99
|
+
- bkerley@brycekerley.net
|
100
|
+
executables: []
|
101
|
+
extensions: []
|
102
|
+
extra_rdoc_files: []
|
103
|
+
files:
|
104
|
+
- .gitignore
|
105
|
+
- Gemfile
|
106
|
+
- LICENSE.txt
|
107
|
+
- README.md
|
108
|
+
- Rakefile
|
109
|
+
- lib/riak-yz-query.rb
|
110
|
+
- lib/riak/yz_query.rb
|
111
|
+
- lib/riak/yz_query/bucket_extension.rb
|
112
|
+
- lib/riak/yz_query/order_clause.rb
|
113
|
+
- lib/riak/yz_query/query_builder.rb
|
114
|
+
- lib/riak/yz_query/version.rb
|
115
|
+
- lib/riak/yz_query/where_clause.rb
|
116
|
+
- riak-yz-query.gemspec
|
117
|
+
- test/helper.rb
|
118
|
+
- test/integration_test.rb
|
119
|
+
- test/query_builder_test.rb
|
120
|
+
- test/where_clause_test.rb
|
121
|
+
homepage: https://github.com/bkerley/riak-yz-query
|
122
|
+
licenses:
|
123
|
+
- MIT
|
124
|
+
metadata: {}
|
125
|
+
post_install_message:
|
126
|
+
rdoc_options: []
|
127
|
+
require_paths:
|
128
|
+
- lib
|
129
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
130
|
+
requirements:
|
131
|
+
- - '>'
|
132
|
+
- !ruby/object:Gem::Version
|
133
|
+
version: '2.0'
|
134
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
135
|
+
requirements:
|
136
|
+
- - '>='
|
137
|
+
- !ruby/object:Gem::Version
|
138
|
+
version: '0'
|
139
|
+
requirements: []
|
140
|
+
rubyforge_project:
|
141
|
+
rubygems_version: 2.0.7
|
142
|
+
signing_key:
|
143
|
+
specification_version: 4
|
144
|
+
summary: Arel-style queries for Riak Yokozuna
|
145
|
+
test_files:
|
146
|
+
- test/helper.rb
|
147
|
+
- test/integration_test.rb
|
148
|
+
- test/query_builder_test.rb
|
149
|
+
- test/where_clause_test.rb
|