MongoError: невозможно преобразовать из строки типа BSON в Date в Meteor - PullRequest
2 голосов
/ 28 мая 2020

В Meteor JS Я хочу найти пользователей, чей день рождения сегодня. У меня есть этот фрагмент кода, который отлично работает на моем компьютере (локально), но не работает на производстве:

let today = new Date()
let users = Meteor.users.find({
    "status.lastLogin": { $not: { $eq: null } },
    $expr: {
        $and: [
            {
                $eq: [
                    {
                        $dayOfMonth: {
                            date: "$profile.birthdate",
                            timezone: "Europe/Paris",
                        },
                    },
                    today.getDate(),
                ],
            },
            {
                $eq: [
                    {
                        $month: {
                            date: "$profile.birthdate",
                            timezone: "Europe/Paris",
                        },
                    },
                    today.getMonth() + 1,
                ],
            },
        ],
    },
})

Мой сервер размещен на Galaxy, а БД на mongodb.com Я проверил тип profile.birthdate, и это Date на mongodb.com Ошибка:

MongoError: невозможно преобразовать из строки типа BSON в Date \ n при подключении. (/app/bundle/programs/server/npm/node_modules/meteor/npm-mongo/node_modules/mongodb/lib/core/connection/pool.js:450:61)\n в Connection.emit (events . js: 311: 20) \ n в Connection.EventEmitter.emit (domain. js: 482: 12) \ n в processMessage (/ app / bundle / programs / server / npm / node_modules / meteor / npm -mongo / node_modules / mongodb / lib / core / connection / connection. js: 384: 10) \ n в TLSSocket.

Кто-нибудь знает, почему это происходит и как я могу исправить это?

Изменить : Следуя совету @Jankapunkt по использованию агрегата и прочитав этот пост , я смог написать лучше (я думаю ...) запрос, и теперь он работает. Это новый код:

const today = new Date()
let users = Meteor.users.aggregate(
    {
        $project: {
            status: "$status",
            roles: "$roles",
            month: {
                $month: {
                    date: "$profile.birthdate",
                    timezone: "Europe/Paris",
                },
            },
            day: {
                $dayOfMonth: {
                    date: "$profile.birthdate",
                    timezone: "Europe/Paris",
                },
            },
        },
    },
    {
        $match: {
            "status.lastLogin": { $ne: null },
            roles: "candidate",
            month: today.getMonth() + 1,
            day: today.getDate(),
        },
    }
)

1 Ответ

0 голосов
/ 02 июня 2020

Здесь есть несколько проблем, которые я хотел бы решить:

  • $expr обычно требуется только в редких случаях или при сопоставлении с регулярным выражением.

  • $dayOfMonth является агрегатным оператором и недоступен в базовых c запросах, но доступны пакеты

  • Meteor имеет встроенная поддержка даты через E JSON, которая расширяет BSON за счет настраиваемых типов (абстрагирует преобразование типов за вас):

Meteor.publish('allbirthdays', function () {
  const today = new Date()
  // correct timezone here
  return Meteor.users.find({ '$profile.birthdate': today })
}

Нет необходимости преобразовывать дату в какое-то время go операторов et c.

  • $and - противоречие, если для одного и того же поля требуются разные значения (birthdate никогда не может быть сегодня и сегодня в месяце) , вы намеревались использовать $or?

  • { $not: { $eq: null } } можно записать как { $ne: null }

  • Всегда отключать services поле если вы публикуете sh пользователей! Сервисы содержат (хешированный) пароль и других поставщиков OAuth, включая токен возобновления, что может привести к серьезным проблемам с безопасностью:

Сохранение / запрос дат без агрегирования

Вышеуказанное методы допускают только точное совпадение даты, поскольку MongoDB предоставляет запрос с указанием даты c только через aggregate.

Следовательно, ваши варианты:

  • A) Используйте совокупный пакет для создания $expr для $month и $dayOfMonth, как в вашем примере кода

  • B) Создайте день рождения только как поле локали (что делает его String type):

export const getDate = () => {
  // todo apply locale, timezone etc.
  return new Date().toLocaleString(undefined, {
    day: 'numeric', month: 'long' 
  })
}

и сохраните его в коллекции пользователя как отдельное поле (например, birthDay):

Meteor.users.update(userId, {
  $set: {
    '$profile.birthDay': getDate() // "June 3"
  }
})

запрос только для в этот день:

Meteor.publish('allbirthdays', function () {
  const today = getDate()
  return Meteor.users.find({ '$profile.birthDay': today })
}
  • C) Сохраните месяц и день как Number типы в отдельных полях:
const today = new Date()
Meteor.users.update(userId, {
  $set: {
    '$profile.birthDay': today.getDate()  // 3
    '$profile.birthMon': today.getMonth() // 6
  }
})

и запрос только для этого день:

Meteor.publish('allbirthdays', function () {
  const today = new Date()
  return Meteor.users.find({
    '$profile.birthDay': today.getDate()
    '$profile.birthMon': today.getMonth()
  })
})
...