ripple_searchable 0.0.1 → 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- 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
|