waistband 0.4.2 → 0.7.0
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/waistband.rb +0 -1
- data/lib/waistband/index.rb +2 -6
- data/lib/waistband/query.rb +12 -169
- data/lib/waistband/version.rb +1 -1
- data/spec/lib/index_spec.rb +1 -5
- data/spec/lib/query_spec.rb +130 -342
- metadata +2 -8
- data/lib/waistband/free_query.rb +0 -34
- data/lib/waistband/model.rb +0 -215
- data/spec/lib/free_query_spec.rb +0 -161
- data/spec/lib/model_spec.rb +0 -217
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: waistband
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.7.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-10-
|
12
|
+
date: 2013-10-17 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: activesupport
|
@@ -109,9 +109,7 @@ files:
|
|
109
109
|
- lib/waistband.rb
|
110
110
|
- lib/waistband/configuration.rb
|
111
111
|
- lib/waistband/connection.rb
|
112
|
-
- lib/waistband/free_query.rb
|
113
112
|
- lib/waistband/index.rb
|
114
|
-
- lib/waistband/model.rb
|
115
113
|
- lib/waistband/query.rb
|
116
114
|
- lib/waistband/query_helpers.rb
|
117
115
|
- lib/waistband/query_result.rb
|
@@ -125,9 +123,7 @@ files:
|
|
125
123
|
- spec/config/waistband/waistband_search.yml
|
126
124
|
- spec/lib/configuration_spec.rb
|
127
125
|
- spec/lib/connection_spec.rb
|
128
|
-
- spec/lib/free_query_spec.rb
|
129
126
|
- spec/lib/index_spec.rb
|
130
|
-
- spec/lib/model_spec.rb
|
131
127
|
- spec/lib/query_result_spec.rb
|
132
128
|
- spec/lib/query_spec.rb
|
133
129
|
- spec/lib/stringified_array_spec.rb
|
@@ -167,9 +163,7 @@ test_files:
|
|
167
163
|
- spec/config/waistband/waistband_search.yml
|
168
164
|
- spec/lib/configuration_spec.rb
|
169
165
|
- spec/lib/connection_spec.rb
|
170
|
-
- spec/lib/free_query_spec.rb
|
171
166
|
- spec/lib/index_spec.rb
|
172
|
-
- spec/lib/model_spec.rb
|
173
167
|
- spec/lib/query_result_spec.rb
|
174
168
|
- spec/lib/query_spec.rb
|
175
169
|
- spec/lib/stringified_array_spec.rb
|
data/lib/waistband/free_query.rb
DELETED
@@ -1,34 +0,0 @@
|
|
1
|
-
require 'active_support/core_ext/hash/indifferent_access'
|
2
|
-
|
3
|
-
module Waistband
|
4
|
-
class FreeQuery
|
5
|
-
|
6
|
-
include ::Waistband::QueryHelpers
|
7
|
-
|
8
|
-
attr_accessor :page, :page_size
|
9
|
-
|
10
|
-
def initialize(index, options = {})
|
11
|
-
@index = index
|
12
|
-
@page = (options[:page] || 1).to_i
|
13
|
-
@page_size = (options[:page_size] || 20).to_i
|
14
|
-
end
|
15
|
-
|
16
|
-
def prepare(hash)
|
17
|
-
@hash = hash.with_indifferent_access
|
18
|
-
end
|
19
|
-
|
20
|
-
private
|
21
|
-
|
22
|
-
def to_hash
|
23
|
-
raise "No query has been prepared yet!" unless @hash
|
24
|
-
|
25
|
-
@hash[:from] = from unless @hash[:from]
|
26
|
-
@hash[:size] = @page_size unless @hash[:size]
|
27
|
-
|
28
|
-
@hash
|
29
|
-
end
|
30
|
-
|
31
|
-
# /private
|
32
|
-
|
33
|
-
end
|
34
|
-
end
|
data/lib/waistband/model.rb
DELETED
@@ -1,215 +0,0 @@
|
|
1
|
-
require 'digest/sha1'
|
2
|
-
require 'json'
|
3
|
-
require 'active_support/core_ext/class/attribute'
|
4
|
-
require 'active_support/values/time_zone'
|
5
|
-
require 'active_support/time_with_zone'
|
6
|
-
|
7
|
-
module Waistband
|
8
|
-
class Model
|
9
|
-
|
10
|
-
class_attribute :index_name, :column_names, :validate_columns,
|
11
|
-
:default_values, :stringify_columns
|
12
|
-
|
13
|
-
class << self
|
14
|
-
|
15
|
-
def index
|
16
|
-
@index ||= Waistband::Index.new(index_name)
|
17
|
-
end
|
18
|
-
|
19
|
-
def find(id)
|
20
|
-
attrs = index.read(id)
|
21
|
-
raise ActiveRecord::RecordNotFound.new("#{self} not found!") unless attrs
|
22
|
-
new attrs
|
23
|
-
end
|
24
|
-
|
25
|
-
def create(attributes)
|
26
|
-
instance = new(attributes)
|
27
|
-
instance.save
|
28
|
-
instance
|
29
|
-
end
|
30
|
-
|
31
|
-
def query
|
32
|
-
q = index.query('')
|
33
|
-
q.add_term('model_type', name.underscore)
|
34
|
-
q
|
35
|
-
end
|
36
|
-
|
37
|
-
def query_desc
|
38
|
-
q = query
|
39
|
-
q.add_sort 'created_at', 'desc'
|
40
|
-
q
|
41
|
-
end
|
42
|
-
|
43
|
-
def query_asc
|
44
|
-
q = query
|
45
|
-
q.add_sort 'created_at', 'asc'
|
46
|
-
q
|
47
|
-
end
|
48
|
-
|
49
|
-
def first
|
50
|
-
attrs = query_asc.results.first.try(:source)
|
51
|
-
return new(attrs) if attrs
|
52
|
-
nil
|
53
|
-
end
|
54
|
-
|
55
|
-
def last
|
56
|
-
attrs = query_desc.results.first.try(:source)
|
57
|
-
return new(attrs) if attrs
|
58
|
-
nil
|
59
|
-
end
|
60
|
-
|
61
|
-
def with_index(name)
|
62
|
-
self.index_name = name.to_s
|
63
|
-
end
|
64
|
-
|
65
|
-
def create_index!
|
66
|
-
Waistband::Index.new(index_name).create!
|
67
|
-
end
|
68
|
-
|
69
|
-
def destroy_index!
|
70
|
-
Waistband::Index.new(index_name).destroy!
|
71
|
-
end
|
72
|
-
|
73
|
-
def validates(*cols)
|
74
|
-
self.validate_columns ||= []
|
75
|
-
self.validate_columns |= cols.map(&:to_sym)
|
76
|
-
end
|
77
|
-
|
78
|
-
def defaults(values = {})
|
79
|
-
self.default_values ||= {}
|
80
|
-
|
81
|
-
values.each do |k, v|
|
82
|
-
self.default_values = self.default_values.merge({k.to_sym => v})
|
83
|
-
end
|
84
|
-
end
|
85
|
-
|
86
|
-
def stringify(*cols)
|
87
|
-
self.stringify_columns ||= []
|
88
|
-
self.stringify_columns |= cols.map(&:to_sym)
|
89
|
-
end
|
90
|
-
|
91
|
-
def columns(*cols)
|
92
|
-
cols |= [:id, :model_type, :created_at, :updated_at]
|
93
|
-
self.column_names ||= []
|
94
|
-
self.column_names |= cols.map(&:to_sym)
|
95
|
-
|
96
|
-
cols.each do |col|
|
97
|
-
# Normal attributes setters and getters
|
98
|
-
class_eval <<-EV, __FILE__, __LINE__ + 1
|
99
|
-
def #{col}
|
100
|
-
read_attribute(:#{col})
|
101
|
-
end
|
102
|
-
|
103
|
-
def #{col}=(val)
|
104
|
-
write_attribute(:#{col}, val)
|
105
|
-
end
|
106
|
-
EV
|
107
|
-
|
108
|
-
# Relationship type columns: `user_id`, `app_id` to `user`, `app`
|
109
|
-
if col =~ /(.*)_id$/
|
110
|
-
class_eval <<-EV, __FILE__, __LINE__ + 1
|
111
|
-
def #{$1}
|
112
|
-
klass = "#{$1}".classify.constantize
|
113
|
-
klass.find(#{col})
|
114
|
-
end
|
115
|
-
|
116
|
-
def #{$1}=(val)
|
117
|
-
write_attribute(:#{$1}_id, val.id)
|
118
|
-
end
|
119
|
-
EV
|
120
|
-
end
|
121
|
-
|
122
|
-
end
|
123
|
-
end
|
124
|
-
|
125
|
-
end # /class << self
|
126
|
-
|
127
|
-
attr_reader :errors
|
128
|
-
|
129
|
-
def initialize(attributes = {})
|
130
|
-
@attributes = (attributes || {}).symbolize_keys
|
131
|
-
|
132
|
-
@attributes.each do |key, val|
|
133
|
-
if self.class.stringify_columns.include?(key)
|
134
|
-
self.send("#{key}=", val)
|
135
|
-
elsif !self.class.column_names.include?(key)
|
136
|
-
raise ArgumentError.new("#{key} is not a valid column name!")
|
137
|
-
end
|
138
|
-
end
|
139
|
-
|
140
|
-
self.class.default_values.each do |k, v|
|
141
|
-
self.send("#{k}=", v) if self.send(k).nil?
|
142
|
-
end
|
143
|
-
|
144
|
-
@errors = ::Waistband::QuickError.new
|
145
|
-
end
|
146
|
-
|
147
|
-
def save
|
148
|
-
return false unless valid?
|
149
|
-
|
150
|
-
before_save
|
151
|
-
|
152
|
-
prev_id = self.id
|
153
|
-
prev_created_at = self.created_at
|
154
|
-
prev_updated_at = self.updated_at
|
155
|
-
|
156
|
-
self.id ||= generated_id
|
157
|
-
self.created_at ||= (Time.zone || Time).now.to_i
|
158
|
-
self.updated_at = (Time.zone || Time).now.to_i
|
159
|
-
self.model_type = self.class.name.downcase
|
160
|
-
|
161
|
-
stored_json = JSON.parse store!
|
162
|
-
stored = !!stored_json.try(:[], 'ok')
|
163
|
-
|
164
|
-
if stored
|
165
|
-
after_save
|
166
|
-
else stored
|
167
|
-
self.id = prev_id
|
168
|
-
self.created_at = prev_created_at
|
169
|
-
self.updated_at = prev_updated_at
|
170
|
-
end
|
171
|
-
|
172
|
-
stored
|
173
|
-
end
|
174
|
-
|
175
|
-
def before_save
|
176
|
-
end
|
177
|
-
|
178
|
-
def after_save
|
179
|
-
end
|
180
|
-
|
181
|
-
def valid?
|
182
|
-
self.class.validate_columns.each do |col|
|
183
|
-
@errors << "#{col} cannot be nil" if self.send(col).nil?
|
184
|
-
end
|
185
|
-
|
186
|
-
@errors.empty?
|
187
|
-
end
|
188
|
-
|
189
|
-
def attributes
|
190
|
-
Hash[self.class.column_names.map{|col| [col, self.send(col)] }]
|
191
|
-
end
|
192
|
-
|
193
|
-
def read_attribute(attribute)
|
194
|
-
@attributes[attribute]
|
195
|
-
end
|
196
|
-
|
197
|
-
def write_attribute(attribute, val)
|
198
|
-
val = val.to_s if self.class.stringify_columns.include?(attribute.to_sym)
|
199
|
-
@attributes[attribute] = val
|
200
|
-
end
|
201
|
-
|
202
|
-
private
|
203
|
-
|
204
|
-
def store!
|
205
|
-
self.class.index.store!(id, attributes)
|
206
|
-
end
|
207
|
-
|
208
|
-
def generated_id
|
209
|
-
@generated_id ||= Digest::SHA1.hexdigest "#{attributes}:#{rand(999999)}"
|
210
|
-
end
|
211
|
-
|
212
|
-
# /private
|
213
|
-
|
214
|
-
end
|
215
|
-
end
|
data/spec/lib/free_query_spec.rb
DELETED
@@ -1,161 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
|
3
|
-
describe Waistband::FreeQuery do
|
4
|
-
|
5
|
-
let(:index) { Waistband::Index.new('search') }
|
6
|
-
let(:geo_index) { Waistband::Index.new('geo') }
|
7
|
-
|
8
|
-
let(:query) { Waistband::FreeQuery.new('search') }
|
9
|
-
let(:geo_query) { Waistband::FreeQuery.new('geo') }
|
10
|
-
|
11
|
-
let(:attrs) do
|
12
|
-
{
|
13
|
-
'query' => {
|
14
|
-
'bool' => {
|
15
|
-
'must' => [
|
16
|
-
{
|
17
|
-
'multi_match' => {
|
18
|
-
'query' => "shopping ikea",
|
19
|
-
'fields' => ['name']
|
20
|
-
},
|
21
|
-
}
|
22
|
-
]
|
23
|
-
}
|
24
|
-
}
|
25
|
-
}
|
26
|
-
end
|
27
|
-
|
28
|
-
it "correclty forms a query hash" do
|
29
|
-
add_result!
|
30
|
-
query.prepare(attrs)
|
31
|
-
|
32
|
-
expect(query.instance_variable_get('@hash')).to eql(attrs)
|
33
|
-
end
|
34
|
-
|
35
|
-
it "finds results for a free query" do
|
36
|
-
add_result!
|
37
|
-
query.prepare(attrs)
|
38
|
-
json = query.send(:execute!)
|
39
|
-
|
40
|
-
json['hits'].should be_a Hash
|
41
|
-
json['hits']['total'].should > 0
|
42
|
-
json['hits']['hits'].size.should eql 2
|
43
|
-
|
44
|
-
hit = json['hits']['hits'].first
|
45
|
-
|
46
|
-
hit['_id'].should match(/^task_.*/)
|
47
|
-
hit['_source'].should be_a Hash
|
48
|
-
hit['_source']['id'].should eql 123123
|
49
|
-
hit['_source']['name'].should eql 'some shopping in ikea'
|
50
|
-
hit['_source']['user_id'].should eql 999
|
51
|
-
hit['_source']['description'].should eql 'i need you to pick up some stuff in ikea'
|
52
|
-
end
|
53
|
-
|
54
|
-
it "permits storing and fetching geo results" do
|
55
|
-
add_geo_results!
|
56
|
-
geo_query.prepare({
|
57
|
-
"query" => {
|
58
|
-
"filtered" => {
|
59
|
-
"query" => { "match_all" => {} },
|
60
|
-
"filter" => {
|
61
|
-
"geo_shape" => {
|
62
|
-
"work_area" => {
|
63
|
-
"relation" => "intersects",
|
64
|
-
"shape" => {
|
65
|
-
"type" => "Point",
|
66
|
-
"coordinates" => [-122.39455,37.7841]
|
67
|
-
}
|
68
|
-
}
|
69
|
-
}
|
70
|
-
}
|
71
|
-
}
|
72
|
-
}
|
73
|
-
})
|
74
|
-
|
75
|
-
json = geo_query.send(:execute!)
|
76
|
-
|
77
|
-
json['hits'].should be_a Hash
|
78
|
-
json['hits']['total'].should eql 3
|
79
|
-
json['hits']['hits'].size.should eql 3
|
80
|
-
|
81
|
-
geo_query.prepare({
|
82
|
-
"query" => {
|
83
|
-
"filtered" => {
|
84
|
-
"query" => { "match_all" => {} },
|
85
|
-
"filter" => {
|
86
|
-
"geo_shape" => {
|
87
|
-
"work_area" => {
|
88
|
-
"relation" => "intersects",
|
89
|
-
"shape" => {
|
90
|
-
"type" => "Point",
|
91
|
-
"coordinates" => [-122.3859222222222,37.78292222222222]
|
92
|
-
}
|
93
|
-
}
|
94
|
-
}
|
95
|
-
}
|
96
|
-
}
|
97
|
-
}
|
98
|
-
})
|
99
|
-
|
100
|
-
json = geo_query.send(:execute!)
|
101
|
-
|
102
|
-
json['hits'].should be_a Hash
|
103
|
-
json['hits']['total'].should eql 1
|
104
|
-
json['hits']['hits'].size.should eql 1
|
105
|
-
end
|
106
|
-
|
107
|
-
def add_result!
|
108
|
-
index.store!("task_123123", {id: 123123, name: 'some shopping in ikea', user_id: 999, description: 'i need you to pick up some stuff in ikea', internal: false})
|
109
|
-
index.store!("task_234234", {id: 234234, name: "some shopping in ikea and trader joe's", user_id: 987, description: 'pick me up some eggs', internal: true})
|
110
|
-
index.refresh
|
111
|
-
end
|
112
|
-
|
113
|
-
def add_geo_results!
|
114
|
-
geo_index.store!("rabbit_1", {
|
115
|
-
id: "rabbit_1",
|
116
|
-
work_area: {
|
117
|
-
type: 'polygon',
|
118
|
-
coordinates: [[
|
119
|
-
[-122.4119,37.78211],
|
120
|
-
[-122.39285,37.79649],
|
121
|
-
[-122.37997,37.78415],
|
122
|
-
[-122.39817,37.77248],
|
123
|
-
[-122.40932,37.77302],
|
124
|
-
[-122.4119,37.78211]
|
125
|
-
]]
|
126
|
-
}
|
127
|
-
})
|
128
|
-
|
129
|
-
geo_index.store!("rabbit_2", {
|
130
|
-
id: "rabbit_2",
|
131
|
-
work_area: {
|
132
|
-
type: 'polygon',
|
133
|
-
coordinates: [[
|
134
|
-
[-122.41087,37.78822],
|
135
|
-
[-122.43174,37.78578],
|
136
|
-
[-122.42816,37.75938],
|
137
|
-
[-122.38787,37.76882],
|
138
|
-
[-122.39199,37.78584],
|
139
|
-
[-122.41087,37.78822]
|
140
|
-
]]
|
141
|
-
}
|
142
|
-
})
|
143
|
-
|
144
|
-
geo_index.store!("rabbit_3", {
|
145
|
-
id: "rabbit_3",
|
146
|
-
work_area: {
|
147
|
-
type: 'polygon',
|
148
|
-
coordinates: [[
|
149
|
-
[-122.41997,37.79744],
|
150
|
-
[-122.39576,37.79975],
|
151
|
-
[-122.38461,37.78469],
|
152
|
-
[-122.40294,37.77738],
|
153
|
-
[-122.41997,37.79744]
|
154
|
-
]]
|
155
|
-
}
|
156
|
-
})
|
157
|
-
|
158
|
-
geo_index.refresh
|
159
|
-
end
|
160
|
-
|
161
|
-
end
|