pg_fts 0.0.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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'