ripple_searchable 0.0.1 → 0.0.2
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/README.md +14 -7
- data/lib/ripple_searchable/criteria.rb +85 -47
- data/lib/ripple_searchable/loggable.rb +26 -0
- data/lib/ripple_searchable/scoping.rb +39 -5
- data/lib/ripple_searchable/searchable.rb +1 -1
- data/lib/ripple_searchable/version.rb +1 -1
- data/lib/ripple_searchable.rb +5 -0
- metadata +3 -2
data/README.md
CHANGED
@@ -1,9 +1,9 @@
|
|
1
1
|
# RippleSearchable
|
2
2
|
|
3
3
|
Mongoid / Active Record style query criteria DSL and Scoping for Ripple
|
4
|
-
using RIAK's solr search interface.
|
4
|
+
using RIAK's solr search interface. (riak_search must be enabled)
|
5
5
|
|
6
|
-
RippleSearchable adds chainable Criteria methods such as :where, :lt, :lte, :gt, :gte, :between
|
6
|
+
RippleSearchable adds named scopes, and chainable Criteria methods such as :where, :lt, :lte, :gt, :gte, :between
|
7
7
|
along with :sort, :skip, :limit options to your Ripple::Document models.
|
8
8
|
|
9
9
|
## Installation
|
@@ -31,15 +31,19 @@ Any of the following criteria can be chained:
|
|
31
31
|
=== Example:
|
32
32
|
|
33
33
|
```ruby
|
34
|
-
Product.where(tags: "nerd", name: "joe", something: 2).
|
35
|
-
1}, {can_sell: 3}).
|
36
|
-
|
34
|
+
Product.where(tags: "nerd", name: "joe", something: 2).
|
35
|
+
or({can_sell:1}, {can_sell: 3}).
|
36
|
+
between(availibility: 1..3, price: [3, 12]).
|
37
|
+
gte(quantity: 0, ratings: 5).
|
38
|
+
sort(:created_at, :desc).limit(5)
|
37
39
|
```
|
38
40
|
|
39
41
|
### Scoping
|
40
42
|
|
41
43
|
Mongoid / Active Record style named scopes:
|
42
44
|
|
45
|
+
Use 'scope' class method to create a named scope that can be accessed from the class level or chained to criteria by the provided name.
|
46
|
+
|
43
47
|
=== Example:
|
44
48
|
|
45
49
|
```ruby
|
@@ -52,9 +56,12 @@ Mongoid / Active Record style named scopes:
|
|
52
56
|
end
|
53
57
|
```
|
54
58
|
|
55
|
-
See docs for method details.
|
59
|
+
See [docs](http://rubydoc.info/github/computadude/ripple_searchable/master/frames) for method details.
|
60
|
+
|
61
|
+
TODO:
|
56
62
|
|
57
|
-
|
63
|
+
Ability to bypass default scopes when using named scopes
|
64
|
+
Write better docs.
|
58
65
|
|
59
66
|
## Contributing
|
60
67
|
|
@@ -27,13 +27,14 @@ module Ripple
|
|
27
27
|
# will append this selector:
|
28
28
|
# "(tags:nerd AND name:Joe AND something:2)"
|
29
29
|
def where(selector = nil)
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
30
|
+
clone.tap do |crit|
|
31
|
+
case selector
|
32
|
+
when String
|
33
|
+
crit.add_restriction selector
|
34
|
+
when Hash
|
35
|
+
crit.add_restriction to_lucene_pair(selector)
|
36
|
+
end
|
35
37
|
end
|
36
|
-
self
|
37
38
|
end
|
38
39
|
|
39
40
|
# Add an OR selector
|
@@ -45,12 +46,13 @@ module Ripple
|
|
45
46
|
# will append this selector:
|
46
47
|
# "((name:Pants) OR (name:Shirt))"
|
47
48
|
def or(*criterion)
|
48
|
-
|
49
|
-
|
50
|
-
|
49
|
+
clone.tap do |crit|
|
50
|
+
crit.add_restriction do
|
51
|
+
criterion.each do |c|
|
52
|
+
crit.add_restriction(to_lucene_pair(c, operator: "OR"), operator: "OR" )
|
53
|
+
end
|
51
54
|
end
|
52
55
|
end
|
53
|
-
self
|
54
56
|
end
|
55
57
|
|
56
58
|
alias :any_of :or
|
@@ -66,12 +68,13 @@ module Ripple
|
|
66
68
|
# will append this selector:
|
67
69
|
# "((availibility:[1 TO 3] AND price:[12 TO 20]))"
|
68
70
|
def between(*criterion)
|
69
|
-
|
70
|
-
|
71
|
-
|
71
|
+
clone.tap do |crit|
|
72
|
+
crit.add_restriction do
|
73
|
+
criterion.each do |c|
|
74
|
+
crit.add_restriction(to_lucene_pair(c, operator: "BETWEEN"))
|
75
|
+
end
|
72
76
|
end
|
73
77
|
end
|
74
|
-
self
|
75
78
|
end
|
76
79
|
|
77
80
|
# Add a 'less or equal than' selector
|
@@ -83,13 +86,14 @@ module Ripple
|
|
83
86
|
# will append this selector:
|
84
87
|
# "((quantity:[* TO 10] AND ratings:[* TO 5]))"
|
85
88
|
def lte(*criterion)
|
86
|
-
|
87
|
-
|
88
|
-
crit.each
|
89
|
-
|
89
|
+
clone.tap do |crit|
|
90
|
+
crit.add_restriction do
|
91
|
+
crit.criterion.each do |c|
|
92
|
+
c.each {|k,v| c[k]=Array.wrap(v).unshift(10**20)}
|
93
|
+
crit.add_restriction(to_lucene_pair(c, operator: "BETWEEN"))
|
94
|
+
end
|
90
95
|
end
|
91
96
|
end
|
92
|
-
self
|
93
97
|
end
|
94
98
|
|
95
99
|
# Add a 'greater or equal than' selector
|
@@ -101,13 +105,14 @@ module Ripple
|
|
101
105
|
# will append this selector:
|
102
106
|
# "((quantity:[0 TO *] AND ratings:[5 TO *]))"
|
103
107
|
def gte(*criterion)
|
104
|
-
|
105
|
-
|
106
|
-
crit.each
|
107
|
-
|
108
|
+
clone.tap do |crit|
|
109
|
+
crit.add_restriction do
|
110
|
+
crit.criterion.each do |c|
|
111
|
+
c.each {|k,v| c[k]=Array.wrap(v).push(10**20)}
|
112
|
+
crit.add_restriction(to_lucene_pair(c, operator: "BETWEEN"))
|
113
|
+
end
|
108
114
|
end
|
109
115
|
end
|
110
|
-
self
|
111
116
|
end
|
112
117
|
|
113
118
|
# Add a 'less than' selector
|
@@ -119,13 +124,14 @@ module Ripple
|
|
119
124
|
# will append this selector:
|
120
125
|
# "((quantity:{* TO 10} AND ratings:{* TO 5}))"
|
121
126
|
def lt(*criterion)
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
127
|
+
clone.tap do |crit|
|
128
|
+
crit.add_restriction do
|
129
|
+
criterion.each do |c|
|
130
|
+
c.each {|k,v| c[k]=Array.wrap(v).unshift("*")}
|
131
|
+
crit.add_restriction(to_lucene_pair(c, operator: "BETWEEN", exclusive: true))
|
132
|
+
end
|
126
133
|
end
|
127
134
|
end
|
128
|
-
self
|
129
135
|
end
|
130
136
|
|
131
137
|
# Add a 'greater than' selector
|
@@ -137,13 +143,14 @@ module Ripple
|
|
137
143
|
# will append this selector:
|
138
144
|
# "((quantity:{0 TO *} AND ratings:{5 TO *}))"
|
139
145
|
def gt(*criterion)
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
146
|
+
clone.tap do |crit|
|
147
|
+
crit.add_restriction do
|
148
|
+
criterion.each do |c|
|
149
|
+
c.each {|k,v| c[k]=Array.wrap(v).push("*")}
|
150
|
+
crit.add_restriction(to_lucene_pair(c, operator: "BETWEEN", exclusive: true))
|
151
|
+
end
|
144
152
|
end
|
145
153
|
end
|
146
|
-
self
|
147
154
|
end
|
148
155
|
|
149
156
|
# Add sort options to criteria
|
@@ -155,13 +162,14 @@ module Ripple
|
|
155
162
|
# will append this sort option:
|
156
163
|
# "availibility asc, created_at desc"
|
157
164
|
def sort(sort_options)
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
165
|
+
clone.tap do |crit|
|
166
|
+
case sort_options
|
167
|
+
when String
|
168
|
+
crit.add_sort_option sort_options
|
169
|
+
when Hash
|
170
|
+
sort_options.each {|k,v| crit.add_sort_option "#{k} #{v.downcase}"}
|
171
|
+
end
|
163
172
|
end
|
164
|
-
self
|
165
173
|
end
|
166
174
|
|
167
175
|
alias :order_by :sort
|
@@ -175,9 +183,10 @@ module Ripple
|
|
175
183
|
#
|
176
184
|
# will limit the number of returned documetns to 10
|
177
185
|
def limit(limit)
|
178
|
-
|
179
|
-
|
180
|
-
|
186
|
+
clone.tap do |crit|
|
187
|
+
crit.clear_cache
|
188
|
+
crit.options[:rows] = limit
|
189
|
+
end
|
181
190
|
end
|
182
191
|
|
183
192
|
alias :rows :limit
|
@@ -189,9 +198,10 @@ module Ripple
|
|
189
198
|
# Product.between(availibility:[1,3]).skip(10)
|
190
199
|
#
|
191
200
|
def skip(skip)
|
192
|
-
|
193
|
-
|
194
|
-
|
201
|
+
clone.tap do |crit|
|
202
|
+
crit.clear_cache
|
203
|
+
crit.options[:start] = skip
|
204
|
+
end
|
195
205
|
end
|
196
206
|
|
197
207
|
alias :start :skip
|
@@ -231,20 +241,48 @@ module Ripple
|
|
231
241
|
end
|
232
242
|
|
233
243
|
def merge(criteria)
|
244
|
+
crit = clone
|
245
|
+
crit.merge!(criteria)
|
246
|
+
crit
|
247
|
+
end
|
248
|
+
|
249
|
+
def merge!(criteria)
|
234
250
|
add_restriction criteria.selector
|
235
251
|
self.options.merge!(criteria.options)
|
236
252
|
self
|
237
253
|
end
|
238
254
|
|
255
|
+
def to_proc
|
256
|
+
->{ self }
|
257
|
+
end
|
258
|
+
|
259
|
+
def ==(other)
|
260
|
+
self.klass == other.klass && self.selector == other.selector && self.options == other.options
|
261
|
+
end
|
262
|
+
|
263
|
+
def initialize_copy(other)
|
264
|
+
@selector = other.selector.dup
|
265
|
+
@options = other.options.dup
|
266
|
+
if other.response.present?
|
267
|
+
@response = other.response.dup
|
268
|
+
@documents = other.documents.dup
|
269
|
+
@docs = other.docs.dup
|
270
|
+
@document_ids = other.document_ids.dup
|
271
|
+
end
|
272
|
+
super
|
273
|
+
end
|
274
|
+
|
239
275
|
def method_missing(name, *args, &block)
|
240
276
|
if klass.respond_to?(name)
|
241
277
|
klass.send(:with_scope, self) do
|
242
278
|
klass.send(name, *args, &block)
|
243
279
|
end
|
280
|
+
else
|
281
|
+
super
|
244
282
|
end
|
245
283
|
end
|
246
284
|
|
247
|
-
|
285
|
+
protected
|
248
286
|
|
249
287
|
def clear_cache
|
250
288
|
@documents, @cached, @response, @total, @docs, @document_ids = [], false
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
module Ripple
|
3
|
+
module Loggable
|
4
|
+
|
5
|
+
def logger
|
6
|
+
return @logger if defined?(@logger)
|
7
|
+
@logger = rails_logger || default_logger
|
8
|
+
end
|
9
|
+
|
10
|
+
def logger=(logger)
|
11
|
+
@logger = logger
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def default_logger
|
17
|
+
logger = Logger.new($stdout)
|
18
|
+
logger.level = Logger::INFO
|
19
|
+
logger
|
20
|
+
end
|
21
|
+
|
22
|
+
def rails_logger
|
23
|
+
defined?(::Rails) && ::Rails.respond_to?(:logger) && ::Rails.logger
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -12,9 +12,26 @@ module Ripple
|
|
12
12
|
|
13
13
|
module ClassMethods
|
14
14
|
|
15
|
+
|
16
|
+
# Create a scope that can be accessed from the class level or chained to
|
17
|
+
# criteria by the provided name.
|
18
|
+
#
|
19
|
+
# === Example
|
20
|
+
#
|
21
|
+
# class Product
|
22
|
+
# include Ripple::Document
|
23
|
+
#
|
24
|
+
# scope :active, where(active: true)
|
25
|
+
# scope :avail, ->(count){ where(quantity: count)}
|
26
|
+
# end
|
27
|
+
#
|
28
|
+
# Product.active.where(name: "peter")
|
29
|
+
#
|
30
|
+
# sets the selector to:
|
31
|
+
# "((active:true)) AND (name:peter)"
|
15
32
|
def scope(name, value, &block)
|
16
33
|
name = name.to_sym
|
17
|
-
|
34
|
+
valid_scope_name?(name)
|
18
35
|
scopes[name] = {
|
19
36
|
scope: strip_default_scope(value),
|
20
37
|
extension: Module.new(&block)
|
@@ -22,12 +39,29 @@ module Ripple
|
|
22
39
|
define_scope_method(name)
|
23
40
|
end
|
24
41
|
|
42
|
+
def default_scope(value)
|
43
|
+
self.default_scoping = if default_scoping
|
44
|
+
->{ default_scoping.call.merge(value.to_proc.call) } unless default_scoping.call == value
|
45
|
+
else
|
46
|
+
value.to_proc
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
25
50
|
def scope_stack
|
26
51
|
Thread.current[:"#{self.bucket_name}_scope_stack"] ||= []
|
27
52
|
end
|
28
53
|
|
29
54
|
def with_default_scope
|
30
|
-
default_scoping
|
55
|
+
default_scoping.try(:call) || without_default_scope
|
56
|
+
end
|
57
|
+
|
58
|
+
def without_default_scope
|
59
|
+
Thread.current[:"#{self.bucket_name}_without_default_scope"] = true
|
60
|
+
scope_stack.last || Criteria.new(self)
|
61
|
+
end
|
62
|
+
|
63
|
+
def without_default_scope?
|
64
|
+
Thread.current[:"#{self.bucket_name}_without_default_scope"]
|
31
65
|
end
|
32
66
|
|
33
67
|
def with_scope(criteria)
|
@@ -42,8 +76,8 @@ module Ripple
|
|
42
76
|
protected
|
43
77
|
|
44
78
|
def valid_scope_name?(name)
|
45
|
-
if logger && respond_to?(name, true)
|
46
|
-
logger.warn "Creating scope :#{name}. " \
|
79
|
+
if Ripple.logger && respond_to?(name, true)
|
80
|
+
Ripple.logger.warn "Creating scope :#{name}. " \
|
47
81
|
"Overwriting existing method #{self.name}.#{name}."
|
48
82
|
end
|
49
83
|
end
|
@@ -62,7 +96,7 @@ module Ripple
|
|
62
96
|
|
63
97
|
def strip_default_scope(value)
|
64
98
|
if value.is_a?(Criteria)
|
65
|
-
|
99
|
+
value.to_proc
|
66
100
|
else
|
67
101
|
value
|
68
102
|
end
|
data/lib/ripple_searchable.rb
CHANGED
@@ -4,8 +4,13 @@ require 'active_support/concern'
|
|
4
4
|
require 'ripple_searchable/version'
|
5
5
|
require 'ripple_searchable/searchable'
|
6
6
|
require 'ripple_searchable/criteria'
|
7
|
+
require 'ripple_searchable/loggable'
|
7
8
|
require 'ripple_searchable/scoping'
|
8
9
|
|
10
|
+
Ripple.class_eval do
|
11
|
+
extend Ripple::Loggable
|
12
|
+
end
|
13
|
+
|
9
14
|
Ripple::Document.class_eval do
|
10
15
|
include Ripple::Searchable
|
11
16
|
include Ripple::Scoping
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ripple_searchable
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.2
|
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: 2012-11-
|
12
|
+
date: 2012-11-30 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: activesupport
|
@@ -101,6 +101,7 @@ files:
|
|
101
101
|
- Rakefile
|
102
102
|
- lib/ripple_searchable.rb
|
103
103
|
- lib/ripple_searchable/criteria.rb
|
104
|
+
- lib/ripple_searchable/loggable.rb
|
104
105
|
- lib/ripple_searchable/scoping.rb
|
105
106
|
- lib/ripple_searchable/searchable.rb
|
106
107
|
- lib/ripple_searchable/version.rb
|