Pymongo 查询过滤版本化数据

Pymongo Query Filtering a versioned data

这是来自我的数据库的示例数据:

  {'data': 
     [{'is_active': True, 'printer_name': 'A', 'vid': 14510}, 
      {'is_active': True, 'printer_name': 'Changed A', 'vid': 14511}
     ]
  }, 
  {'data': 
    [{'is_active': False, 'printer_name': 'B', 'vid': 14512}]
  }

这里的vid字段就是version id。每当编辑记录时,修改后的数据都会被推送到列表中,因此它的视频比旧版本高。

现在我想定义一个名为 get_all_active_printers 的方法,它 return 所有带有 is_active :True

的打印机

这是我的尝试,但它 return 两台打印机都不应该 return 打印机 B

def get_all_active_printers():
    return printers.find(
        {'data.is_active':  True}, {"data": {"$slice": -1}, '_id': 0, 'vid': 0})

我的查询有什么问题?

编辑 1 以回应 WanBachtiar

的评论

这是使用命令 print([c for c in get_all_active_printers()])

的实际输出
[{'data': [{'printer_name': 'Changed A', 'vid': 1451906336.6602068, 'is_active': True, 'user_id': ObjectId('566bbf0d680fdc1ac922be4c')}]}, {'data': [{'printer_name': 'B', 'vid': 1451906343.8941162, 'is_active': False, 'user_id': ObjectId('566bbf0d680fdc1ac922be4c')}]}]

正如您在实际输出中看到的那样 - 打印机 Bis_active 值为 False,但 get_all_active_printers 仍然 returns B

这是我的版本详细信息:

在 Ubuntu 14.04,如果重要的话。

编辑 2

注意到另一个问题。查询是 returning vid 字段,即使在投影中明确提到了 'vid': 0

* 编辑 3*

我不确定你说的是什么意思

"make sure that there is no other documents for {'printer_name': 'B'}"

。是的,第二个数据(在打印机 B 上)有第二行。那是第一个数据 - 当字段 is_activetrue 时创建打印机时。后来就变成了false。这是数据库的快照:

但我想过滤最新数据,因为旧数据仅用于保留审计线索。

如果我将 'data.is_active': True 移动到以下代码中的投影:

def get_all_active_printers():
    return printers.find(
        {}, {'data': {'$slice': -1}, 'data.is_active': True, '_id': 0, 'vid': 0})

我收到以下错误消息:

pymongo.errors.OperationFailure: database error: You cannot currently mix including and excluding fields. Contact us if this is an issue.

那么如何根据上面的快照根据最新数据进行过滤?对不起,如果我的问题之前没有说清楚。

感谢您澄清问题。 所以你想查询只有最新元素 is_active: True.

的文档

不幸的是,在您的情况下,find({'data.is_active': True}) 会找到包含任何 data 元素和 is_active:True 的所有文档,而不仅仅是数组中的最后一个元素。此外,在不知道数组长度的情况下,您无法使用 array.i 语法引用数组的最后一个元素。

然而还有其他ways/alternatives:

  1. 使用 $push, $each and $position to insert new elements to the front of the array. Mongo Shell 示例更新:
/* When updating */
db.printers.update(
    /* Filter document for printer B */
    {"data.printer_name": 'B'}, 
    /* Push a new document to the front of the array */
    {"$push": { 
            "data": { 
                $each: [{'is_active': false, 'printer_name': "B", 'vid': 14513 }],
                $position: 0 
            } 
        } 
    }
);

/* When querying now you know that the latest one is on index 0 */
db.printers.find(
    {"data.0.is_active": true},
    {"data": { $slice: 1} }
);

请注意,$position 在 MongoDB v2.6 中是新的。

  1. 使用MongoDBaggregation to $unwind the array, $group then $match进行筛选。例如:
db.printers.aggregate([
    {$unwind: '$data' }, 
    {$sort: { 'data.vid' : 1 } }, 
    {$group: {
        '_id': { 'printer_name' : '$data.printer_name', id:'$_id' },
        'vid': { $max: '$data.vid' }, 
        'is_active' : { $last: '$data.is_active' } 
        } 
    }, 
    {$match:{"is_active": true}}
]);


re-consider 文档架构可能对您有益。例如,与其拥有文档数组,不如考虑将它们扁平化以便于查询。

{'is_active': true, 'printer_name': 'A', 'vid': 14512} 
{'is_active': false, 'printer_name': 'B', 'vid': 14513}

有关不同版本跟踪架构设计的更多示例和讨论,请参阅以下博客文章:

也是关于模式设计的有用参考:Data Modeling Introduction


The query is returning vid field, even though have clearly mentioned 'vid': 0 in the projection.

您可以使用 "data.vid": 0 而不是 vid:0 来隐藏它。


If i move 'data.is_active': True to the projections as in the following code... I get the following error message.

您必须遵守投影规则。有关预测的更多信息,请参阅 projecting fields from query results

如果您正在开始一个新项目,我建议您使用 MongoDB 的最新稳定版本,目前是 v3.2.0

此致,

万.