bug-test-api
Version:
Express.js API project: bug-test-api
183 lines (161 loc) • 6.02 kB
JavaScript
const db = require('../models');
const Questions = db.Questions;
class QuestionsService {
async findAll({ page = 1, limit = 10, filters = {} }) {
try {
const offset = (page - 1) * limit;
const { search, sort, order, ...directFilters } = filters;
const where = {};
const orderClause = [];
// Apply direct column filters
Object.keys(directFilters).forEach(key => {
if (directFilters[key] !== undefined && directFilters[key] !== '') {
// Support partial matching for text fields
const attributes = Object.keys(Questions.rawAttributes);
if (attributes.includes(key)) {
const fieldType = Questions.rawAttributes[key].type;
if (fieldType.key === 'STRING' || fieldType.key === 'TEXT') {
const { Op } = require('sequelize');
where[key] = { [Op.iLike]: `%${directFilters[key]}%` };
} else {
where[key] = directFilters[key];
}
}
}
});
// Apply search across text fields (only if no direct filters conflict)
if (search && search.trim()) {
const { Op } = require('sequelize');
const searchTerm = search.trim();
// Get model attributes to search in text fields
const attributes = Object.keys(Questions.rawAttributes);
const searchableFields = attributes.filter(attr => {
const type = Questions.rawAttributes[attr].type;
return type.key === 'STRING' || type.key === 'TEXT';
}).filter(field => !directFilters[field]); // Exclude fields already filtered
if (searchableFields.length > 0) {
// Combine with existing where conditions using AND
const searchCondition = {
[Op.or]: searchableFields.map(field => ({
[field]: { [Op.iLike]: `%${searchTerm}%` }
}))
};
// If there are existing conditions, combine with AND
if (Object.keys(where).length > 0) {
where[Op.and] = [where, searchCondition];
// Clear the direct where conditions since they're now in Op.and
Object.keys(where).forEach(key => {
if (key !== Op.and) delete where[key];
});
} else {
Object.assign(where, searchCondition);
}
}
}
// Apply sorting
if (sort) {
const sortDirection = order && order.toLowerCase() === 'asc' ? 'ASC' : 'DESC';
const attributes = Object.keys(Questions.rawAttributes);
if (attributes.includes(sort)) {
orderClause.push([sort, sortDirection]);
} else {
orderClause.push(['id', 'DESC']);
}
} else {
orderClause.push(['id', 'DESC']);
}
// Try to include related data if associations exist
const includeOptions = [];
try {
if (Questions.associations) {
Object.keys(Questions.associations).forEach(associationName => {
const association = Questions.associations[associationName];
if (association.associationType === 'BelongsTo' || association.associationType === 'HasMany') {
includeOptions.push({
model: association.target,
as: associationName,
required: false // Use LEFT JOIN to avoid filtering out records
});
}
});
}
} catch (error) {
// Associations not available or error occurred, continue without includes
console.warn('Could not load associations for Questions:', error.message);
}
const { count, rows } = await Questions.findAndCountAll({
where,
limit: parseInt(limit),
offset: parseInt(offset),
order: orderClause,
include: includeOptions
});
return {
data: rows,
total: count,
page: parseInt(page),
limit: parseInt(limit)
};
} catch (error) {
throw new Error(`Error fetching questions: ${error.message}`);
}
}
async findById(id) {
try {
// Try to include related data if associations exist
const includeOptions = [];
try {
if (Questions.associations) {
Object.keys(Questions.associations).forEach(associationName => {
const association = Questions.associations[associationName];
if (association.associationType === 'BelongsTo' || association.associationType === 'HasMany') {
includeOptions.push({
model: association.target,
as: associationName,
required: false // Use LEFT JOIN to avoid filtering out records
});
}
});
}
} catch (error) {
// Associations not available or error occurred, continue without includes
console.warn('Could not load associations for Questions:', error.message);
}
return await Questions.findByPk(id, { include: includeOptions });
} catch (error) {
throw new Error(`Error fetching questions: ${error.message}`);
}
}
async create(data) {
try {
return await Questions.create(data);
} catch (error) {
throw new Error(`Error creating questions: ${error.message}`);
}
}
async update(id, data) {
try {
const [updated] = await Questions.update(data, {
where: { id },
returning: true
});
if (updated) {
return await Questions.findByPk(id);
}
return null;
} catch (error) {
throw new Error(`Error updating questions: ${error.message}`);
}
}
async delete(id) {
try {
const result = await Questions.destroy({
where: { id }
});
return result > 0;
} catch (error) {
throw new Error(`Error deleting questions: ${error.message}`);
}
}
}
module.exports = new QuestionsService();