Pymongo查询过滤版本化数据

时间:2016-01-03 23:00:06

标签: mongodb pymongo

这是我数据库中的示例数据:

  {'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。无论何时编辑记录,修改后的数据都会被推送到列表中,因此它的vid比旧版本更高。

现在我要定义一个名为get_all_active_printers的方法,该方法返回所有is_active :True

的打印机

这是我的尝试,但是当它不应该返回打印机时返回两台打印机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')}]}]

正如您在实际输出中看到的那样 - 打印机is_active的{​​{1}}值为False,但B仍然返回get_all_active_printers

这是我的版本详情:

  • Python 3.4.3
  • pymongo 3.2
  • mongodb 2.4.9

在Ubuntu 14.04上,如果重要的话。

编辑2

还有另一个问题。该查询返回B字段,即使在投影中已明确提及vid

*编辑3 *

当你说

时,我不确定你的意思
  

"确保{' printer_name':' B'}"

没有其他文件

。是的,第二个数据(在打印机B上)有第二行。这是第一个数据 - 当字段'vid': 0is_active时创建打印机。之后它变为true。这是数据库的快照:

enter image description here

但我希望过滤最新数据,因为旧数据仅用于保留审计跟踪。

如果我将false移动到投影中,如下面的代码所示:

'data.is_active': True

我收到以下错误消息:

  

pymongo.errors.OperationFailure:数据库错误:你当前不能   混合包括和排除字段。如果这是一个问题,请联系我们。

那么鉴于上面的快照,我如何基于最新数据进行过滤?对不起,如果我的问题早些时候没有说清楚。

1 个答案:

答案 0 :(得分:2)

感谢您澄清问题。 因此,您希望查询仅包含is_active: True的最新元素的文档。

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

然而,还有其他方法/替代方案:

  1. 使用$push$each$position进行更新,以便在数组前面插入新元素。 Mongo Shell示例:
  2. /* 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. 使用MongoDB aggregation$unwind数组$group然后$match进行过滤。例如:
    2. 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}}
      ]);
      


      重新考虑文档架构可能对您有所帮助。例如,您可以考虑让它们保持平坦以便于查询,而不是拥有一系列文档。

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

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

      也是架构设计的有用参考:Data Modeling Introduction


        

      查询返回vid字段,即使有明确提及   ' vid':投影中的0。

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


        

      如果我按照以下代码移动' data.is_active':True到投影...我收到以下错误消息。

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

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

      问候,

      万。