neighbor-redis 0.1.0 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +8 -0
- data/README.md +9 -3
- data/lib/neighbor/redis/index.rb +39 -19
- data/lib/neighbor/redis/version.rb +1 -1
- metadata +4 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 15f6a05f33831afcb282262440f8a4ccd73264c109ada3d5de1383af76cab304
|
4
|
+
data.tar.gz: 977b93e67d4b62db75837bf72dff4959a5b6aae8c940828273bee22563f172ad
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f6148b2717ddbb7cef8e9d762866b3c0d981be8e57e6a9e7c8a28f3a37b8402b5add0383e2676bc21b1c93dd687e10ae2fdaf9bfb31969adc80d7fb43def01a4
|
7
|
+
data.tar.gz: e7bc98f4436a8e48c84215e837461312bf0d610e9f5103a8d6f0ce39aa800970f5adb4c341ff691e21a059d30e4540eda4ef1df3d17f7d313923e6799860beb3
|
data/CHANGELOG.md
CHANGED
data/README.md
CHANGED
@@ -2,14 +2,14 @@
|
|
2
2
|
|
3
3
|
Nearest neighbor search for Ruby and Redis
|
4
4
|
|
5
|
-
[![Build Status](https://github.com/ankane/neighbor-redis/workflows/build/badge.svg
|
5
|
+
[![Build Status](https://github.com/ankane/neighbor-redis/actions/workflows/build.yml/badge.svg)](https://github.com/ankane/neighbor-redis/actions)
|
6
6
|
|
7
7
|
## Installation
|
8
8
|
|
9
9
|
First, [install RediSearch](https://redis.io/docs/stack/search/quick_start/). With Docker, use:
|
10
10
|
|
11
11
|
```sh
|
12
|
-
docker run -p 6379:6379 redis/redis-stack-server
|
12
|
+
docker run -p 6379:6379 redis/redis-stack-server
|
13
13
|
```
|
14
14
|
|
15
15
|
Add this line to your application’s Gemfile:
|
@@ -41,7 +41,7 @@ index.add(2, [2, 2, 2])
|
|
41
41
|
index.add(3, [1, 1, 2])
|
42
42
|
```
|
43
43
|
|
44
|
-
Note: IDs are stored and returned as strings (uses less total
|
44
|
+
Note: IDs are stored and returned as strings (uses less total memory)
|
45
45
|
|
46
46
|
Get the nearest neighbors to an item
|
47
47
|
|
@@ -118,6 +118,12 @@ Add multiple items
|
|
118
118
|
index.add_all(ids, embeddings)
|
119
119
|
```
|
120
120
|
|
121
|
+
Get an item
|
122
|
+
|
123
|
+
```ruby
|
124
|
+
index.find(id)
|
125
|
+
```
|
126
|
+
|
121
127
|
Remove an item
|
122
128
|
|
123
129
|
```ruby
|
data/lib/neighbor/redis/index.rb
CHANGED
@@ -52,7 +52,7 @@ module Neighbor
|
|
52
52
|
params = {
|
53
53
|
"TYPE" => @float64 ? "FLOAT64" : "FLOAT32",
|
54
54
|
"DIM" => @dimensions,
|
55
|
-
"DISTANCE_METRIC" => @distance_metric
|
55
|
+
"DISTANCE_METRIC" => @distance_metric
|
56
56
|
}.merge(create_params)
|
57
57
|
|
58
58
|
command = ["FT.CREATE", @index_name]
|
@@ -70,7 +70,7 @@ module Neighbor
|
|
70
70
|
# fix for invalid value for Float(): "-nan"
|
71
71
|
true
|
72
72
|
rescue => e
|
73
|
-
raise unless e.message.include?("
|
73
|
+
raise unless e.message.downcase.include?("unknown index name")
|
74
74
|
false
|
75
75
|
end
|
76
76
|
|
@@ -167,31 +167,51 @@ module Neighbor
|
|
167
167
|
|
168
168
|
def search_by_blob(blob, count)
|
169
169
|
resp = redis.call("FT.SEARCH", @index_name, "*=>[KNN #{count.to_i} @v $BLOB]", "PARAMS", "2", "BLOB", blob, "SORTBY", "__v_score", "DIALECT", "2")
|
170
|
-
|
170
|
+
resp.is_a?(Hash) ? parse_results_hash(resp) : parse_results_array(resp)
|
171
|
+
end
|
172
|
+
|
173
|
+
def parse_results_hash(resp)
|
174
|
+
prefix_length = nil
|
175
|
+
resp["results"].map do |result|
|
176
|
+
key = result["id"]
|
177
|
+
info = result["extra_attributes"]
|
178
|
+
prefix_length ||= find_prefix_length(key)
|
179
|
+
search_result(key, info, prefix_length)
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
def parse_results_array(resp)
|
171
184
|
prefix_length = nil
|
172
|
-
|
185
|
+
resp.shift.times.map do |i|
|
173
186
|
key, info = resp.shift(2)
|
174
187
|
info = info.each_slice(2).to_h
|
175
|
-
score = info["__v_score"].to_f
|
176
|
-
distance =
|
177
|
-
case @distance_metric
|
178
|
-
when "L2"
|
179
|
-
Math.sqrt(score)
|
180
|
-
when "IP"
|
181
|
-
(score * -1) + 1
|
182
|
-
else
|
183
|
-
score
|
184
|
-
end
|
185
|
-
|
186
188
|
prefix_length ||= find_prefix_length(key)
|
189
|
+
search_result(key, info, prefix_length)
|
190
|
+
end
|
191
|
+
end
|
187
192
|
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
193
|
+
def search_result(key, info, prefix_length)
|
194
|
+
score = info["__v_score"].to_f
|
195
|
+
distance = calculate_distance(score)
|
196
|
+
|
197
|
+
{
|
198
|
+
id: key[prefix_length..-1],
|
199
|
+
distance: distance
|
200
|
+
}
|
201
|
+
end
|
202
|
+
|
203
|
+
def calculate_distance(score)
|
204
|
+
case @distance_metric
|
205
|
+
when "L2"
|
206
|
+
Math.sqrt(score)
|
207
|
+
when "IP"
|
208
|
+
(score * -1) + 1
|
209
|
+
else
|
210
|
+
score
|
192
211
|
end
|
193
212
|
end
|
194
213
|
|
214
|
+
# can't just remove @prefix since may be an alias
|
195
215
|
def find_prefix_length(key)
|
196
216
|
key[@global_prefix.length..-1].index(":") + @global_prefix.length + 1
|
197
217
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: neighbor-redis
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Andrew Kane
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2024-10-23 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: redis-client
|
@@ -51,14 +51,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
51
51
|
requirements:
|
52
52
|
- - ">="
|
53
53
|
- !ruby/object:Gem::Version
|
54
|
-
version: '
|
54
|
+
version: '3.1'
|
55
55
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
56
56
|
requirements:
|
57
57
|
- - ">="
|
58
58
|
- !ruby/object:Gem::Version
|
59
59
|
version: '0'
|
60
60
|
requirements: []
|
61
|
-
rubygems_version: 3.
|
61
|
+
rubygems_version: 3.5.16
|
62
62
|
signing_key:
|
63
63
|
specification_version: 4
|
64
64
|
summary: Nearest neighbor search for Ruby and Redis
|