Class: Phronomy::VectorStore::RedisSearch
- Defined in:
- lib/phronomy/vector_store/redis_search.rb
Overview
Redis-backed vector store using the RediSearch module (FT.* commands).
Requires:
- The +redis+ gem (add to your Gemfile)
- A Redis server with the RediSearch (RedisSearch) module enabled (or Redis Stack which bundles RediSearch)
Vectors are stored as FLOAT32 binary blobs in Redis Hash fields and searched using the KNN approximate-nearest-neighbour algorithm.
Instance Method Summary collapse
- #add(id:, embedding:, metadata: {}) ⇒ Object
- #clear ⇒ Object
-
#initialize(redis:, index_name: "phronomy_vectors", dimension: nil) ⇒ RedisSearch
constructor
A new instance of RedisSearch.
- #remove(id:) ⇒ Object
-
#search(query_embedding:, k: 5) ⇒ Array<Hash>
Sorted by descending similarity score.
Constructor Details
#initialize(redis:, index_name: "phronomy_vectors", dimension: nil) ⇒ RedisSearch
Returns a new instance of RedisSearch.
33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
# File 'lib/phronomy/vector_store/redis_search.rb', line 33 def initialize(redis:, index_name: "phronomy_vectors", dimension: nil) begin require "redis" rescue LoadError raise LoadError, "redis gem is required for Phronomy::VectorStore::RedisSearch. " \ "Add `gem 'redis'` to your Gemfile." end @redis = redis @index_name = index_name @dimension = dimension @index_created = false @mutex = Mutex.new end |
Instance Method Details
#add(id:, embedding:, metadata: {}) ⇒ Object
51 52 53 54 55 56 57 58 59 60 61 62 63 |
# File 'lib/phronomy/vector_store/redis_search.rb', line 51 def add(id:, embedding:, metadata: {}) # Establish expected dimension on first add (not race-free for concurrent # first adds), then validate, then create/reuse the index. @dimension ||= .size (, @dimension) ensure_index!(@dimension) @redis.call( "HSET", "#{DOC_PREFIX}#{id}", "embedding", pack_vector(), "metadata", .to_json ) self end |
#clear ⇒ Object
95 96 97 98 99 100 101 102 103 104 105 |
# File 'lib/phronomy/vector_store/redis_search.rb', line 95 def clear @mutex.synchronize do begin @redis.call("FT.DROPINDEX", @index_name, "DD") rescue => e raise unless e..to_s.include?("Unknown Index name") end @index_created = false end self end |
#remove(id:) ⇒ Object
90 91 92 93 |
# File 'lib/phronomy/vector_store/redis_search.rb', line 90 def remove(id:) @redis.call("DEL", "#{DOC_PREFIX}#{id}") self end |
#search(query_embedding:, k: 5) ⇒ Array<Hash>
Returns sorted by descending similarity score.
68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 |
# File 'lib/phronomy/vector_store/redis_search.rb', line 68 def search(query_embedding:, k: 5) # search never establishes dimension. If dimension is unknown and the # index has not been created yet, there are no documents to return. return [] if @dimension.nil? && !@index_created (, @dimension) ensure_index!(@dimension) k_safe = Integer(k) blob = pack_vector() raw = @redis.call( "FT.SEARCH", @index_name, "*=>[KNN #{k_safe} @embedding $BLOB AS score]", "PARAMS", 2, "BLOB", blob, "SORTBY", "score", "RETURN", 2, "score", "metadata", "DIALECT", 2 ) parse_results(raw) end |