Class: Woods::Storage::MetadataStore::SQLite

Inherits:
Object
  • Object
show all
Includes:
Interface
Defined in:
lib/woods/storage/metadata_store.rb

Overview

SQLite-backed metadata store using the JSON1 extension.

Stores unit metadata as JSON in a single table with type indexing for efficient filtering. Uses upsert semantics for store operations.

Examples:

store = SQLite.new(":memory:")
store.store("User", { type: "model", namespace: "Admin" })
store.find("User")  # => { "type" => "model", "namespace" => "Admin" }

Instance Method Summary collapse

Constructor Details

#initialize(db_path = ':memory:') ⇒ SQLite

Returns a new instance of SQLite.

Parameters:

  • db_path (String) (defaults to: ':memory:')

    Path to the SQLite database file, or “:memory:” for in-memory



223
224
225
226
227
228
229
230
231
232
233
234
235
# File 'lib/woods/storage/metadata_store.rb', line 223

def initialize(db_path = ':memory:')
  begin
    require 'sqlite3'
  rescue LoadError
    raise Woods::ConfigurationError,
          'metadata_store: :sqlite requires the sqlite3 gem in your Gemfile. ' \
          "Add `gem 'sqlite3'` and re-bundle, or set " \
          "`config.metadata_store = :in_memory` if you don't need cross-process persistence."
  end
  @db = ::SQLite3::Database.new(db_path)
  @db.results_as_hash = true
  create_table
end

Instance Method Details

#countObject

See Also:



293
294
295
# File 'lib/woods/storage/metadata_store.rb', line 293

def count
  @db.get_first_value('SELECT COUNT(*) FROM units')
end

#delete(id) ⇒ Object

See Also:



288
289
290
# File 'lib/woods/storage/metadata_store.rb', line 288

def delete(id)
  @db.execute('DELETE FROM units WHERE id = ?', [id])
end

#find(id) ⇒ Object

See Also:



250
251
252
253
254
255
# File 'lib/woods/storage/metadata_store.rb', line 250

def find(id)
  row = @db.get_first_row('SELECT data FROM units WHERE id = ?', [id])
  return nil unless row

  JSON.parse(row['data'])
end

#find_batch(ids) ⇒ Object



258
259
260
261
262
263
264
265
266
# File 'lib/woods/storage/metadata_store.rb', line 258

def find_batch(ids)
  return {} if ids.empty?

  placeholders = Array.new(ids.size, '?').join(', ')
  rows = @db.execute("SELECT id, data FROM units WHERE id IN (#{placeholders})", ids)
  rows.to_h do |row|
    [row['id'], JSON.parse(row['data'])]
  end
end

#find_by_type(type) ⇒ Object



269
270
271
272
# File 'lib/woods/storage/metadata_store.rb', line 269

def find_by_type(type)
  rows = @db.execute('SELECT id, data FROM units WHERE type = ?', [type.to_s])
  rows.map { |row| parse_row(row) }
end

#search(query, fields: nil) ⇒ Object

See Also:



275
276
277
278
279
280
281
282
283
284
285
# File 'lib/woods/storage/metadata_store.rb', line 275

def search(query, fields: nil)
  if fields
    conditions = fields.map { "json_extract(data, '$.#{_1}') LIKE ?" }.join(' OR ')
    params = fields.map { "%#{query}%" }
    rows = @db.execute("SELECT id, data FROM units WHERE #{conditions}", params)
  else
    rows = @db.execute('SELECT id, data FROM units WHERE data LIKE ?', ["%#{query}%"])
  end

  rows.map { |row| parse_row(row) }
end

#store(id, metadata) ⇒ Object

See Also:



238
239
240
241
242
243
244
245
246
247
# File 'lib/woods/storage/metadata_store.rb', line 238

def store(id, )
  type = [:type] || ['type']
  data = JSON.generate()

  @db.execute(<<~SQL, [id, type.to_s, data, Time.now.iso8601])
    INSERT INTO units (id, type, data, updated_at) VALUES (?, ?, ?, ?)
    ON CONFLICT(id) DO UPDATE SET
      type = excluded.type, data = excluded.data, updated_at = excluded.updated_at
  SQL
end