gpt4 book ai didi

node.js - MongoDB将相关的收集项目计数与其他收集结果合并

转载 作者:太空宇宙 更新时间:2023-11-03 23:07:56 25 4
gpt4 key购买 nike

我是mongodb的新手,正在尝试弄清楚如何有效地查询集合中的每个项目。

我有projects收藏和tasks收藏

//projects
{
_id: ObjectId(),
name: String
}
//tasks
{
_id: ObjectId(),
projectId: ObjectId(), //reference project id
completed: Bool
}


我想获取所有项目,然后计算每个项目的 completedincomplete任务

db.projects.find({})...
//perhaps something similar in output
[
{
_id: ObjectId(), //projectId
name: String
completed: Number,
incomplete: Number
}
]


我使用猫鼬作为ORM。我不知道这在猫鼬甚至本地mongodb查询中是否可行。感谢任何帮助。谢谢!

最佳答案

无论您以何种方式看待,只要您具有这样的规范化关系,就需要两个查询来获取包含“任务”集合中的详细信息并填写“项目”集合中的详细信息的结果。 MongoDB不会以任何方式使用联接,而mongoose也不例外。猫鼬确实提供了.populate(),但这仅是方便魔术,它实际上是在运行另一个查询并将结果合并到引用的字段值上。

因此,在这种情况下,您最终可能会考虑将项目信息嵌入到任务中。当然会有重复,但是使用单一集合会使查询模式更加简单。

通过使用参考模型将集合分开,您基本上可以使用两种方法。但是首先,您可以使用aggregate来获得更多符合实际需求的结果:

      Task.aggregate(
[
{ "$group": {
"_id": "$projectId",
"completed": {
"$sum": {
"$cond": [ "$completed", 1, 0 ]
}
},
"incomplete": {
"$sum": {
"$cond": [ "$completed", 0, 1 ]
}
}
}}
],
function(err,results) {

}
);


这仅使用 $group管道以便在“任务”集合中累积“ projectid”的值。为了计算“完成”和“未完成”的值,我们使用 $cond运算符,该运算符是三元运算符,用于确定将哪个值传递给 $sum。由于此处的第一个条件或“ if”条件是布尔值,因此现有的布尔值“ complete”字段将起作用,将第三个参数的 true传递给“ then”或“ else”。

这些结果还可以,但是它们不包含来自“项目”集合的任何有关所收集“ _id”值的信息。一种使输出看起来像这样的方法是从返回的“结果”对象的聚合结果回调中调用 .populate()的模型形式:

    Project.populate(results,{ "path": "_id" },callback);


在这种形式中, .populate()调用将对象或数据数组作为第一个参数,第二个是填充的选项文档,此处的必填字段用于“路径”。这将处理所有项目,并从被称为将这些对象插入回调中的结果数据的模型中“填充”。

作为完整的示例清单:

var async = require('async'),
mongoose = require('mongoose'),
Schema = mongoose.Schema;

var projectSchema = new Schema({
"name": String
});

var taskSchema = new Schema({
"projectId": { "type": Schema.Types.ObjectId, "ref": "Project" },
"completed": { "type": Boolean, "default": false }
});

var Project = mongoose.model( "Project", projectSchema );
var Task = mongoose.model( "Task", taskSchema );

mongoose.connect('mongodb://localhost/test');

async.waterfall(
[
function(callback) {
async.each([Project,Task],function(model,callback) {
model.remove({},callback);
},
function(err) {
callback(err);
});
},

function(callback) {
Project.create({ "name": "Project1" },callback);
},

function(project,callback) {
Project.create({ "name": "Project2" },callback);
},

function(project,callback) {
Task.create({ "projectId": project },callback);
},

function(task,callback) {
Task.aggregate(
[
{ "$group": {
"_id": "$projectId",
"completed": {
"$sum": {
"$cond": [ "$completed", 1, 0 ]
}
},
"incomplete": {
"$sum": {
"$cond": [ "$completed", 0, 1 ]
}
}
}}
],
function(err,results) {
if (err) callback(err);
Project.populate(results,{ "path": "_id" },callback);
}
);
}
],
function(err,results) {
if (err) throw err;
console.log( JSON.stringify( results, undefined, 4 ));
process.exit();
}
);


这将产生如下结果:

[
{
"_id": {
"_id": "54beef3178ef08ca249b98ef",
"name": "Project2",
"__v": 0
},
"completed": 0,
"incomplete": 1
}
]


因此, .populate()对于这种聚合结果非常有效,甚至可以有效地用作另一个查询,并且通常应适合大多数用途。但是,清单中包含一个特定的示例,其中创建了“两个”项目,但是当然只有“一个”任务仅引用了一个项目。

由于聚合正在处理“任务”集合,因此它不了解那里未引用的任何“项目”。为了获得带有计算出的总数的“项目”的完整列表,您需要在运行两个查询和“合并”结果时更加具体。

这基本上是对不同键和数据的“哈希合并”,但是,此模块的好帮手是名为 nedb的模块,该模块允许您以与MongoDB查询和操作更一致的方式应用逻辑。

基本上,您想从具有扩展字段的“项目”集合中复制数据,然后要与聚合结果“合并”或 .update()该信息。再次作为完整清单来演示:

var async = require('async'),
mongoose = require('mongoose'),
Schema = mongoose.Schema,
DataStore = require('nedb'),
db = new DataStore();


var projectSchema = new Schema({
"name": String
});

var taskSchema = new Schema({
"projectId": { "type": Schema.Types.ObjectId, "ref": "Project" },
"completed": { "type": Boolean, "default": false }
});

var Project = mongoose.model( "Project", projectSchema );
var Task = mongoose.model( "Task", taskSchema );

mongoose.connect('mongodb://localhost/test');

async.waterfall(
[
function(callback) {
async.each([Project,Task],function(model,callback) {
model.remove({},callback);
},
function(err) {
callback(err);
});
},

function(callback) {
Project.create({ "name": "Project1" },callback);
},

function(project,callback) {
Project.create({ "name": "Project2" },callback);
},

function(project,callback) {
Task.create({ "projectId": project },callback);
},

function(task,callback) {
async.series(
[

function(callback) {
Project.find({},function(err,projects) {
async.eachLimit(projects,10,function(project,callback) {
db.insert({
"projectId": project._id.toString(),
"name": project.name,
"completed": 0,
"incomplete": 0
},callback);
},callback);
});
},

function(callback) {
Task.aggregate(
[
{ "$group": {
"_id": "$projectId",
"completed": {
"$sum": {
"$cond": [ "$completed", 1, 0 ]
}
},
"incomplete": {
"$sum": {
"$cond": [ "$completed", 0, 1 ]
}
}
}}
],
function(err,results) {
async.eachLimit(results,10,function(result,callback) {
db.update(
{ "projectId": result._id.toString() },
{ "$set": {
"complete": result.complete,
"incomplete": result.incomplete
}
},
callback
);
},callback);
}
);
},

],

function(err) {
if (err) callback(err);
db.find({},{ "_id": 0 },callback);
}
);
}
],
function(err,results) {
if (err) throw err;
console.log( JSON.stringify( results, undefined, 4 ));
process.exit();
}


结果如下:

[
{
"projectId": "54beef4c23d4e4e0246379db",
"name": "Project2",
"completed": 0,
"incomplete": 1
},
{
"projectId": "54beef4c23d4e4e0246379da",
"name": "Project1",
"completed": 0,
"incomplete": 0
}
]


该列表列出了每个“项目”中的数据,并包括与之相关的“任务”集合中的计算值。

因此,您可以采取几种方法。同样,最终最好还是将“任务”嵌入“项目”项中,这还是一种简单的聚合方法。而且,如果您要嵌入任务信息,则还可以在“项目”对象上维护“完成”和“未完成”的计数器,并在使用 $inc标记任务数组中的项已完成时,只需更新这些计数器即可。操作员。

var taskSchema = new Schema({
"completed": { "type": Boolean, "default": false }
});

var projectSchema = new Schema({
"name": String,
"completed": { "type": Number, "default": 0 },
"incomplete": { "type": Number, "default": 0 }
"tasks": [taskSchema]
});

var Project = mongoose.model( "Project", projectSchema );
// cheat for a model object with no collection
var Task = mongoose.model( "Task", taskSchema, undefined );

// Then in later code

// Adding a task
var task = new Task();
Project.update(
{ "task._id": { "$ne": task._id } },
{
"$push": { "tasks": task },
"$inc": {
"completed": ( task.completed ) ? 1 : 0,
"incomplete": ( !task.completed ) ? 1 : 0;
}
},
callback
);

// Removing a task
Project.update(
{ "task._id": task._id },
{
"$pull": { "tasks": { "_id": task._id } },
"$inc": {
"completed": ( task.completed ) ? -1 : 0,
"incomplete": ( !task.completed ) ? -1 : 0;
}
},
callback
);


// Marking complete
Project.update(
{ "tasks": { "$elemMatch": { "_id": task._id, "completed": false } }},
{
"$set": { "tasks.$.completed": true },
"$inc": {
"completed": 1,
"incomplete": -1
}
},
callback
);


尽管您必须知道当前任务状态,计数器更新才能正常工作,但这很容易编写代码,并且可能至少应该在传递给方法的对象中包含这些详细信息。

我个人将重新建模为后一种形式,然后执行此操作。您可以执行查询“合并”,如两个示例所示,但这当然是有代价的。

关于node.js - MongoDB将相关的收集项目计数与其他收集结果合并,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28049610/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com