Как ФИЛЬТР возвращал данные между двумя датами из mongodb с использованием агрегации: совпадение, поиск и проект? - PullRequest
0 голосов
/ 26 мая 2020

Я хотел бы получать все уведомления между начальной и конечной датами, которые связаны с реестром, когда я передаю идентификатор пользователя.

Схема реестра

const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const RegisterSchema = new Schema({
    userId: {type: Schema.Types.ObjectId, required: true},
    accessToken: {type:String, required: true, default: null},
})
module.exports = Register = mongoose.model( 'register', RegisterSchema)

Вот какой-то регистр data

[
  {
    "_id": "5eac9e815fc57b07f5d0d29f",
    "userId": "5ea108babb65b800172b11be",
    "accessToken": "111"
  },
  {
    "_id": "5ecaeba3c7b910d3276df839",
    "userId": "5e6c2dddad72870c84f8476b",
    "accessToken": "222"
  }
]

Следующий документ содержит данные, которые связаны со схемой Register через accessToken

Notifications

const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const NotificationSchema = new Schema({
    accessToken: {type:String, required: true},
    summaryId: {type:Number, required: true},
    dateCreated: {type: Date, default: Date.now},
})
module.exports = Notification = mongoose.model( 'notification', NotificationSchema)

Вот некоторые данные уведомления

[{
    "_id": "5ebf0390c719e60004f42e74",
    "accessToken": "111",
    "summaryId": 1111,
    "dateCreated": "17 Apr 2020" }, 
  {
    "_id": "6ebf0390c719e60004f42e76",
    "accessToken": "222",
    "summaryId": 2221,
    "dateCreated": "18 Apr 2020" },
  {
    "_id": "6ebf0390c719e60004f42e78",
    "accessToken": "111",
    "summaryId": 1112,
    "dateCreated": "25 May 2020" },
  {
    "_id": "6ebf0390c719e60004f42e80",
    "accessToken": "222",
    "summaryId": 2222,
    "dateCreated": "26 May 2020" }
]

Попробуйте 1

        var userId = '5ea108babb65b800172b11be'
        var dateStart = '27 Apr 2020';
        var dateEnd   = '27 May 2020'; 

        var match = {$match: { userId: mongoose.Types.ObjectId(userId) } };

        var lookup ={
            $lookup:
            {
                from: "notifications",
                localField: "accessToken",
                foreignField: "accessToken",
                as: "testingThis"
            }
        };

        project = {
            $project: {
                items: {
                    $filter: {
                    input: "$items",
                    as: "item",
                    cond: { {"dateCreated": {'$gte': dateStart, '$lte': dateEnd }} }
                    }
                }
            }
        };

        var agg = [
            match,
            lookup,
            project
        ];


        Register.aggregate(agg)
        .then( events => {
            if(events){
                return resolve(events);
            }else{
                return reject({success:false});
            }
        })
        .catch(err => {
            console.log('ERROR ' + JSON.stringify(err.message));
            return reject({success:false});
        })  

Попробуйте 1 Ошибка

Я ожидаю увидеть уведомление за 25 мая для accessToken 111, но получаю сообщение об ошибке:

ERROR : {"\"An object representing an expression must have exactly one field: { $gte: new Date(1588017802546), $lte: new Date(1590609802546) }\""}

Попробуйте 2

Я избавился от ошибки ... но ничего не вернул:

        var dateCondition = { $and: [
            { $gte: [ "$$item.dateCreated", dateStart.getTime() ] },
            { $lte: [ "$$item.dateCreated", dateEnd.getTime() ] }
          ] }

          project = {
            $project: {
                items: {
                    $filter: {
                    input: "$items",
                    as: "item",
                    cond: dateCondition
                    }
                }
            }
        };

Вот как выглядит мой проект:

{
  "$project": {
    "items": {
      "$filter": {
        "input": "$items",
        "as": "item",
        "cond": {
          "$and": [
            {"$gte": ["$$item.dateCreated",1588019227296] },
            {"$lte": ["$$item.dateCreated",1590611227296] }
          ] } } } }
}

Попробуйте 3

используя совет из комментариев ... Я изменил 'items' (из попытки 2) на 'notifications'

        var dateCondition = { $and: [
            { $gte: [ "$$item.dateCreated", dateStart.getTime() ] },
            { $lte: [ "$$item.dateCreated", dateEnd.getTime() ] }
          ] }

          project = {
            $project: {
                notifications: {
                    $filter: {
                    input: "$notifications",
                    as: "item",
                    cond: dateCondition
                    }
                }
            }
        };

все еще не работает

так что, пытаясь максимально упростить, чтобы заставить это работать ... Я пробую это с кратким идентификатором

Попробуйте 4 * 1 042 * dateCondition = { $and: [ { $gte: [ "$$item.summaryId", 1 ] }, { $lte: [ "$$item.summaryId", 555555 ] } ] } project = { $project: { notifications: { $filter: { input: "$notifications", as: "item", cond: dateCondition } } } }; что работает ... так что я думаю, что это проблема с датой. Final Code - работает!


        // make sure the input dates are REALLY date objects
        var dateStart = new Date(inputDateStart);
        var dateEnd = new Date(inputDateEnd);     

        var match = {$match: { userId: mongoose.Types.ObjectId(userId) } };

        var lookup ={
            $lookup:
            {
                from: "my_Notifications",
                localField: "accessToken",
                foreignField: "accessToken",
                as: "notifications"
            }
        };

        var dateCondition = { $and: [
            { $gte: [ "$$item.dateCreated", dateStart ] },
            { $lte: [ "$$item.dateCreated", dateEnd ] }
          ]}  

        project = {
            $project: {
                notifications: {
                    $filter: {
                    input: "$notifications",
                    as: "item",
                    cond: dateCondition
                    } } }
        };

        var agg = [
            match,
            lookup,
            project
        ];

        Register.aggregate(agg)
        .then( ..... )

1 Ответ

0 голосов
/ 28 мая 2020

Ваше решение выглядит почти правильно, при условии, что dateStart и dateStart на самом деле являются Date объектами, а не String s.

Ваш Try 2 был неполным I ' м не уверен, что он использует $lookup из Попробуйте 1 или нет. Если это так, вы должны убедиться, что вывод $lookup совпадает с вводом $filter. Поэтому вы должны изменить as в $lookup на input из $filter

{
  $lookup: {
    from: "notifications",
    localField: "accessToken",
    foreignField: "accessToken",
    as: "items" // here
  }

}

Альтернативное решение

Я не уверен, что вы хотите как выход. Если вам нужен только массив уведомлений без объекта пользователя, вы можете попробовать следующее.

[{
  $match: { userId: mongoose.Types.ObjectId(userId) }
}, {
  $lookup: {
    from: "notifications",
    localField: "accessToken", // don't forget to index register.accessToken
    foreignField: "accessToken", // don't forget to index notification.accessToken
    as: "notifications"
  }
}, {
  $unwind: "$notifications"
}, {
  $match: { 
    dateCreated: { $gte: dateStart, $lte: dateEnd } // dateStart, dateEnd should be Date objects
  }
}, { // optional, move notifications to top lvel
  $replaceRoot: { root: '$notifications' }
}]
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...