PSA/ee/server/migrations/202410291105_create_tickets_by_concept.cjs
Hermes 284313f908
Some checks are pending
Bidi Control Character Guard / bidi-control-guard (push) Waiting to run
Circular Dependency Check / Check for new circular dependencies (push) Waiting to run
Citus Migration Smoke / Combined migrations on single-node Citus (push) Waiting to run
E2E Fresh Install Tests / fresh-install-e2e (push) Waiting to run
ext-v2 guardrails / Run ext-v2 guard and ESLint (push) Waiting to run
Integration Tests / Check for relevant changes (push) Waiting to run
Integration Tests / ${{ (github.event_name == 'schedule' || github.event.inputs.suite == 'full') && 'Full integration suite' || 'Tier-1 integration subset' }} (push) Blocked by required conditions
Mobile checks / Mobile lint + typecheck (push) Waiting to run
Mobile checks / Mobile unit tests (push) Waiting to run
Mobile checks / Mobile dependency audit (report) (push) Waiting to run
Mobile checks / Mobile reproducibility checks (push) Waiting to run
Secrets guard (env backups) / Ensure no tracked env backup files (push) Waiting to run
Temporal Readiness / fast-readiness (push) Waiting to run
Temporal Readiness / docker-parity (push) Waiting to run
TypeScript Type Check / Nx affected typecheck (push) Waiting to run
Unit Tests / Skipped-test budget (push) Waiting to run
Unit Tests / Nx affected unit tests (push) Waiting to run
Unit Tests / Server unit coverage (informational) (push) Waiting to run
Validate Tenant Management Schema / Check for relevant changes (push) Waiting to run
Validate Tenant Management Schema / Validate Tenant Management Schema (push) Blocked by required conditions
EE Workflows Build Guard / ee-workflows-build-guard (push) Waiting to run
Initial import of AlgaPSA codebase from PSA server
Excluded: .git, node_modules, secrets/, compose.env, assemblyscript tgz

Source: /opt/alga-psa on psa.joliet.tech
2026-06-22 16:12:17 -05:00

146 lines
4.3 KiB
JavaScript

exports.up = async function(knex) {
// get_tickets_by_concept takes a pgvector argument; skip where the
// extension isn't available (e.g. the Citus migration smoke job).
const vectorCheck = await knex.raw(
"SELECT EXISTS (SELECT 1 FROM pg_available_extensions WHERE name = 'vector') AS available"
);
if (!vectorCheck.rows?.[0]?.available) {
console.log('[tickets-by-concept] pgvector not available; skipping');
return;
}
// Create get_tickets_by_concept function
await knex.schema.raw(`
CREATE OR REPLACE FUNCTION public.get_tickets_by_concept(
embedding vector,
tenant_name UUID,
concept text,
max_distance double precision
)
RETURNS TABLE(
ticket_id UUID,
ticket_number TEXT,
title text,
distance double precision,
entered_at timestamp with time zone
)
LANGUAGE plpgsql
AS $function$
BEGIN
RETURN QUERY
WITH
vector_search AS (
SELECT
t.ticket_id,
t.ticket_number,
t.tenant,
t.title,
v.vector <-> embedding AS distance_d,
t.entered_at
FROM
vectors v
JOIN tickets t ON v.ticket_id = t.ticket_id AND v.tenant = t.tenant
WHERE
t.tenant = tenant_name
AND v.vector <-> embedding < max_distance
),
title_search AS (
SELECT
t.ticket_id,
t.ticket_number,
t.tenant,
t.title,
0.1 AS distance_d,
t.entered_at
FROM
tickets t
WHERE
t.tenant = tenant_name
AND t.title_index @@ to_tsquery('english', concept)
),
comment_search AS (
SELECT
t.ticket_id,
t.ticket_number,
t.tenant,
t.title,
0.1 AS distance_d,
t.entered_at
FROM
comments c
JOIN tickets t ON c.ticket_id = t.ticket_id AND c.tenant = t.tenant
WHERE
t.tenant = tenant_name
AND c.note_index @@ to_tsquery('english', concept)
),
document_search AS (
SELECT
t.ticket_id,
t.ticket_number,
t.tenant,
t.title,
0.1 AS distance_d,
t.entered_at
FROM
documents d
JOIN tickets t ON d.ticket_id = t.ticket_id AND d.tenant = t.tenant
WHERE
t.tenant = tenant_name
AND d.content_index @@ to_tsquery('english', concept)
),
combined_results AS (
SELECT * FROM vector_search
UNION ALL
SELECT * FROM title_search
UNION ALL
SELECT * FROM comment_search
UNION ALL
SELECT * FROM document_search
),
ranked_results AS (
SELECT
cr.ticket_id,
cr.ticket_number,
cr.tenant,
cr.title,
cr.distance_d,
cr.entered_at,
ROW_NUMBER() OVER (PARTITION BY cr.ticket_id ORDER BY cr.entered_at DESC) AS rn
FROM
combined_results cr
WHERE
cr.tenant = tenant_name
),
unique_results AS (
SELECT
rr.ticket_id,
rr.ticket_number,
rr.title,
MIN(rr.distance_d) AS distance,
MIN(rr.entered_at) AS entered_at
FROM
ranked_results rr
WHERE
rn = 1
GROUP BY
rr.ticket_id, rr.ticket_number, rr.title
)
SELECT
ur.ticket_id,
ur.ticket_number,
ur.title,
ur.distance,
ur.entered_at
FROM
unique_results ur
ORDER BY
ur.entered_at DESC;
END;
$function$
;`);
};
exports.down = async function(knex) {
await knex.raw('DROP FUNCTION IF EXISTS get_tickets_by_concept');
};