Skip to content

Commit c579a87

Browse files
authored
Merge pull request #117 from gabriel-curtino/master
Use rowid instead id, allowing custom primary keys
2 parents d506c51 + 09452a6 commit c579a87

10 files changed

+155
-67
lines changed

lib/litestack/litesearch/index.rb

+4-4
Original file line numberDiff line numberDiff line change
@@ -103,21 +103,21 @@ def search(term, options = {})
103103
generate_results(rs)
104104
end
105105

106-
def similar(id, limit = 10)
106+
def similar(rowid, limit = 10)
107107
# pp term = @db.execute(@schema.sql_for(:similarity_query), id)
108108
rs = if @schema.schema[:tokenizer] == :trigram
109109
# just use the normal similarity approach for now
110110
# need to recondisder that for trigram indexes later
111-
@stmts[:similar].execute(id, limit) # standard:disable Style/IdenticalConditionalBranches
111+
@stmts[:similar].execute(rowid, limit) # standard:disable Style/IdenticalConditionalBranches
112112
else
113-
@stmts[:similar].execute(id, limit) # standard:disable Style/IdenticalConditionalBranches
113+
@stmts[:similar].execute(rowid, limit) # standard:disable Style/IdenticalConditionalBranches
114114
end
115115

116116
generate_results(rs)
117117
end
118118

119119
def clear!
120-
@stmts[:delete_all].execute!(id)
120+
@stmts[:delete_all].execute!(rowid)
121121
end
122122

123123
def drop!

lib/litestack/litesearch/model.rb

+48-20
Original file line numberDiff line numberDiff line change
@@ -24,24 +24,18 @@ def search_models
2424
end
2525

2626
module InstanceMethods
27+
# rowid = id by default
28+
def rowid
29+
id
30+
end
31+
2732
def similar(limit = 10)
28-
conn = self.class.get_connection
29-
idx = conn.search_index(self.class.send(:index_name))
30-
r_a_h = conn.results_as_hash
31-
conn.results_as_hash = true
32-
rs = idx.similar(id, limit)
33-
conn.results_as_hash = r_a_h
34-
result = []
35-
rs.each do |row|
36-
obj = self.class.fetch_row(row["id"])
37-
obj.search_rank = row["search_rank"]
38-
result << obj
39-
end
40-
result
33+
self.class.similar(rowid, limit)
4134
end
4235
end
4336

4437
module ClassMethods
38+
4539
def litesearch
4640
# it is possible that this code is running when there is no table created yet
4741
if !defined?(ActiveRecord::Base).nil? && ancestors.include?(ActiveRecord::Base)
@@ -90,6 +84,22 @@ def drop_index!
9084
get_connection.search_index(index_name).drop!
9185
end
9286

87+
def similar(rowid, limit = 10)
88+
conn = get_connection
89+
idx = conn.search_index(send(:index_name))
90+
r_a_h = conn.results_as_hash
91+
conn.results_as_hash = true
92+
rs = idx.similar(rowid, limit)
93+
conn.results_as_hash = r_a_h
94+
result = []
95+
rs.each do |row|
96+
obj = fetch_row(row["rowid"])
97+
obj.search_rank = row["search_rank"]
98+
result << obj
99+
end
100+
result
101+
end
102+
93103
def search_all(term, options = {})
94104
options[:offset] ||= 0
95105
options[:limit] ||= 25
@@ -136,6 +146,7 @@ def create_instance(row)
136146
end
137147

138148
module ActiveRecordSchemaMethods
149+
139150
attr_accessor :model_class
140151

141152
def field(name, attributes = {})
@@ -161,17 +172,26 @@ def field(name, attributes = {})
161172
def allowed_attributes
162173
super + [:polymorphic, :as, :action_text]
163174
end
175+
164176
end
165177

166-
module ActiveRecordInstanceMethods; end
178+
module ActiveRecordInstanceMethods
179+
def rowid
180+
self.class.rowid(id)
181+
end
182+
end
167183

168184
module ActiveRecordClassMethods
169185
def get_connection
170186
connection.raw_connection
171187
end
172188

173-
def fetch_row(id)
174-
find(id)
189+
def rowid(id)
190+
where(primary_key => id).limit(1).pluck(:rowid)&.first
191+
end
192+
193+
def fetch_row(rowid)
194+
find_by("rowid = ?", rowid)
175195
end
176196

177197
def search(term)
@@ -185,7 +205,7 @@ def search(term)
185205
self.select(
186206
"#{table_name}.*"
187207
).joins(
188-
"INNER JOIN #{index_name} ON #{table_name}.id = #{index_name}.rowid AND rank != 0 AND #{index_name} MATCH ", Arel.sql("'#{term}'")
208+
"INNER JOIN #{index_name} ON #{table_name}.rowid = #{index_name}.rowid AND rank != 0 AND #{index_name} MATCH ", Arel.sql("'#{term}'")
189209
).select(
190210
"-#{index_name}.rank AS search_rank"
191211
).order(
@@ -199,6 +219,10 @@ def create_instance(row)
199219
end
200220

201221
module SequelInstanceMethods
222+
def rowid
223+
self.class.rowid(id)
224+
end
225+
202226
def search_rank
203227
@values[:search_rank]
204228
end
@@ -209,8 +233,12 @@ def search_rank=(rank)
209233
end
210234

211235
module SequelClassMethods
212-
def fetch_row(id)
213-
self[id]
236+
def rowid(id)
237+
where(primary_key => id).get(:rowid)
238+
end
239+
240+
def fetch_row(rowid)
241+
self[rowid] # where(Sequel.lit("rowid = ?", rowid)).first
214242
end
215243

216244
def get_connection
@@ -221,7 +249,7 @@ def search(term)
221249
dataset.select(
222250
Sequel.lit("#{table_name}.*, -#{index_name}.rank AS search_rank")
223251
).inner_join(
224-
Sequel.lit("#{index_name}(:term) ON #{table_name}.id = #{index_name}.rowid AND rank != 0", {term: term})
252+
Sequel.lit("#{index_name}(:term) ON #{table_name}.rowid = #{index_name}.rowid AND rank != 0", {term: term})
225253
).order(
226254
Sequel.lit("rank")
227255
)

lib/litestack/litesearch/schema.rb

+5-1
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,10 @@ def table(table_name)
5252
@schema[:table] = table_name
5353
end
5454

55+
def primary_key(new_primary_key)
56+
@schema[:primary_key] = new_primary_key
57+
end
58+
5559
def fields(field_names)
5660
field_names.each { |f| field f }
5761
end
@@ -185,7 +189,7 @@ def clean
185189
end
186190

187191
def allowed_attributes
188-
[:weight, :col, :target, :source, :conditions, :reference]
192+
[:weight, :col, :target, :source, :conditions, :reference, :primary_key]
189193
end
190194
end
191195

lib/litestack/litesearch/schema_adapters/backed_adapter.rb

+9-8
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ def create_primary_triggers_sql(active = false)
4646
DELETE FROM #{name} WHERE rowid = NEW.rowid;
4747
END;
4848
CREATE TRIGGER #{name}_delete AFTER DELETE ON #{table} BEGIN
49-
DELETE FROM #{name} WHERE rowid = OLD.id;
49+
DELETE FROM #{name} WHERE rowid = OLD.rowid;
5050
END;
5151
SQL
5252
end
@@ -59,10 +59,10 @@ def drop_secondary_trigger_poly_sql(target_table, target_col, col)
5959
"DROP TRIGGER IF EXISTS #{target_table}_#{target_col}_#{name}_update;"
6060
end
6161

62-
def create_secondary_trigger_sql(target_table, target_col, col)
62+
def create_secondary_trigger_sql(target_table, target_col, col, primary_key)
6363
<<~SQL
6464
CREATE TRIGGER IF NOT EXISTS #{target_table}_#{target_col}_#{col}_#{name}_update AFTER UPDATE OF #{target_col} ON #{target_table} BEGIN
65-
#{rebuild_sql} AND #{table}.#{col} = NEW.id;
65+
#{rebuild_sql} AND #{table}.#{col} = NEW.#{primary_key};
6666
END;
6767
SQL
6868
end
@@ -98,7 +98,7 @@ def create_secondary_triggers_sql
9898
@schema[:fields].each do |name, field|
9999
if field[:trigger_sql]
100100
if field[:col]
101-
sql << create_secondary_trigger_sql(field[:target_table], field[:target_col], field[:col])
101+
sql << create_secondary_trigger_sql(field[:target_table], field[:target_col], field[:col], field[:primary_key])
102102
elsif field[:source]
103103
sql << create_secondary_trigger_poly_sql(field[:target_table], field[:target_col], name, field[:conditions])
104104
end
@@ -108,18 +108,19 @@ def create_secondary_triggers_sql
108108
end
109109

110110
def rebuild_sql
111-
"INSERT OR REPLACE INTO #{name}(rowid, #{active_field_names.join(", ")}) SELECT #{table}.id, #{select_cols_sql} FROM #{joins_sql} #{filter_sql}"
111+
"INSERT OR REPLACE INTO #{name}(rowid, #{active_field_names.join(", ")}) SELECT #{table}.rowid, #{select_cols_sql} FROM #{joins_sql} #{filter_sql}"
112112
end
113113

114114
def enrich_schema
115115
@schema[:fields].each do |name, field|
116116
if field[:target] && !field[:target].start_with?("#{table}.")
117117
field[:target] = field[:target].downcase
118118
target_table, target_col = field[:target].split(".")
119+
field[:primary_key] = :id unless field[:primary_key]
119120
field[:col] = :"#{name}_id" unless field[:col]
120121
field[:target_table] = target_table.to_sym
121122
field[:target_col] = target_col.to_sym
122-
field[:sql] = "(SELECT #{field[:target_col]} FROM #{field[:target_table]} WHERE id = NEW.#{field[:col]})"
123+
field[:sql] = "(SELECT #{field[:target_col]} FROM #{field[:target_table]} WHERE #{field[:primary_key]} = NEW.#{field[:col]})"
123124
field[:trigger_sql] = true # create_secondary_trigger_sql(field[:target_table], field[:target_col], field[:col])
124125
field[:target_table_alias] = "#{field[:target_table]}_#{name}"
125126
elsif field[:source]
@@ -167,9 +168,9 @@ def joins_sql
167168
join_table = +""
168169
join_table << "#{field[:target_table]} AS #{field[:target_table_alias]} ON "
169170
if field[:col]
170-
join_table << "#{field[:target_table_alias]}.id = #{@schema[:table]}.#{field[:col]}" if field[:col]
171+
join_table << "#{field[:target_table_alias]}.#{field[:primary_key]} = #{@schema[:table]}.#{field[:col]}" if field[:col]
171172
elsif field[:source]
172-
join_table << "#{field[:target_table_alias]}.#{field[:reference]} = #{@schema[:table]}.id"
173+
join_table << "#{field[:target_table_alias]}.#{field[:reference]} = #{@schema[:table]}.rowid"
173174
if field[:conditions]
174175
join_table << " AND "
175176
join_table << field[:conditions].collect { |k, v| "#{field[:target_table_alias]}.#{k} = '#{v}'" }.join(" AND ")

lib/litestack/litesearch/schema_adapters/basic_adapter.rb

+11-7
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,10 @@ def table
1414
@schema[:table]
1515
end
1616

17+
def primary_key
18+
@schema[:primary_key] || :id
19+
end
20+
1721
def fields
1822
@schema[:fields]
1923
end
@@ -78,8 +82,8 @@ def sql_for(method, *args)
7882
def generate_sql
7983
@sql[:create_index] = :create_index_sql
8084
@sql[:create_vocab_tables] = :create_vocab_tables_sql
81-
@sql[:insert] = "INSERT OR REPLACE INTO #{name}(rowid, #{active_col_names_sql}) VALUES (:id, #{active_col_names_var_sql}) RETURNING rowid"
82-
@sql[:delete] = "DELETE FROM #{name} WHERE rowid = :id"
85+
@sql[:insert] = "INSERT OR REPLACE INTO #{name}(rowid, #{active_col_names_sql}) VALUES (:rowid, #{active_col_names_var_sql}) RETURNING rowid"
86+
@sql[:delete] = "DELETE FROM #{name} WHERE rowid = :rowid"
8387
@sql[:count] = "SELECT count(*) FROM #{name}(:term)"
8488
@sql[:count_all] = "SELECT count(*) FROM #{name}"
8589
@sql[:delete_all] = "DELETE FROM #{name}"
@@ -89,11 +93,11 @@ def generate_sql
8993
@sql[:ranks] = :ranks_sql
9094
@sql[:set_config_value] = "INSERT OR REPLACE INTO #{name}_config(k, v) VALUES (:key, :value)"
9195
@sql[:get_config_value] = "SELECT v FROM #{name}_config WHERE k = :key"
92-
@sql[:search] = "SELECT rowid AS id, -rank AS search_rank FROM #{name}(:term) WHERE rank !=0 ORDER BY rank LIMIT :limit OFFSET :offset"
93-
@sql[:similarity_terms] = "SELECT DISTINCT term FROM #{name}_instance WHERE doc = :id AND FLOOR(term) IS NULL AND LENGTH(term) > 2 AND NOT instr(term, ' ') AND NOT instr(term, '-') AND NOT instr(term, ':') AND NOT instr(term, '#') AND NOT instr(term, '_') LIMIT 15"
94-
@sql[:similarity_query] = "SELECT group_concat('\"' || term || '\"', ' OR ') FROM #{name}_row WHERE term IN (#{@sql[:similarity_terms]})"
95-
@sql[:similarity_search] = "SELECT rowid AS id, -rank AS search_rank FROM #{name}(:term) WHERE rowid != :id ORDER BY rank LIMIT :limit"
96-
@sql[:similar] = "SELECT rowid AS id, -rank AS search_rank FROM #{name} WHERE #{name} = (#{@sql[:similarity_query]}) AND rowid != :id ORDER BY rank LIMIT :limit"
96+
@sql[:search] = "SELECT rowid, -rank AS search_rank FROM #{name}(:term) WHERE rank !=0 ORDER BY rank LIMIT :limit OFFSET :offset"
97+
@sql[:similarity_terms] = "SELECT DISTINCT term FROM #{name}_instance WHERE doc = :rowid AND FLOOR(term) IS NULL AND LENGTH(term) > 2 AND NOT instr(term, ' ') AND NOT instr(term, '-') AND NOT instr(term, ':') AND NOT instr(term, '#') AND NOT instr(term, '_') LIMIT 15"
98+
@sql[:similarity_query] = "SELECT group_concat(term, ' OR ') FROM #{name}_row WHERE term IN (#{@sql[:similarity_terms]})"
99+
@sql[:similarity_search] = "SELECT rowid, -rank AS search_rank FROM #{name}(:term) WHERE rowid != :rowid ORDER BY rank LIMIT :limit"
100+
@sql[:similar] = "SELECT rowid, -rank AS search_rank FROM #{name} WHERE #{name} = (#{@sql[:similarity_query]}) AND rowid != :rowid ORDER BY rank LIMIT :limit"
97101
@sql[:update_index] = "UPDATE sqlite_schema SET sql = :sql WHERE name = '#{name}'"
98102
@sql[:update_content_table] = "UPDATE sqlite_schema SET sql = :sql WHERE name = '#{name}_content'"
99103
end

lib/litestack/litesearch/schema_adapters/standalone_adapter.rb

+3-3
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,11 @@ def generate_sql
55
@sql[:adjust_temp_content] = "UPDATE sqlite_schema SET sql (SELECT sql FROM sqlite_schema WHERE name = '#{name}_content') WHERE name = #{name}_content_temp"
66
@sql[:restore_content] = "ALTER TABLE #{name}_content_temp RENAME TO #{name}_content"
77
@sql[:rebuild] = "INSERT INTO #{name}(#{name}) VALUES ('rebuild')"
8-
@sql[:similar] = "SELECT rowid AS id, *, -rank AS search_rank FROM #{name} WHERE #{name} = (#{@sql[:similarity_query]}) AND rowid != :id ORDER BY rank LIMIT :limit"
8+
@sql[:similar] = "SELECT rowid, *, -rank AS search_rank FROM #{name} WHERE #{name} = (#{@sql[:similarity_query]}) AND rowid != :rowid ORDER BY rank LIMIT :limit"
99
@sql[:drop_content_table] = "DROP TABLE #{name}_content"
1010
@sql[:drop_content_col] = :drop_content_col_sql
1111
@sql[:create_content_table] = :create_content_table_sql
12-
@sql[:search] = "SELECT rowid AS id, *, -rank AS search_rank FROM #{name}(:term) WHERE rank !=0 ORDER BY rank LIMIT :limit OFFSET :offset"
12+
@sql[:search] = "SELECT rowid, *, -rank AS search_rank FROM #{name}(:term) WHERE rank !=0 ORDER BY rank LIMIT :limit OFFSET :offset"
1313
end
1414

1515
private
@@ -26,6 +26,6 @@ def drop_content_col_sql(col_index)
2626
def create_content_table_sql(count)
2727
cols = []
2828
count.times { |i| cols << "c#{i}" }
29-
"CREATE TABLE #{name}_content(id INTEGER PRIMARY KEY, #{cols.join(", ")})"
29+
"CREATE TABLE #{name}_content(rowid INTEGER PRIMARY KEY, #{cols.join(", ")})"
3030
end
3131
end

0 commit comments

Comments
 (0)