代码之家  ›  专栏  ›  技术社区  ›  Faysal Ahmed

聚合中的MongoDB内部查询

  •  1
  • Faysal Ahmed  · 技术社区  · 6 年前

    这是我收藏的简单形式。

    { 
      "_id" : "abcdfg", 
      "job_id" : "job_111", 
      "user_id" : "user_001",
      "job_createdAt" : ISODate("2018-03-02T15:02:24.122+0000"),
      "score" : 240.91185185185185
    }
    

    假设一个用户发布了3个作业。 job_111, job_112, job_113 . 现在 user1,user2,user3,user4 已匹配(表示集合中有文档)到 job_112 and 113 . 和 user5 与所有3个作业匹配。 user6 完全匹配于 job_111

    现在,在我的聚合查询中,我想显示用户列表和他们匹配的作业数。但有一个条件:用户与 job_112 and job_113 具有更高的优先级(因为它们是最近创建的),并将首先显示。现在我这样提问

    [
      { $match: { job_id: { $in: ['job_112', 'job_113'] } } },      
      {
        $group:
          { _id: '$user_id', matched: { $sum: 1 }, score: { $max: '$score' } }
      },
      { $sort: { score: -1 } },
      { $skip: skip },
      { $limit: limit }
    ]
    

    这会让我得到像这样的东西

    [
     {
       user_id: 'user1',
       matched: 2
     },
     {
       user_id: 'user2',
       matched: 2
     },
     {
       user_id: 'user4',
       matched: 2
     },
     {
       user_id: 'user5',
       matched: 2
     }
    ]
    

    现在,当这个列表结束时(我使用分页和聚合计数计算出来),我想显示与匹配的用户 工作 只有。现在我的问题变成这样了

    [
      { $match: { job_id: { $in: ['job_111'] } } },      
      {
        $group:
          { _id: '$user_id', matched: { $sum: 1 }, score: { $max: '$score' } }
      },
      { $sort: { score: -1 } },
      { $skip: 0 },// << skip value resets to 0 since $in value changes
      { $limit: limit }
    ]
    

    这将返回类似这样的结果

    [
     {
       user_id: 'user5',
       matched: 1
     },
     {
       user_id: 'user6',
       matched: 1
     },
    ]
    

    现在这个结果有两个问题,我不想显示 小精灵 再次在名单上和他匹配的“否”是错误的。从技术上讲,它是3,但返回1是因为我的查询使它计算得像1。

    如何更新聚合查询以解决问题。我知道有办法排除用户 $nin 在用户字段中查找,但我不会事先得到用户列表,而该列表可能在真实场景中只有几百个。有没有办法在运行时找到匹配的列表 job_112 and/or job_113 以前?

    欢迎就如何改进此方法或任何其他新方法提出任何建议。

    2 回复  |  直到 6 年前
        1
  •  0
  •   mickl    6 年前

    您可以在一个查询中完成。你可以从 $match 但包括所有的工作。然后你可以用 $group 具有 $push 为每个用户收集所有作业。在最后一个阶段,有两种可能性:a matched 字段可以是 1 代表 job_111 $size 属于 $filter -已收集作业的ed数组。不会有少于 因为这两个案例中必须有一个匹配,所以您可以使用 $max 得到 2

    db.col.aggregate([
        {
            $match: { job_id: { $in: ["job_111", "job_112", "job_113"] } }
        },
        {
            $group: {
                _id: "$user_id",
                jobs: { $push: "$job_id" }
            }
        },
        {
            $project: {
                matched: {
                    $max: [ 1,
                        {  
                            $size: { 
                                $filter: { 
                                    input: "$jobs", 
                                    as: "job", 
                                    cond: { $in: [ "$$job", ["job_112", "job_113"] ] } 
                                }   
                            } 
                        }
                    ]
                }
            }
        }
    ])
    
        2
  •  0
  •   Faysal Ahmed    6 年前

    作为参考,我将发布最终对我有效的解决方案

    [
      { $match: find },
      {
        $group: {
          _id: '$user_id',
          jobs: { $push: '$job_id' },
          matched: { $sum: 1 },
          score: { $max: '$score' }
        }
      },
      {
        $project: {
          _id: '$_id',
          matched: '$matched',
          score: '$score',
          jobs: '$jobs',
          rangeType: {
            $cond: {
              if: {
                $gt: [
                  {
                    $size: {
                      $setIntersection: ['$jobs',['job_112', 'job_113'] ]
                    }
                  },
                  0
                ]
              },
              then: 10,
              else: 0
            }
          }
        }
      },
      { $sort: { rangeType: -1, score: -1 } },
      { $skip: skip },
      { $limit: limit }
    ]