2013-09-20 3 views
4

Как я могу запросить определенный месяц в mongodb, а не в диапазоне дат, мне нужен месяц, чтобы составить список дней рождения клиента для текущего месяца.Mongodb query specific month | year not date

В SQL будет что-то вроде этого:

SELECT * FROM customer WHERE MONTH(bday)='09' 

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

ответ

4

Вы можете сделать это с помощью aggregate с оператором $month проекции:

db.customer.aggregate([ 
    {$project: {name: 1, month: {$month: '$bday'}}}, 
    {$match: {month: 9}} 
]); 
+0

Вот что мне нужно. Но теперь у меня есть небольшая проблема: «Исключение: невозможно преобразовать из BSON типа EOO в Date» errorCode: 16006 Я считаю, что это потому, что в некоторых документах нет поля bday. Итак, как я могу использовать $ project с оператором $ exists, делающим запрос только с документами, у которых есть поле $ bday? –

+2

Добавьте '$ match' в начало вашего конвейера, который использует' $ exists', чтобы отфильтровать документы перед тем, как перейти к '$ project'. – JohnnyHK

+2

Теперь работает отлично, вот окончательный запрос: https://gist.github.com/lslucas/d95767f8da0013bb92a4 –

1

Если вы обеспокоены эффективностью, вы можете хранить данные месяц в отдельном поле в каждом документе.

+0

Для меня это не лучший или умный способ, но спасибо. –

5

С MongoDB 3.6 и новее, вы можете использовать оператор в $expr в запросе find(). Это позволяет создавать выражения запросов, которые сравнивают поля из того же документа на этапе $match.

db.customer.find({ "$expr": { "$eq": [{ "$month": "$bday" }, 9] } }) 

Для других версий MongoDB, рассмотреть возможность запуска в трубопровод агрегации, который использует $redact оператора, поскольку это позволяет включить с одного конвейера, функциональность с $project создать поле, которое представляет месяц поля даты и $match для фильтрации документов , которые соответствуют заданному состоянию месяца, являющемуся сентябрем.

В приведенном выше описании, $redact использует $cond tenary оператора в качестве средства, чтобы обеспечить условное выражение, которое будет создавать системную переменную, которая делает редакции. Логическое выражение в $cond проверит для равенства поля даты оператора с заданным значением, если это соответствует тогда $redact будет возвращать документы, используя системный переменную $$KEEP и выброса в противном случае с помощью $$PRUNE.

Запуск следующего трубопровода должен дать вам желаемый результат:

db.customer.aggregate([ 
    { "$match": { "bday": { "$exists": true } } }, 
    { 
     "$redact": { 
      "$cond": [ 
       { "$eq": [{ "$month": "$bday" }, 9] }, 
       "$$KEEP", 
       "$$PRUNE" 
      ] 
     } 
    } 
]) 

Это похоже на комбо $project + $match но вы должны затем выбрать все остальное поля, которые поступают в трубопровод:

db.customer.aggregate([ 
    { "$match": { "bday": { "$exists": true } } }, 
    { 
     "$project": { 
      "month": { "$month": "$bday" }, 
      "bday": 1, 
      "field1": 1, 
      "field2": 1, 
      ..... 
     } 
    }, 
    { "$match": { "month": 9 } } 
]) 

С другой альтернативы, хотя и медленный запрос, используя метод find() с $where как:

db.customer.find({ "$where": "this.bday.getMonth() === 8" }) 
+1

Я никогда раньше не видел $ redact. Спасибо за подсказку, это будет очень полезно. –

+1

Последнее исполнение не для производства, но очень удобно для повседневного использования :) спасибо! (плюс вам нужно помнить, чтобы вычесть число для соответствия getMonth) – Crisboot

+0

'{" $ where ":" this.bday.getMonth() === 8 "}' В этом запросе могу ли я использовать '$ in'? У меня есть целый ряд месяцев, как [2, 4,7]. Если 'bday.getMonth()' равно любому значению моего массива, мне нужна эта запись –