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

如何用LINQ和C#驱动调用存储JS函数为查询对象添加分数字段

好的,我来帮你解决这个问题。首先得明确:LINQ to MongoDB(也就是你用的AsQueryable()方式)并不支持直接在投影阶段调用数据库端的存储JavaScript函数——因为LINQ需要把表达式树转换成MongoDB原生查询语言,自定义存储函数不在它的转换范围内。所以我们得换用MongoDB的Aggregation Pipeline来实现需求,这也是最适合的方案。

下面分两种可行方案给你详细说明:


方案一:调用数据库存储的JavaScript函数(匹配你的需求)

首先要确保你的candidateScore函数已经正确注册到MongoDB中,你可以在Mongo Shell里执行以下命令完成注册:

db.system.js.save({
    _id: "candidateScore",
    value: function(professionalProfileRequirements, professionalProfile) {
        var score = 0;
        if (professionalProfileRequirements.Province === professionalProfile.Province) {
            score += 1000;
        }
        if (professionalProfileRequirements.Role && professionalProfile.Roles && professionalProfile.ExperienceLevel){
            if (professionalProfile.Roles.includes(professionalProfileRequirements.Role)) {
                switch (professionalProfile.ExperienceLevel) {
                    case 'ExperienceLevel_NoExperience': score += 1; break;
                    case 'ExperienceLevel_LessThanOneYear': score += 2; break;
                    case 'ExperienceLevel_BetweenOneAndThreeYears': score += 4; break;
                    case 'ExperienceLevel_BetweenThreeAndFiveYears': score += 6; break;
                    case 'ExperienceLevel_MoreThan5Years': score += 10; break;
                }
            }
        }
        return score;
    }
})

接下来用C#的Aggregation Pipeline实现完整逻辑,对应你原来的LINQ查询步骤:

// 先准备好分数计算的参数
var scoreRequirements = new ProfessionalProfileRequirements
{
    Role = "你的目标角色",
    Province = "目标省份",
    LanguagesRequired = new List<string>{"中文"},
    SkillsRequired = new List<string>{"C#"}
};

// 构建聚合查询
var professionalProfiles = await _mongoContext.Database
    .GetCollection<Profile>("profiles")
    .Aggregate()
    // 1. 左连接Users集合(对应你原来的join操作)
    .Lookup(
        foreignCollectionName: "users",
        localField: nameof(Profile.UserId),
        foreignField: "_id", // 注意:这里要匹配User集合的Id存储字段,若你的User.Id映射为"Id.Value"则替换成该值
        @as: "matchedUsers"
    )
    // 2. 提取第一个匹配用户的IsConfirmed字段(对应users.First().IsConfirmed)
    .AddFields(new BsonDocument("IsConfirmed", new BsonDocument("$arrayElemAt", new BsonArray{"$matchedUsers.IsConfirmed", 0})))
    // 3. 过滤已确认的用户
    .Match(p => p.IsConfirmed)
    // 4. 调用存储的JS函数计算Score
    .AddFields(new BsonDocument("Score", new BsonDocument(
        "$function", new BsonDocument
        {
            {"body", "candidateScore"}, // 存储函数的名称
            {"args", new BsonArray {BsonSerializer.Serialize(scoreRequirements), "$$ROOT"}}, // 传入需求参数和当前文档
            {"lang", "js"}
        }
    )))
    // 5. 投影成你需要的ProfessionalProfile结构
    .Project<ProfessionalProfile>(new BsonDocument
    {
        {nameof(ProfessionalProfile.Id), "$_id"},
        {nameof(ProfessionalProfile.Name), "$Name"},
        {nameof(ProfessionalProfile.UserId), "$UserId"},
        {nameof(ProfessionalProfile.IsConfirmed), "$IsConfirmed"},
        {nameof(ProfessionalProfile.Score), "$Score"}
    })
    // 6. 按名称排序(对应你原来的orderby)
    .Sort(Builders<ProfessionalProfile>.Sort.Ascending(p => p.Name))
    .ToListAsync();

注意:$function是MongoDB 4.4及以上版本支持的特性,如果你的MongoDB版本较低,建议升级;若无法升级,可以用已废弃的$eval替代,但不推荐长期使用。


方案二:用MongoDB原生操作符实现分数计算(性能更优)

如果不想依赖存储JS函数,推荐把分数计算逻辑转换成MongoDB原生聚合操作符——因为JS函数是单线程执行的,大数据量下性能不如原生操作符。以下是等价的聚合实现:

var scoreRequirements = new ProfessionalProfileRequirements
{
    Role = "你的目标角色",
    Province = "目标省份",
    // 其他参数...
};

var professionalProfiles = await _mongoContext.Database
    .GetCollection<Profile>("profiles")
    .Aggregate()
    .Lookup("users", nameof(Profile.UserId), "_id", "matchedUsers")
    .AddFields(new BsonDocument("IsConfirmed", new BsonDocument("$arrayElemAt", new BsonArray{"$matchedUsers.IsConfirmed", 0})))
    .Match(p => p.IsConfirmed)
    // 用原生操作符实现分数计算,替代JS函数
    .AddFields(new BsonDocument("Score", new BsonDocument("$add", new BsonArray
    {
        // 省份匹配加1000分
        new BsonDocument("$cond", new BsonArray
        {
            new BsonDocument("$eq", new BsonArray{"$Province", scoreRequirements.Province}),
            1000,
            0
        }),
        // 角色匹配+经验等级加分
        new BsonDocument("$cond", new BsonArray
        {
            new BsonDocument("$and", new BsonArray
            {
                scoreRequirements.Role != null,
                new BsonDocument("$ne", new BsonArray{"$Roles", BsonNull.Value}),
                new BsonDocument("$ne", new BsonArray{"$ExperienceLevel", BsonNull.Value}),
                new BsonDocument("$in", new BsonArray{scoreRequirements.Role, "$Roles"})
            }),
            new BsonDocument("$switch", new BsonDocument
            {
                {"branches", new BsonArray
                {
                    new BsonDocument{"case", new BsonDocument("$eq", new BsonArray{"$ExperienceLevel", "ExperienceLevel_NoExperience"}), "then", 1},
                    new BsonDocument{"case", new BsonDocument("$eq", new BsonArray{"$ExperienceLevel", "ExperienceLevel_LessThanOneYear"}), "then", 2},
                    new BsonDocument{"case", new BsonDocument("$eq", new BsonArray{"$ExperienceLevel", "ExperienceLevel_BetweenOneAndThreeYears"}), "then", 4},
                    new BsonDocument{"case", new BsonDocument("$eq", new BsonArray{"$ExperienceLevel", "ExperienceLevel_BetweenThreeAndFiveYears"}), "then", 6},
                    new BsonDocument{"case", new BsonDocument("$eq", new BsonArray{"$ExperienceLevel", "ExperienceLevel_MoreThan5Years"}), "then", 10}
                }},
                {"default", 0}
            }),
            0
        })
    })))
    .Project<ProfessionalProfile>(new BsonDocument
    {
        {nameof(ProfessionalProfile.Id), "$_id"},
        {nameof(ProfessionalProfile.Name), "$Name"},
        {nameof(ProfessionalProfile.UserId), "$UserId"},
        {nameof(ProfessionalProfile.IsConfirmed), "$IsConfirmed"},
        {nameof(ProfessionalProfile.Score), "$Score"}
    })
    .Sort(Builders<ProfessionalProfile>.Sort.Ascending(p => p.Name))
    .ToListAsync();

这个方案的优势是完全使用MongoDB原生操作,性能更好,也不需要维护数据库端的JS函数,适合长期使用。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.27 06:57:06