Após expandir um pouco minha API com validações e uma classe de editoras resolvi fazer uma query para livros que poderia contar informações de editoras/autores/livros tinha um pouco de dificuldade de implementar. Gostaria de saber se tem alguma maneira mais prática de implementar. Implementei a partir dessa configuração de query
livroSchema.statics.queryConfig = {
titulo: 'regex',
lancamento: 'exact',
'autor.name': 'regex',
'autor.nationality': 'regex',
'editora.razaoSocial': 'regex',
'editora.nomeFantasia': 'regex',
'editora.cnpj': 'exact',
'editora.telefone': 'exact',
'editora.email': 'regex',
'editora.status': 'exact',
};
static async listBooksByQuery(req, res, next) {
try {
const { bookQuery, authorQuery, publisherQuery } = separateQueryParams(req.query);
// If any of the book attributes were used as search parameters
let bookList = [];
if (Object.keys(bookQuery).length > 0) {
if (bookQuery.titulo) bookQuery.titulo = new RegExp(bookQuery.titulo, 'i');
bookList = await books.find(bookQuery).populate(['autor', 'editora']).exec();
// Throw not found
LivroController.checkEmpty(bookList);
// Check if any properties related to author or publisher are included in the query
if (Object.keys(authorQuery).length > 0 || Object.keys(publisherQuery).length > 0) {
// Filter books that match the specified author and publisher
bookList = await LivroController.filterByAuthorOrPublisher(bookList, authorQuery, publisherQuery);
}
} else {
bookList = await LivroController.searchByAuthorOrPublisher(authorQuery, publisherQuery);
}
return res.status(200).json(bookList);
} catch (error) {
next(error);
}
}
function separateQueryParams(query) {
const bookQuery = {};
const authorQuery = {};
const publisherQuery = {};
for (const [key, value] of Object.entries(query)) {
if (key.startsWith('autor.')) {
const subKey = key.split('.').pop(); // get proper key for author collection
authorQuery[subKey] = value;
} else if (key.startsWith('editora.')) {
const subKey = key.split('.').pop(); //get proper key for publisher collection
publisherQuery[subKey] = value;
} else {
bookQuery[key] = value;
}
}
return { bookQuery, authorQuery, publisherQuery };
}
export default separateQueryParams;
static checkEmpty(list) {
if (!list || list.length === 0) {
throw new NotFound('No Books found within these parameters');
}
}
static async filterByAuthorOrPublisher(bookList, authorQuery, publisherQuery) {
// Get Author and Publisher ID based on the query
const authorIds = await LivroController.getIdsByQuery(authorQuery, author);
const publisherIds = await LivroController.getIdsByQuery(publisherQuery, publisher);
// Filter the books found matching with authors and publishers
return bookList.filter((book) => {
const isAuthorMatch = authorIds ? authorIds.some((id) => id.equals(book.autor._id)) : true;
const isPublisherMatch = publisherIds ? publisherIds.some((id) => id.equals(book.editora._id)) : true;
return isAuthorMatch && isPublisherMatch;
});
}
static async getIdsByQuery(query, model) {
if (Object.keys(query).length > 0) {
const results = await model.find(buildQuery(query, model));
return results.map((item) => item._id);
}
return null;
}
static async searchByAuthorOrPublisher(authorQuery, publisherQuery) {
// Get Author and Publisher ID based on the query
const authorIds = await LivroController.getIdsByQuery(authorQuery, author);
const publisherIds = await LivroController.getIdsByQuery(publisherQuery, publisher);
// Search any book matches by author and publisher
return books
.find({
...(authorIds ? { autor: { $in: authorIds } } : {}),
...(publisherIds ? { editora: { $in: publisherIds } } : {}),
})
.populate(['autor', 'editora'])
.exec();
}