Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/mongodb/11.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
MongoDB统计相关集合中的数百万文档_Mongodb_Aggregation Framework - Fatal编程技术网

MongoDB统计相关集合中的数百万文档

MongoDB统计相关集合中的数百万文档,mongodb,aggregation-framework,Mongodb,Aggregation Framework,所以,我被卡住了,我的第一颗子弹落在了Stackoverflow上,在多年的潜伏之后,我确实需要一些好的建议。 我有两种文档类型: 文章 目前约有1.5万篇文章,但随着客户的加入,文章数量迅速增加。我们不希望这里有限制 { "_id" : ObjectId("5bd054d8fd5298d07ddc293a"), "title" : "A neat title" } 活动 每篇文章大约1k个活动,在用户导航的每个营销相关阶段编写(例如:查看或共享文章)。增加网站流量将增加文

所以,我被卡住了,我的第一颗子弹落在了Stackoverflow上,在多年的潜伏之后,我确实需要一些好的建议。 我有两种文档类型:

文章

目前约有1.5万篇文章,但随着客户的加入,文章数量迅速增加。我们不希望这里有限制

{ 
    "_id" : ObjectId("5bd054d8fd5298d07ddc293a"), 
    "title" : "A neat title"
}
活动

每篇文章大约1k个活动,在用户导航的每个营销相关阶段编写(例如:查看或共享文章)。增加网站流量将增加文章和活动之间的1/1000比例

{ 
    "_id" : ObjectId("5bbdae8afd529871473c1111"), 
    "article" : ObjectId("5bd054d8fd5298d07ddc293a"), 
    "what" : "view"
}
{ 
    "_id" : ObjectId("5bbdae8afd529871473c2222"), 
    "article" : ObjectId("5bd054d8fd5298d07ddc293a"), 
    "what" : "share"
}
我的目标是汇总相关活动的文章:

{ 
    "_id" : ObjectId("5bd054d8fd5298d07ddc293a"), 
    "title" : "A neat title",
    "statistics" : {
        'view':1,
        'share':1,
     }
}
Activity.article和Activity.what上的索引都已设置

在小数据集上,我可以通过此聚合轻松实现我的目标:

db.article.aggregate([
{ $match: { 
    ... some unrelevant match
}},
{ $lookup: {
     from: "activity",
     localField: "_id",
     foreignField: "article",
     as: "activities"
}},
{ $project: {
    data: '$$ROOT',
    views: {$filter: {
        input: '$activities',
        as: 'view',
        cond: {$eq: ['$$what', 'view']}
    }},
    shares: {$filter: {
        input: '$activities',
        as: 'share',
        cond: {$eq: ['$$what', 'share']}
    }}
}},
{ $addFields: {
        'data.statistics.views': { $size: '$views' },
        'data.statistics.shares': { $size: '$shares' }
}},
{ $project: { 
    'data.activities': 0,
    'views': 0,
    'shares': 0
}},
{ $replaceRoot: { newRoot: '$data' } },
])
只要$lookup没有超过16MB的限制,这就给了我想要的东西。如果我有数百万个活动,聚合将失败,即使文档中说明:

该限额仅适用于退回的文件;在管道处理过程中,文档可能会超过此大小

我已经尝试了什么:

  • 添加allowDiskUse/fails时,它似乎没有写入任何内容,因为我在数据目录中没有看到_tmp文件夹
  • 添加allowDiskUse+cursor/也失败
  • 在带有{$out:“result”}/的临时集合中保存结果失败
  • 使用/更改聚合是可行的,但对于150万个活动,结果会在10秒内返回,因为在展开之后,管道的每个阶段(即:组回以重建文档)都无法使用现有索引
  • 更改/它可以工作,但对于20万个活动,它需要1.5分钟(我停止测试150万次),结果会在6秒钟内返回。这可能是我最好的马
  • 我甚至试过这样的方法:

    db.article.aggregate([
        { $match: { 
            ...
        }},
        { $addFields: {'statistics.views': db.activity.find({ "article": ObjectId('5bd054d8fd5298d07ddc293a'), "what" : "view" }).count()
    ])
    
    这非常有效(0.008秒/篇)。问题是我无法“变量化”该ObjectId:

    db.article.aggregate([
        { $match: { 
                ...
        }},
        { $addFields: {
    
                'statistics.views': db.activity.find({ "article": ObjectId('5bd054d8fd5298d07ddc293a'), "what" : "view" }).count(),
    // ^ returns correct count
    
                'statistics.querystring': { $let: {
                vars:   { articleid: "$_id", whatvalue: 'view' },
                in:     { 'query':{ $concat: [ "db.activity.find( { 'article': ObjectId('", { $toString: "$$articleid" }, "'), 'what' : '", "$$whatvalue", "' } ).count()" ] } }
                }},
    // ^ returns correct query to string
    
    
                'statistics.variablequery': { $let: {
                vars: { articleid: "$_id", whatvalue: 'view' },
                in:  db.activity.find( { "article": '$$articleid', "what" : "$$whatvalue" } ).count()
                }},
    // ^ returns 0
    
        }}
    ])
    

    我对每种解决方案都持开放态度,即使我在写活动时排除了在文章中增加计数器的可能性,也可以更改我的收藏,因为我需要按日期筛选(即:给我上周的所有份额)

    活动文档将有多大?因为它们看起来很小,所以我会将活动作为数组保存在文章文档中。文档限制为16mb,这样就可以了,您可以避免磁盘上的_id和重复的article id字段,从而使磁盘上的数据更小。记住MongoDB不是传统的SQL数据库-嵌入的字段和文档是您的朋友

    如果活动将是无限的(即可以永远增长),那么我建议采用bucketing方法,即每天每篇文章都有一个活动文档,类似于:

    { 
        "_id" : {
           "article" : ObjectId("5bbdae8afd529871473c2222"),
           "when": "2018-12-27"
        },
        "activities" : [
           {"what": "view", "when": "12:01"},
           {"what": "share", "when": "13:16"}
        ]
    }
    

    您可以在“何时”字段中存储完整的时间戳或ISODATE,但这种方法更具可读性,而且可能在磁盘上更紧凑。

    也许我错了,但在文章中将“数百万”活动放入一个数组将违反16 mb的限制……我不认为这是每篇文章的数百万个活动。更像是100万篇文章,每篇都有100甚至1000个活动。没问题。让我更好地理解:我创建一篇文章,在它的生命周期中,我在子数组中创建活动。当我达到~1000个活动时,我会用空数组复制文章?或者更好,每天我都用一个空的“活动”数组复制文章。。。或者更好的是,我可以创建一个DailyReport对象,其中包含活动列表和类型/小时/用户的预计算索引。。。我正在进行头脑风暴,但这似乎是一个很好的解决方案,你所描述的就是“bucketing”。退房请用大概的尺寸更新您的问题-总共有多少篇文章,每篇文章将有多少活动?在现有文章中添加活动的频率是多少?