documentrix 0.3.0 → 0.3.1

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 68c409476101e4632597c139494f3fc1fe67bc0af7a655e5e5b3e4ebbd58f58c
4
- data.tar.gz: fcd07ca7694b3fbed81c3a25f3fa9d9a675d120e5b82b43a13f5f71970012f3a
3
+ metadata.gz: c3ab97426ab9fbd4ec832a422a18d574f4c03b11f22391cbc6eded16b9ba0609
4
+ data.tar.gz: 991215fb26b4165a4b3562075d9742e3756218e2a24f3f32a550e232f59fb92f
5
5
  SHA512:
6
- metadata.gz: cf1e95f2d994bb130b89bff9d2e43062621b849b5d0f7fb4543092b1f491bab4b9dd9e51047c3dfb216090a4f9bc853c08ba35719dea7409c34a15758070fcba
7
- data.tar.gz: 59aa3347c07c2521f5661326a55d73733470ef30c62e86fc7d1ff53a4acb6cbc2da06e4c7814896b59ca16bdfab8933a7018f3f41ab30ce29593c5fac480f342
6
+ metadata.gz: d67e453e27428bcd8364349e988556af844caab5faa8a8edb3dcfba9650554b8dbfc66ad322a6f9f59d5ac64350d7006bf899a16b6c83f92909793301b6954d5
7
+ data.tar.gz: 49706d4984b27ee7b3d0f8ef8a9efec4a8e74d8bef78d78f15ed5c76f5bacc0faf20c36cef739aa78c1f68e5733696d16999bbd14228ae7be8e6cf9c9baf0a75
data/CHANGES.md CHANGED
@@ -1,5 +1,16 @@
1
1
  # Changes
2
2
 
3
+ ## 2026-05-18 v0.3.1
4
+
5
+ - Fixed scoping bugs in `clear_by_source` and `source_exist?` by implementing a
6
+ `key LIKE ?` constraint.
7
+ - Added regression tests for prefix isolation in
8
+ `spec/documentrix/documents/cache/sqlite_cache_spec.rb`.
9
+ - Introduced the `start_with_prefix` method in
10
+ `lib/documentrix/documents/cache/sqlite_cache.rb` to unify prefix patterns.
11
+ - Updated `tags`, `size`, `clear_all_with_prefix`, and `each` to utilize the
12
+ new `start_with_prefix` method.
13
+
3
14
  ## 2026-05-17 v0.3.0
4
15
 
5
16
  ### New Features
data/documentrix.gemspec CHANGED
@@ -1,9 +1,9 @@
1
1
  # -*- encoding: utf-8 -*-
2
- # stub: documentrix 0.3.0 ruby lib
2
+ # stub: documentrix 0.3.1 ruby lib
3
3
 
4
4
  Gem::Specification.new do |s|
5
5
  s.name = "documentrix".freeze
6
- s.version = "0.3.0".freeze
6
+ s.version = "0.3.1".freeze
7
7
 
8
8
  s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version=
9
9
  s.require_paths = ["lib".freeze]
@@ -114,7 +114,7 @@ class Documentrix::Documents::Cache::SQLiteCache
114
114
  result = Documentrix::Utils::Tags.new
115
115
  execute(%{
116
116
  SELECT DISTINCT(tags) FROM records WHERE key LIKE ?
117
- }, [ "#@prefix%" ]
117
+ }, [ start_with_prefix ]
118
118
  ).flatten.each do
119
119
  JSON(_1).each { |t| result.add(t) }
120
120
  end
@@ -126,7 +126,10 @@ class Documentrix::Documents::Cache::SQLiteCache
126
126
  #
127
127
  # @return [ Integer ] the count of records
128
128
  def size
129
- execute(%{SELECT COUNT(*) FROM records WHERE key LIKE ?}, [ "#@prefix%" ]).flatten.first
129
+ execute(
130
+ %{SELECT COUNT(*) FROM records WHERE key LIKE ?},
131
+ [ start_with_prefix ]
132
+ ).flatten.first
130
133
  end
131
134
 
132
135
  # The clear_for_tags method clears the cache for specific tags by deleting
@@ -154,7 +157,7 @@ class Documentrix::Documents::Cache::SQLiteCache
154
157
  #
155
158
  # @return [ Documentrix::Documents::RedisBackedMemoryCache ] self
156
159
  def clear_all_with_prefix
157
- execute(%{DELETE FROM records WHERE key LIKE ?}, [ "#@prefix%" ])
160
+ execute(%{DELETE FROM records WHERE key LIKE ?}, [ start_with_prefix ])
158
161
  self
159
162
  end
160
163
 
@@ -172,9 +175,21 @@ class Documentrix::Documents::Cache::SQLiteCache
172
175
  def clear_by_source(source, digest: nil, operator: ?=)
173
176
  operator = '!=' if operator != ?=
174
177
  if digest
175
- execute(%{DELETE FROM records WHERE source = ? AND digest #{operator} ? }, [ source, digest ])
178
+ execute(
179
+ %{
180
+ DELETE FROM records
181
+ WHERE key LIKE ? AND source = ? AND digest #{operator} ?
182
+ },
183
+ [ start_with_prefix, source, digest ]
184
+ )
176
185
  else
177
- execute(%{DELETE FROM records WHERE source = ?}, [ source ])
186
+ execute(
187
+ %{
188
+ DELETE FROM records
189
+ WHERE key LIKE ? AND source = ?
190
+ },
191
+ [ start_with_prefix, source ]
192
+ )
178
193
  end
179
194
  self
180
195
  end
@@ -194,9 +209,19 @@ class Documentrix::Documents::Cache::SQLiteCache
194
209
  def source_exist?(source, digest: nil, operator: ?=)
195
210
  operator = '!=' if operator != ?=
196
211
  if digest
197
- !!execute(%{SELECT 1 FROM records WHERE source = ? AND digest #{operator} ? }, [ source, digest ]).first
212
+ !!execute(
213
+ %{
214
+ SELECT 1 FROM records WHERE key LIKE ? AND source = ? AND digest #{operator} ?
215
+ },
216
+ [ start_with_prefix, source, digest ]
217
+ ).first
198
218
  else
199
- !!execute(%{SELECT 1 FROM records WHERE source = ?}, [ source ]).first
219
+ !!execute(
220
+ %{
221
+ SELECT 1 FROM records WHERE key LIKE ? AND source = ?
222
+ },
223
+ [ start_with_prefix, source ]
224
+ ).first
200
225
  end
201
226
  end
202
227
 
@@ -211,10 +236,9 @@ class Documentrix::Documents::Cache::SQLiteCache
211
236
  block or return enum_for(__method__)
212
237
 
213
238
  execute(%{
214
- SELECT DISTINCT source
215
- FROM records
239
+ SELECT DISTINCT source FROM records
216
240
  WHERE key LIKE ? AND source IS NOT NULL
217
- }, [ "#@prefix%" ]).each do |source,|
241
+ }, [ start_with_prefix ]).each do |source,|
218
242
  source = source.full? or next
219
243
 
220
244
  block.(source)
@@ -257,7 +281,7 @@ class Documentrix::Documents::Cache::SQLiteCache
257
281
  # cache.each do |key, value|
258
282
  # puts "#{key}: #{value}"
259
283
  # end
260
- def each(prefix: "#@prefix%", &block)
284
+ def each(prefix: start_with_prefix, &block)
261
285
  block or return enum_for(__method__, prefix:)
262
286
 
263
287
  execute(%{
@@ -315,7 +339,7 @@ class Documentrix::Documents::Cache::SQLiteCache
315
339
  SELECT key, tags, embedding_id
316
340
  FROM records
317
341
  WHERE key LIKE ?#{tags_where}
318
- }, [ "#@prefix%" ])
342
+ }, [ start_with_prefix ])
319
343
  if tags_filter
320
344
  records = records.select { |key, tags, embedding_id|
321
345
  (tags_filter & JSON(tags.to_s).to_a).size >= 1
@@ -368,6 +392,13 @@ class Documentrix::Documents::Cache::SQLiteCache
368
392
 
369
393
  private
370
394
 
395
+ # Returns the SQL LIKE pattern for records starting with the current prefix.
396
+ #
397
+ # @return [ String ] the prefix pattern used in SQL WHERE clauses
398
+ def start_with_prefix
399
+ "#@prefix%"
400
+ end
401
+
371
402
  # The execute method executes an SQL query on the database by calling the
372
403
  # \@database.execute method.
373
404
  #
@@ -1,6 +1,6 @@
1
1
  module Documentrix
2
2
  # Documentrix version
3
- VERSION = '0.3.0'
3
+ VERSION = '0.3.1'
4
4
  VERSION_ARRAY = VERSION.split('.').map(&:to_i) # :nodoc:
5
5
  VERSION_MAJOR = VERSION_ARRAY[0] # :nodoc:
6
6
  VERSION_MINOR = VERSION_ARRAY[1] # :nodoc:
@@ -250,6 +250,47 @@ describe Documentrix::Documents::SQLiteCache do
250
250
  ]
251
251
  end
252
252
 
253
+ describe 'Prefix Isolation' do
254
+ let(:cache2) { cache.dup }
255
+
256
+ before do
257
+ cache2.prefix = 'other-'
258
+
259
+ # Setup shared sources and tags across prefixes
260
+ cache['foo'] = test_value.merge(source: 'shared.txt', tags: %w[ a ])
261
+ cache2['bar'] = test_value.merge(source: 'shared.txt', tags: %w[ a ])
262
+ end
263
+
264
+ it 'does not leak clear_by_source' do
265
+ expect {
266
+ cache.clear_by_source('shared.txt')
267
+ }.to change { cache.size }.from(1).to(0)
268
+
269
+ expect(cache2.size).to eq 1
270
+ expect(cache2.key?('bar')).to be true
271
+ end
272
+
273
+ it 'does not leak source_exist?' do
274
+ # Ensure we are checking a source that ONLY exists in the other prefix
275
+ cache.clear_all_with_prefix
276
+ cache2['baz'] = test_value.merge(source: 'only-in-2.txt')
277
+
278
+ expect(cache.source_exist?('only-in-2.txt')).to be false
279
+ expect(cache2.source_exist?('only-in-2.txt')).to be true
280
+ end
281
+
282
+ it 'does not leak tags' do
283
+ cache.clear_all_with_prefix
284
+ cache2.clear_all_with_prefix
285
+
286
+ cache['foo'] = test_value.merge(tags: %w[ prefix1 ])
287
+ cache2['bar'] = test_value.merge(tags: %w[ prefix2 ])
288
+
289
+ expect(cache.tags.to_a).to match_array(['prefix1'])
290
+ expect(cache2.tags.to_a).to match_array(['prefix2'])
291
+ end
292
+ end
293
+
253
294
  describe '#find_records' do
254
295
  let(:needle) { [ 0.5 ] * 1_024 }
255
296
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: documentrix
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.3.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Florian Frank