You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

MongoDB:JS聚合查询转C#时处理缺失数组字段的问题

MongoDB用户警报统计问题及修复

需求

基于长整型userid,统计MongoDB中siren集合里用户的三类数据:

  • 拥有的警报数量
  • 订阅的警报数量
  • 负责的警报数量

Siren文档结构(仅展示相关字段)

{
  "_id": ObjectId( "65fc94593273fc2ab6ff8960"),
  "ownerid": NumberLong( "99999999"), 
  "listener": [
    NumberLong( "11111111"),
    NumberLong( "00000000")
  ],
  "responsible": [
    NumberLong( "11111111")
  ]
}

可用的JS聚合查询

db.sirens.aggregate([
  {
    "$match": {
      "$or": [
        { "ownerid": userId },
        { "listener": userId },
        { "responsible": userId }
      ]
    }
  },
  {
    "$group": {
      "_id": null,
      "owner": { "$sum": { "$cond": [{ "$eq": ["$ownerid", userId ] }, 1, 0] }},
      "responsible": { "$sum": { "$cond": [{ "$and": [
                 { "$ne": ["$responsible", null] },
                 { "$isArray": "$responsible" }, 
                 { "$in": [userId, "$responsible"] }
              ] }, 1, 0 ] }},
      "listener": { "$sum": { "$cond": [{ "$and": [
                 { "$ne": ["$listener", null] },
                 { "$isArray": "$listener" }, 
                 { "$in": [userId, "$listener"] }
              ] }, 1, 0 ] }}   
    }
  }
]);

问题

listener和responsible字段可能缺失,JS中可以用$isArray处理这种情况,但C#无法直接实现该逻辑,原代码执行时会报错。

原C#实现代码

var query = sirens.AsQueryable<SirenRepresentation>()
    .Where(_sirena => _sirena.OwnerId == userId
                      || (_sirena.Listener != null && _sirena.Listener.Any(x => x == userId))
                      || (_sirena.Responsible != null && _sirena.Responsible.Any(x => x == userId)))
    .GroupBy(s => true)
    .Select(g => new UserStatistics
    {
      SirenasCount = g.Sum(_siren => _siren.OwnerId == userId ? 1 : 0),
      Subscriptions = g.Sum(_siren => (_siren.Listener != null && _siren.Listener.Contains(userId)) ? 1 : 0),
      Responsible = g.Sum(_siren => (_sirena.Responsible != null && _sirena.Responsible.Contains(userId)) ? 1 : 0)
    });
    
public class SirenRepresentation
{
  [BsonId]
  public ObjectId Id { get; set; }
  [BsonElement("ownerid"), BsonRepresentation(BsonType.Int64)]
  public long OwnerId { get; set; }
  [BsonRepresentation(BsonType.Int64)]
  [BsonElement("listener")]
  public long[] Listener { get;  set; } = [];
  [BsonRepresentation(BsonType.Int64)]
  [BsonElement("responsible")]
  public long[] Responsible { get;  set; } = [];
  [BsonElement("requests")]
//...
}

public class UserStatistics{
  public int SirenasCount{get;set;}
  public int Subscriptions{get;set;}
  public int Responsible{get;set;}
}

异常信息

Exception has occurred: CLR/MongoDB.Driver.MongoCommandException An
exception of type 'MongoDB.Driver.MongoCommandException' occurred in
System.Private.CoreLib.dll but was not handled in user code: 'Command
aggregate failed: PlanExecutor error during aggregation :: caused by
:: $in requires an array as a second argument, found: missing.'

修复后的C#方法

public async Task<UserStatistics> Get(long userId)
{
  var query = sirens.AsQueryable()
  .Where(_sirena => _sirena.OwnerId == userId
                    || _sirena.Listener.Any(x => x == userId)
                    || _sirena.Responsible.Any(x => x == userId))
  .GroupBy(keySelector: x => true,
    resultSelector: (_, _sirens) => new UserStatistics
      {
        SirenasCount = _sirens.Sum(x => x.OwnerId == userId ? 1 : 0),
        Subscriptions = _sirens.Sum(_sirena => (_sirena.Listener ?? new long[] { }).Contains(userId) ? 1 : 0),
        Responsible = _sirens.Sum(_sirena => (_sirena.Responsible ?? new long[] { }).Contains(userId) ? 1 : 0)
      })
  .FirstOrDefaultAsync();
  return await query;
}

补充说明

MongoDB.Driver 2.24的some_array is Array转换功能存在bug,在MongoDB v7.0.6中无法正常工作,需要修改表达式才能运行。


内容的提问来源于stack exchange,提问作者HedgehogNSK

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.26 13:59:55