У меня есть настройки nodejs / mongodb и mongoose. Все последняя версия.

Я вроде "новенький" в NOSQL и пытаюсь переписать его с помощью NOSQL и mongoose ORM с помощью такого SQL-запроса:

select * from documents where count(select * from subdocuments where subdocuments.documentId = documents.id) = 5 order by documents.id desc limit 1

В моем случае (вложенные коллекции) основная структура коллекции:

   documents: [{
         _id: ObjectId
         subdocuments: [{
              _id: ObjectId
         }]
   }]

Поэтому мне нужно получить только один последний объект, у которого есть subdocuments = 5, с помощью функции documents.findOne mongoose.

Можно ли сделать все эти агрегаты с помощью mongodb / mongoose? Я не могу найти ничего подходящего для моего случая.

Спасибо!

0
featureoffuture 18 Авг 2019 в 07:45

2 ответа

Лучший ответ

Вы можете использовать оператор $ size для поиска документов с полем массива имеет указанную длину:

await documents.findOne({ subdocuments: { $size: 5 } });

В вашем случае вы хотите получить последний документ с _id в порядке убывания, чтобы вы могли добавить опцию сортировки к findOne():

await documents.findOne({ subdocuments: { $size: 5 } }, {}, { sort: { '_id' : -1 } });

Или же:

await documents.findOne({ subdocuments: { $size: 5 } }).sort({_id: -1});

Чтобы найти документ с длиной вложенных документов меньше 5, вы можете использовать $ где :

await documents.findOne({$where: 'this.subdocuments.length<5'}, {}, { sort: { '_id' : -1 } });
1
Cuong Le Ngoc 18 Авг 2019 в 05:53

Существует оператор по имени $expr, который позволяет вам использовать агрегационные выражения в языке запросов. Это очень удобно здесь, где вам понадобятся агрегирующие операторы в вашем запросе поиска.

Разобьем ваш запрос на куски для понимания, давайте возьмем проверку

subdocuments.documentId = documents.id

В монго это означает итерацию массива субдокументов для поиска субдокументов, которые удовлетворяют вышеуказанным критериям и могут быть выражены следующим образом с использованием $filter:

{
    '$filter': {
        'input': '$subdocuments',
        'cond': { '$eq': ['$$this._id', '$_id'] }
    }
}

Бит подсчета обычно выражается с помощью $size оператор, так что принимая результат из вышеупомянутого, этот запрос

count(select * from subdocuments where subdocuments.documentId = documents.id)

Переводится в выражение

{
    '$size': {
        '$filter': {
            'input': '$subdocuments',
            'cond': { '$eq': ['$$this._id', '$_id'] }
        }
    }
}

Используйте оператор сравнения $gt, чтобы выполнить условие

where count(select * from subdocuments where subdocuments.documentId = documents.id) > 5

Так как

{
    '$gt': [
        { '$size': {
            '$filter': {
                'input': '$subdocuments',
                'cond': { '$eq': ['$$this._id', '$_id'] }
            }
        } },
        5
    ]
}

Для равенства это просто показать с помощью $eq

{
    '$eq': [
        { '$size': {
            '$filter': {
                'input': '$subdocuments',
                'cond': { '$eq': ['$$this._id', '$_id'] }
            }
        } },
        5
    ]
}

Вышеприведенное можно затем использовать с { {X0}} вместе с $ expr as

db.collection.find({
    '$expr': {
        '$eq': [
            { '$size': {
                '$filter': {
                    'input': '$subdocuments',
                    'cond': { '$eq': ['$$this._id', '$_id'] }
                }
            } },
            5
        ]
    }
})

Сортировка и ограничение выполняются с использованием sort() и limit() методы курсора соответственно

db.collection.find({
    '$expr': {
        '$eq': [
            { '$size': {
                '$filter': {
                    'input': '$subdocuments',
                    'cond': { '$eq': ['$$this._id', '$_id'] }
                }
            } },
            5
        ]
    }
}).sort({ '_id': -1 }).limit(1)

Этот запрос также имеет вариант агрегации. С структурой агрегирования, выражения, используемые в конвейере, похожи.

Часть поиска выполняется в $match шаг конвейера:

db.collection.aggregate([
    { '$match': {
        '$expr': {
            '$eq': [
                { '$size': {
                    '$filter': {
                        'input': '$subdocuments',
                        'cond': { '$eq': ['$$this._id', '$_id'] }
                    }
                } },
                5
            ]
        }
    } }
])

Сортировка и ограничение передаются в $sort и $limit трубопровод этапы соответственно

db.collection.aggregate([
    { '$match': {
        '$expr': {
            '$eq': [
                { '$size': {
                    '$filter': {
                        'input': '$subdocuments',
                        'cond': { '$eq': ['$$this._id', '$_id'] }
                    }
                } },
                5
            ]
        }
    } },
    { '$sort': { '_id': 1 } },
    { '$limit': 1 }
])
1
chridam 18 Авг 2019 в 08:59