MongoEngine
で指定した日付の範囲内のドキュメントを取り出す。
コレクションSomeCollection
は以下のようになっています。
なお、MongoEngine
を使っているので、Python
上ではSomeDocument
クラスでコレクションを定義しています。
{
"_id": ObjectID(...),
"name": "abc1",
"days": [
{
"day": ISODate("2017-09-30T00:00:00Z"),
... // Other fields..
},
{
"day": ISODate("2017-10-01T00:00:00Z"),
... // Other fields..
},
{
"day": ISODate("2017-10-02T00:00:00Z")
... // Other fields..
},
...
... // More documents here
...
]
}
ここから、該当する日付のドキュメントを取り出す場合を考えました。 例えば、2017年11月のデータを取り出した時は次のようになります。
[
{
"day": ISODate("2017-10-01T00:00:00Z"),
... // Other fields..
},
{
"day": ISODate("2017-10-02T00:00:00Z")
... // Other fields..
},
...
... // More November 2017 documents here
...
]
結構苦労して探した結果、3. API Reference — MongoEngine 0.15.0 documentationとAggregation Pipeline — MongoDB Manual 3.4によると、
次のようにSomeDocument.objects.aggregate(*pipeline, **kwargs)
を使うとよいです。パイプラインの作成が大事です。
実例の前にPython
で上の定義を載せておきます。
MongoEngine
は、flask-mongoengine==0.9.3
から利用しています。
# MongoEngineインスタンスを取得
from app import db
# ドキュメントの定義
class Day(db.EmbeddedDocument):
day = db.DateTimeField(required=True)
# ... other fileds here
class SomeDocument(db.Document):
name = db.StringField(max_length=255, required=True)
days = db.EmbeddedDocumentListField('Day')
必要なところで、抽出を行います。
import datetime as dt
# 抽出する日付の始端と終端。ここでは2017年10月分
start_day = dt.datetime(2017, 10, 1)
end_day = dt.datetime(2017, 11, 1)
# パイプラインを作成
pipeline = [
{'$unwind': '$days'},
{'$match': { 'days.day': {'$gte': start_day,
'$lt': end_day}
}},
# $projectで抽出するフィールド名と作成されるフィールド名を指定できる
{'$project': {"day": "$days.day",
"somefield": "$days.somefield",
}},
]
# カーソルオブジェクトが返ってくる
cursor = SomeDocument.objects.aggregate(*pipeline)
for doc in cursor:
do_something(doc)
# リストが欲しいなら作る
doclist = [doc for doc in cursor]
$gteが、>=のはずなのに10/2からのデータになってしまう問題があります。
これは、"$gt": dt.datetime(2017, 9, 30)
に取り替えれば回避できそうです。
原因ははっきりしませんが、Python
のdatetime
クラスのタイムゾーンとかnative
とかaware
あたりにありそうです。
$unwind
は、$unwind (aggregation) — MongoDB Manual 3.4を、
$match
などについては、$match (aggregation) — MongoDB Manual 3.4が、参考になります。
MongoDB
にDateField
があればいいのにと思いました。
コメント