pg_fts 0.0.0 → 0.2.0

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.
@@ -0,0 +1,314 @@
1
+ # one document, many sources
2
+ class PG::FTS::Index::OneToMany
3
+ attr_reader :table, :fields, :catalog, :document, :key, :foreign_key, :name
4
+ alias source table
5
+
6
+ include PG::FTS::Index
7
+ include PG::FTS::Naming
8
+ include PG::FTS::TSVector
9
+
10
+ # rubocop:disable Metrics/ParameterLists
11
+ def initialize(table, *fields,
12
+ document: nil, key: nil, foreign_key: nil,
13
+ catalog: nil,
14
+ as: nil)
15
+ @table = table
16
+ @fields = fields
17
+ @catalog = catalog || PG::FTS.catalog
18
+ @document = document || fail(ArgumentError, ':document required')
19
+ @key = key || :id
20
+ @foreign_key = foreign_key || :"#{document}_id"
21
+ @name = as
22
+ end
23
+ # rubocop:enable Metrics/ParameterLists
24
+
25
+ def build_query
26
+ <<-SQL.gsub(/^ {4}/, '')
27
+ INSERT INTO "#{PG::FTS.table}" (
28
+ "document_id",
29
+ "document_table",
30
+ "source_id",
31
+ "source_table",
32
+ "tsv")
33
+ SELECT "#{document}"."#{key}",
34
+ '#{document}',
35
+ "#{source}"."#{key}",
36
+ '#{source}',
37
+ #{ts_vector(source)}
38
+ FROM "#{document}", "#{source}"
39
+ WHERE "#{document}"."#{key}" = "#{source}"."#{foreign_key}";
40
+ SQL
41
+ end
42
+
43
+ def on_source_insert_query
44
+ <<-SQL.gsub(/^ {4}/, '')
45
+ INSERT INTO "#{PG::FTS.table}" (
46
+ "document_id",
47
+ "document_table",
48
+ "source_id",
49
+ "source_table",
50
+ "tsv")
51
+ SELECT
52
+ "#{document}"."#{key}",
53
+ '#{document}',
54
+ NEW."#{key}",
55
+ '#{source}',
56
+ #{ts_vector}
57
+ FROM "#{document}"
58
+ WHERE "#{document}"."#{key}" = NEW."#{foreign_key}";
59
+ SQL
60
+ end
61
+
62
+ def on_source_insert_procedure
63
+ <<-SQL.gsub(/^ {4}/, '')
64
+ CREATE FUNCTION "#{on_source_insert_procedure_name}"()
65
+ RETURNS TRIGGER AS $$
66
+ BEGIN
67
+ IF NEW."#{foreign_key}" IS NULL THEN
68
+ RETURN NEW;
69
+ END IF;
70
+ #{on_source_insert_query.gsub(/^/, ' ')}
71
+ RETURN NEW;
72
+ END;
73
+ $$ LANGUAGE plpgsql;
74
+ SQL
75
+ end
76
+
77
+ def on_source_update_query
78
+ <<-SQL.gsub(/^ {4}/, '')
79
+ UPDATE "#{PG::FTS.table}"
80
+ SET
81
+ "tsv" = (#{ts_vector}),
82
+ "document_table" = '#{document}',
83
+ "document_id" = NEW."#{foreign_key}"
84
+ FROM "#{document}"
85
+ WHERE "#{document}"."#{key}" = NEW."#{foreign_key}"
86
+ AND "#{PG::FTS.table}"."source_id" = OLD."#{key}"
87
+ AND "#{PG::FTS.table}"."source_table" = '#{source}'
88
+ AND "#{PG::FTS.table}"."document_id" = OLD."#{foreign_key}"
89
+ AND "#{PG::FTS.table}"."document_table" = '#{document}';
90
+ SQL
91
+ end
92
+
93
+ def on_source_update_procedure
94
+ <<-SQL.gsub(/^ {4}/, '')
95
+ CREATE FUNCTION "#{on_source_update_procedure_name}"()
96
+ RETURNS TRIGGER AS $$
97
+ BEGIN
98
+ #{on_source_update_query.gsub(/^/, ' ')}
99
+ IF NOT FOUND THEN
100
+ #{on_source_insert_query.gsub(/^/, ' ')}
101
+ IF NOT FOUND THEN
102
+ #{on_source_delete_query.gsub(/^/, ' ')}
103
+ END IF;
104
+ RETURN NEW;
105
+ END IF;
106
+ RETURN NEW;
107
+ END;
108
+ $$ LANGUAGE plpgsql;
109
+ SQL
110
+ end
111
+
112
+ def on_source_delete_query
113
+ <<-SQL.gsub(/^ {4}/, '')
114
+ DELETE FROM "#{PG::FTS.table}"
115
+ WHERE "#{PG::FTS.table}"."source_id" = OLD."#{key}"
116
+ AND "#{PG::FTS.table}"."source_table" = '#{source}'
117
+ AND "#{PG::FTS.table}"."document_id" = OLD."#{foreign_key}"
118
+ AND "#{PG::FTS.table}"."document_table" = '#{document}';
119
+ SQL
120
+ end
121
+
122
+ def on_source_delete_procedure
123
+ <<-SQL.gsub(/^ {4}/, '')
124
+ CREATE FUNCTION "#{on_source_delete_procedure_name}"()
125
+ RETURNS TRIGGER AS $$
126
+ BEGIN
127
+ #{on_source_delete_query.gsub(/^/, ' ')}
128
+ RETURN OLD;
129
+ END;
130
+ $$ LANGUAGE plpgsql;
131
+ SQL
132
+ end
133
+
134
+ def on_source_truncate_query
135
+ <<-SQL.gsub(/^ {4}/, '')
136
+ DELETE FROM "#{PG::FTS.table}"
137
+ WHERE "#{PG::FTS.table}"."source_table" = '#{source}'
138
+ AND "#{PG::FTS.table}"."document_table" = '#{document}';
139
+ SQL
140
+ end
141
+
142
+ def on_source_truncate_procedure
143
+ <<-SQL.gsub(/^ {4}/, '')
144
+ CREATE FUNCTION "#{on_source_truncate_procedure_name}"()
145
+ RETURNS TRIGGER AS $$
146
+ BEGIN
147
+ #{on_source_truncate_query.gsub(/^/, ' ')}
148
+ RETURN NULL;
149
+ END;
150
+ $$ LANGUAGE plpgsql;
151
+ SQL
152
+ end
153
+
154
+ def on_source_insert_trigger
155
+ <<-SQL.gsub(/^ {4}/, '')
156
+ CREATE TRIGGER "#{on_source_insert_trigger_name}"
157
+ AFTER INSERT ON "#{source}"
158
+ FOR EACH ROW
159
+ EXECUTE PROCEDURE "#{on_source_insert_procedure_name}"();
160
+ SQL
161
+ end
162
+
163
+ def on_source_update_trigger
164
+ <<-SQL.gsub(/^ {4}/, '')
165
+ CREATE TRIGGER "#{on_source_update_trigger_name}"
166
+ AFTER UPDATE ON "#{source}"
167
+ FOR EACH ROW
168
+ EXECUTE PROCEDURE "#{on_source_update_procedure_name}"();
169
+ SQL
170
+ end
171
+
172
+ def on_source_delete_trigger
173
+ <<-SQL.gsub(/^ {4}/, '')
174
+ CREATE TRIGGER "#{on_source_delete_trigger_name}"
175
+ AFTER DELETE ON "#{source}"
176
+ FOR EACH ROW
177
+ EXECUTE PROCEDURE "#{on_source_delete_procedure_name}"();
178
+ SQL
179
+ end
180
+
181
+ def on_source_truncate_trigger
182
+ <<-SQL.gsub(/^ {4}/, '')
183
+ CREATE TRIGGER "#{on_source_truncate_trigger_name}"
184
+ AFTER TRUNCATE ON "#{source}"
185
+ FOR EACH STATEMENT
186
+ EXECUTE PROCEDURE "#{on_source_truncate_procedure_name}"();
187
+ SQL
188
+ end
189
+
190
+ def on_document_insert_query
191
+ <<-SQL.gsub(/^ {4}/, '')
192
+ INSERT INTO "#{PG::FTS.table}" (
193
+ "document_id",
194
+ "document_table",
195
+ "source_id",
196
+ "source_table",
197
+ "tsv")
198
+ SELECT
199
+ NEW."#{key}",
200
+ '#{document}',
201
+ "#{source}"."#{key}",
202
+ '#{source}',
203
+ #{ts_vector(source)}
204
+ FROM "#{source}"
205
+ WHERE "#{source}"."#{foreign_key}" = NEW."#{key}";
206
+ SQL
207
+ end
208
+
209
+ def on_document_insert_procedure
210
+ <<-SQL.gsub(/^ {4}/, '')
211
+ CREATE FUNCTION "#{on_document_insert_procedure_name}"()
212
+ RETURNS TRIGGER AS $$
213
+ BEGIN
214
+ #{on_document_insert_query.gsub(/^/, ' ')}
215
+ RETURN NEW;
216
+ END;
217
+ $$ LANGUAGE plpgsql;
218
+ SQL
219
+ end
220
+
221
+ def on_document_update_query
222
+ <<-SQL.gsub(/^ {4}/, '')
223
+ SQL
224
+ end
225
+
226
+ def on_document_update_procedure
227
+ <<-SQL.gsub(/^ {4}/, '')
228
+ CREATE FUNCTION "#{on_document_update_procedure_name}"()
229
+ RETURNS TRIGGER AS $$
230
+ BEGIN
231
+ #{on_document_update_query.gsub(/^/, ' ')}
232
+ RETURN NEW;
233
+ END;
234
+ $$ LANGUAGE plpgsql;
235
+ SQL
236
+ end
237
+
238
+ def on_document_delete_query
239
+ <<-SQL.gsub(/^ {4}/, '')
240
+ DELETE FROM "#{PG::FTS.table}"
241
+ WHERE "#{PG::FTS.table}"."source_table" = '#{source}'
242
+ AND "#{PG::FTS.table}"."document_id" = OLD."#{key}"
243
+ AND "#{PG::FTS.table}"."document_table" = '#{document}';
244
+ SQL
245
+ end
246
+
247
+ def on_document_delete_procedure
248
+ <<-SQL.gsub(/^ {4}/, '')
249
+ CREATE FUNCTION "#{on_document_delete_procedure_name}"()
250
+ RETURNS TRIGGER AS $$
251
+ BEGIN
252
+ #{on_document_delete_query.gsub(/^/, ' ')}
253
+ RETURN OLD;
254
+ END;
255
+ $$ LANGUAGE plpgsql;
256
+ SQL
257
+ end
258
+
259
+ def on_document_truncate_query
260
+ <<-SQL.gsub(/^ {4}/, '')
261
+ DELETE FROM "#{PG::FTS.table}"
262
+ WHERE "#{PG::FTS.table}"."source_table" = '#{source}'
263
+ AND "#{PG::FTS.table}"."document_table" = '#{document}';
264
+ SQL
265
+ end
266
+
267
+ def on_document_truncate_procedure
268
+ <<-SQL.gsub(/^ {4}/, '')
269
+ CREATE FUNCTION "#{on_document_truncate_procedure_name}"()
270
+ RETURNS TRIGGER AS $$
271
+ BEGIN
272
+ #{on_document_truncate_query.gsub(/^/, ' ')}
273
+ RETURN NULL;
274
+ END;
275
+ $$ LANGUAGE plpgsql;
276
+ SQL
277
+ end
278
+
279
+ def on_document_insert_trigger
280
+ <<-SQL.gsub(/^ {4}/, '')
281
+ CREATE TRIGGER "#{on_document_insert_trigger_name}"
282
+ AFTER INSERT ON "#{document}"
283
+ FOR EACH ROW
284
+ EXECUTE PROCEDURE "#{on_document_insert_procedure_name}"();
285
+ SQL
286
+ end
287
+
288
+ def on_document_update_trigger
289
+ <<-SQL.gsub(/^ {4}/, '')
290
+ CREATE TRIGGER "#{on_document_update_trigger_name}"
291
+ AFTER UPDATE ON "#{document}"
292
+ FOR EACH ROW
293
+ EXECUTE PROCEDURE "#{on_document_update_procedure_name}"();
294
+ SQL
295
+ end
296
+
297
+ def on_document_delete_trigger
298
+ <<-SQL.gsub(/^ {4}/, '')
299
+ CREATE TRIGGER "#{on_document_delete_trigger_name}"
300
+ AFTER DELETE ON "#{document}"
301
+ FOR EACH ROW
302
+ EXECUTE PROCEDURE "#{on_document_delete_procedure_name}"();
303
+ SQL
304
+ end
305
+
306
+ def on_document_truncate_trigger
307
+ <<-SQL.gsub(/^ {4}/, '')
308
+ CREATE TRIGGER "#{on_document_truncate_trigger_name}"
309
+ AFTER TRUNCATE ON "#{document}"
310
+ FOR EACH STATEMENT
311
+ EXECUTE PROCEDURE "#{on_document_truncate_procedure_name}"();
312
+ SQL
313
+ end
314
+ end
@@ -0,0 +1,160 @@
1
+ class PG::FTS::Index::Self
2
+ attr_reader :table, :fields, :key, :catalog, :name
3
+ alias source table
4
+ alias document table
5
+ alias foreign_key key
6
+
7
+ include PG::FTS::Index
8
+ include PG::FTS::Naming
9
+ include PG::FTS::TSVector
10
+
11
+ def initialize(table, *fields, key: nil, catalog: nil, as: nil)
12
+ @table = table
13
+ @fields = fields
14
+ @key = key || :id
15
+ @catalog = catalog || PG::FTS.catalog
16
+ @name = as
17
+ end
18
+
19
+ def build_query
20
+ <<-SQL.gsub(/^ {4}/, '')
21
+ INSERT INTO "#{PG::FTS.table}" (
22
+ "document_id",
23
+ "document_table",
24
+ "source_id",
25
+ "source_table",
26
+ "tsv")
27
+ SELECT "#{document}"."#{key}",
28
+ '#{document}',
29
+ "#{source}"."#{key}",
30
+ '#{source}',
31
+ #{ts_vector(source)}
32
+ FROM "#{document}";
33
+ SQL
34
+ end
35
+
36
+ def on_document_insert_query
37
+ <<-SQL.gsub(/^ {4}/, '')
38
+ INSERT INTO "#{PG::FTS.table}" (
39
+ "document_id",
40
+ "document_table",
41
+ "source_id",
42
+ "source_table",
43
+ "tsv")
44
+ VALUES (
45
+ NEW."#{key}",
46
+ '#{document}',
47
+ NEW."#{key}",
48
+ '#{source}',
49
+ #{ts_vector});
50
+ SQL
51
+ end
52
+
53
+ def on_document_insert_procedure
54
+ <<-SQL.gsub(/^ {4}/, '')
55
+ CREATE FUNCTION "#{on_document_insert_procedure_name}"() RETURNS TRIGGER AS $$
56
+ BEGIN
57
+ #{on_document_insert_query.gsub(/^/, ' ')}
58
+ RETURN NEW;
59
+ END;
60
+ $$ LANGUAGE plpgsql;
61
+ SQL
62
+ end
63
+
64
+ def on_document_update_query
65
+ <<-SQL.gsub(/^ {4}/, '')
66
+ UPDATE "#{PG::FTS.table}" SET "tsv" = (#{ts_vector})
67
+ WHERE "#{PG::FTS.table}"."source_id" = NEW."#{key}"
68
+ AND "#{PG::FTS.table}"."source_table" = '#{source}'
69
+ AND "#{PG::FTS.table}"."document_id" = NEW."#{key}"
70
+ AND "#{PG::FTS.table}"."document_table" = '#{document}';
71
+ SQL
72
+ end
73
+
74
+ def on_document_update_procedure
75
+ <<-SQL.gsub(/^ {4}/, '')
76
+ CREATE FUNCTION "#{on_document_update_procedure_name}"() RETURNS TRIGGER AS $$
77
+ BEGIN
78
+ #{on_document_update_query.gsub(/^/, ' ')}
79
+ RETURN NEW;
80
+ END;
81
+ $$ LANGUAGE plpgsql;
82
+ SQL
83
+ end
84
+
85
+ def on_document_delete_query
86
+ <<-SQL.gsub(/^ {4}/, '')
87
+ DELETE FROM "#{PG::FTS.table}"
88
+ WHERE "#{PG::FTS.table}"."source_id" = OLD."#{key}"
89
+ AND "#{PG::FTS.table}"."source_table" = '#{source}'
90
+ AND "#{PG::FTS.table}"."document_id" = OLD."#{key}"
91
+ AND "#{PG::FTS.table}"."document_table" = '#{document}';
92
+ SQL
93
+ end
94
+
95
+ def on_document_delete_procedure
96
+ <<-SQL.gsub(/^ {4}/, '')
97
+ CREATE FUNCTION "#{on_document_delete_procedure_name}"() RETURNS TRIGGER AS $$
98
+ BEGIN
99
+ #{on_document_delete_query.gsub(/^/, ' ')}
100
+ RETURN OLD;
101
+ END;
102
+ $$ LANGUAGE plpgsql;
103
+ SQL
104
+ end
105
+
106
+ def on_document_truncate_query
107
+ <<-SQL.gsub(/^ {4}/, '')
108
+ DELETE FROM "#{PG::FTS.table}"
109
+ WHERE "#{PG::FTS.table}"."source_table" = '#{source}'
110
+ AND "#{PG::FTS.table}"."document_table" = '#{document}';
111
+ SQL
112
+ end
113
+
114
+ def on_document_truncate_procedure
115
+ <<-SQL.gsub(/^ {4}/, '')
116
+ CREATE FUNCTION "#{on_document_truncate_procedure_name}"() RETURNS TRIGGER AS $$
117
+ BEGIN
118
+ #{on_document_truncate_query.gsub(/^/, ' ')}
119
+ RETURN NULL;
120
+ END;
121
+ $$ LANGUAGE plpgsql;
122
+ SQL
123
+ end
124
+
125
+ def on_document_insert_trigger
126
+ <<-SQL.gsub(/^ {4}/, '')
127
+ CREATE TRIGGER "#{on_document_insert_trigger_name}"
128
+ AFTER INSERT ON "#{document}"
129
+ FOR EACH ROW
130
+ EXECUTE PROCEDURE "#{on_document_insert_procedure_name}"();
131
+ SQL
132
+ end
133
+
134
+ def on_document_update_trigger
135
+ <<-SQL.gsub(/^ {4}/, '')
136
+ CREATE TRIGGER "#{on_document_update_trigger_name}"
137
+ AFTER UPDATE ON "#{document}"
138
+ FOR EACH ROW
139
+ EXECUTE PROCEDURE "#{on_document_update_procedure_name}"();
140
+ SQL
141
+ end
142
+
143
+ def on_document_delete_trigger
144
+ <<-SQL.gsub(/^ {4}/, '')
145
+ CREATE TRIGGER "#{on_document_delete_trigger_name}"
146
+ AFTER DELETE ON "#{document}"
147
+ FOR EACH ROW
148
+ EXECUTE PROCEDURE "#{on_document_delete_procedure_name}"();
149
+ SQL
150
+ end
151
+
152
+ def on_document_truncate_trigger
153
+ <<-SQL.gsub(/^ {4}/, '')
154
+ CREATE TRIGGER "#{on_document_truncate_trigger_name}"
155
+ AFTER TRUNCATE ON "#{document}"
156
+ FOR EACH STATEMENT
157
+ EXECUTE PROCEDURE "#{on_document_truncate_procedure_name}"();
158
+ SQL
159
+ end
160
+ end
@@ -0,0 +1,151 @@
1
+ module PG::FTS::Index
2
+ def create
3
+ [:document, :source, :link].each do |type|
4
+ [:insert, :update, :delete, :truncate].each do |op|
5
+ [:procedure, :trigger].each do |item|
6
+ name = "on_#{type}_#{op}_#{item}"
7
+ yield(send(name)) if respond_to?(name)
8
+ end
9
+ end
10
+ end
11
+ end
12
+
13
+ def drop_procedure_query(name)
14
+ <<-SQL.gsub(/^ {4}/, '')
15
+ DROP FUNCTION IF EXISTS "#{send(name + '_name')}" ();
16
+ SQL
17
+ end
18
+
19
+ def drop_trigger_query(name, type)
20
+ t = case type
21
+ when :document then document
22
+ when :source then source
23
+ when :link then link
24
+ end
25
+
26
+ <<-SQL.gsub(/^ {4}/, '')
27
+ DROP TRIGGER IF EXISTS "#{send(name + '_name')}" ON "#{t}";
28
+ SQL
29
+ end
30
+
31
+ def drop
32
+ [:document, :source, :link].each do |type|
33
+ [:insert, :update, :delete, :truncate].each do |op|
34
+ name = "on_#{type}_#{op}_trigger"
35
+ yield(drop_trigger_query(name, type)) if respond_to?(name)
36
+ name = "on_#{type}_#{op}_procedure"
37
+ yield(drop_procedure_query(name)) if respond_to?(name)
38
+ end
39
+ end
40
+ end
41
+
42
+ def clear
43
+ yield(on_document_truncate_query)
44
+ end
45
+
46
+ def build
47
+ yield(build_query)
48
+ end
49
+
50
+ def rebuild(&executor)
51
+ clear(&executor)
52
+ build(&executor)
53
+ end
54
+
55
+ class << self
56
+ def drop_all_procedures
57
+ procedures = yield <<-SQL.gsub(/^ {6}/, '')
58
+ SELECT routine_name AS name
59
+ FROM information_schema.routines
60
+ WHERE routine_name LIKE '%_tsv'
61
+ AND routine_type = 'FUNCTION';
62
+ SQL
63
+
64
+ procedures.each do |procedure|
65
+ yield <<-SQL.gsub(/^ {8}/, '')
66
+ DROP FUNCTION IF EXISTS "#{procedure['name']}" ();
67
+ SQL
68
+ end
69
+ end
70
+
71
+ def drop_all_triggers
72
+ triggers = yield <<-SQL.gsub(/^ {6}/, '')
73
+ SELECT tgname AS name, relname AS table
74
+ FROM pg_trigger
75
+ INNER JOIN pg_class ON pg_class.oid = tgrelid
76
+ WHERE tgname like '%_tsv';
77
+ SQL
78
+
79
+ triggers.each do |trigger|
80
+ yield <<-SQL.gsub(/^ {8}/, '')
81
+ DROP TRIGGER IF EXISTS "#{trigger['name']}" ON "#{trigger['table']}";
82
+ SQL
83
+ end
84
+ end
85
+
86
+ def drop_all(&executor)
87
+ drop_all_triggers(&executor)
88
+ drop_all_procedures(&executor)
89
+ end
90
+
91
+ def create(*indices, &executor)
92
+ indices.each { |index| index.create(&executor) }
93
+ end
94
+
95
+ def drop(*indices, &executor)
96
+ indices.each { |index| index.drop(&executor) }
97
+ end
98
+
99
+ def clear(*indices, &executor)
100
+ indices.each { |index| index.clear(&executor) }
101
+ end
102
+
103
+ def build(*indices, &executor)
104
+ indices.each { |index| index.build(&executor) }
105
+ end
106
+
107
+ def rebuild(*indices, &executor)
108
+ indices.each { |index| index.rebuild(&executor) }
109
+ end
110
+
111
+ def recreate(*indices, &executor)
112
+ drop_all(&executor)
113
+ create(*indices, &executor)
114
+ end
115
+
116
+ def reset(*indices, &executor)
117
+ recreate(*indices, &executor)
118
+ PG::FTS.clear(&executor)
119
+ end
120
+ end
121
+
122
+ module Module
123
+ def create(&executor)
124
+ PG::FTS::Index.create(*constants.map { |e| const_get(e) }, &executor)
125
+ end
126
+
127
+ def drop(&executor)
128
+ PG::FTS::Index.drop(*constants.map { |e| const_get(e) }, &executor)
129
+ end
130
+
131
+ def clear(&executor)
132
+ PG::FTS::Index.clear(*constants.map { |e| const_get(e) }, &executor)
133
+ end
134
+
135
+ def build(&executor)
136
+ PG::FTS::Index.build(*constants.map { |e| const_get(e) }, &executor)
137
+ end
138
+
139
+ def rebuild(&executor)
140
+ PG::FTS::Index.rebuild(*constants.map { |e| const_get(e) }, &executor)
141
+ end
142
+ end
143
+ end
144
+
145
+ require 'pg/fts/naming'
146
+ require 'pg/fts/ts_vector'
147
+ require 'pg/fts/index/self'
148
+ require 'pg/fts/index/many_to_one'
149
+ require 'pg/fts/index/one_to_many'
150
+ require 'pg/fts/index/momo'
151
+ require 'pg/fts/index/ommo'