设置特定子字段的联合之间的差异

时间:2017-06-09 21:33:29

标签: python mongodb set aggregation-framework pymongo

我有一个大型集合,可以或多或少地建模为由以下代码创建的集合:

import string
from random import randint, random, choice


documents = []


for i in range(100):
    letters =  choice(string.letters[0:15])

    documents.append({'hgvs_id': "".join([str(randint(0,9)), letters]),
                      'sample_id': "CDE",
                     'number': i*random()*50 - 30 })

    documents.append({'hgvs_id': "".join([str(randint(0,9)), letters]),
              'sample_id': 'ABC',
              'number': i*random()*50 - 30 })

    documents.append({'hgvs_id': "".join([str(randint(0,9)), letters]),
                      'sample_id': 'GEF',
                      'number': i*random()*50 - 30 })


for i in range(10):    # add some unique values for sample_id 'ABC'

    letters = choice(string.letters[0:15])
    documents.append({'hgvs_id': "55" + letters,
                      'sample_id': 'ABC',
                      'number': i*random()*50 - 30 })

collection.insert_many(documents)

我正在尝试检索在具有特定hgvs_id(此处为sample_id)的文档中出现的唯一 ABC,但不包含在包含另外两个。通常,会有更多sample_id而不是三个。

这听起来很简单,但到目前为止我一直没有成功。鉴于我正在使用的集合的大小(~30GB),我一直在尝试使用aggregate框架,如下所示:

sample_1 = collection.aggregate(
    [
            {'$group':
                    {
                    '_id': '$hgvs_id',

                    #'sample_id' : {"addToSet": '$hgvs_id'},
                    'matchedDocuments':
                                    {'$push':
                                            {
                                                    'id': '$_id',
                                                    'sample_name': "$sample_id",
                                                    'hgvs_ids': "$hgvs_id"
                                            }
                                    },
                    }
            },
            {'$match': {
                   "$and": [
                            {'matchedDocuments': {"$elemMatch": {'sample_name': 'ABC'}}},
                            # Some other operation????
                           ]
                     }
   } 
])  #, allowDiskUse=True) may be needed

这会返回(理解为)hgvs_id等于sample_id ABC的所有{{1}}。任何线索都不仅仅是值得赞赏的。

1 个答案:

答案 0 :(得分:1)

如果分组值的“设置”中只有 sample_id,那么$size一个

使用MongoDB 3.4,您可以组合使用$in

[
  { "$group": { 
    "_id": "$hgvs_id",
    "samples": { "$addToSet": "$sample_id" }
  }},
  { "$redact": {
    "$cond": {
      "if": { 
        "$and": [
          { "$in": [ "ABC", "$samples" ] },
          { "$eq": [ { "$size": "$samples" }, 1 ] }
        ]
      },
      "then": "$$KEEP",
      "else": "$$PRUNE"
    }
  }}
]

否则请使用$setIntersection语法稍长一些:

[
  { "$group": { 
    "_id": "$hgvs_id",
    "samples": { "$addToSet": "$sample_id" }
  }},
  { "$redact": {
    "$cond": {
      "if": { 
        "$and": [
          { "$eq": [ { "$size": { "$setIntersection": [ "$samples", ["ABC"] ] } }, 1 ] },
          { "$eq": [ { "$size": "$samples" }, 1 ] }
        ]
      },
      "then": "$$KEEP",
      "else": "$$PRUNE"
    }
  }}
]

或者可能是最简单的形式支持所有支持聚合的版本:

  { "$group": { 
    "_id": "$hgvs_id",
    "samples": { "$addToSet": "$sample_id" }
  }},
  { "$match": {
    "$and": [{ "samples": "ABC" },{ "samples": { "$size": 1 } }]
  }}
]

同样的原则适用于任何数量的参数,因为“set”产生的参数大小与包含特定值的大小相同。