litestack 0.4.1 → 0.4.2

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.
Files changed (66) hide show
  1. checksums.yaml +4 -4
  2. data/.standard.yml +3 -0
  3. data/BENCHMARKS.md +23 -7
  4. data/CHANGELOG.md +11 -0
  5. data/Gemfile +1 -7
  6. data/Gemfile.lock +92 -0
  7. data/README.md +120 -6
  8. data/ROADMAP.md +45 -0
  9. data/Rakefile +3 -1
  10. data/WHYLITESTACK.md +1 -1
  11. data/assets/litecache_metrics.png +0 -0
  12. data/assets/litedb_metrics.png +0 -0
  13. data/assets/litemetric_logo_teal.png +0 -0
  14. data/assets/litesearch_logo_teal.png +0 -0
  15. data/bench/bench.rb +17 -10
  16. data/bench/bench_cache_rails.rb +10 -13
  17. data/bench/bench_cache_raw.rb +17 -22
  18. data/bench/bench_jobs_rails.rb +18 -12
  19. data/bench/bench_jobs_raw.rb +17 -10
  20. data/bench/bench_queue.rb +4 -6
  21. data/bench/rails_job.rb +5 -7
  22. data/bench/skjob.rb +4 -4
  23. data/bench/uljob.rb +6 -6
  24. data/lib/action_cable/subscription_adapter/litecable.rb +5 -8
  25. data/lib/active_job/queue_adapters/litejob_adapter.rb +6 -8
  26. data/lib/active_record/connection_adapters/litedb_adapter.rb +65 -75
  27. data/lib/active_support/cache/litecache.rb +38 -41
  28. data/lib/generators/litestack/install/install_generator.rb +3 -3
  29. data/lib/generators/litestack/install/templates/database.yml +7 -1
  30. data/lib/litestack/liteboard/liteboard.rb +269 -149
  31. data/lib/litestack/litecable.rb +41 -37
  32. data/lib/litestack/litecable.sql.yml +22 -11
  33. data/lib/litestack/litecache.rb +79 -88
  34. data/lib/litestack/litecache.sql.yml +81 -22
  35. data/lib/litestack/litecache.yml +1 -1
  36. data/lib/litestack/litedb.rb +35 -40
  37. data/lib/litestack/litejob.rb +30 -29
  38. data/lib/litestack/litejobqueue.rb +63 -65
  39. data/lib/litestack/litemetric.rb +80 -92
  40. data/lib/litestack/litemetric.sql.yml +244 -234
  41. data/lib/litestack/litemetric_collector.sql.yml +38 -41
  42. data/lib/litestack/litequeue.rb +39 -41
  43. data/lib/litestack/litequeue.sql.yml +39 -31
  44. data/lib/litestack/litescheduler.rb +15 -15
  45. data/lib/litestack/litesearch/index.rb +93 -63
  46. data/lib/litestack/litesearch/model.rb +66 -65
  47. data/lib/litestack/litesearch/schema.rb +53 -56
  48. data/lib/litestack/litesearch/schema_adapters/backed_adapter.rb +46 -50
  49. data/lib/litestack/litesearch/schema_adapters/basic_adapter.rb +44 -35
  50. data/lib/litestack/litesearch/schema_adapters/contentless_adapter.rb +3 -6
  51. data/lib/litestack/litesearch/schema_adapters/standalone_adapter.rb +7 -9
  52. data/lib/litestack/litesearch/schema_adapters.rb +4 -9
  53. data/lib/litestack/litesearch.rb +6 -9
  54. data/lib/litestack/litesupport.rb +76 -86
  55. data/lib/litestack/railtie.rb +1 -1
  56. data/lib/litestack/version.rb +2 -2
  57. data/lib/litestack.rb +6 -4
  58. data/lib/railties/rails/commands/dbconsole.rb +11 -15
  59. data/lib/sequel/adapters/litedb.rb +16 -21
  60. data/lib/sequel/adapters/shared/litedb.rb +168 -168
  61. data/scripts/build_metrics.rb +91 -0
  62. data/scripts/test_cable.rb +30 -0
  63. data/scripts/test_job_retry.rb +33 -0
  64. data/scripts/test_metrics.rb +60 -0
  65. data/template.rb +2 -2
  66. metadata +101 -6
@@ -1,8 +1,7 @@
1
- require 'oj'
2
- require_relative './schema.rb'
1
+ require "oj"
2
+ require_relative "./schema"
3
3
 
4
4
  class Litesearch::Index
5
-
6
5
  DEFAULT_SEARCH_OPTIONS = {limit: 25, offset: 0}
7
6
 
8
7
  def initialize(db, name)
@@ -17,14 +16,14 @@ class Litesearch::Index
17
16
  # if they differ in tokenizer then rebuild if auto-rebuild is on (error otherwise)
18
17
  # if they differ in both then update the structure and rebuild if auto-rebuild is on (error otherwise)
19
18
  load_index(name) if exists?(name)
20
-
19
+
21
20
  if block_given?
22
21
  schema = Litesearch::Schema.new
23
22
  schema.schema[:name] = name
24
23
  yield schema
25
24
  schema.post_init
26
25
  # now that we have a schema object we need to check if we need to create or modify and existing index
27
- @db.transaction(:immediate) do
26
+ if @db.transaction_active?
28
27
  if exists?(name)
29
28
  load_index(name)
30
29
  do_modify(schema)
@@ -32,111 +31,141 @@ class Litesearch::Index
32
31
  do_create(schema)
33
32
  end
34
33
  prepare_statements
35
- end
36
- else
37
- if exists?(name)
38
- # an index already exists, load it from the database and return the index instance to the caller
39
- load_index(name)
40
- prepare_statements
41
34
  else
42
- raise "index does not exist and no schema was supplied"
35
+ @db.transaction(:immediate) do
36
+ if exists?(name)
37
+ load_index(name)
38
+ do_modify(schema)
39
+ else
40
+ do_create(schema)
41
+ end
42
+ prepare_statements
43
+ end
43
44
  end
45
+ elsif exists?(name)
46
+ load_index(name)
47
+ prepare_statements
48
+ # an index already exists, load it from the database and return the index instance to the caller
49
+ else
50
+ raise "index does not exist and no schema was supplied"
44
51
  end
45
52
  end
46
-
53
+
47
54
  def load_index(name)
48
55
  # we cannot use get_config_value here since the schema object is not created yet, should we allow something here?
49
- @schema = Litesearch::Schema.new(Oj.load(@db.get_first_value("SELECT v from #{name}_config where k = ?", :litesearch_schema.to_s))) rescue nil
56
+ @schema = begin
57
+ Litesearch::Schema.new(Oj.load(@db.get_first_value("SELECT v from #{name}_config where k = ?", :litesearch_schema.to_s)))
58
+ rescue
59
+ nil
60
+ end
50
61
  raise "index configuration not found, either corrupted or not a litesearch index!" if @schema.nil?
51
62
  self
52
63
  end
53
-
64
+
54
65
  def modify
55
66
  schema = Litesearch::Schema.new
56
67
  yield schema
57
68
  schema.schema[:name] = @schema.schema[:name]
58
69
  do_modify(schema)
59
70
  end
60
-
71
+
61
72
  def rebuild!
62
- @db.transaction(:immediate) do
63
- do_rebuild
73
+ if @db.transaction_active?
74
+ do_rebuild
75
+ else
76
+ @db.transaction(:immediate) { do_rebuild }
64
77
  end
65
78
  end
66
-
79
+
67
80
  def add(document)
68
81
  @stmts[:insert].execute!(document)
69
- return @db.last_insert_row_id
82
+ @db.last_insert_row_id
70
83
  end
71
-
84
+
72
85
  def remove(id)
73
86
  @stmts[:delete].execute!(id)
74
87
  end
75
-
88
+
76
89
  def count(term = nil)
77
90
  if term
78
91
  @stmts[:count].execute!(term)[0][0]
79
92
  else
80
- @stmts[:count_all].execute!()[0][0]
93
+ @stmts[:count_all].execute![0][0]
81
94
  end
82
95
  end
83
-
96
+
84
97
  # search options include
85
98
  # limit: how many records to return
86
99
  # offset: start from which record
87
100
  def search(term, options = {})
88
- result = []
89
101
  options = DEFAULT_SEARCH_OPTIONS.merge(options)
90
102
  rs = @stmts[:search].execute(term, options[:limit], options[:offset])
91
- if @db.results_as_hash
92
- rs.each_hash do |hash|
93
- result << hash
94
- end
103
+ generate_results(rs)
104
+ end
105
+
106
+ def similar(id, limit=10)
107
+ # pp term = @db.execute(@schema.sql_for(:similarity_query), id)
108
+ if @schema.schema[:tokenizer] == :trigram
109
+ # just use the normal similarity approach for now
110
+ # need to recondisder that for trigram indexes later
111
+ rs = @stmts[:similar].execute(id, limit)
95
112
  else
96
- result = rs.to_a
97
- end
98
- result
113
+ rs = @stmts[:similar].execute(id, limit)
114
+ end
115
+ generate_results(rs)
99
116
  end
100
-
117
+
101
118
  def clear!
102
119
  @stmts[:delete_all].execute!(id)
103
120
  end
104
-
121
+
105
122
  def drop!
106
123
  if @schema.get(:type) == :backed
107
124
  @db.execute_batch(@schema.sql_for(:drop_primary_triggers))
108
- if secondary_triggers_sql = @schema.sql_for(:create_secondary_triggers)
125
+ if @schema.sql_for(:create_secondary_triggers)
109
126
  @db.execute_batch(@schema.sql_for(:drop_secondary_triggers))
110
127
  end
111
128
  end
112
129
  @db.execute(@schema.sql_for(:drop))
113
130
  end
114
-
115
-
131
+
116
132
  private
117
-
133
+
134
+ def generate_results(rs)
135
+ result = []
136
+ if @db.results_as_hash
137
+ rs.each_hash do |hash|
138
+ result << hash
139
+ end
140
+ else
141
+ result = rs.to_a
142
+ end
143
+ result
144
+ end
145
+
118
146
  def exists?(name)
119
147
  @db.get_first_value("SELECT count(*) FROM SQLITE_MASTER WHERE name = ? AND type = 'table' AND (sql like '%fts5%' OR sql like '%FTS5%')", name.to_s) == 1
120
148
  end
121
149
 
122
150
  def prepare_statements
123
- stmt_names = [:insert, :delete, :delete_all, :drop, :count, :count_all, :search]
151
+ stmt_names = [:insert, :delete, :delete_all, :drop, :count, :count_all, :search, :similar]
124
152
  stmt_names.each do |stmt_name|
125
153
  @stmts[stmt_name] = @db.prepare(@schema.sql_for(stmt_name))
126
- end
154
+ end
127
155
  end
128
-
129
- def do_create(schema)
130
- @schema = schema
156
+
157
+ def do_create(schema)
158
+ @schema = schema
131
159
  @schema.clean
132
160
  # create index
133
161
  @db.execute(schema.sql_for(:create_index, true))
162
+ @db.execute_batch(schema.sql_for(:create_vocab_tables))
134
163
  # adjust ranking function
135
164
  @db.execute(schema.sql_for(:ranks, true))
136
165
  # create triggers (if any)
137
166
  if @schema.get(:type) == :backed
138
167
  @db.execute_batch(@schema.sql_for(:create_primary_triggers))
139
- if secondary_triggers_sql = @schema.sql_for(:create_secondary_triggers)
168
+ if (secondary_triggers_sql = @schema.sql_for(:create_secondary_triggers))
140
169
  @db.execute_batch(secondary_triggers_sql)
141
170
  end
142
171
  @db.execute(@schema.sql_for(:rebuild)) if @schema.get(:rebuild_on_create)
@@ -152,11 +181,11 @@ class Litesearch::Index
152
181
  requires_schema_change = false
153
182
  requires_trigger_change = false
154
183
  requires_rebuild = false
155
- if changes[:fields] || changes[:table] || changes[:tokenizer] || changes[:filter_column] || changes[:removed_fields_count] > 0# any change here will require a schema change
184
+ if changes[:fields] || changes[:table] || changes[:tokenizer] || changes[:filter_column] || changes[:removed_fields_count] > 0 # any change here will require a schema change
156
185
  requires_schema_change = true
157
186
  # only a change in tokenizer
158
187
  requires_rebuild = changes[:tokenizer] || new_schema.get(:rebuild_on_modify)
159
- requires_trigger_change = (changes[:table] || changes[:fields] || changes[:filter_column]) && @schema.get(:type) == :backed
188
+ requires_trigger_change = (changes[:table] || changes[:fields] || changes[:filter_column]) && @schema.get(:type) == :backed
160
189
  end
161
190
  if requires_schema_change
162
191
  # 1. enable schema editing
@@ -169,12 +198,12 @@ class Litesearch::Index
169
198
  @db.execute(new_schema.sql_for(:expand_data), changes[:extra_fields_count])
170
199
  @db.execute(new_schema.sql_for(:expand_docsize), changes[:extra_fields_count])
171
200
  @db.execute("PRAGMA WRITABLE_SCHEMA = RESET")
172
- # need to reprepare statements
201
+ # need to reprepare statements
173
202
  end
174
203
  if requires_trigger_change
175
204
  @db.execute_batch(new_schema.sql_for(:drop_primary_triggers))
176
205
  @db.execute_batch(new_schema.sql_for(:create_primary_triggers))
177
- if secondary_triggers_sql = new_schema.sql_for(:create_secondary_triggers)
206
+ if (secondary_triggers_sql = new_schema.sql_for(:create_secondary_triggers))
178
207
  @db.execute_batch(new_schema.sql_for(:drop_secondary_triggers))
179
208
  @db.execute_batch(secondary_triggers_sql)
180
209
  end
@@ -183,48 +212,49 @@ class Litesearch::Index
183
212
  @schema = new_schema
184
213
  set_config_value(:litesearch_schema, @schema.schema)
185
214
  prepare_statements
186
- #save_schema
215
+ # save_schema
187
216
  end
217
+ # update the weights if they changed
218
+ @db.execute(@schema.sql_for(:ranks, true)) if changes[:weights]
219
+ @db.execute_batch(@schema.sql_for(:create_vocab_tables))
188
220
  do_rebuild if requires_rebuild
189
- # update the weights if they changed
190
- @db.execute(@schema.sql_for(:ranks)) if changes[:weights]
191
221
  end
192
222
 
193
223
  def do_rebuild
194
224
  # remove any zero weight columns
195
225
  if @schema.get(:type) == :backed
196
226
  @db.execute_batch(@schema.sql_for(:drop_primary_triggers))
197
- if secondary_triggers_sql = @schema.sql_for(:create_secondary_triggers)
227
+ if (secondary_triggers_sql = @schema.sql_for(:create_secondary_triggers))
198
228
  @db.execute_batch(@schema.sql_for(:drop_secondary_triggers))
199
229
  end
200
230
  @db.execute(@schema.sql_for(:drop))
201
231
  @db.execute(@schema.sql_for(:create_index, true))
202
232
  @db.execute_batch(@schema.sql_for(:create_primary_triggers))
203
233
  @db.execute_batch(secondary_triggers_sql) if secondary_triggers_sql
204
- @db.execute(@schema.sql_for(:rebuild))
234
+ @db.execute(@schema.sql_for(:rebuild))
205
235
  elsif @schema.get(:type) == :standalone
206
236
  removables = []
207
- @schema.get(:fields).each_with_index{|f, i| removables << [f[0], i] if f[1][:weight] == 0 }
237
+ @schema.get(:fields).each_with_index { |f, i| removables << [f[0], i] if f[1][:weight] == 0 }
208
238
  removables.each do |col|
209
239
  @db.execute(@schema.sql_for(:drop_content_col, col[1]))
210
- @schema.get(:fields).delete(col[0])
240
+ @schema.get(:fields).delete(col[0])
211
241
  end
212
242
  @db.execute("PRAGMA WRITABLE_SCHEMA = TRUE")
213
243
  @db.execute(@schema.sql_for(:update_index), @schema.sql_for(:create_index, true))
214
- @db.execute(@schema.sql_for(:update_content_table), @schema.sql_for(:create_content_table, @schema.schema[:fields].count))
244
+ @db.execute(@schema.sql_for(:update_content_table), @schema.sql_for(:create_content_table, @schema.schema[:fields].count))
215
245
  @db.execute("PRAGMA WRITABLE_SCHEMA = RESET")
216
- @db.execute(@schema.sql_for(:rebuild))
246
+ @db.execute(@schema.sql_for(:rebuild))
217
247
  end
248
+ @db.execute_batch(@schema.sql_for(:create_vocab_tables))
218
249
  set_config_value(:litesearch_schema, @schema.schema)
219
- @db.execute(@schema.sql_for(:ranks, true))
220
- end
221
-
250
+ @db.execute(@schema.sql_for(:ranks, true))
251
+ end
252
+
222
253
  def get_config_value(key)
223
- Oj.load(@db.get_first_value(@schema.sql_for(:get_config_value), key.to_s)) #rescue nil
254
+ Oj.load(@db.get_first_value(@schema.sql_for(:get_config_value), key.to_s)) # rescue nil
224
255
  end
225
-
256
+
226
257
  def set_config_value(key, value)
227
258
  @db.execute(@schema.sql_for(:set_config_value), key.to_s, Oj.dump(value))
228
259
  end
229
-
230
260
  end
@@ -1,61 +1,75 @@
1
1
  module Litesearch::Model
2
-
3
2
  def self.included(klass)
4
3
  klass.include InstanceMethods
5
- klass.extend ClassMethods
4
+ klass.extend ClassMethods
6
5
  klass.attribute :search_rank, :float if klass.respond_to? :attribute
7
- if defined?(Sequel::Model) != nil && klass.ancestors.include?(Sequel::Model)
8
- klass.include Litesearch::Model::SequelInstanceMethods
9
- klass.extend Litesearch::Model::SequelClassMethods
10
- Sequel::Model.extend Litesearch::Model::BaseClassMethods
11
- elsif defined?(ActiveRecord::Base) != nil && klass.ancestors.include?(ActiveRecord::Base)
12
- klass.include Litesearch::Model::ActiveRecordInstanceMethods
13
- klass.extend Litesearch::Model::ActiveRecordClassMethods
14
- ActiveRecord::Base.extend Litesearch::Model::BaseClassMethods
6
+ if !defined?(Sequel::Model).nil? && klass.ancestors.include?(Sequel::Model)
7
+ klass.include Litesearch::Model::SequelInstanceMethods
8
+ klass.extend Litesearch::Model::SequelClassMethods
9
+ Sequel::Model.extend Litesearch::Model::BaseClassMethods
10
+ elsif !defined?(ActiveRecord::Base).nil? && klass.ancestors.include?(ActiveRecord::Base)
11
+ klass.include Litesearch::Model::ActiveRecordInstanceMethods
12
+ klass.extend Litesearch::Model::ActiveRecordClassMethods
13
+ ActiveRecord::Base.extend Litesearch::Model::BaseClassMethods
15
14
  end
16
15
  end
17
-
16
+
18
17
  module BaseClassMethods
19
18
  def search_models
20
19
  @@models ||= {}
21
- end
20
+ end
22
21
  end
23
-
22
+
24
23
  module InstanceMethods
25
-
24
+ def similar(limit=10)
25
+ conn = self.class.get_connection
26
+ idx = conn.search_index(self.class.send(:index_name))
27
+ r_a_h = conn.results_as_hash
28
+ conn.results_as_hash = true
29
+ rs = idx.similar(id, limit)
30
+ conn.results_as_hash = r_a_h
31
+ result = []
32
+ rs.each do |row|
33
+ obj = self.class.fetch_row(row["id"])
34
+ obj.search_rank = row["search_rank"]
35
+ result << obj
36
+ end
37
+ result
38
+ end
39
+
26
40
  end
27
-
28
- module ClassMethods
29
-
41
+
42
+ module ClassMethods
30
43
  def litesearch
31
44
  idx = get_connection.search_index(index_name) do |schema|
32
45
  schema.type :backed
33
46
  schema.table table_name.to_sym
34
47
  yield schema
35
48
  schema.post_init
36
- @schema = schema #save the schema
49
+ @schema = schema # save the schema
37
50
  end
38
- if defined?(Sequel::Model) != nil && self.ancestors.include?(Sequel::Model)
39
- Sequel::Model.search_models[self.name] = self
40
- elsif defined?(ActiveRecord::Base) != nil && self.ancestors.include?(ActiveRecord::Base)
41
- ActiveRecord::Base.search_models[self.name] = self
51
+ if !defined?(Sequel::Model).nil? && ancestors.include?(Sequel::Model)
52
+ Sequel::Model.search_models[name] = self
53
+ elsif !defined?(ActiveRecord::Base).nil? && ancestors.include?(ActiveRecord::Base)
54
+ ActiveRecord::Base.search_models[name] = self
42
55
  end
43
56
  idx
44
57
  end
45
-
58
+
46
59
  def rebuild_index!
47
60
  get_connection.search_index(index_name).rebuild!
48
61
  end
49
-
62
+
50
63
  def drop_index!
51
64
  get_connection.search_index(index_name).drop!
52
65
  end
53
-
54
- def search_all(term, options={})
66
+
67
+ def search_all(term, options = {})
55
68
  options[:offset] ||= 0
56
69
  options[:limit] ||= 25
57
- selects = []
58
- if models = options[:models]
70
+ options[:term] = term
71
+ selects = []
72
+ if (models = options[:models])
59
73
  models_hash = {}
60
74
  models.each do |model|
61
75
  models_hash[model.name] = model
@@ -63,13 +77,15 @@ module Litesearch::Model
63
77
  else
64
78
  models_hash = search_models
65
79
  end
80
+ # remove the models from the options hash before passing it ot the query
81
+ options.delete(:models)
66
82
  models_hash.each do |name, klass|
67
- selects << "SELECT '#{name}' AS model, rowid, -rank AS search_rank FROM #{index_name_for_table(klass.table_name)}('#{term}')"
83
+ selects << "SELECT '#{name}' AS model, rowid, -rank AS search_rank FROM #{index_name_for_table(klass.table_name)}(:term)"
68
84
  end
69
85
  conn = get_connection
70
- sql = selects.join(" UNION ") << " ORDER BY search_rank DESC LIMIT #{options[:limit]} OFFSET #{options[:offset]}"
86
+ sql = selects.join(" UNION ") << " ORDER BY search_rank DESC LIMIT :limit OFFSET :offset"
71
87
  result = []
72
- rs = conn.query(sql) #, options[:limit], options[:offset])
88
+ rs = conn.query(sql, options) # , options[:limit], options[:offset])
73
89
  rs.each_hash do |row|
74
90
  obj = models_hash[row["model"]].fetch_row(row["rowid"])
75
91
  obj.search_rank = row["search_rank"]
@@ -78,39 +94,32 @@ module Litesearch::Model
78
94
  rs.close
79
95
  result
80
96
  end
81
-
82
- # AR specific
83
-
84
- private
85
-
97
+
86
98
  def index_name
87
99
  "#{table_name}_search_idx"
88
100
  end
89
-
101
+
90
102
  def index_name_for_table(table)
91
103
  "#{table}_search_idx"
92
104
  end
93
-
105
+
94
106
  # create a new instance of self with the row as an argument
95
107
  def create_instance(row)
96
- self.new(row)
108
+ new(row)
97
109
  end
98
-
99
-
100
110
  end
101
-
102
- module ActiveRecordInstanceMethods;end
103
-
111
+
112
+ module ActiveRecordInstanceMethods; end
113
+
104
114
  module ActiveRecordClassMethods
105
-
106
115
  def get_connection
107
116
  connection.raw_connection
108
117
  end
109
-
118
+
110
119
  def fetch_row(id)
111
120
  find(id)
112
121
  end
113
-
122
+
114
123
  def search(term)
115
124
  self.select(
116
125
  "#{table_name}.*"
@@ -120,30 +129,25 @@ module Litesearch::Model
120
129
  "-#{index_name}.rank AS search_rank"
121
130
  ).order(
122
131
  Arel.sql("#{index_name}.rank")
123
- )
132
+ )
124
133
  end
125
134
 
126
- private
127
-
128
135
  def create_instance(row)
129
136
  instantiate(row)
130
137
  end
131
138
  end
132
-
139
+
133
140
  module SequelInstanceMethods
134
-
135
141
  def search_rank
136
142
  @values[:search_rank]
137
143
  end
138
-
144
+
139
145
  def search_rank=(rank)
140
146
  @values[:search_rank] = rank
141
147
  end
142
-
143
148
  end
144
-
149
+
145
150
  module SequelClassMethods
146
-
147
151
  def fetch_row(id)
148
152
  self[id]
149
153
  end
@@ -156,23 +160,20 @@ module Litesearch::Model
156
160
  dataset.select(
157
161
  Sequel.lit("#{table_name}.*, -#{index_name}.rank AS search_rank")
158
162
  ).inner_join(
159
- Sequel.lit("#{index_name}('#{term}') ON #{table_name}.id = #{index_name}.rowid AND rank != 0")
163
+ Sequel.lit("#{index_name}(:term) ON #{table_name}.id = #{index_name}.rowid AND rank != 0", {term: term})
160
164
  ).order(
161
- Sequel.lit('rank')
165
+ Sequel.lit("rank")
162
166
  )
163
167
  end
164
168
 
165
- private
166
-
167
169
  def create_instance(row)
168
170
  # we need to convert keys to symbols first!
169
171
  row.keys.each do |k|
170
- next if k.is_a? Symbol
171
- row[k.to_sym] = row[k]
172
- row.delete(k)
172
+ next if k.is_a? Symbol
173
+ row[k.to_sym] = row[k]
174
+ row.delete(k)
173
175
  end
174
- self.call(row)
176
+ call(row)
175
177
  end
176
178
  end
177
-
178
179
  end