lunar 0.4.1 → 0.5.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/.gitignore +2 -1
- data/LICENSE +1 -1
- data/README.markdown +116 -0
- data/Rakefile +6 -5
- data/VERSION +1 -1
- data/lib/lunar.rb +112 -24
- data/lib/lunar/connection.rb +51 -0
- data/lib/lunar/fuzzy_matches.rb +24 -0
- data/lib/lunar/fuzzy_word.rb +2 -2
- data/lib/lunar/index.rb +200 -94
- data/lib/lunar/keyword_matches.rb +32 -0
- data/lib/lunar/lunar_nest.rb +19 -0
- data/lib/lunar/range_matches.rb +28 -0
- data/lib/lunar/result_set.rb +85 -28
- data/lib/lunar/scoring.rb +4 -2
- data/lib/lunar/stopwords.rb +15 -0
- data/lib/lunar/words.rb +6 -3
- data/lunar.gemspec +31 -60
- data/test/helper.rb +4 -5
- data/test/test_fuzzy_indexing.rb +105 -0
- data/test/test_index.rb +150 -0
- data/test/test_lunar.rb +178 -1
- data/test/test_lunar_fuzzy_word.rb +4 -4
- data/test/test_lunar_nest.rb +46 -0
- data/test/{test_lunar_scoring.rb → test_scoring.rb} +5 -5
- metadata +72 -68
- data/.document +0 -5
- data/DATA +0 -41
- data/README.md +0 -80
- data/examples/ohm.rb +0 -40
- data/lib/lunar/search.rb +0 -68
- data/lib/lunar/sets.rb +0 -86
- data/test/test_lunar_fuzzy.rb +0 -118
- data/test/test_lunar_index.rb +0 -191
- data/test/test_lunar_search.rb +0 -261
- data/test/test_sets.rb +0 -48
- data/vendor/nest/nest.rb +0 -7
- data/vendor/redis/.gitignore +0 -9
- data/vendor/redis/LICENSE +0 -20
- data/vendor/redis/README.markdown +0 -120
- data/vendor/redis/Rakefile +0 -75
- data/vendor/redis/benchmarking/logging.rb +0 -62
- data/vendor/redis/benchmarking/pipeline.rb +0 -44
- data/vendor/redis/benchmarking/speed.rb +0 -21
- data/vendor/redis/benchmarking/suite.rb +0 -24
- data/vendor/redis/benchmarking/worker.rb +0 -71
- data/vendor/redis/bin/distredis +0 -33
- data/vendor/redis/examples/basic.rb +0 -15
- data/vendor/redis/examples/dist_redis.rb +0 -43
- data/vendor/redis/examples/incr-decr.rb +0 -17
- data/vendor/redis/examples/list.rb +0 -26
- data/vendor/redis/examples/pubsub.rb +0 -25
- data/vendor/redis/examples/sets.rb +0 -36
- data/vendor/redis/lib/edis.rb +0 -3
- data/vendor/redis/lib/redis.rb +0 -496
- data/vendor/redis/lib/redis/client.rb +0 -265
- data/vendor/redis/lib/redis/dist_redis.rb +0 -118
- data/vendor/redis/lib/redis/distributed.rb +0 -460
- data/vendor/redis/lib/redis/hash_ring.rb +0 -131
- data/vendor/redis/lib/redis/pipeline.rb +0 -13
- data/vendor/redis/lib/redis/raketasks.rb +0 -1
- data/vendor/redis/lib/redis/subscribe.rb +0 -79
- data/vendor/redis/profile.rb +0 -22
- data/vendor/redis/tasks/redis.tasks.rb +0 -140
- data/vendor/redis/test/db/.gitignore +0 -1
- data/vendor/redis/test/distributed_test.rb +0 -1131
- data/vendor/redis/test/redis_test.rb +0 -1134
- data/vendor/redis/test/test.conf +0 -8
- data/vendor/redis/test/test_helper.rb +0 -113
@@ -0,0 +1,32 @@
|
|
1
|
+
module Lunar
|
2
|
+
# @private Used internally by Lunar::search to get all the word matches
|
3
|
+
# given `nest`, `att` and it's `val`.
|
4
|
+
class KeywordMatches
|
5
|
+
attr :nest
|
6
|
+
attr :att
|
7
|
+
attr :value
|
8
|
+
|
9
|
+
def initialize(nest, att, value)
|
10
|
+
@nest, @att, @value = nest, att.to_sym, value
|
11
|
+
end
|
12
|
+
|
13
|
+
def distkey
|
14
|
+
nest[{ att => value }.hash].tap do |dk|
|
15
|
+
dk.zunionstore keys.flatten
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
protected
|
20
|
+
def keys
|
21
|
+
if att == :q
|
22
|
+
metaphones.map { |m| nest['*'][m].keys }
|
23
|
+
else
|
24
|
+
metaphones.map { |m| nest[att][m] }
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def metaphones
|
29
|
+
Words.new(value).map { |word| Lunar.metaphone(word) }
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Lunar
|
2
|
+
# @private Provides convenience methods to look up keys.
|
3
|
+
# Since the Redis KEYS command is actually pretty slow,
|
4
|
+
# i'm considering an alternative approach of manually maintaining
|
5
|
+
# the sets to manage all the groups of keys.
|
6
|
+
class LunarNest < Nest
|
7
|
+
def keys
|
8
|
+
redis.keys self
|
9
|
+
end
|
10
|
+
|
11
|
+
def matches
|
12
|
+
regex = Regexp.new(self.gsub('*', '(.*)'))
|
13
|
+
keys.map { |key|
|
14
|
+
match = key.match(regex)
|
15
|
+
[LunarNest.new(key, redis), *match[1, match.size - 1]]
|
16
|
+
}
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module Lunar
|
2
|
+
# @private Used internally by Lunar::search to get all the range matches
|
3
|
+
# given `nest`, `att` and it's `val`.
|
4
|
+
class RangeMatches
|
5
|
+
MAX_RESULTS = 10_000
|
6
|
+
|
7
|
+
attr :nest
|
8
|
+
attr :att
|
9
|
+
attr :range
|
10
|
+
|
11
|
+
def initialize(nest, att, range)
|
12
|
+
@nest, @att, @range = nest, att.to_sym, range
|
13
|
+
end
|
14
|
+
|
15
|
+
|
16
|
+
def distkey
|
17
|
+
nest[{ att => range }.hash].tap do |dk|
|
18
|
+
res = zset.zrangebyscore(range.first, range.last, :limit => [0, MAX_RESULTS])
|
19
|
+
res.each { |val| dk.zadd(1, val) }
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
def zset
|
25
|
+
@sortedset ||= nest[:Numbers][att]
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
data/lib/lunar/result_set.rb
CHANGED
@@ -1,43 +1,100 @@
|
|
1
1
|
module Lunar
|
2
|
+
# This wraps a Lunar search result set. You can do all the
|
3
|
+
# expected operations with an Enumerable.
|
4
|
+
#
|
5
|
+
# @example:
|
6
|
+
#
|
7
|
+
# results = Lunar.search(Gadget, :q => "Apple Macbook Pro")
|
8
|
+
# results.class == Lunar::ResultSet
|
9
|
+
# # => true
|
10
|
+
#
|
11
|
+
# results.kind_of?(Enumerable)
|
12
|
+
# # => true
|
13
|
+
#
|
14
|
+
# `sort` and `sort_by` commands are available and directly calls
|
15
|
+
# the Redis SORT command.
|
16
|
+
#
|
17
|
+
# results = Lunar.search(Gadget, :q => "Apple Macbook Pro")
|
18
|
+
# results.sort(:by => :name, :order => "ALPHA")
|
19
|
+
# results.sort(:by => :name, :order => "ALPHA ASC")
|
20
|
+
# results.sort(:by => :name, :order => "ALPHA DESC")
|
21
|
+
#
|
22
|
+
# @see http://code.google.com/p/redis/wiki/SortCommand
|
2
23
|
class ResultSet
|
3
24
|
include Enumerable
|
4
25
|
|
5
|
-
attr :
|
26
|
+
attr :distkey
|
27
|
+
attr :nest
|
28
|
+
attr :sortables
|
6
29
|
|
7
|
-
def initialize(
|
8
|
-
@
|
9
|
-
@
|
30
|
+
def initialize(distkey, nest, finder)
|
31
|
+
@distkey = distkey
|
32
|
+
@nest = nest
|
33
|
+
@finder = finder
|
34
|
+
@sortables = @nest[:Sortables]['*']
|
10
35
|
end
|
11
36
|
|
12
|
-
def each
|
13
|
-
|
14
|
-
end
|
15
|
-
end
|
16
|
-
|
17
|
-
class SortedResultSet < ResultSet
|
18
|
-
def all(options = {})
|
19
|
-
start = Integer(options[:start] || 0)
|
20
|
-
limit = Integer(options[:limit] || 0)
|
21
|
-
finish = start + limit - 1
|
22
|
-
|
23
|
-
Lunar.redis.zrevrange(key, start, finish).map(&@block)
|
37
|
+
def each
|
38
|
+
objects(distkey.zrange(0, -1)).each { |e| yield e }
|
24
39
|
end
|
25
40
|
|
26
|
-
|
27
|
-
|
41
|
+
# Provides syntatic sugar for `sort`.
|
42
|
+
#
|
43
|
+
# @example:
|
44
|
+
#
|
45
|
+
# results = Lunar.search(Gadget, :q => "apple")
|
46
|
+
# results.sort(:by => :votes)
|
47
|
+
# results.sort_by(:votes)
|
48
|
+
#
|
49
|
+
# @param [#to_s] att the field in the namespace you want to sort by.
|
50
|
+
# @param [Hash] opts the various opts to pass to Redis SORT command.
|
51
|
+
# @option opts [#to_s] :order the direction you want to sort i.e. ASC DESC ALPHA
|
52
|
+
# @option opts [Array] :limit offset and max results to return.
|
53
|
+
#
|
54
|
+
# @return [Array] Array of objects as defined by the `finder`.
|
55
|
+
# @see http://code.google.com/p/redis/wiki/SortCommand
|
56
|
+
def sort_by(att, opts = {})
|
57
|
+
sort(opts.merge(:by => att))
|
28
58
|
end
|
29
|
-
end
|
30
59
|
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
60
|
+
# Gives the ability to sort the search results via a `sortable` field
|
61
|
+
# in your index.
|
62
|
+
#
|
63
|
+
# @example:
|
64
|
+
#
|
65
|
+
# Lunar.index Gadget do |i|
|
66
|
+
# i.id 1001
|
67
|
+
# i.text :title, "Apple Macbook Pro"
|
68
|
+
# i.sortable :votes, 10
|
69
|
+
# end
|
70
|
+
#
|
71
|
+
# Lunar.index Gadget do |i|
|
72
|
+
# i.id 1002
|
73
|
+
# i.text :title, "Apple iPad"
|
74
|
+
# i.sortable :votes, 50
|
75
|
+
# end
|
76
|
+
#
|
77
|
+
# results = Lunar.search(Gadget, :q => "apple")
|
78
|
+
# sorted = results.sort(:by => :votes, :order => "DESC")
|
79
|
+
#
|
80
|
+
# sorted == [Gadget[1002], Gadget[1001]]
|
81
|
+
# # => true
|
82
|
+
#
|
83
|
+
# @param [Hash] opts the various opts to pass to Redis SORT command.
|
84
|
+
# @option opts [#to_s] :by the field in the namespace you want to sort by.
|
85
|
+
# @option opts [#to_s] :order the direction you want to sort i.e. ASC DESC ALPHA
|
86
|
+
# @option opts [Array] :limit offset and max results to return.
|
87
|
+
#
|
88
|
+
# @return [Array] Array of objects as defined by the `finder`.
|
89
|
+
# @see http://code.google.com/p/redis/wiki/SortCommand
|
90
|
+
def sort(opts = {})
|
91
|
+
opts[:by] = sortables[opts[:by]] if opts[:by]
|
92
|
+
objects(distkey.sort(opts))
|
37
93
|
end
|
38
94
|
|
39
|
-
|
40
|
-
|
95
|
+
protected
|
96
|
+
def objects(ids)
|
97
|
+
ids.map(&@finder)
|
41
98
|
end
|
42
99
|
end
|
43
|
-
end
|
100
|
+
end
|
data/lib/lunar/scoring.rb
CHANGED
@@ -1,11 +1,13 @@
|
|
1
1
|
module Lunar
|
2
|
+
# @private internally used by Lunar::Index to count the words
|
3
|
+
# of a given text.
|
2
4
|
class Scoring
|
3
5
|
def initialize(words)
|
4
6
|
@words = Words.new(words)
|
5
7
|
end
|
6
8
|
|
7
9
|
def scores
|
8
|
-
@words.inject(Hash.new(0)) { |a, w| a[w.downcase] += 1
|
10
|
+
@words.inject(Hash.new(0)) { |a, w| a[w.downcase] += 1 and a }
|
9
11
|
end
|
10
12
|
end
|
11
|
-
end
|
13
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module Lunar
|
2
|
+
module Stopwords
|
3
|
+
def include?(word)
|
4
|
+
stopwords.include?(word)
|
5
|
+
end
|
6
|
+
|
7
|
+
private
|
8
|
+
def stopwords
|
9
|
+
%w(an and are as at be but by for if in into is it no not of on or s
|
10
|
+
such t that the their then there these they this to was will with)
|
11
|
+
end
|
12
|
+
|
13
|
+
module_function :stopwords, :include?
|
14
|
+
end
|
15
|
+
end
|
data/lib/lunar/words.rb
CHANGED
@@ -1,14 +1,17 @@
|
|
1
1
|
require 'iconv'
|
2
2
|
|
3
3
|
module Lunar
|
4
|
+
# @private Internally used to determine the words given some str.
|
5
|
+
# i.e. Words.new("the quick brown") == %w(the quick brown)
|
4
6
|
class Words < Array
|
5
7
|
SEPARATOR = /\s+/
|
6
8
|
|
7
9
|
def initialize(str)
|
8
10
|
words = str.split(SEPARATOR).
|
9
11
|
reject { |w| w.to_s.strip.empty? }.
|
10
|
-
map { |w| sanitize(w) }
|
11
|
-
|
12
|
+
map { |w| sanitize(w) }.
|
13
|
+
reject { |w| Stopwords.include?(w) }
|
14
|
+
|
12
15
|
super(words)
|
13
16
|
end
|
14
17
|
|
@@ -18,4 +21,4 @@ module Lunar
|
|
18
21
|
gsub(/[^a-zA-Z0-9\-_]/, '').downcase
|
19
22
|
end
|
20
23
|
end
|
21
|
-
end
|
24
|
+
end
|
data/lunar.gemspec
CHANGED
@@ -5,92 +5,57 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = %q{lunar}
|
8
|
-
s.version = "0.
|
8
|
+
s.version = "0.5.0"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Cyril David"]
|
12
|
-
s.date = %q{2010-05-
|
13
|
-
s.description = %q{
|
12
|
+
s.date = %q{2010-05-21}
|
13
|
+
s.description = %q{Features full text searching via metaphones, range querying for numbers, fuzzy searching and sorting based on customer fields}
|
14
14
|
s.email = %q{cyx.ucron@gmail.com}
|
15
15
|
s.extra_rdoc_files = [
|
16
16
|
"LICENSE",
|
17
|
-
"README.
|
17
|
+
"README.markdown"
|
18
18
|
]
|
19
19
|
s.files = [
|
20
|
-
".
|
21
|
-
".gitignore",
|
22
|
-
"DATA",
|
20
|
+
".gitignore",
|
23
21
|
"LICENSE",
|
24
|
-
"README.
|
22
|
+
"README.markdown",
|
25
23
|
"Rakefile",
|
26
24
|
"VERSION",
|
27
|
-
"examples/ohm.rb",
|
28
25
|
"lib/lunar.rb",
|
26
|
+
"lib/lunar/connection.rb",
|
27
|
+
"lib/lunar/fuzzy_matches.rb",
|
29
28
|
"lib/lunar/fuzzy_word.rb",
|
30
29
|
"lib/lunar/index.rb",
|
30
|
+
"lib/lunar/keyword_matches.rb",
|
31
|
+
"lib/lunar/lunar_nest.rb",
|
32
|
+
"lib/lunar/range_matches.rb",
|
31
33
|
"lib/lunar/result_set.rb",
|
32
34
|
"lib/lunar/scoring.rb",
|
33
|
-
"lib/lunar/
|
34
|
-
"lib/lunar/sets.rb",
|
35
|
+
"lib/lunar/stopwords.rb",
|
35
36
|
"lib/lunar/words.rb",
|
36
37
|
"lunar.gemspec",
|
37
38
|
"test/helper.rb",
|
39
|
+
"test/test_fuzzy_indexing.rb",
|
40
|
+
"test/test_index.rb",
|
38
41
|
"test/test_lunar.rb",
|
39
|
-
"test/test_lunar_fuzzy.rb",
|
40
42
|
"test/test_lunar_fuzzy_word.rb",
|
41
|
-
"test/
|
42
|
-
"test/
|
43
|
-
"test/test_lunar_search.rb",
|
44
|
-
"test/test_sets.rb",
|
45
|
-
"vendor/nest/nest.rb",
|
46
|
-
"vendor/redis/.gitignore",
|
47
|
-
"vendor/redis/LICENSE",
|
48
|
-
"vendor/redis/README.markdown",
|
49
|
-
"vendor/redis/Rakefile",
|
50
|
-
"vendor/redis/benchmarking/logging.rb",
|
51
|
-
"vendor/redis/benchmarking/pipeline.rb",
|
52
|
-
"vendor/redis/benchmarking/speed.rb",
|
53
|
-
"vendor/redis/benchmarking/suite.rb",
|
54
|
-
"vendor/redis/benchmarking/worker.rb",
|
55
|
-
"vendor/redis/bin/distredis",
|
56
|
-
"vendor/redis/examples/basic.rb",
|
57
|
-
"vendor/redis/examples/dist_redis.rb",
|
58
|
-
"vendor/redis/examples/incr-decr.rb",
|
59
|
-
"vendor/redis/examples/list.rb",
|
60
|
-
"vendor/redis/examples/pubsub.rb",
|
61
|
-
"vendor/redis/examples/sets.rb",
|
62
|
-
"vendor/redis/lib/edis.rb",
|
63
|
-
"vendor/redis/lib/redis.rb",
|
64
|
-
"vendor/redis/lib/redis/client.rb",
|
65
|
-
"vendor/redis/lib/redis/dist_redis.rb",
|
66
|
-
"vendor/redis/lib/redis/distributed.rb",
|
67
|
-
"vendor/redis/lib/redis/hash_ring.rb",
|
68
|
-
"vendor/redis/lib/redis/pipeline.rb",
|
69
|
-
"vendor/redis/lib/redis/raketasks.rb",
|
70
|
-
"vendor/redis/lib/redis/subscribe.rb",
|
71
|
-
"vendor/redis/profile.rb",
|
72
|
-
"vendor/redis/tasks/redis.tasks.rb",
|
73
|
-
"vendor/redis/test/db/.gitignore",
|
74
|
-
"vendor/redis/test/distributed_test.rb",
|
75
|
-
"vendor/redis/test/redis_test.rb",
|
76
|
-
"vendor/redis/test/test.conf",
|
77
|
-
"vendor/redis/test/test_helper.rb"
|
43
|
+
"test/test_lunar_nest.rb",
|
44
|
+
"test/test_scoring.rb"
|
78
45
|
]
|
79
46
|
s.homepage = %q{http://github.com/sinefunc/lunar}
|
80
47
|
s.rdoc_options = ["--charset=UTF-8"]
|
81
48
|
s.require_paths = ["lib"]
|
82
|
-
s.rubygems_version = %q{1.3.
|
83
|
-
s.summary = %q{
|
49
|
+
s.rubygems_version = %q{1.3.6}
|
50
|
+
s.summary = %q{A redis based full text search engine}
|
84
51
|
s.test_files = [
|
85
52
|
"test/helper.rb",
|
53
|
+
"test/test_fuzzy_indexing.rb",
|
54
|
+
"test/test_index.rb",
|
86
55
|
"test/test_lunar.rb",
|
87
|
-
"test/test_lunar_fuzzy.rb",
|
88
56
|
"test/test_lunar_fuzzy_word.rb",
|
89
|
-
"test/
|
90
|
-
"test/
|
91
|
-
"test/test_lunar_search.rb",
|
92
|
-
"test/test_sets.rb",
|
93
|
-
"examples/ohm.rb"
|
57
|
+
"test/test_lunar_nest.rb",
|
58
|
+
"test/test_scoring.rb"
|
94
59
|
]
|
95
60
|
|
96
61
|
if s.respond_to? :specification_version then
|
@@ -98,15 +63,21 @@ Gem::Specification.new do |s|
|
|
98
63
|
s.specification_version = 3
|
99
64
|
|
100
65
|
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
|
66
|
+
s.add_runtime_dependency(%q<redis>, [">= 2.0.0"])
|
67
|
+
s.add_runtime_dependency(%q<nest>, [">= 0.0.4"])
|
68
|
+
s.add_runtime_dependency(%q<text>, [">= 0"])
|
101
69
|
s.add_development_dependency(%q<contest>, [">= 0"])
|
102
|
-
s.add_development_dependency(%q<mocha>, [">= 0"])
|
103
70
|
else
|
71
|
+
s.add_dependency(%q<redis>, [">= 2.0.0"])
|
72
|
+
s.add_dependency(%q<nest>, [">= 0.0.4"])
|
73
|
+
s.add_dependency(%q<text>, [">= 0"])
|
104
74
|
s.add_dependency(%q<contest>, [">= 0"])
|
105
|
-
s.add_dependency(%q<mocha>, [">= 0"])
|
106
75
|
end
|
107
76
|
else
|
77
|
+
s.add_dependency(%q<redis>, [">= 2.0.0"])
|
78
|
+
s.add_dependency(%q<nest>, [">= 0.0.4"])
|
79
|
+
s.add_dependency(%q<text>, [">= 0"])
|
108
80
|
s.add_dependency(%q<contest>, [">= 0"])
|
109
|
-
s.add_dependency(%q<mocha>, [">= 0"])
|
110
81
|
end
|
111
82
|
end
|
112
83
|
|
data/test/helper.rb
CHANGED
@@ -1,14 +1,13 @@
|
|
1
1
|
require 'rubygems'
|
2
2
|
require 'test/unit'
|
3
3
|
require 'contest'
|
4
|
-
require 'mocha'
|
5
4
|
|
6
5
|
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
7
6
|
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
8
7
|
require 'lunar'
|
9
8
|
|
10
|
-
$LOAD_PATH.unshift(File.dirname(__FILE__) + '/../vendor/redis/lib')
|
11
|
-
require 'redis'
|
12
|
-
|
13
9
|
class Test::Unit::TestCase
|
14
|
-
|
10
|
+
def setup
|
11
|
+
Lunar.redis.flushdb
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,105 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
class LunarFuzzyTest < Test::Unit::TestCase
|
4
|
+
context "when setting fuzzy name, 'Yukihiro Matsumoto'" do
|
5
|
+
setup do
|
6
|
+
@index = Lunar.index 'Item' do |i|
|
7
|
+
i.id 1001
|
8
|
+
i.fuzzy :name, 'Yukihiro Matsumoto'
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
should "store Lunar:Item:Fuzzies:name:Y up to o and M up to o" do
|
13
|
+
fname, lname = 'yukihiro', 'matsumoto'
|
14
|
+
nest = Lunar.nest[:Item][:Fuzzies][:name]
|
15
|
+
|
16
|
+
(1..fname.length).each do |length|
|
17
|
+
key = nest[encode(fname[0, length])]
|
18
|
+
assert_equal '1', key.zscore(1001)
|
19
|
+
end
|
20
|
+
|
21
|
+
(1..lname.length).each do |length|
|
22
|
+
key = nest[encode(lname[0, length])]
|
23
|
+
assert_equal '1', key.zscore(1001)
|
24
|
+
end
|
25
|
+
|
26
|
+
assert_equal %w{matsumoto yukihiro},
|
27
|
+
Lunar.nest[:Item][:Fuzzies][1001][:name].smembers.sort
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
context "when creating an index that already exists" do
|
32
|
+
setup do
|
33
|
+
@index = Lunar.index 'Item' do |i|
|
34
|
+
i.id 1001
|
35
|
+
i.fuzzy :name, 'Yukihiro Matsumoto'
|
36
|
+
end
|
37
|
+
|
38
|
+
@index = Lunar.index 'Item' do |i|
|
39
|
+
i.id 1001
|
40
|
+
i.fuzzy :name, 'Martin Fowler Yuki'
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
should "remove all fuzzy entries for Yukihiro Matsumoto" do
|
45
|
+
fname, lname = 'yukihiro', 'matsumoto'
|
46
|
+
nest = Lunar.nest[:Item][:Fuzzies][:name]
|
47
|
+
|
48
|
+
(5..fname.length).each do |length|
|
49
|
+
key = nest[encode(fname[0, length])]
|
50
|
+
assert_nil key.zscore('1001')
|
51
|
+
end
|
52
|
+
|
53
|
+
(3..lname.length).each do |length|
|
54
|
+
key = nest[encode(lname[0, length])]
|
55
|
+
assert_nil key.zscore('1001')
|
56
|
+
end
|
57
|
+
|
58
|
+
assert_equal %w{fowler martin yuki},
|
59
|
+
Lunar.nest[:Item][:Fuzzies][1001][:name].smembers.sort
|
60
|
+
end
|
61
|
+
|
62
|
+
should "store Lunar:Item:name:M up to n and F up to r etc..." do
|
63
|
+
fname, lname, triple = 'martin', 'fowler', 'yuki'
|
64
|
+
nest = Lunar.nest[:Item][:Fuzzies][:name]
|
65
|
+
|
66
|
+
%w{martin fowler yuki}.each do |word|
|
67
|
+
(1..word.length).each do |length|
|
68
|
+
key = nest[encode(word[0, length])]
|
69
|
+
assert_equal '1', key.zscore(1001)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
context "on delete" do
|
76
|
+
setup do
|
77
|
+
@index = Lunar.index 'Item' do |i|
|
78
|
+
i.id 1001
|
79
|
+
i.fuzzy :name, 'Yukihiro Matsumoto'
|
80
|
+
end
|
81
|
+
|
82
|
+
Lunar.delete('Item', 1001)
|
83
|
+
end
|
84
|
+
|
85
|
+
should "remove all fuzzy entries for Yukihiro Matsumoto" do
|
86
|
+
nest = Lunar.nest[:Item][:Fuzzies][:name]
|
87
|
+
|
88
|
+
%w{yukihiro matsumoto}.each do |word|
|
89
|
+
(1..word.length).each do |length|
|
90
|
+
key = nest[encode(word[0, length])]
|
91
|
+
assert_nil key.zscore(1001)
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
should "also remove the key Lunar:Item:Fuzzies:1001:name" do
|
97
|
+
assert ! Lunar.redis.exists("Lunar:Item:Fuzzies:1001:name")
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
protected
|
102
|
+
def encode(str)
|
103
|
+
Lunar.encode(str)
|
104
|
+
end
|
105
|
+
end
|