Как по трем моделям найти необходимую новость?
Передаю данные на бекенд(обычный урл строку) и обрабатываю таким образом:
const Tag = require('../models/tag.model')
const Card = require('../models/card.model')
const Post = require('../models/post.model')
module.exports.getData = async (req, res) => {
try {
const tag = await Tag.findOne({tag: req.params.data})
const card = await Card.findOne({card: req.params.data})
const post = await Post.findOne({post: req.params.data})
if(tag) {
res.json(tag)
} else if(card) {
res.json(card)
} else if(post) {
res.json(post)
}
} catch(e) {
res.status(500).json(e)
}
}
После обработки одна из моделей находит у себя статью/новость и передают найденную новость обратно. Все работает, но может есть более красивый способ обработать?
Ответы (2 шт):
Применив фукциональное прграммирование, ваш код можно улучшить добавлением еще одной функции, которая в качестве аргументов может принимать неограниченное количество моделей. Использовать этот метод можно таким способом:
// в самом первом аргументе будут находится входные данные для моделей
// в данном случае это req.params.data
// все остальные аргументы являются моделями (Tag, Card, Post, ...)
findInModels(data, Tag, Card, Post, Model4, ...ModelN)
След шагом, я бы улучшил этот участок кода:
const tag = await Tag.findOne({tag: req.params.data});
const card = await Card.findOne({card: req.params.data});
const post = await Post.findOne({post: req.params.data});
Многие из разработчиков понимают как удобен async/await и для чего он нужен, но не многие понимают что иногда применив несколько await в одном методе мы делаем шаг назад от идеологии асинхронного программирования. Если обратиться к документации async/await и еще раз внимательно сфокусироваться на фразе
Функция async может содержать выражение await, которое приостанавливает выполнение функции async и ожидает ответа
и теперь после прочитанного сфокусироваться на нашем коде, можно проанализировать след. выполнение:
await // стоп. интерпретатор ожидает результата
// вернул результат, интерпретатор пошел дальше
await // стоп. интерпретатор ожидает результата
// вернул результат, интерпретатор пошел дальше
await // стоп. интерпретатор ожидает результата
// вернул результат, интерпретатор пошел дальше
Улучшить это можно довольно просто, с помощью Promise объекта. В зависимости от стратегии поиска, вы можете применить один из этих вариантов поиска необходимого результата:
В случае если вам нужно вернуть тоько первый удачный вариант, вам подойдет Promise.any(), ну а если вам важно получить все результаты, тогда подойдет Promise.allSettled() или Promise.all(). А теперь давайте рассмотрим варианты улучшения.
const promises = [
Tag.findOne({tag: req.params.data}),
Card.findOne({card: req.params.data}),
Post.findOne({post: req.params.data})
];
const response = await Promise.any(promises);
const response = await Promise.allSettled(promises);
Теперь когда мы получим результат, response будет массивом этих результатов и чтобы не выстраивать многоэтажные if выражения, мы можем отфильтровать наши данные с помощью filter таким образом:
// здесь я не уверен в том, что вам должно прити в качестве пустого
// результата, я проверяю на undefined, но вы можете заменить это правило
// на свое усмотрение добавляя логические сопоставления && и ||.
const articles = response.filter((item) => item !== undefined);
Так как filter всегда возвращает массив, нам осталось проверить наличие содержания и вывести результат. В вашем примере нет обработки запроса в случае когда в качестве результатов данные приходят пустые. Проанализируем еще раз ваш пример кода, в частности этот участок:
// теперь представьте что вам пришел пустой результат. Тоесть по
// входным данным, которые вы передавали вначале req.params.data,
// по ним ничего небыло найдено. В результате tag, card и post
// пришли пустые. Как думаете что отправится в качестве результата?
if(tag) {
// это правило не выполнится, данные пустые
res.json(tag);
} else if(card) {
// это правило не выполнится, данные пустые
res.json(card);
} else if(post) {
// это правило не выполнится, данные пустые
res.json(post);
}
// В этой точке интерпретатор достиг дна логического блока,
// а в возврата нет. Ваш клиент призадумается как мин. на 30 сек.
// (в зависимости от настроек) пока не вернет ошибку о том что сервер
// не отвечает.
Поэтому я предлагаю исправить это вернув пустой результат null:
const article = articles.length ? articles.shift() : null;
res.json(article);
Реализация всего кода:
const Tag = require('../models/tag.model');
const Card = require('../models/card.model');
const Post = require('../models/post.model');
function findInModels() {
// входные аргументы достаем из объекта arguments и преобразуем в массив
const args = [...arguments];
if (args.length < 2) {
// Должно быть как минимум 2 аргумента
const result = Promise.reject('There must be at least 2 arguments');
// по контракту наш метод должен возвращать список промис объектов
return [result];
}
// извлекаем самый первый аргумент (это входные данные для моделей)
// в предыдущей версии это былы данные req.params.data
const value = args.shift();
return args.map((model) => {
// для начала нам нужно узнать имя модели, чтобы построить параметры.
// для этого мы вытаскиваем имя конструктора и переводим в нижний регистр
// я надеюсь что ваши модели в оригинале так и називаются как и в примере
const modelName = model.constructor.name.toLowerCase();
// создаем параметр { tag: data } или { card: data } или { post: data }
// подробней о defineProperty смотрите на MDN.
const queryParams = Object.defineProperty({}, modelName, { value });
return model.findOne(queryParams);
});
}
module.exports.getData = async (req, res) => {
const { data } = req.params;
try {
const promises = findInModels(data, Tag, Card, Post);
const response = await Promise.allSettled(promises);
const articles = response.filter((article) => article !== undefined);
const article = articles.length ? articles.shift() : null;
return res.json(article);
} catch (e) {
res.status(500).json(e);
}
};
Подробнее об Object.defineProperty
module.exports.getData = async (req, res) => {
const {data} = req.params
try {
const promises = [
Tag.findOne({tag: data}),
Card.findOne({card: data}),
Post.findOne({post: data})
]
const response = await Promise.allSettled(promises)
const article = response.filter(item => item.value !== null)
// const article = articles.length ? articles.shift() : null
console.log(article[0].value)
return res.json(article[0].value.category)
} catch (e) {
res.status(500).json(e)
}
}
Вот так получилось реализовать только. Как-то коряво.