Как осуществить поиск по двум коллекциям в MongoDB?

Здравствуйте! Можете, пожалуйста, помочь мне разобраться, как реализовать поиск по двум коллекциям с возможностью пагинации? В настоящее время мой код выглядит так: <br/><br/><pre><code>search = async ({ initiatorId, query }: Pick&lt;Pagination, 'query'&gt; &amp; { initiatorId: Types.ObjectId }) =&gt; { ... }</code></pre><br/><br/> Как видно, в нем отсутствуют параметры limit, page и skip, что затрудняет реализацию пагинации из-за выполнения двух отдельных запросов. Я уверен, что можно переписать это в один запрос, так как есть общие поля для поиска (name, login), но мне не хватает знаний, чтобы сделать это самостоятельно. Буду признателен за вашу помощь!
  • 12 октября 2024 г. 13:42
Ответы на вопрос 1
Для реализации поиска по двум коллекциям в MongoDB с использованием пагинации, можно воспользоваться агрегатами и операцией `$unionWith`. Данная операция позволяет объединять результаты из разных коллекций в один результат запроса, что очень удобно для ваших целей.

Ниже приведен пример того, как это может быть реализовано. Предположим, у вас есть две коллекции: `collectionA` и `collectionB`, и вы хотите искать по полям `name` и `login`.

Вот как может выглядеть ваш код:

```javascript
const mongoose = require('mongoose');
const { Types } = mongoose;

const search = async ({ initiatorId, query, limit = 10, page = 1 }) => {
    const skip = (page - 1) * limit;

    const pipeline = [
        {
            $match: {
                $or: [
                    { name: { $regex: query, $options: 'i' } },
                    { login: { $regex: query, $options: 'i' } },
                ],
                // Здесь можно добавить дополнительные условия,
                // например, проверка на initiatorId и другие поля
            }
        },
        {
            $facet: {
                data: [
                    { $skip: skip },
                    { $limit: limit }
                ],
                total: [
                    { $count: "count" }
                ]
            }
        },
        {
            $unwind: "$total"
        }
    ];

    const resultsA = await collectionA.aggregate(pipeline).exec();
    const resultsB = await collectionB.aggregate(pipeline).exec();
    
    // Объединяем результаты из обеих коллекций
    const combinedResults = [...resultsA[0].data, ...resultsB[0].data];

    return {
        data: combinedResults,
        total: (resultsA[0].total.count + resultsB[0].total.count),
        page,
        limit,
    };
};
```

### Пояснение:
1. **Параметры `limit`, `page`, `skip`:** Мы используем эти параметры, чтобы контролировать количество возвращаемых результатов и реализовать пагинацию. `skip` вычисляет, сколько документов пропустить для правильной страницы.

2. **`$match`:** Мы фильтруем результаты по полям `name` и `login` с использованием регулярного выражения для поиска совпадений. 

3. **`$facet`:** Позволяет разбить агрегацию на несколько частей. В данном случае мы получаем данные с учетом пагинации и общее количество документов. 

4. **Сбор результатов:** Мы выполняем агрегацию по обеим коллекциям и объединяем результаты в массив.

5. **Возвращаемая структура (объект):** У нас есть `data` с событиями, `total` с общим количеством найденных результатов, номера страницы и лимит.

Обратите внимание, что количество документов может варьироваться в зависимости от ваших ожиданий и требований, и вы можете адаптировать этот код под свои нужды.
Похожие вопросы