elastic_queue 0.0.10 → 0.0.11
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/Gemfile.lock +25 -4
- data/elastic_queue.gemspec +3 -1
- data/lib/elastic_queue/base.rb +1 -1
- data/lib/elastic_queue/filters.rb +13 -4
- data/lib/elastic_queue/persistence.rb +31 -4
- data/lib/elastic_queue/query.rb +19 -15
- data/lib/elastic_queue/queueable.rb +1 -0
- data/lib/elastic_queue/results.rb +7 -5
- data/lib/elastic_queue/version.rb +1 -1
- data/spec/factories.rb +0 -64
- data/spec/lib/integration/elastic_queue_base_spec.rb +70 -0
- data/spec/lib/integration/elastic_queue_filters_spec.rb +123 -0
- data/spec/lib/integration/elastic_queue_persistence_spec.rb +100 -0
- data/spec/lib/integration/elastic_queue_query_spec.rb +82 -0
- data/spec/lib/integration/elastic_queue_queuable_spec.rb +83 -0
- data/spec/lib/integration/elastic_queue_sorts_spec.rb +60 -0
- data/spec/spec_helper.rb +2 -9
- data/spec/support/database_helper.rb +37 -0
- data/spec/support/elastic_queue_helper.rb +17 -5
- metadata +44 -6
- data/spec/lib/elastic_queue_functional_spec.rb +0 -276
- data/spec/lib/elastic_queue_unit_spec.rb +0 -220
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5f6a30f14c2ff7bb0330c854ea5653d295d4f24b
|
4
|
+
data.tar.gz: 6aeca9a70bf31dc9b65b0c9862dc835c45535ca2
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 71487d775f4a86d6f9f17abb25173d671b7a153769bdf906e4bd100deede733efec86c85235388e84954a39239448d3afdef7d5c9d5a801dcbd48ab0f47ff990
|
7
|
+
data.tar.gz: 4e17e151ca7cb3885eea1f496e283d5cd0f4c6532b1d56d67c34469bd4205875eb071c1322c3bc712ba6ca7e1d36e86e9b8bd8fc8269ea586b369c6da007641d
|
data/.gitignore
CHANGED
data/Gemfile.lock
CHANGED
@@ -1,7 +1,8 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
elastic_queue (0.0.
|
4
|
+
elastic_queue (0.0.11)
|
5
|
+
activerecord (~> 3.0)
|
5
6
|
activesupport (~> 3.0)
|
6
7
|
elasticsearch (~> 0.4)
|
7
8
|
will_paginate (~> 3.0)
|
@@ -9,9 +10,26 @@ PATH
|
|
9
10
|
GEM
|
10
11
|
remote: https://rubygems.org/
|
11
12
|
specs:
|
12
|
-
|
13
|
+
activemodel (3.2.18)
|
14
|
+
activesupport (= 3.2.18)
|
15
|
+
builder (~> 3.0.0)
|
16
|
+
activerecord (3.2.18)
|
17
|
+
activemodel (= 3.2.18)
|
18
|
+
activesupport (= 3.2.18)
|
19
|
+
arel (~> 3.0.2)
|
20
|
+
tzinfo (~> 0.3.29)
|
21
|
+
activesupport (3.2.18)
|
13
22
|
i18n (~> 0.6, >= 0.6.4)
|
14
23
|
multi_json (~> 1.0)
|
24
|
+
arel (3.0.3)
|
25
|
+
builder (3.0.4)
|
26
|
+
columnize (0.8.9)
|
27
|
+
debugger (1.6.6)
|
28
|
+
columnize (>= 0.3.1)
|
29
|
+
debugger-linecache (~> 1.2.0)
|
30
|
+
debugger-ruby_core_source (~> 1.3.2)
|
31
|
+
debugger-linecache (1.2.0)
|
32
|
+
debugger-ruby_core_source (1.3.2)
|
15
33
|
diff-lcs (1.2.5)
|
16
34
|
elasticsearch (0.4.11)
|
17
35
|
elasticsearch-api (= 0.4.11)
|
@@ -26,7 +44,7 @@ GEM
|
|
26
44
|
faraday (0.9.0)
|
27
45
|
multipart-post (>= 1.2, < 3)
|
28
46
|
i18n (0.6.9)
|
29
|
-
multi_json (1.
|
47
|
+
multi_json (1.10.0)
|
30
48
|
multipart-post (2.0.0)
|
31
49
|
rake (10.3.1)
|
32
50
|
rspec (2.14.1)
|
@@ -38,15 +56,18 @@ GEM
|
|
38
56
|
diff-lcs (>= 1.1.3, < 2.0)
|
39
57
|
rspec-mocks (2.14.6)
|
40
58
|
sqlite3 (1.3.9)
|
59
|
+
tzinfo (0.3.39)
|
41
60
|
will_paginate (3.0.5)
|
42
61
|
|
43
62
|
PLATFORMS
|
44
63
|
ruby
|
45
64
|
|
46
65
|
DEPENDENCIES
|
66
|
+
activesupport (~> 3.0)
|
47
67
|
bundler (~> 1.0)
|
68
|
+
debugger (~> 1.6)
|
48
69
|
elastic_queue!
|
49
70
|
factory_girl (~> 4.0)
|
50
71
|
rake (~> 10.0)
|
51
72
|
rspec (~> 2.6)
|
52
|
-
sqlite3
|
73
|
+
sqlite3 (~> 1.3)
|
data/elastic_queue.gemspec
CHANGED
@@ -19,6 +19,7 @@ Gem::Specification.new do |s|
|
|
19
19
|
s.test_files = Dir['spec/**/*.rb']
|
20
20
|
|
21
21
|
s.add_runtime_dependency 'activesupport', '~> 3.0'
|
22
|
+
s.add_runtime_dependency 'activerecord', '~>3.0'
|
22
23
|
s.add_runtime_dependency 'elasticsearch', '~> 0.4'
|
23
24
|
s.add_runtime_dependency 'will_paginate', '~> 3.0'
|
24
25
|
s.add_development_dependency 'bundler', '~> 1.0'
|
@@ -26,4 +27,5 @@ Gem::Specification.new do |s|
|
|
26
27
|
s.add_development_dependency 'factory_girl', '~> 4.0'
|
27
28
|
s.add_development_dependency 'sqlite3', '~> 1.3'
|
28
29
|
s.add_development_dependency 'rake', '~> 10.0'
|
29
|
-
|
30
|
+
s.add_development_dependency 'debugger', '~>1.6'
|
31
|
+
end
|
data/lib/elastic_queue/base.rb
CHANGED
@@ -9,7 +9,7 @@ module ElasticQueue
|
|
9
9
|
include Percolation
|
10
10
|
|
11
11
|
def self.search_client
|
12
|
-
Elasticsearch::Client.new hosts: ElasticQueue::OPTIONS[:elasticsearch_hosts]
|
12
|
+
@search_client ||= Elasticsearch::Client.new hosts: ElasticQueue::OPTIONS[:elasticsearch_hosts]
|
13
13
|
end
|
14
14
|
|
15
15
|
def self.models(*models)
|
@@ -12,18 +12,27 @@ module ElasticQueue
|
|
12
12
|
private
|
13
13
|
|
14
14
|
def option_to_filter(key, value)
|
15
|
-
|
15
|
+
# return and_options(value) if key == :and
|
16
|
+
if [:or, :and].include?(key)
|
17
|
+
join_options(key, value)
|
18
|
+
elsif value.is_a? Array
|
16
19
|
or_filter(key, value)
|
17
20
|
elsif value.is_a? Hash
|
18
|
-
|
19
|
-
time_filter(key, value)
|
21
|
+
comparison_filter(key, value)
|
20
22
|
elsif value.nil?
|
23
|
+
# e.g. name: nil
|
21
24
|
null_filter(key, value)
|
22
25
|
else
|
26
|
+
# e.g. status: 'fresh'
|
23
27
|
term_filter(key, value)
|
24
28
|
end
|
25
29
|
end
|
26
30
|
|
31
|
+
def join_options(operator, options)
|
32
|
+
conditions = options.map { |o| options_to_filters(o) }.flatten
|
33
|
+
{ operator => conditions }
|
34
|
+
end
|
35
|
+
|
27
36
|
def or_filter(term, values)
|
28
37
|
# flatten here because ranges return arrays
|
29
38
|
conditions = values.map { |v| option_to_filter(term, v) }.flatten
|
@@ -35,7 +44,7 @@ module ElasticQueue
|
|
35
44
|
end
|
36
45
|
|
37
46
|
# take something like follow_up: { before: 'hii', after: 'low' }
|
38
|
-
def
|
47
|
+
def comparison_filter(term, value)
|
39
48
|
value.map do |k, v|
|
40
49
|
comparator = k.to_sym.in?([:after, :greater_than, :gt]) ? :gt : :lt
|
41
50
|
range_filter(term, v, comparator)
|
@@ -13,7 +13,7 @@ module ElasticQueue
|
|
13
13
|
end
|
14
14
|
|
15
15
|
def create_index
|
16
|
-
search_client.indices.create index: index_name
|
16
|
+
search_client.indices.create index: index_name, body: default_index_settings
|
17
17
|
add_mappings
|
18
18
|
end
|
19
19
|
|
@@ -26,12 +26,14 @@ module ElasticQueue
|
|
26
26
|
search_client.indices.refresh index: index_name
|
27
27
|
end
|
28
28
|
|
29
|
-
|
29
|
+
# you can pass scopes into bulk_index to be used when fetching records
|
30
|
+
# bulk_index(scopes: { some_model: [:scope1, :scope2], some_other_model: [:scope3] }) will fetch SomeModel.scope1.scope2 and SomeOtherModel.scope3 and index only those records.
|
31
|
+
def bulk_index(scopes: {}, batch_size: 10_000)
|
30
32
|
create_index unless index_exists?
|
31
33
|
model_classes.each do |klass|
|
32
34
|
# modelclass(model).includes(associations_for_index(model)).
|
33
35
|
index_type = klass.to_s.underscore
|
34
|
-
klass.find_in_batches(batch_size: batch_size) do |batch|
|
36
|
+
scoped_class(klass, scopes).find_in_batches(batch_size: batch_size) do |batch|
|
35
37
|
body = []
|
36
38
|
batch.each do |instance|
|
37
39
|
body << { index: { _index: index_name, _id: instance.id, _type: index_type, data: instance.indexed_for_queue } }
|
@@ -41,9 +43,34 @@ module ElasticQueue
|
|
41
43
|
end
|
42
44
|
end
|
43
45
|
|
46
|
+
def scoped_class(klass, scopes)
|
47
|
+
return klass unless scopes[klass.to_s.underscore.to_sym]
|
48
|
+
scopes[klass.to_s.underscore.to_sym].each do |scope|
|
49
|
+
klass = klass.send(scope)
|
50
|
+
end
|
51
|
+
klass
|
52
|
+
end
|
53
|
+
|
54
|
+
def default_index_settings
|
55
|
+
{
|
56
|
+
settings: {
|
57
|
+
analysis: {
|
58
|
+
analyzer: {
|
59
|
+
default: {
|
60
|
+
type: :custom,
|
61
|
+
tokenizer: :whitespace,
|
62
|
+
filter: [:lowercase]
|
63
|
+
}
|
64
|
+
}
|
65
|
+
}
|
66
|
+
}
|
67
|
+
}
|
68
|
+
end
|
69
|
+
|
44
70
|
def add_mappings
|
45
71
|
model_classes.each do |klass|
|
46
|
-
|
72
|
+
mapping = klass.queue_mapping
|
73
|
+
search_client.indices.put_mapping index: index_name, type: klass.to_s.underscore, body: mapping if mapping.present?
|
47
74
|
end
|
48
75
|
end
|
49
76
|
|
data/lib/elastic_queue/query.rb
CHANGED
@@ -45,40 +45,39 @@ module ElasticQueue
|
|
45
45
|
|
46
46
|
def paginate(options = {})
|
47
47
|
options.each { |k, v| @options.send("#{k}=", v) }
|
48
|
-
|
48
|
+
Results.new(@queue, execute(paginate: true), @options).paginate
|
49
49
|
end
|
50
50
|
|
51
|
+
# TODO: remove if not using, add per_page if using
|
51
52
|
def page=(page)
|
52
53
|
@options.page = (page)
|
53
54
|
end
|
54
55
|
|
55
56
|
def all
|
56
|
-
|
57
|
+
Results.new(@queue, execute, @options).instantiated_queue_items
|
57
58
|
end
|
58
59
|
|
59
60
|
# return just the ids of the records (useful when combined with SQL queries)
|
60
61
|
def ids
|
61
|
-
|
62
|
-
results[:hits][:hits].map { |h| h[:_source][:id] }
|
62
|
+
execute[:hits][:hits].map { |h| h[:_source][:id] }
|
63
63
|
end
|
64
64
|
|
65
65
|
def count
|
66
|
-
|
67
|
-
res[:hits][:total].to_i
|
66
|
+
execute(count: true, paginate: false)[:hits][:total].to_i
|
68
67
|
end
|
69
68
|
|
70
|
-
|
69
|
+
private
|
70
|
+
|
71
|
+
def execute(count: false, paginate: false)
|
71
72
|
begin
|
72
|
-
search =
|
73
|
-
search = substitute_page(search) if !count && search['hits']['hits'].length == 0 && search['hits']['total'] != 0
|
73
|
+
search = paginate ? execute_paginated_query : execute_all_query( count: count )
|
74
|
+
search = substitute_page(search) if paginate && !count && search['hits']['hits'].length == 0 && search['hits']['total'] != 0
|
74
75
|
rescue Elasticsearch::Transport::Transport::Errors::BadRequest
|
75
76
|
search = failed_search
|
76
77
|
end
|
77
78
|
search.with_indifferent_access
|
78
79
|
end
|
79
80
|
|
80
|
-
private
|
81
|
-
|
82
81
|
# this allows you to chain scopes
|
83
82
|
# the 2+ scopes in the chain will be called
|
84
83
|
# on a query object and not on the base object
|
@@ -89,16 +88,21 @@ module ElasticQueue
|
|
89
88
|
end
|
90
89
|
end
|
91
90
|
|
92
|
-
def
|
93
|
-
|
94
|
-
|
91
|
+
def execute_all_query(count: false)
|
92
|
+
record_count = @queue.search_client.search index: @queue.index_name, body: body, search_type: 'count'
|
93
|
+
return record_count if count
|
94
|
+
@queue.search_client.search index: @queue.index_name, body: body, search_type: 'query_then_fetch', from: 0, size: record_count['hits']['total'].to_i
|
95
|
+
end
|
96
|
+
|
97
|
+
def execute_paginated_query
|
98
|
+
@queue.search_client.search index: @queue.index_name, body: body, search_type: 'query_then_fetch', from: @options.from, size: @options.per_page
|
95
99
|
end
|
96
100
|
|
97
101
|
def substitute_page(search)
|
98
102
|
total_hits = search['hits']['total'].to_i
|
99
103
|
per_page = @options.per_page
|
100
104
|
@options.page = (total_hits / per_page.to_f).ceil
|
101
|
-
|
105
|
+
execute_paginated_query
|
102
106
|
end
|
103
107
|
|
104
108
|
def failed_search
|
@@ -2,9 +2,8 @@ require 'will_paginate/collection'
|
|
2
2
|
|
3
3
|
module ElasticQueue
|
4
4
|
class Results
|
5
|
-
attr_reader :paginate
|
6
5
|
|
7
|
-
|
6
|
+
attr_reader :instantiated_queue_items
|
8
7
|
|
9
8
|
def initialize(queue, search_results, query_options)
|
10
9
|
@queue = queue
|
@@ -12,19 +11,22 @@ module ElasticQueue
|
|
12
11
|
@start = query_options.page
|
13
12
|
@per_page = query_options.per_page
|
14
13
|
@total = search_results[:hits][:total]
|
15
|
-
|
14
|
+
end
|
15
|
+
|
16
|
+
def paginate
|
17
|
+
WillPaginate::Collection.create(@start, @per_page, @total) do |pager|
|
16
18
|
pager.replace(@instantiated_queue_items)
|
17
19
|
end
|
18
20
|
end
|
19
21
|
|
22
|
+
private
|
23
|
+
|
20
24
|
def instantiate_queue_items(search_results)
|
21
25
|
grouped_results, sort_order = group_sorted_results(search_results)
|
22
26
|
records = fetch_records(grouped_results)
|
23
27
|
sort_records(records, sort_order)
|
24
28
|
end
|
25
29
|
|
26
|
-
private
|
27
|
-
|
28
30
|
# group the results by { class_name: [ids] } and save their sorted order
|
29
31
|
def group_sorted_results(search_results)
|
30
32
|
grouped_results = {}
|
data/spec/factories.rb
CHANGED
@@ -1,64 +0,0 @@
|
|
1
|
-
FactoryGirl.define do
|
2
|
-
sequence :email_address do |n|
|
3
|
-
"testy#{n}@example.com"
|
4
|
-
end
|
5
|
-
|
6
|
-
sequence :user_id do
|
7
|
-
(10000...10005).to_a.sample
|
8
|
-
end
|
9
|
-
|
10
|
-
sequence :agent_fee_sales_session_status do
|
11
|
-
['active', 'dead'].sample
|
12
|
-
end
|
13
|
-
|
14
|
-
factory :agent_fee_sales_session do
|
15
|
-
agent
|
16
|
-
status { generate(:agent_fee_sales_session_status) }
|
17
|
-
assigned_to { generate(:user_id) }
|
18
|
-
assigned_at Time.parse('2013-12-02 08:40:33')
|
19
|
-
expires_at Time.parse('2014-06-02 08:40:33')
|
20
|
-
follow_up Time.parse('2013-12-17 06:00:00')
|
21
|
-
hot '1'
|
22
|
-
priority 'high'
|
23
|
-
created_at Time.parse('2013-11-18 14:36:05')
|
24
|
-
updated_at Time.parse('2013-12-17 11:31:08')
|
25
|
-
factory :null_follow_up_agent_fee_sales_session do
|
26
|
-
follow_up nil
|
27
|
-
end
|
28
|
-
end
|
29
|
-
|
30
|
-
factory :agent do
|
31
|
-
after(:build) { |agent| agent.class.skip_callback(:save, :after, :notate_changes) }
|
32
|
-
user
|
33
|
-
company
|
34
|
-
status 'active'
|
35
|
-
name_on_license 'Testy'
|
36
|
-
license_type 'Salesperson'
|
37
|
-
license_number '111111'
|
38
|
-
license_state 'CA'
|
39
|
-
broker_name 'Ali'
|
40
|
-
years_in_real_estate '12 years'
|
41
|
-
end
|
42
|
-
|
43
|
-
factory :company do
|
44
|
-
name 'Flywheel'
|
45
|
-
address '233 Post st.'
|
46
|
-
city 'San Francisco'
|
47
|
-
state 'CA'
|
48
|
-
zip '94104'
|
49
|
-
end
|
50
|
-
|
51
|
-
factory :user do
|
52
|
-
after(:build) { |user| user.class.skip_callback(:save, :after, :notate_changes) }
|
53
|
-
email { generate(:email_address) }
|
54
|
-
login { |u| u.email }
|
55
|
-
password '1234567'
|
56
|
-
first_name 'Testy'
|
57
|
-
last_name 'Testerson'
|
58
|
-
phone_office '415-555-5555'
|
59
|
-
factory :agent_user do
|
60
|
-
user_type 'agent'
|
61
|
-
end
|
62
|
-
end
|
63
|
-
|
64
|
-
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe 'ElasticQueue::Base integration' do
|
4
|
+
describe '#search_client' do
|
5
|
+
it 'returns an Elasticsearch::Transport::Client' do
|
6
|
+
expect(ElasticQueue::Base.search_client).to be_a Elasticsearch::Transport::Client
|
7
|
+
end
|
8
|
+
|
9
|
+
it 'returns the same client every time' do
|
10
|
+
expect(ElasticQueue::Base.search_client.object_id).to eq ElasticQueue::Base.search_client.object_id
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
describe '#models, also tests(#tell_models, #model_names, #model_classes)' do
|
15
|
+
it '#models sets @models and tells the model about itself' do
|
16
|
+
class Cannibal < ActiveRecord::Base
|
17
|
+
include ElasticQueue::Queueable
|
18
|
+
end
|
19
|
+
# Cannibal.stub(:add_queue)
|
20
|
+
# Cannibal.should_receive(:add_queue).with(:"elastic_queue/base")
|
21
|
+
ElasticQueue::Base.models(:cannibal)
|
22
|
+
expect(ElasticQueue::Base.instance_variable_get('@models')).to eq [:cannibal]
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
describe '#index_name, #index_name =' do
|
27
|
+
pending('trivial')
|
28
|
+
end
|
29
|
+
|
30
|
+
describe '#eager_load' do
|
31
|
+
pending
|
32
|
+
end
|
33
|
+
|
34
|
+
describe '#eager_loads' do
|
35
|
+
pending
|
36
|
+
end
|
37
|
+
|
38
|
+
describe '#scopes' do
|
39
|
+
pending
|
40
|
+
end
|
41
|
+
|
42
|
+
describe '#scopes' do
|
43
|
+
pending
|
44
|
+
end
|
45
|
+
|
46
|
+
describe '#default_scope' do
|
47
|
+
pending
|
48
|
+
end
|
49
|
+
|
50
|
+
describe '#query' do
|
51
|
+
pending
|
52
|
+
end
|
53
|
+
|
54
|
+
describe '#filter' do
|
55
|
+
pending
|
56
|
+
end
|
57
|
+
|
58
|
+
describe '#count' do
|
59
|
+
pending
|
60
|
+
end
|
61
|
+
|
62
|
+
describe '#paginate' do
|
63
|
+
pending('not implemented yet')
|
64
|
+
end
|
65
|
+
|
66
|
+
describe 'instance #query' do
|
67
|
+
pending
|
68
|
+
end
|
69
|
+
|
70
|
+
end
|
@@ -0,0 +1,123 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe 'ElasticQueue::Filters integration' do
|
4
|
+
before :all do
|
5
|
+
class Animal < ActiveRecord::Base
|
6
|
+
include ElasticQueue::Queueable
|
7
|
+
queues :test_animals_queue
|
8
|
+
queue_attributes :dangerous, :cute, :birthdate, :name
|
9
|
+
not_analyzed_queue_attributes :species, :description
|
10
|
+
end
|
11
|
+
|
12
|
+
class TestAnimalsQueue < ElasticQueue::Base
|
13
|
+
models :animal
|
14
|
+
end
|
15
|
+
|
16
|
+
TestAnimalsQueue.create_index
|
17
|
+
|
18
|
+
@create_animals = -> {
|
19
|
+
Animal.create({ name: 'a', birthdate: Date.today.at_midnight - 1.year })
|
20
|
+
Animal.create({ name: 'b', birthdate: Date.today.at_midnight - 2.years })
|
21
|
+
Animal.create({ name: 'c', birthdate: Date.today.at_midnight - 3.years })
|
22
|
+
}
|
23
|
+
end
|
24
|
+
|
25
|
+
after :all do
|
26
|
+
[:Animal, :TestAnimalsQueue].each do |constant|
|
27
|
+
Object.send(:remove_const, constant)
|
28
|
+
end
|
29
|
+
delete_index('test_animals_queue')
|
30
|
+
end
|
31
|
+
|
32
|
+
describe 'ElasticQueue::Query#filter' do
|
33
|
+
after :each do
|
34
|
+
Animal.all.each(&:destroy)
|
35
|
+
end
|
36
|
+
|
37
|
+
it 'can filter on one value' do
|
38
|
+
@create_animals.call
|
39
|
+
expect(TestAnimalsQueue.query.filter(name: 'a').all.map(&:name)).to eq ['a']
|
40
|
+
end
|
41
|
+
|
42
|
+
it 'can filter by a less than or greater than a time' do
|
43
|
+
@create_animals.call
|
44
|
+
expect(TestAnimalsQueue.query.filter(birthdate: { after: Date.today - 1.year - 1.day }).all.map(&:name)).to eq ['a']
|
45
|
+
expect(TestAnimalsQueue.query.filter(birthdate: { before: Date.today - 2.years - 1.day }).all.map(&:name)).to eq ['c']
|
46
|
+
end
|
47
|
+
|
48
|
+
it 'can filter by a less than and greater than a time' do
|
49
|
+
@create_animals.call
|
50
|
+
expect(TestAnimalsQueue.query.filter(birthdate: { after: Date.today - 2.year - 1.day, before: Date.today - 1.year - 1.day}).all.map(&:name)).to eq ['b']
|
51
|
+
end
|
52
|
+
|
53
|
+
it 'can filter by less than or greater than a string' do
|
54
|
+
@create_animals.call
|
55
|
+
expect(TestAnimalsQueue.query.filter(name: { after: 'a', before: 'c' }).all.map(&:name)).to eq ['b']
|
56
|
+
end
|
57
|
+
|
58
|
+
it 'doesn\'t error if you try to filter on an nonexistent value' do
|
59
|
+
@create_animals.call
|
60
|
+
expect(TestAnimalsQueue.query.filter(likes_peanut_butter: true).all.map(&:name)).to eq []
|
61
|
+
end
|
62
|
+
|
63
|
+
it 'filters underscored values as one word' do
|
64
|
+
Animal.create({ name: 'pin_head' })
|
65
|
+
Animal.create({ name: 'pin' })
|
66
|
+
expect(TestAnimalsQueue.query.filter(name: 'pin').all.map(&:name)).to eq ['pin']
|
67
|
+
end
|
68
|
+
|
69
|
+
it 'treats parentheses as letters' do
|
70
|
+
Animal.create({ name: 'Sr. Honks-a-lot' , species: '(Silly) Goose' })
|
71
|
+
expect(TestAnimalsQueue.query.filter(species: 'Goose').all.map(&:name)).to eq []
|
72
|
+
expect(TestAnimalsQueue.query.filter(species: '(Silly) Goose').all.map(&:name)).to eq ['Sr. Honks-a-lot']
|
73
|
+
end
|
74
|
+
|
75
|
+
it 'automatically joins multiple filter values with an OR' do
|
76
|
+
@create_animals.call
|
77
|
+
expect(TestAnimalsQueue.query.filter(name: ['a', 'b']).all.map(&:name).sort).to eq ['a', 'b']
|
78
|
+
end
|
79
|
+
|
80
|
+
it 'defaults to joining multiple filter keys with an AND' do
|
81
|
+
Animal.create({ name: 'x', species: 'dog' })
|
82
|
+
Animal.create({ name: 'y', species: 'dog' })
|
83
|
+
expect(TestAnimalsQueue.query.filter(name: 'x').filter(species: 'dog').all.map(&:name)).to eq ['x']
|
84
|
+
end
|
85
|
+
|
86
|
+
it 'can join multiple filter keys with an OR' do
|
87
|
+
Animal.create({ name: 'x', species: 'dog' })
|
88
|
+
Animal.create({ name: 'y', species: 'cat' })
|
89
|
+
Animal.create({ name: 'z', species: 'rat' })
|
90
|
+
expect(TestAnimalsQueue.query.filter(or: [{ name: 'x' }, { species: 'cat' }]).all.map(&:name).sort).to eq ['x', 'y']
|
91
|
+
end
|
92
|
+
|
93
|
+
it 'can join multiple filter values with multiple filter keys with an OR' do
|
94
|
+
Animal.create({ name: 'x', species: 'dog' })
|
95
|
+
Animal.create({ name: 'y', species: 'cat' })
|
96
|
+
Animal.create({ name: 'z', species: 'chicken' })
|
97
|
+
Animal.create({ name: 'a', species: 'chicken' })
|
98
|
+
expect(TestAnimalsQueue.query.filter(or: [{ name: 'z' }, { species: ['cat', 'dog'] }]).all.map(&:name).sort).to eq ['x', 'y', 'z']
|
99
|
+
end
|
100
|
+
|
101
|
+
it 'can nest multiple ands and ors' do
|
102
|
+
Animal.create({ name: 'rusty', species: 'dog', dangerous: false })
|
103
|
+
Animal.create({ name: 'killer', species: 'mountain lion', dangerous: true })
|
104
|
+
Animal.create({ name: 'cock-a-doodle-doo', species: 'chicken', dangerous: false })
|
105
|
+
Animal.create({ name: 'old bess', species: 'cow', dangerous: true })
|
106
|
+
Animal.create({ name: 'speedy', species: 'horse', dangerous: false })
|
107
|
+
expect(TestAnimalsQueue.query.filter({
|
108
|
+
# (rusty, killer, speedy) && ( rusty || lucky, killer, old bess, cock-a-doodle-doo)
|
109
|
+
name: ['rusty', 'killer', 'speedy'], #(rusty, killer, speedy) && (
|
110
|
+
or: [
|
111
|
+
and: [ # rusty ||
|
112
|
+
{ species: ['dog', 'mountain lion'] },
|
113
|
+
{ dangerous: false }
|
114
|
+
],
|
115
|
+
or: [ #speedy, killer, old bess, cock-a-doodle-doo )
|
116
|
+
{ species: ['horse', 'chicken'] },
|
117
|
+
{ dangerous: true }
|
118
|
+
]
|
119
|
+
]
|
120
|
+
}).all.map(&:name).sort).to eq ['killer', 'rusty', 'speedy']
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|