Files
my-links/database/migrations/1716561885061_create_full_text_searches_table.ts
2024-06-02 23:59:57 +02:00

54 lines
2.3 KiB
TypeScript

import { BaseSchema } from '@adonisjs/lucid/schema';
export default class extends BaseSchema {
async up() {
this.schema.raw(`
CREATE EXTENSION IF NOT EXISTS unaccent;
CREATE EXTENSION IF NOT EXISTS pg_trgm;
`);
this.schema.raw(`
CREATE INDEX ON links USING gin(to_tsvector('english', name));
CREATE INDEX ON collections USING gin(to_tsvector('english', name));
CREATE INDEX ON links USING gin(to_tsvector('french', name));
CREATE INDEX ON collections USING gin(to_tsvector('french', name));
`);
this.schema.raw(`
CREATE OR REPLACE FUNCTION search_text(search_query TEXT, p_author_id INTEGER)
RETURNS TABLE (
id INTEGER,
type TEXT,
name VARCHAR(254),
url TEXT,
collection_id INTEGER,
matched_part TEXT,
rank DOUBLE PRECISION
)
AS $$
BEGIN
RETURN QUERY
SELECT links.id, 'link' AS type, links.name, links.url, collections.id AS collection_id,
ts_headline('english', unaccent(links.name), plainto_tsquery('english', unaccent(search_query))) AS matched_part,
ts_rank_cd(to_tsvector('english', unaccent(links.name)), plainto_tsquery('english', unaccent(search_query)))::DOUBLE PRECISION AS rank
FROM links
LEFT JOIN collections ON links.collection_id = collections.id
WHERE unaccent(links.name) ILIKE '%' || unaccent(search_query) || '%'
AND (p_author_id IS NULL OR links.author_id = p_author_id)
UNION ALL
SELECT collections.id, 'collection' AS type, collections.name, NULL AS url, NULL AS collection_id,
ts_headline('english', unaccent(collections.name), plainto_tsquery('english', unaccent(search_query))) AS matched_part,
ts_rank_cd(to_tsvector('english', unaccent(collections.name)), plainto_tsquery('english', unaccent(search_query)))::DOUBLE PRECISION AS rank
FROM collections
WHERE unaccent(collections.name) ILIKE '%' || unaccent(search_query) || '%'
AND (p_author_id IS NULL OR collections.author_id = p_author_id)
ORDER BY rank DESC NULLS LAST, matched_part DESC NULLS LAST;
END;
$$
LANGUAGE plpgsql;
`);
}
async down() {
this.schema.raw('DROP FUNCTION IF EXISTS search_text');
}
}