redis-autosuggest 0.0.1 → 0.1.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/README.md +52 -2
- data/lib/redis-autosuggest.rb +7 -1
- data/lib/redis/autosuggest.rb +6 -1
- data/lib/redis/autosuggest/config.rb +6 -2
- data/lib/redis/autosuggest/file.rb +23 -0
- data/lib/redis/autosuggest/init.rb +17 -0
- data/lib/redis/autosuggest/rails.rb +49 -0
- data/lib/redis/autosuggest/version.rb +1 -1
- data/redis-autosuggest.gemspec +0 -1
- data/test/autosuggest_test.rb +7 -11
- data/test/file_test.rb +33 -0
- data/test/test_helper.rb +12 -0
- data/test/text/example.txt +3 -0
- data/test/text/example_with_score.txt +3 -0
- metadata +10 -17
data/README.md
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
# Redis::Autosuggest
|
2
2
|
|
3
|
-
|
3
|
+
Provides autocompletions through Redis, with the ability to rank
|
4
|
+
results and integrate with Rails
|
4
5
|
|
5
6
|
## Installation
|
6
7
|
|
@@ -18,7 +19,56 @@ Or install it yourself as:
|
|
18
19
|
|
19
20
|
## Usage
|
20
21
|
|
21
|
-
|
22
|
+
By default Autosuggest creates a new Redis client on db 0 at localhost:6379.
|
23
|
+
|
24
|
+
To change the server/port:
|
25
|
+
```ruby
|
26
|
+
r = Redis.new(:host => "my_host", :port => my_port)
|
27
|
+
Redis::Autosuggest.redis = r
|
28
|
+
```
|
29
|
+
|
30
|
+
To add items to be use for autocompletions:
|
31
|
+
```ruby
|
32
|
+
Redis::Autosuggest.add("North By Northwest", "Northern Exposure")
|
33
|
+
```
|
34
|
+
|
35
|
+
To check for autocompletions for an item:
|
36
|
+
```ruby
|
37
|
+
Redis::Autosuggest.suggest("nor")
|
38
|
+
# => ["north by northwest", "northern exposure"]
|
39
|
+
```
|
40
|
+
Autocompletions will be ordered their score value (descending).
|
41
|
+
|
42
|
+
Some other usage examples:
|
43
|
+
```ruby
|
44
|
+
# Add items with initial scores
|
45
|
+
Redis::Autosuggest.suggest("North By Northwest", 9, Northern Exposure, 3)
|
46
|
+
# Increment an item's score
|
47
|
+
Redis::Autosuggest.suggest("North By Northwest", 1)
|
48
|
+
```
|
49
|
+
|
50
|
+
## Rails support
|
51
|
+
|
52
|
+
Autosuggest can also be integrated with Rails. Include it in a model:
|
53
|
+
```ruby
|
54
|
+
class Movie < ActiveRecord::Base
|
55
|
+
include Redis::Autosuggest
|
56
|
+
|
57
|
+
attr_accessible :movie_title
|
58
|
+
autosuggest :movie_title
|
59
|
+
end
|
60
|
+
```
|
61
|
+
|
62
|
+
For first time usage, seed the Redis db with the autosuggest sources:
|
63
|
+
```ruby
|
64
|
+
Redis::Autosuggest.init_rails_sources
|
65
|
+
```
|
66
|
+
|
67
|
+
You can optionally specify a numeric field to be used as the initial score for an item
|
68
|
+
when it is added:
|
69
|
+
```ruby
|
70
|
+
autosuggest :movie_title, :rank_by => imdb_rating
|
71
|
+
```
|
22
72
|
|
23
73
|
## Contributing
|
24
74
|
|
data/lib/redis-autosuggest.rb
CHANGED
@@ -1,5 +1,11 @@
|
|
1
1
|
require 'redis'
|
2
2
|
require 'redis-namespace'
|
3
3
|
require 'redis/autosuggest'
|
4
|
-
require 'redis/autosuggest/version'
|
5
4
|
require 'redis/autosuggest/config'
|
5
|
+
require 'redis/autosuggest/file'
|
6
|
+
require 'redis/autosuggest/init'
|
7
|
+
require 'redis/autosuggest/version'
|
8
|
+
|
9
|
+
if defined?(Rails)
|
10
|
+
require 'redis/autosuggest/rails'
|
11
|
+
end
|
data/lib/redis/autosuggest.rb
CHANGED
@@ -12,7 +12,7 @@ class Redis
|
|
12
12
|
add_item(i.downcase)
|
13
13
|
end
|
14
14
|
end
|
15
|
-
|
15
|
+
|
16
16
|
# Add item(s) along with their scores.
|
17
17
|
# add_with_score("item1", 4, "item2", 1, "item3", 0)
|
18
18
|
def add_with_score(*fields)
|
@@ -55,6 +55,11 @@ class Redis
|
|
55
55
|
top_ids.empty? ? [] : @db.hmget(@items, top_ids)
|
56
56
|
end
|
57
57
|
|
58
|
+
# Get the score of an item
|
59
|
+
def get_score(item)
|
60
|
+
@substrings.zscore(item.downcase, get_id(item.downcase))
|
61
|
+
end
|
62
|
+
|
58
63
|
private
|
59
64
|
def add_item(item, score=0)
|
60
65
|
id = self.db.hlen(self.items)
|
@@ -23,15 +23,19 @@ class Redis
|
|
23
23
|
|
24
24
|
# Key to a sorted set holding all id of items in the autosuggest database sorted
|
25
25
|
# by their score
|
26
|
-
@leaderboard = "
|
26
|
+
@leaderboard = "lead"
|
27
27
|
|
28
28
|
# Leaderboard off by default
|
29
29
|
@use_leaderboard = false
|
30
30
|
|
31
|
+
# Sources to be used for Autocomplete in rails.
|
32
|
+
# Example: { Movie => :movie_title }
|
33
|
+
@rails_sources = {}
|
34
|
+
|
31
35
|
class << self
|
32
36
|
attr_reader :redis
|
33
37
|
attr_accessor :db, :items, :substrings, :max_per_substring, :max_results,
|
34
|
-
:leaderboard, :use_leaderboard
|
38
|
+
:leaderboard, :use_leaderboard, :rails_sources
|
35
39
|
|
36
40
|
def redis=(redis)
|
37
41
|
@redis = redis
|
@@ -0,0 +1,23 @@
|
|
1
|
+
class Redis
|
2
|
+
module Autosuggest
|
3
|
+
|
4
|
+
class << self
|
5
|
+
|
6
|
+
# Add items to the autosuggest database from a file.
|
7
|
+
# Each line be a string representing the item
|
8
|
+
def add_from_file(file)
|
9
|
+
add(*(File.open(file, "r").map { |l| l.strip }))
|
10
|
+
end
|
11
|
+
|
12
|
+
# Add items and their to the autosuggest database from a file.
|
13
|
+
# Each line be a string representing the item followed by its score
|
14
|
+
# Example:
|
15
|
+
# item1 0.4
|
16
|
+
# item2 2.1
|
17
|
+
# item3 5.2
|
18
|
+
def add_with_score_from_file(file)
|
19
|
+
add_with_score(*(File.open(file, "r").map { |l| l.split(" ")}.flatten))
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
class Redis
|
2
|
+
module Autosuggest
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
module ClassMethods
|
6
|
+
|
7
|
+
def autosuggest(column, options={})
|
8
|
+
hash = Redis::Autosuggest.rails_sources[self]
|
9
|
+
if hash.nil?
|
10
|
+
Redis::Autosuggest.rails_sources[self] = { column => options }
|
11
|
+
else
|
12
|
+
hash[column] = options
|
13
|
+
end
|
14
|
+
|
15
|
+
# hook onto rails callbacks to update autosuggest db if a source is modified
|
16
|
+
class_eval <<-HERE
|
17
|
+
after_create :add_to_autosuggest
|
18
|
+
def add_to_autosuggest
|
19
|
+
Redis::Autosuggest.rails_sources[self.class].each do |column, options|
|
20
|
+
score = self.send(options[:rank_by]) if !options[:rank_by].nil?
|
21
|
+
score ||= 0
|
22
|
+
Redis::Autosuggest.add_with_score(self.send(column), score)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
after_update :check_if_changed
|
27
|
+
def check_if_changed
|
28
|
+
Redis::Autosuggest.rails_sources[self.class].each_key do |column|
|
29
|
+
next if !self.send("#{column}_changed?")
|
30
|
+
old_item = self.send("#{column}_was")
|
31
|
+
score = Redis::Autosuggest.get_score(old_item)
|
32
|
+
Redis::Autosuggest.remove(old_item)
|
33
|
+
Redis::Autosuggest.add_with_score(self.send(column), score)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
before_destroy :remove_from_autosuggest
|
38
|
+
def remove_from_autosuggest
|
39
|
+
Redis::Autosuggest.rails_sources[self.class].each_key do |column|
|
40
|
+
Redis::Autosuggest.remove(self.send(column))
|
41
|
+
end
|
42
|
+
end
|
43
|
+
HERE
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
|
data/redis-autosuggest.gemspec
CHANGED
data/test/autosuggest_test.rb
CHANGED
@@ -1,18 +1,9 @@
|
|
1
1
|
require 'test_helper'
|
2
2
|
|
3
3
|
class TestAutosuggest < MiniTest::Unit::TestCase
|
4
|
-
|
4
|
+
|
5
5
|
def self.unused_db
|
6
|
-
@unused_db ||= Redis.new(:db => db_picker)
|
7
|
-
end
|
8
|
-
|
9
|
-
# get an unused db so that we can safely clear all keys
|
10
|
-
def self.db_picker
|
11
|
-
redis = Redis.new
|
12
|
-
(0..15).each do |i|
|
13
|
-
redis.select(i)
|
14
|
-
return i if redis.keys.empty?
|
15
|
-
end
|
6
|
+
@unused_db ||= Redis.new(:db => TestHelper.db_picker)
|
16
7
|
end
|
17
8
|
|
18
9
|
def setup
|
@@ -109,5 +100,10 @@ class TestAutosuggest < MiniTest::Unit::TestCase
|
|
109
100
|
Redis::Autosuggest.use_leaderboard = false
|
110
101
|
end
|
111
102
|
|
103
|
+
def test_getting_an_items_score
|
104
|
+
Redis::Autosuggest.add_with_score(@str1, 3)
|
105
|
+
assert_equal 3, Redis::Autosuggest.get_score(@str1)
|
106
|
+
end
|
107
|
+
|
112
108
|
MiniTest::Unit.after_tests { self.unused_db.flushdb }
|
113
109
|
end
|
data/test/file_test.rb
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class TestFile < MiniTest::Unit::TestCase
|
4
|
+
|
5
|
+
def self.unused_db
|
6
|
+
@unused_db ||= Redis.new(:db => TestHelper.db_picker)
|
7
|
+
end
|
8
|
+
|
9
|
+
def setup
|
10
|
+
self.class.unused_db.flushdb
|
11
|
+
Redis::Autosuggest.redis = self.class.unused_db
|
12
|
+
@db = Redis::Autosuggest.db
|
13
|
+
@subs = Redis::Autosuggest.substrings
|
14
|
+
end
|
15
|
+
|
16
|
+
def test_adding_from_file
|
17
|
+
Redis::Autosuggest.add_from_file("test/text/example.txt")
|
18
|
+
assert @db.hgetall(Redis::Autosuggest.items).size == 3
|
19
|
+
assert @subs.keys.size == 10
|
20
|
+
end
|
21
|
+
|
22
|
+
def test_adding_with_score_from_file
|
23
|
+
Redis::Autosuggest.add_with_score_from_file("test/text/example_with_score.txt")
|
24
|
+
assert @db.hgetall(Redis::Autosuggest.items).size == 3
|
25
|
+
assert @subs.keys.size == 10
|
26
|
+
assert_equal 12, @subs.zscore("one", 0)
|
27
|
+
assert_equal 4, @subs.zscore("two", 1)
|
28
|
+
assert_equal 3, @subs.zscore("three", 2)
|
29
|
+
end
|
30
|
+
|
31
|
+
MiniTest::Unit.after_tests { self.unused_db.flushdb }
|
32
|
+
end
|
33
|
+
|
data/test/test_helper.rb
CHANGED
@@ -4,3 +4,15 @@ require 'debugger'
|
|
4
4
|
require 'redis'
|
5
5
|
require 'redis-namespace'
|
6
6
|
require 'redis-autosuggest'
|
7
|
+
|
8
|
+
|
9
|
+
class TestHelper
|
10
|
+
# get an unused db so that we can safely clear all keys
|
11
|
+
def self.db_picker
|
12
|
+
redis = Redis.new
|
13
|
+
(0..15).each do |i|
|
14
|
+
redis.select(i)
|
15
|
+
return i if redis.keys.empty?
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: redis-autosuggest
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 0.1.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -43,22 +43,6 @@ dependencies:
|
|
43
43
|
- - ~>
|
44
44
|
- !ruby/object:Gem::Version
|
45
45
|
version: 1.2.1
|
46
|
-
- !ruby/object:Gem::Dependency
|
47
|
-
name: debugger
|
48
|
-
requirement: !ruby/object:Gem::Requirement
|
49
|
-
none: false
|
50
|
-
requirements:
|
51
|
-
- - ~>
|
52
|
-
- !ruby/object:Gem::Version
|
53
|
-
version: 1.2.0
|
54
|
-
type: :development
|
55
|
-
prerelease: false
|
56
|
-
version_requirements: !ruby/object:Gem::Requirement
|
57
|
-
none: false
|
58
|
-
requirements:
|
59
|
-
- - ~>
|
60
|
-
- !ruby/object:Gem::Version
|
61
|
-
version: 1.2.0
|
62
46
|
- !ruby/object:Gem::Dependency
|
63
47
|
name: minitest
|
64
48
|
requirement: !ruby/object:Gem::Requirement
|
@@ -91,10 +75,16 @@ files:
|
|
91
75
|
- lib/redis-autosuggest.rb
|
92
76
|
- lib/redis/autosuggest.rb
|
93
77
|
- lib/redis/autosuggest/config.rb
|
78
|
+
- lib/redis/autosuggest/file.rb
|
79
|
+
- lib/redis/autosuggest/init.rb
|
80
|
+
- lib/redis/autosuggest/rails.rb
|
94
81
|
- lib/redis/autosuggest/version.rb
|
95
82
|
- redis-autosuggest.gemspec
|
96
83
|
- test/autosuggest_test.rb
|
84
|
+
- test/file_test.rb
|
97
85
|
- test/test_helper.rb
|
86
|
+
- test/text/example.txt
|
87
|
+
- test/text/example_with_score.txt
|
98
88
|
homepage: https://github.com/aphan/redis-autosuggest
|
99
89
|
licenses: []
|
100
90
|
post_install_message:
|
@@ -121,4 +111,7 @@ specification_version: 3
|
|
121
111
|
summary: Suggestions/autocompletions with Redis and Ruby
|
122
112
|
test_files:
|
123
113
|
- test/autosuggest_test.rb
|
114
|
+
- test/file_test.rb
|
124
115
|
- test/test_helper.rb
|
116
|
+
- test/text/example.txt
|
117
|
+
- test/text/example_with_score.txt
|