###admin
db.currentOp();
// show queries
db.currentOp({
"active": true,
"secs_running": {"$gt" : 3},
"ns": /^swag\./
});
// queries not using any index
db.adminCommand({
"currentOp": true,
"op": "query",
"planSummary": "COLLSCAN"
});
// operations with high numYields
db.adminCommand({
"currentOp": true,
"ns": /^swag\./,
"numYields": {"$gte": 100}
})
上面可以做为某些调优手段
Find Documents With An Array Field
$in: [...]means “intersection” or “any element in”$all: [...]means “subset” or “contain”$elemMatch: {...}means “any element match”$not: {$elemMatch: {$nin: [...]}}means “subset” or “in”
db.getCollection('message').find({
unlocks: {
$elemMatch: {
_cls: 'PointsUnlock',
user: ObjectId("57f662e727a79d07993faec5")
}
}
})
Find Documents With Computed Values Using $expr
db.getCollection('user').find({
$expr: {
$eq: [{$size: '$follows'}, {$size: '$blocks'}]
}
})
for loop
var oldTags = ['famous', 'newstar', 'featured', 'western', 'recommended', 'popular'];
oldTags.forEach(function(tag) {
db.getCollection('user').updateMany({tags: tag}, {$addToSet: {tags: 'badge:' + tag}});
});
python interface
insert an element into an array at certain postion
slot = 2
Post.objects.filter(id=post_id, media__id__ne=media_id).update_one(__raw__={
'$push': {
'media': {
'$each': [{'id': bson.ObjectId(media_id)}],
'$position': slot,
}
}
})
remove elements
采用pull的操作?
import bson
user_id = '5a66d5c2af9c462c617ce552'
tags = ['valid_tag_1', 'future_tag']
updated_result = User._get_collection().update_one(
{'_id': bson.ObjectId(user_id)},
{'$pull': {'schedules': {'tag': {'$in': tags}}}},
)
print(updated_result.raw_result)
# {'n': 1, 'nModified': 1, 'ok': 1.0, 'updatedExisting': True}
upsert
tag_schedule = TagSchedule.objects \
.filter(user=user_id, tag='vip') \
.modify(
started_at=started_at,
ended_at=ended_at,
upsert=True
)
设置upsert=True
Aggregation Pipeline
$match: Filters documents.$project: Modifies document fields.$group: Groups documents by fields.$lookup: Joins another collection.$replaceRoot: Promotes an embedded document field to the top level and replace all other fields.$unwind: Expanses an array field into multiple documents along with original documents.$facet: Processes multiple pipelines within one stage and output to different fields.
match multiple condition
import werkzeug
user_agent = werkzeug.UserAgent('swag/2.25.1 (iPhone; iOS 11.4.1; Scale/2.00; com.live.swag.enterprise; zh-tw)')
user_preferences = ['gender:female', 'gender:male']
user_tags = ['beta', 'vip']
user_platforms = [user_agent.platform]
def stages():
now = utils.utcnow()
yield {'$match': {
'$and': [
{'nbf': {'$lte': now}},
{'exp': {'$gt': now}},
{'requirements': {'$elemMatch': {
'preferences': {'$not': {'$elemMatch': {'$nin': user_preferences}}},
'tags': {'$not': {'$elemMatch': {'$nin': user_tags}}},
'platforms': {'$not': {'$elemMatch': {'$nin': user_platforms}}},
'$or': [
{'$and': [
{'version_major_min': {'$lte': user_agent.version.major}},
{'version_minor_min': {'$lte': user_agent.version.minor}},
]},
{'$and': [
{'version_minor_min': {'$exists': False}},
{'version_minor_min': {'$exists': False}},
]},
],
}}},
],
}}
yield {'$project': {
'name': True,
'nbf': True,
'exp': True,
'positions': {'$objectToArray': '$positions'},
}}
yield {'$unwind': '$positions'}
yield {'$sort': {
'exp': 1,
}}
yield {'$project': {
'_id': False,
'name': True,
'position': '$positions.k',
'url': {'$arrayElemAt': ['$positions.v.urls', 0]},
'startedAt': {'$floor': {'$divide': [{'$subtract': ['$nbf', constants.UNIX_EPOCH]}, 1000]}},
'endedAt': {'$floor': {'$divide': [{'$subtract': ['$exp', constants.UNIX_EPOCH]}, 1000]}},
}}
yield {'$group': {
'_id': '$position',
'items': {'$push': '$$ROOT'},
}}
try:
docs = Promotion.objects.aggregate(*stages())
except StopIteration:
docs = []
else:
docs = list(docs)
project负责显示项,unwind负责展开?
Collect Items With $group And $addToSet
def stages():
now = utils.utcnow()
yield {'$match': {
'schedules': {'$elemMatch': {
'nbf': {'$lte': now},
'exp': {'$gte': now}
}}
}}
yield {'$unwind': '$schedules'}
yield {'$match': {
'schedules.nbf': {'$lte': now},
'schedules.exp': {'$gte': now}
}}
yield {'$project': {
'_id': False,
'id': '$_id',
'username': True,
'tag': '$schedules.tag',
'nbf': '$schedules.nbf',
'exp': '$schedules.exp'
}}
yield {'$group': {
'_id': '$id',
'tags': {'$addToSet': '$tag'},
}}
for user_tag_schedule in User.objects.aggregate(*stages()):
print(user_tag_schedule)
# output:
# {'_id': ObjectId('579b9387b7af8e1fd1635da9'), 'tags': ['stats']}
# {'_id': ObjectId('5a66d5c2af9c462c617ce552'), 'tags': ['chat', 'vip']}
Do Advanced $project With $let
If you find youself want to do $project twice to tackle some fields, you should use $let.
def stages():
yield {'$match': {
'purchases.user': g.user.id,
}}
yield {'$project': {
'_id': False,
'id': '$_id',
'name': True,
'image': {
'$ifNull': [{'$arrayElemAt': ['$images', 0]}, None],
},
'purchasedAt': {
'$let': {
'vars': {
'purchase': {
'$arrayElemAt': [
{
'$filter': {
'input': '$purchases',
'as': 'purchase',
'cond': {
'$and': [
{'$eq': ['$$purchase.user', g.user.id]},
],
},
},
},
0,
],
},
},
'in': '$$purchase.timestamp',
},
},
}}
try:
docs = MessagePackProduct.objects.aggregate(*stages())
except StopIteration:
docs = []
else:
for doc in docs:
print(doc)
Query The First Element In An Array Field With $arrayElemAt And $filter
def stages():
category_tag = 'category:user'
currency = 'usd'
platform = 'ios'
yield {'$match': {
'active': True,
'tags': category_tag,
}}
yield {'$project': {
'_id': False,
'id': '$_id',
'name': True,
'caption': True,
'description': True,
'image': {
'$ifNull': [{'$arrayElemAt': ['$images', 0]}, None],
},
'preview_message': True,
'metadata': True,
'created_at': True,
'updated_at': True,
'active': True,
'sku': {
'$ifNull': [
{
'$arrayElemAt': [
{
'$filter': {
'input': '$skus',
'as': 'sku',
'cond': {
'$and': [
{'$eq': ['$$sku.currency', currency]},
{'$eq': ['$$sku.attributes.platform', platform]},
]
}
},
},
0
]
},
None
],
},
'tags': True,
'total': True,
'is_bought': {'$in': [g.user.id, {'$ifNull': ['$purchases.user', []]}]},
}}
yield {'$sort': {'is_bought': 1, 'created_at': -1}}
try:
docs = MessagePackProduct.objects.aggregate(*stages())
except StopIteration:
docs = []
else:
for doc in docs:
print(doc)
Join Another Collection Using $lookup
def stages():
yield {'$match': {
'tags': 'pack:prod_CR1u34BIpDbHeo',
}}
yield {'$lookup': {
'from': 'user',
'localField': 'sender',
'foreignField': '_id',
'as': 'sender_data',
}}
yield {'$unwind': '$sender_data'}
yield {'$project': {
'_id': False,
'id': '$_id',
'sender': {
'id': '$sender_data._id',
'username': '$sender_data.username',
},
'caption': True,
'posted_at': True,
}}
yield {'$sort': {'posted_at': -1}}
try:
docs = Message.objects.aggregate(*stages())
except StopIteration:
docs = []
else:
for doc in docs:
print(doc)
lookup作为join操作
Count Documents In Another Collection With $lookup (JOIN)
def stages():
category_tag = f'category:{category}'
yield {'$match': {
'active': True,
'tags': category_tag,
}}
yield {'$addFields': {
'message_pack_id_tag': {'$concat': ['pack:', '$_id']},
}}
yield {'$lookup': {
'from': 'message',
'localField': 'message_pack_id_tag',
'foreignField': 'tags',
'as': 'total',
}}
yield {'$addFields': {
'total': {'$size': '$total'}
}}
yield {'$project': {
'_id': False,
'id': '$_id',
'name': True,
'total': True,
}}
try:
docs = MessagePackProduct.objects.aggregate(*stages())
except StopIteration:
docs = []
else:
for doc in docs:
print(doc)
useful tools
monitoring
$ mongotop
$ mongostat
$ pip install mongotail
$ mongotail 127.0.0.1:27017/swag -l 2
$ mongotail 127.0.0.1:27017/swag -f
$ pip install mtools
$ mloginfo mongod.log