webupdate
This commit is contained in:
195
backend/utils/queryBuilders.js
Normal file
195
backend/utils/queryBuilders.js
Normal file
@@ -0,0 +1,195 @@
|
||||
/**
|
||||
* Optimized Query Builders
|
||||
* Reusable SQL query builders with proper field selection and pagination
|
||||
*/
|
||||
|
||||
const PRODUCT_BASE_FIELDS = [
|
||||
"p.id",
|
||||
"p.name",
|
||||
"p.slug",
|
||||
"p.shortdescription",
|
||||
"p.description",
|
||||
"p.price",
|
||||
"p.category",
|
||||
"p.stockquantity",
|
||||
"p.sku",
|
||||
"p.weight",
|
||||
"p.dimensions",
|
||||
"p.material",
|
||||
"p.isfeatured",
|
||||
"p.isbestseller",
|
||||
"p.createdat",
|
||||
];
|
||||
|
||||
const PRODUCT_IMAGE_AGG = `
|
||||
COALESCE(
|
||||
json_agg(
|
||||
json_build_object(
|
||||
'id', pi.id,
|
||||
'image_url', pi.image_url,
|
||||
'color_variant', pi.color_variant,
|
||||
'color_code', pi.color_code,
|
||||
'alt_text', pi.alt_text,
|
||||
'is_primary', pi.is_primary,
|
||||
'display_order', pi.display_order,
|
||||
'variant_price', pi.variant_price,
|
||||
'variant_stock', pi.variant_stock
|
||||
) ORDER BY pi.display_order, pi.created_at
|
||||
) FILTER (WHERE pi.id IS NOT NULL),
|
||||
'[]'::json
|
||||
) as images
|
||||
`;
|
||||
|
||||
/**
|
||||
* Build product query with images
|
||||
* @param {Object} options - Query options
|
||||
* @param {string[]} options.fields - Additional fields to select
|
||||
* @param {string} options.where - WHERE clause
|
||||
* @param {string} options.orderBy - ORDER BY clause
|
||||
* @param {number} options.limit - LIMIT value
|
||||
* @returns {string} SQL query
|
||||
*/
|
||||
const buildProductQuery = ({
|
||||
fields = [],
|
||||
where = "p.isactive = true",
|
||||
orderBy = "p.createdat DESC",
|
||||
limit = null,
|
||||
} = {}) => {
|
||||
const selectFields = [...PRODUCT_BASE_FIELDS, ...fields].join(", ");
|
||||
|
||||
return `
|
||||
SELECT ${selectFields}, ${PRODUCT_IMAGE_AGG}
|
||||
FROM products p
|
||||
LEFT JOIN product_images pi ON pi.product_id = p.id
|
||||
WHERE ${where}
|
||||
GROUP BY p.id
|
||||
ORDER BY ${orderBy}
|
||||
${limit ? `LIMIT ${limit}` : ""}
|
||||
`.trim();
|
||||
};
|
||||
|
||||
/**
|
||||
* Build optimized query for single product by ID or slug
|
||||
* @param {string} identifier - Product ID or slug
|
||||
* @returns {Object} Query object with text and values
|
||||
*/
|
||||
const buildSingleProductQuery = (identifier) => {
|
||||
const isUUID = identifier.length === 36 && identifier.indexOf("-") === 8;
|
||||
const whereClause = isUUID ? "p.id = $1" : "(p.id = $1 OR p.slug = $1)";
|
||||
|
||||
return {
|
||||
text:
|
||||
buildProductQuery({ where: `${whereClause} AND p.isactive = true` }) +
|
||||
" LIMIT 1",
|
||||
values: [identifier],
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Build blog post query with field selection
|
||||
* @param {Object} options - Query options
|
||||
* @param {boolean} options.includeContent - Include full content
|
||||
* @param {boolean} options.publishedOnly - Filter by published status
|
||||
* @returns {string} SQL query
|
||||
*/
|
||||
const buildBlogQuery = ({
|
||||
includeContent = true,
|
||||
publishedOnly = true,
|
||||
} = {}) => {
|
||||
const fields = includeContent
|
||||
? "id, title, slug, excerpt, content, featuredimage, imageurl, images, ispublished, createdat"
|
||||
: "id, title, slug, excerpt, featuredimage, imageurl, ispublished, createdat";
|
||||
|
||||
const whereClause = publishedOnly ? "WHERE ispublished = true" : "";
|
||||
|
||||
return `SELECT ${fields} FROM blogposts ${whereClause} ORDER BY createdat DESC`;
|
||||
};
|
||||
|
||||
/**
|
||||
* Build pages query with field selection
|
||||
* @param {Object} options - Query options
|
||||
* @param {boolean} options.includeContent - Include page content
|
||||
* @param {boolean} options.activeOnly - Filter by active status
|
||||
* @returns {string} SQL query
|
||||
*/
|
||||
const buildPagesQuery = ({ includeContent = true, activeOnly = true } = {}) => {
|
||||
const fields = includeContent
|
||||
? "id, title, slug, pagecontent as content, metatitle, metadescription, isactive, createdat"
|
||||
: "id, title, slug, metatitle, metadescription, isactive, createdat";
|
||||
|
||||
const whereClause = activeOnly ? "WHERE isactive = true" : "";
|
||||
|
||||
return `SELECT ${fields} FROM pages ${whereClause} ORDER BY createdat DESC`;
|
||||
};
|
||||
|
||||
/**
|
||||
* Build portfolio projects query
|
||||
* @param {boolean} activeOnly - Filter by active status
|
||||
* @returns {string} SQL query
|
||||
*/
|
||||
const buildPortfolioQuery = (activeOnly = true) => {
|
||||
const whereClause = activeOnly ? "WHERE isactive = true" : "";
|
||||
|
||||
return `
|
||||
SELECT
|
||||
id, title, description, imageurl, images,
|
||||
category, categoryid, isactive, createdat, displayorder
|
||||
FROM portfolioprojects
|
||||
${whereClause}
|
||||
ORDER BY displayorder ASC, createdat DESC
|
||||
`.trim();
|
||||
};
|
||||
|
||||
/**
|
||||
* Build categories query
|
||||
* @returns {string} SQL query
|
||||
*/
|
||||
const buildCategoriesQuery = () => {
|
||||
return `
|
||||
SELECT DISTINCT category
|
||||
FROM products
|
||||
WHERE isactive = true
|
||||
AND category IS NOT NULL
|
||||
AND category != ''
|
||||
ORDER BY category ASC
|
||||
`.trim();
|
||||
};
|
||||
|
||||
/**
|
||||
* Pagination helper
|
||||
* @param {number} page - Page number (1-indexed)
|
||||
* @param {number} limit - Items per page
|
||||
* @returns {Object} Offset and limit
|
||||
*/
|
||||
const getPagination = (page = 1, limit = 20) => {
|
||||
const validPage = Math.max(1, parseInt(page) || 1);
|
||||
const validLimit = Math.min(100, Math.max(1, parseInt(limit) || 20));
|
||||
const offset = (validPage - 1) * validLimit;
|
||||
|
||||
return { offset, limit: validLimit, page: validPage };
|
||||
};
|
||||
|
||||
/**
|
||||
* Add pagination to query
|
||||
* @param {string} query - Base SQL query
|
||||
* @param {number} page - Page number
|
||||
* @param {number} limit - Items per page
|
||||
* @returns {string} SQL query with pagination
|
||||
*/
|
||||
const addPagination = (query, page, limit) => {
|
||||
const { offset, limit: validLimit } = getPagination(page, limit);
|
||||
return `${query} LIMIT ${validLimit} OFFSET ${offset}`;
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
buildProductQuery,
|
||||
buildSingleProductQuery,
|
||||
buildBlogQuery,
|
||||
buildPagesQuery,
|
||||
buildPortfolioQuery,
|
||||
buildCategoriesQuery,
|
||||
getPagination,
|
||||
addPagination,
|
||||
PRODUCT_BASE_FIELDS,
|
||||
PRODUCT_IMAGE_AGG,
|
||||
};
|
||||
Reference in New Issue
Block a user