Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/mongodb/12.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_Mapreduce_Report_Aggregation Framework - Fatal编程技术网

MongoDB聚合事件集合的线性漏斗,是否可能?

MongoDB聚合事件集合的线性漏斗,是否可能?,mongodb,mapreduce,report,aggregation-framework,Mongodb,Mapreduce,Report,Aggregation Framework,我有许多事件文档,每个事件都有许多字段,但与我的查询相关的字段有: person_id-对触发事件的人员的引用 事件-用于标识事件的字符串键 发生时间-事件发生时的utc 我想要实现的是: 有关事件键列表,例如“['event_1'、'event_2'、'event_3'] 按顺序获取执行每个事件和该事件之前所有事件的人数,即: 执行事件_1的人数 执行事件_1和事件_2的人数 执行事件_1、事件_2和事件_3的人数 等 第二个目标是能够获得每个事件的平均发生时间,以便我可以计算每个事

我有许多事件文档,每个事件都有许多字段,但与我的查询相关的字段有:

  • person_id-对触发事件的人员的引用
  • 事件-用于标识事件的字符串键
  • 发生时间-事件发生时的utc
我想要实现的是:

  • 有关事件键列表,例如“['event_1'、'event_2'、'event_3']
  • 按顺序获取执行每个事件和该事件之前所有事件的人数,即:
    • 执行事件_1的人数
    • 执行事件_1和事件_2的人数
    • 执行事件_1、事件_2和事件_3的人数
  • 第二个目标是能够获得每个事件的平均发生时间,以便我可以计算每个事件之间的平均时间
我得到的最好结果是以下两张地图:

db.events.mapReduce(function () {
  emit(this.person_id, {
    e: [{
      e: this.event,
      o: this.occurred_at
    }]
  })
}, function (key, values) {
  return {
    e: [].concat.apply([], values.map(function (x) {
      return x.e
    }))
  }
}, {
  query: {
    account_id: ObjectId('52011239b1b9229f92000003'),
    event: {
      $in: ['event_a', 'event_b', 'event_c','event_d','event_e','event_f']
    }
  },
  out: 'people_funnel_chains',
  sort: { person_id: 1, occurred_at: 1 }
})
然后:

db.people_funnel_chains.mapReduce(function() {
  funnel = ['event_a', 'event_b', 'event_c','event_d','event_e','event_f']
  events = this.value.e;
  for (var e in funnel) {
    e = funnel[e];
    if ((i = events.map(function (x) {
      return x.e
    }).indexOf(e)) > -1) {
      emit(e, { c: 1, o: events[i].o })
      events = events.slice(i + 1, events.length);
    } else {
      break;
    }
  }
}, function(key,values) {
    return {
        c: Array.sum(values.map(function(x) { return x.c })),
        o: new Date(Array.sum(values.map(function(x) { return x.o.getTime() }))/values.length)
    };
}, { out: {inline: 1} })
我希望通过使用聚合框架实时实现这一点,但看不到实现这一点的方法。对于10秒的数千条记录,这需要10秒的时间,我可以增量运行它,这意味着它的速度足够快,可以接收新数据,但是如果我想修改原始查询(例如更改事件链),它不能在一个请求中完成,我希望它能够做到这一点

使用Cursor.forEach()更新 使用Cursor.forEach()我已经在这方面取得了巨大的改进(基本上消除了对第一个map reduce的需求)

我想知道在内存中使用数据定制的东西是否能够改进这一点?将MongoDB中成千上万的记录存入内存(在另一台机器上)将是一个瓶颈,有没有一种我不知道的技术可以做到这一点?

我写了一篇文章,但作为总结,您需要做的是根据您关心的操作来投影您的操作,将操作字段的值映射到适当的关键字名称中,按人分组聚合三个操作的时间(可选次数),然后投影新字段,检查操作1之后是否执行了操作2,行动3是在行动2之后完成的。。。最后一个阶段只是总结了只做了1次,或1次,然后2次,或1次,然后2次,然后3次的人数

使用函数生成聚合管道,可以根据传入的操作数组生成结果

在我的测试用例中,整个管道在200毫秒内运行,收集了40000个文档(这是在我的小型笔记本电脑上)

正如正确指出的那样,我所描述的一般解决方案假设,虽然参与者可以多次采取任何行动,但他们只能从行动1推进到行动2,但不能直接从行动1跳到行动3(将行动顺序解释为描述在完成行动2之前无法执行行动3的先决条件)

事实证明,聚合框架甚至可以用于顺序完全任意的事件序列,但您仍然想知道在某个时刻有多少人执行了顺序action1、action2、action3

对原始答案作出的主要调整是在中间增加一个额外的两级步骤。此步骤展开“按人员收集”文档,以对其重新分组,查找第一个操作第一次出现之后的第二个操作的第一次出现

一旦我们确定了动作1的最终比较结果,随后是动作2的最早出现,并将其与动作3的最新出现进行比较

它可能可以被推广到处理任意数量的事件,但两个事件之后的每一个额外事件都会向聚合添加两个以上的阶段


以下是您想要的答案。

这是少数几个聚合框架没有帮助的情况之一,老实说,我甚至看不到这种情况实时发生,数据库很难有效地做到这一点这可能是通过实现自定义(您自己的内存分析引擎)来解决的,或者查看其他数据库选项。我目前实际使用的解决方案是按person\u id和executed\u at(使用索引)对事件排序,然后使用Cursor.forEach()进行迭代。在我的MPB上,它可以在大约4秒内减少25k人362k个事件,比上面列出的地图减少所需的50秒还要快。我想知道定制是否能够在这方面有所改进,因为从MongoDB中将成千上万的记录存入内存永远是一个瓶颈,是否有一种我不知道的技术可以做到这一点?每个事件是否会对每个人发生多次?当你说“按顺序”时,你是否只对第一件事感兴趣?我认为这可以通过聚合框架来实现,实际上,至少部分/大部分-您可能需要两个聚合框架查询…@msaspence,因此使用单个聚合管道看起来非常简单。我不是100%清楚你的平均时间是什么意思-我可以举一个例子来计算动作之间的平均时间。如果我错了,请纠正我的错误,但只有当每个事件的第一个实例符合你想要的顺序时,此解决方案才会起作用?例如,如果你在寻找序列e1,e2,e3,并且有人触发了以下序列:e1,e3,e2,e3,它只会注册为他们已经到达。如果你可以假设e3在e2被激活之前不能被激活,这很好,不幸的是,我们无法做出这样的假设。我不清楚每个动作是否可以多次执行,以及您是否关心每个动作是否按照严格的顺序执行-最初,我有每个动作的第一次和最后一次用于更复杂的比较。如果您明确您的确切要求,我可以解释您如何执行调整算法
var time = new Date().getTime(), funnel_event_keys = ['event_a', 'event_b', 'event_c','event_d','event_e','event_f'], looking_for_i = 0, looking_for = funnel_event_keys[0], funnel = {}, last_person_id = null;
for (var i in funnel_event_keys) { funnel[funnel_event_keys[i]] = [0,null] };
db.events.find({
  account_id: ObjectId('52011239b1b9229f92000003'),
  event: {
    $in: funnel_event_keys
  }
}, { person_id: 1, event: 1, occurred_at: 1 }).sort({ person_id: 1, occurred_at: 1 }).forEach(function(e) {

  var current_person_id = e['person_id'].str; 

  if (last_person_id != current_person_id) {
    looking_for_i = 0;
    looking_for = funnel_event_keys[0]
  }

  if (e['event'] == looking_for) {
    var funnel_event = funnel[looking_for]
    funnel_event[0] = funnel_event[0] + 1;
    funnel_event[1] = ((funnel_event[1] || e['occurred_at'].getTime()) + e['occurred_at'].getTime())/2;
    looking_for_i = looking_for_i + 1;
    looking_for = funnel_event_keys[looking_for_i]
  }

  last_person_id = current_person_id;
})
funnel;
new Date().getTime() - time;