litestack 0.4.1 → 0.4.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (67) hide show
  1. checksums.yaml +4 -4
  2. data/.standard.yml +3 -0
  3. data/BENCHMARKS.md +23 -7
  4. data/CHANGELOG.md +35 -0
  5. data/Gemfile +1 -7
  6. data/README.md +124 -6
  7. data/ROADMAP.md +45 -0
  8. data/Rakefile +3 -1
  9. data/WHYLITESTACK.md +1 -1
  10. data/assets/litecache_metrics.png +0 -0
  11. data/assets/litedb_metrics.png +0 -0
  12. data/assets/litemetric_logo_teal.png +0 -0
  13. data/assets/litesearch_logo_teal.png +0 -0
  14. data/bench/bench.rb +17 -10
  15. data/bench/bench_cache_rails.rb +45 -14
  16. data/bench/bench_cache_raw.rb +44 -28
  17. data/bench/bench_jobs_rails.rb +18 -12
  18. data/bench/bench_jobs_raw.rb +17 -10
  19. data/bench/bench_queue.rb +4 -6
  20. data/bench/rails_job.rb +5 -7
  21. data/bench/skjob.rb +4 -4
  22. data/bench/uljob.rb +6 -6
  23. data/bin/liteboard +2 -1
  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 +72 -84
  27. data/lib/active_support/cache/litecache.rb +61 -41
  28. data/lib/generators/litestack/install/install_generator.rb +3 -3
  29. data/lib/generators/litestack/install/templates/cable.yml +0 -3
  30. data/lib/generators/litestack/install/templates/database.yml +7 -1
  31. data/lib/litestack/liteboard/liteboard.rb +269 -149
  32. data/lib/litestack/litecable.rb +41 -37
  33. data/lib/litestack/litecable.sql.yml +22 -11
  34. data/lib/litestack/litecache.rb +118 -93
  35. data/lib/litestack/litecache.sql.yml +83 -22
  36. data/lib/litestack/litecache.yml +1 -1
  37. data/lib/litestack/litedb.rb +35 -40
  38. data/lib/litestack/litejob.rb +30 -29
  39. data/lib/litestack/litejobqueue.rb +63 -65
  40. data/lib/litestack/litemetric.rb +80 -92
  41. data/lib/litestack/litemetric.sql.yml +244 -234
  42. data/lib/litestack/litemetric_collector.sql.yml +38 -41
  43. data/lib/litestack/litequeue.rb +39 -41
  44. data/lib/litestack/litequeue.sql.yml +39 -31
  45. data/lib/litestack/litescheduler.rb +24 -18
  46. data/lib/litestack/litesearch/index.rb +93 -63
  47. data/lib/litestack/litesearch/model.rb +66 -65
  48. data/lib/litestack/litesearch/schema.rb +53 -56
  49. data/lib/litestack/litesearch/schema_adapters/backed_adapter.rb +46 -50
  50. data/lib/litestack/litesearch/schema_adapters/basic_adapter.rb +44 -35
  51. data/lib/litestack/litesearch/schema_adapters/contentless_adapter.rb +3 -6
  52. data/lib/litestack/litesearch/schema_adapters/standalone_adapter.rb +7 -9
  53. data/lib/litestack/litesearch/schema_adapters.rb +4 -9
  54. data/lib/litestack/litesearch.rb +6 -9
  55. data/lib/litestack/litesupport.rb +78 -87
  56. data/lib/litestack/railtie.rb +1 -1
  57. data/lib/litestack/version.rb +2 -2
  58. data/lib/litestack.rb +6 -4
  59. data/lib/railties/rails/commands/dbconsole.rb +16 -20
  60. data/lib/sequel/adapters/litedb.rb +16 -21
  61. data/lib/sequel/adapters/shared/litedb.rb +168 -168
  62. data/scripts/build_metrics.rb +91 -0
  63. data/scripts/test_cable.rb +30 -0
  64. data/scripts/test_job_retry.rb +33 -0
  65. data/scripts/test_metrics.rb +60 -0
  66. data/template.rb +2 -2
  67. metadata +115 -7
@@ -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