elastic_queue 0.0.10 → 0.0.11
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 +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
|