1

我一直在测试将我们的一个系统迁移到 Marklogic 9 并使用光学 API。

我们的功能之一涉及按 member_id、member_name 对声明进行分组并获取总和和计数,所以我做了这样的事情:

var results = op.fromView('test', 'claims')
  .groupBy(['member_id', 'member_name'], [
         op.count('num_claims', 'claim_no'),
         op.sum('total_amount', 'claim_amount')
         ])
  .orderBy(op.desc('total_amount'))
  .limit(200)
  .result()
  .toArray();

以上工作正常。结果的形式

[
  { 
    member_id: 1, 
    member_name: 'Bob', 
    num_claims: 10, 
    total_amount: 500
  }, 
  ...
]

但是,我们也有一个“公司”字段,其中每个索赔都在不同的公司下提交。基本上相关的视图列是claim_no、member_id、member_name、company、claim_amount

我希望能够显示一个列,其中列出了 member_id/member_name 已为其提出索赔的不同公司,以及每家公司有多少索赔。

即我希望我的结果是这样的:

[
  { 
    member_id: 1, 
    member_name: 'Bob', 
    num_claims: 10, 
    total_amount: 500,
    companies: [
      {
        company: 'Ajax Co',
        num_claims: 8
      },
      {
        company: 'Side Gig',
        num_claims: 2
      }
    ]
  }, 
  ...
]

我试过这样的事情:

results = results.map((member, index, array) => {
  var companies = op.fromView('test', 'claims')
    .where(op.eq(op.col('member_id'), member.member_id))
    .groupBy('company', [
      op.count('num_claims', 'claim_no')      
    ])
    .result()
    .toArray();
  member.companies = companies;
  return member;
});

并且输出看起来是正确的,但执行起来也很慢——差不多一分钟(索赔文件总数约为 120k)

在我们之前的 ML8 实现中,我们为每个成员预先生成摘要文档 - 因此检索速度相当快,但缺点是每当我们获得一堆新数据时,都必须重新生成所有摘要文档。我希望 ML9 的光学 API 可以更轻松地即时进行检索/分组/聚合,这样我们就不必这样做了。

理论上,我可以将公司添加到 groupBy 字段,然后根据需要合并结果查询中的行。但是这种方法的问题是我不能保证我会得到总金额的前 200 名(就像我原来的查询一样)

所以,问题是:有没有更好的方法在合理的执行时间内做到这一点?还是我应该坚持预先生成摘要文档?

4

1 回答 1

5

如果我理解正确,您应该能够使用一个分组两次的光学查询来实现它。

  • 第一组应该聚合到公司级别
  • 第二组应该聚合到成员级别,使用数组聚合收集详细信息

查询可能类似于以下内容:

const results =
  op.fromView('test', 'claims')
    .groupBy(['member_id', 'company'], [
        'member_name',
        op.count('company_claims', 'claim_no'),
        op.sum('company_amount', 'claim_amount')
        ])
    .select(['member_id',
        'member_name',
        'company_claims',
        'company_amount',
        op.as('company_desc', op.jsonObject([
                op.prop('company',    op.col('company')),
                op.prop('num_claims', op.col('company_claims'))
                ]))
        ])
    .groupBy(['member_id'], [
        'member_name',
        op.sum('num_claims',   'company_claims'),
        op.sum('total_amount', 'company_amount'),
        op.arrayAggregate('companies', 'company_desc')
        ])
    .orderBy(op.desc('total_amount'))
    .limit(200)
    .result()
    .toArray();

顺便说一句,如果您在聚合列表中指定一列,则会对其进行抽样。如果该列对整个组具有相同的值(我认为“member_name”就是这种情况),您可以对其进行采样,而不是将其指定为附加的分组键。

此外,在现代 JavaScript 中,通常避免使用 var 来支持 const 或 let。

希望有帮助,

于 2017-11-16T19:16:15.930 回答