Как написать эффективный запрос mongo для сложных структур

В качестве базы данных я использую 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 для двусторонней связи между клиентом и сервером.


1
72
1

Ответ:

Решено

Вы можете использовать агрегацию.

  1. $match: копирует ваши $or условия filter объекта.
  2. $project: измените форму документов, $filter изменив массивы настроек Urls и DraftUrls так, чтобы они содержали только совпадения с вашими $match условиями. По сути, это позволит избавиться от объектов, подобных тем, которые содержат test10 в ваших образцах документов.
  3. $project: выведите Urls и DraftUrls как один массив с именем urls.
  4. $unwind: новый массив urls на отдельные объекты.
  5. $group: эти новые объекты и добавьте значения в один массив, используя $addToSet, чтобы избежать дублирования.
  6. $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
    }
  }
])

Рабочий пример смотрите ЗДЕСЬ.