В качестве базы данных я использую express/nodejs (без mongoose) и mongodb. У меня есть коллекция страниц, которая выглядит примерно так
{
_id: ..
Urls: [
{
IncomingUrl: "/test/test1",
Status: "active",
},
{
IncomingUrl: "/test/test2",
Status: "active",
}
],
DraftUrls: [
// same structure as Urls
]
//other fields which arent related to the ques
}
Теперь, создавая страницу, я просматриваю эту коллекцию, чтобы определить, существует ли какой-либо URL-адрес в массиве URL-адресов, который я предоставляю в теле запроса, в каком-либо существующем документе страницы.
И если есть повторяющийся URL-адрес, предоставьте в ответ список повторяющихся URL-адресов.
Теперь проблема, с которой я столкнулся, заключается в том, что если бы мне просто нужно было получить счетчик, я мог бы использовать фильтр поиска, например
const filter = { $or: [
{ "Urls.IncomingUrl": { $in: urls } },
{ "DraftUrls.IncomingUrl": { $in: urls } }
] }
А затем использовал запрос типа
db.collection(PageCollection).find(filter).countDocuments();
И это дало бы количество повторяющихся URL-адресов.
Но в моем случае мне нужно получить повторяющиеся URL-адреса, а не счетчик, поэтому, если я использую что-то вроде этого
const duplicateUrlPages = db.collection(PageCollection).find(filter).toArray();
А затем запустите вложенный цикл for для URL-адресов и дубликатовUrlPages, тогда это будет слишком дорого.
Может ли кто-нибудь подсказать, как я могу эффективно получить только список URL-адресов среди входных URL-адресов, которые уже существуют в любом документе страницы под его Urls.IncomingUrl или DraftUrls.IncomingUrl
Пример:
Предположим, в моей БД есть два таких документа.
Document1: {
// ....
Urls: [
{ IncomingUrl: "test1", status: "active" },
// ...
],
DraftUrls: [
{ IncomingUrl: "test2", status: "inactive" },
// ...
]
}
Document2: {
// ....
Urls: [
{ IncomingUrl: "test4", status: "active" },
// ...
],
DraftUrls: [
{ IncomingUrl: "test10", status: "inactive" },
// ...
]
}
И я предоставляю тело функции контроллера запросов POST как
{
// ...
urls: ["test1", "test2", "test3", "test4"]
}
Затем мне нужен массив ответов, например:
["test1", "test2", "test4"]
Поскольку test1, test2 и test4 уже существуют.
🤔 А знаете ли вы, что...
Node.js поддерживает работу с WebSocket для двусторонней связи между клиентом и сервером.
Вы можете использовать агрегацию.
$match
: копирует ваши $or
условия filter
объекта.$project
: измените форму документов, $filter
изменив массивы настроек Urls
и DraftUrls
так, чтобы они содержали только совпадения с вашими $match
условиями. По сути, это позволит избавиться от объектов, подобных тем, которые содержат test10
в ваших образцах документов.$project
: выведите Urls
и DraftUrls
как один массив с именем urls
.$unwind
: новый массив urls
на отдельные объекты.$group
: эти новые объекты и добавьте значения в один массив, используя $addToSet
, чтобы избежать дублирования.$project
: дополнительный этап, позволяющий избавиться от избыточного поля _id
.const urls = ["test1", "test2", "test3", "test4"];
const filter = { $or: [
{ "Urls.IncomingUrl": { $in: urls } },
{ "DraftUrls.IncomingUrl": { $in: urls } }
] }
db.collection(PageCollection).aggregate([
{
$match: filter
},
{
$project: {
"Urls": {
$map: {
input: {
$filter: {
input: "$Urls",
as: "u",
cond: {
$in: [
"$$u.IncomingUrl",
urls
]
}
}
},
as: "rls",
in: "$$rls.IncomingUrl"
}
},
"DraftUrls": {
$map: {
input: {
$filter: {
input: "$DraftUrls",
as: "du",
cond: {
$in: [
"$$du.IncomingUrl",
urls
]
}
}
},
as: "drls",
in: "$$drls.IncomingUrl"
}
}
}
},
{
$project: {
urls: {
$concatArrays: [
"$DraftUrls",
"$Urls"
]
},
_id: 0
}
},
{
$unwind: "$urls"
},
{
$group: {
_id: null,
urls: {
$addToSet: "$urls"
}
}
},
{
$project: {
_id: 0
}
}
])
Рабочий пример смотрите ЗДЕСЬ.