59 lines
1.6 KiB
TypeScript
59 lines
1.6 KiB
TypeScript
import { Pool } from 'pg';
|
|
|
|
export interface CategoryNode {
|
|
id: number;
|
|
name: string;
|
|
path: string;
|
|
}
|
|
|
|
export class CategoryRepository {
|
|
// Inject the database connection pool
|
|
constructor(private db: Pool) { }
|
|
|
|
/**
|
|
* Finds all descendants of a given path (including the path itself).
|
|
* Uses the ltree operator <@ (is-descendant-of).
|
|
*/
|
|
async findAllDescendants(parentPath: string): Promise<CategoryNode[]> {
|
|
const query = `
|
|
SELECT id, name, path
|
|
FROM categories
|
|
WHERE path <@ $1::ltree
|
|
ORDER BY path ASC;
|
|
`;
|
|
const result = await this.db.query(query, [parentPath]);
|
|
return result.rows;
|
|
}
|
|
|
|
/**
|
|
* Finds only the direct children of a given path.
|
|
* Uses lquery pattern matching (parent.*) and matches the exact next depth level.
|
|
*/
|
|
async findDirectChildren(parentPath: string): Promise<CategoryNode[]> {
|
|
const query = `
|
|
SELECT id, name, path
|
|
FROM categories
|
|
WHERE path ~ $1::lquery
|
|
AND nlevel(path) = nlevel($2::ltree) + 1;
|
|
`;
|
|
const values = [`${parentPath}.*`, parentPath];
|
|
const result = await this.db.query(query, values);
|
|
return result.rows;
|
|
}
|
|
|
|
/**
|
|
* Finds all ancestors of a path (useful for breadcrumbs).
|
|
* Uses the ltree operator @> (is-ancestor-of).
|
|
*/
|
|
async findAncestors(childPath: string): Promise<CategoryNode[]> {
|
|
const query = `
|
|
SELECT id, name, path
|
|
FROM categories
|
|
WHERE path @> $1::ltree
|
|
ORDER BY nlevel(path) ASC;
|
|
`;
|
|
const result = await this.db.query(query, [childPath]);
|
|
return result.rows;
|
|
}
|
|
}
|