# MongoEngine mongoengine 是基于 pymongo å°è£…çš„ mongodb orm. 使用有一些有点: - æ›´ç›´è§‚çš„ api å°è£…,ä¸ç”¨ä½¿ç”¨ mongo 蹩脚的 dsl æ“作数æ®åº“. - å˜åœ¨æ•°æ® model, å…‹æœ schema-free 带æ¥çš„一些问题. - 使用方便 ## 多进程æœåС噍ä¸è¿žæŽ¥çš„æ³¨æ„项 `uwsgi`使用 prefork 模型时, è¦æ³¨æ„ä¸‹é˜²æ¢ socket fd 被共享带æ¥çš„问题. åˆå§‹åŒ–连接时ä¸è¦å³æ—¶è¿žæŽ¥,而是ç‰åˆ°å®žé™…使用时å†åŽ»ç¬¬ä¸€æ¬¡å»ºç«‹è¿žæŽ¥. ```python connect( alias=conf.social_mongodb_alias, host=conf.social_mongodb_host, replicaSet=conf.social_mongodb_replicaset, connect=False, read_preference=ReadPreference.SECONDARY_PREFERRED ) ``` 注æ„`connect=False`. ## 注æ„分æžä¸šåŠ¡åœºæ™¯å¯¹ä¸€è‡´æ€§çš„è¦æ±‚ `mongodb`æœ¬èº«ä¸æ”¯æŒè·¨æ–‡æ¡£çš„事务, å› æ¤æœ¬èº«å¯¹å¼ºä¸€è‡´æ€§æœ‰è¦æ±‚的跨文档业务ä¸åº”该选择使用它. `wiredtiger`å¼•æ“Žæœ¬èº«æ”¯æŒæ–‡æ¡£çº§åˆ«çš„é”, 但是在 `mongodb` ä¸, 我们ä¸èƒ½æ˜¾å¼çš„利用它, å› æ¤éœ€è¦åœ¨ä¸€äº›åœºæ™¯ä¸æ‰¾å¯ç”¨çš„åŽŸåæ“ä½œè§£å†³é—®é¢˜. ## 更新或创建 如果å˜åœ¨æŒ‡å®šæ–‡æ¡£,更新之, å¦åˆ™, 创建新文档. ``` ModelA.object(query..).update(updates.., upsert=True) ``` éœ€è¦æ³¨æ„, 使用 upsert æ—¶, 如果创建文档, 模型ä¸å®šä¹‰çš„ default 值,ä¸ä¼šåœ¨æ–°å»ºæ–‡æ¡£ä¸ä½“现, å› æ¤ update çš„å—æ®µéœ€è¦æ»¡è¶³ required å—æ®µéƒ½æœ‰æ›´æ–°å€¼. ## 原生查询与 mongoengine 查询混用 mongoengine æœ‰ä¸€äº›åŽŸç”ŸæŸ¥è¯¢ç‰¹æ€§ä¸æ”¯æŒ, 在应用ä¸å¯èƒ½å‡ºçŽ°æ··ç”¨çš„æƒ…å†µ, 使用`__raw__={}`æ¥æè¿°åŽŸç”ŸæŸ¥è¯¢. 一个实际的混用案例. ```python data = dict( __raw__={ '$addToSet': { 'items': { '$each': item_ids } } }, inc__item_num=len(item_ids) ) n = MvPlaylist.objects(pk=playlist_id).update_one(**data) ``` 使用`addToSet`这个 mongodb æ“作, åŒæ—¶ç”¨ mongoengine 的方å¼, 对 item_num è¿™ä¸ªå—æ®µå¢žåŠ äº†`len(item_ids)`个数é‡. ## æ¨¡åž‹å¢žåŠ å—æ®µ `mongoengine`的模型, å¢žåŠ å—æ®µ, å¯¹äºŽè€æ•°æ®å¹¶æ— 实际影å“, å› æ¤éœ€è¦è¯„ä¼°å—æ®µä¸å˜åœ¨çš„业务影å“. 出现影å“åŽ, æœ‰ä¸¤ç§æ–¹å¼è§£å†³è¿™ç§é—®é¢˜. 1. ä½¿ç”¨è„šæœ¬å¯¹è€æ•°æ®è¿›è¡Œè¡¥å……. 2. 在业务代ç ä¸åšå…¼å®¹. å°½é‡ä½¿ç”¨ç¬¬ä¸€ç§æ–¹æ³•. 如果数æ®ä¸å‡ºçŽ°äº†`mongoengine`模型ä¸ä¸å˜åœ¨çš„å—æ®µ, 会报异常, 直接修改数æ®,éœ€è¦æ³¨æ„这个问题. å¯ä»¥åœ¨`meta`设置`strict`为`False`æ¥å–消这个é™åˆ¶. ## ListField 索引 åŠ åœ¨`ListField`, å³`array`上的索引, 在引擎级别是`btree`索引. ## 使用DictField 在keyä¸ç¡®å®šçš„场景ä¸ä½¿ç”¨. 一个实际案例, 投票系统, æ¯ä¸€ä¸ªæŠ•票项目的选项都ä¸ä¸€è‡´, 怎么维护æ¯ä¸ªé€‰é¡¹çš„计数器. 定义一个model. ``` class Vote(Document): meta = { 'db_alias': 'social', 'indexes': ['name', ] } name = StringField(default='') # <option_key, count> options = DictField() ``` æ¯æ¬¡æŠ•票的时候, 对指定的optionè®¡æ•°å™¨åŠ 1. ``` Vote.objects(name=name).update_one(__raw__={ '$inc': { 'options.{}'.format(option): 1 } }, upsert=True) ``` 使用`__raw__`, è€Œä¸æ˜¯ç›´æŽ¥`inc__options__option`çš„åŽŸå› æ˜¯, option是å˜åŒ–çš„. ## Mongo ä¸åˆ¤æ–ä¸€ä¸ªå—æ®µç±»åž‹æ˜¯array ``` rs = db.comment.find({'sns_likes': {'$gte': []}}) ``` 使用`$type`没有æˆåŠŸ, ä¸çŸ¥é“ä»€ä¹ˆåŽŸå› . ## åªåœ¨åˆ›å»ºæ—¶ä¿®æ”¹æŸå—段 ``` n = UserFavorRecords.objects(user_id=pk).update( __raw__={ '$push': { 'records': { '$each': [ {'aid': aid, 'ftime': datetime.now()} ] } }, '$setOnInsert': { 'has_merged_device': False } }, user_id=pk, user_type=0, mtime=datetime.now(), upsert=True ) ``` 使用`setOnInsert`函数达到这个目的. ## 计数 获å–`array`å—æ®µçš„长度, 用`count`方法, ä¸è¦ä½¿ç”¨`len`, ä¸ç„¶ä¼šæŠŠæ•´ä¸ªæ•°æ®é›†å…¨éƒ¨å–回æ¥è®¡æ•°.