好的,这就是我想要做的。我有一个用户架构,其中包含一个团队密钥和一个数组作为其值。例如:
user = {
team : Array
}
数组的值是字符串(团队名称)。我有一个团队架构,其中包含团队信息,如名册,州等。这是我的问题。如果我的用户在数组中有多个团队名称,那么我如何将所有数据聚合到一个变量然后将其传递给我的视图。我以为我可以使用for循环,但视图似乎在聚合完成之前呈现。这是我试过的:
// if there is there is more than one team name in the user's team array
if( data[0].teams.length > 1 ){
for(var i = 0; i < data[0].teams.length; i++){
// for each team name in the user array
// find the corresponding team and add that data to the array called team
Team.find( { team : data[0].teams[i] }, function(err, data){
if(err){
throw err;
}
team.push(data);
});
}
// render the view
res.render('coach', {
user: req.user,
fname: self.fname,
lname: self.lname,
email: self.email,
phone: self.phone,
address: self.address,
state: self.state,
teams : [team]
});
}
编辑(添加架构)
用户架构(查看团队数组,找到相应的团队,根据用户架构团队数组中的名称从团队架构返回数据)
var userSchema = new Schema({
username : { type: String, required: true, index: { unique: true } },
fname : { type: String, required: true },
lname : { type: String, required: true },
password : { type: String, required: true },
type : { type: String, required: true },
phone : String,
address : String,
state: String,
conference: String,
email : { type: String, required: true, index: { unique : true } },
teams: Array,
});
团队架构:
var teamsSchema = new Schema({
coach: String,
state: String,
conference: String,
team: String,
roster: [{fname: String, lname: String, number: String}]
});
答案 0 :(得分:1)
首先,查找操作是异步的,虽然您在推送到team
数组之前等待响应,但在使用res.render()
进行响应之前,您并没有等待所有响应。下面的内容应该表明我的意思:
// if there is there is more than one team name in the user's team array
if( data[0].teams ){
var teamArr = [];
var responses = 0;
for(var i=0; i<data[0].teams.length; i++){
// for each team name in the user array
// find the corresponding team and add that data to the array called team
Team.find( { team : data[0].teams[i] }, function(err, team){
if(err){
throw err;
}
responses ++;
teamArr.push(team);
// if all teams have been pushed then call render
if (responses == data[0].teams.length - 1) {
// render the view
res.render('coach', {
user: req.user,
fname: self.fname,
lname: self.lname,
email: self.email,
phone: self.phone,
address: self.address,
state: self.state,
teams : teamArr
});
}
});
}
}
编辑:我更改了一些命名以避免潜在的命名冲突(例如两个数据变量)
其次,如果您使用mongoose更改文档结构,则另一个选项是,如果您在数组中存储对团队文档对象的引用而不是名称字符串,则可以使用mongoose populate方法并填充返回整个文档之前的数组。
更新:根据提供的Schema,您可以使用populate,如下所示:
var userSchema = new Schema({
username : { type: String, required: true, index: { unique: true } },
....
email : { type: String, required: true, index: { unique : true } },
teams: [{
type: Schema.Types.ObjectId, ref: 'teams'
}],
});
然后在返回之前填充文档,避免使用for循环:
User.findOne({_id: userId})
.populate('teams')
.exec(function (err, user) {
if (err) return handleError(err);
res.render('coach', {
user: filterUser(user);
});
});
其中user.teams
现在包含一组填充的团队,filterUser()
返回一个只包含您需要的属性的用户对象;
答案 1 :(得分:1)
现在你已经发布了你的架构很清楚你正在尝试做什么,也清楚如何展示你的各种方法来做得更好。
第一个和基本的情况是,您似乎有一个“团队”数组,其中包含可能存储在您的用户对象上的“团队名称”的“字符串”值。这似乎对您有用,并且确实具有在检索用户时可以访问这些名称的优势。
问题是你正在迭代结果并为数组中的每个元素发出.find()
,这不是最有效的方法。您基本上可以将$in
运算符与查询和现有数据一起使用,并通过调用.toObject()
来转换mongoose文档,绕过您将整个“团队数据”合并到用户对象时可能尝试做的事情。到一个普通的JavaScript对象:
User.findById( req.user, function(err,user) {
var data = user.toObject();
Team.find({ "team": "$in": data.teams }, function(err,teams) {
data.teams = teams;
res.render( 'coach', data );
});
});
这是一种简单的方法,但实际上有一些mongoose可用的功能,如果您稍微更改模式以引用Team
模型中的数据,它将为您执行此操作。因此,您只需使用.populate()
填写:
var userSchema = new Schema({
username : { type: String, required: true, index: { unique: true } },
fname : { type: String, required: true },
lname : { type: String, required: true },
password : { type: String, required: true },
type : { type: String, required: true },
phone : String,
address : String,
state: String,
conference: String,
email : { type: String, required: true, index: { unique : true } },
teams: [{ type: ObjectId, ref: "Team" }]
});
现在只存储引用_id
值以及从何处获取它。所以现在您的查询变为:
User.findById( req.user).populate("teams").exec(function(err,user) {
res.render("coach", user);
});
所以这样做基本上等同于第一个例子,但是使用_id
值并以更简洁的方式,user
对象现在已经从{Team
中提取了所有数据{1}}模式匹配存储在用户数组中的_id
值列表。
当然,您没有像以前那样立即访问团队名称,但您可以通过选择要填充的字段来解决此问题:
User.findById( req.user).populate("teams","team").exec(function(err,user) {
res.render("coach", user);
});
因此,只会从每个对象中提取“团队”字段,并且与原来的结果大致相同,尽管实际上有两个查询(在引擎盖下)。
最后,如果你能够接受一点点数据复制的概念,那么最有效的方法就是简单地嵌入数据。虽然存储了重复数据,但读取效率最高,因为它是单个查询。如果您不需要定期更新该数据,则可能值得考虑:
var userSchema = new Schema({
username : { type: String, required: true, index: { unique: true } },
fname : { type: String, required: true },
lname : { type: String, required: true },
password : { type: String, required: true },
type : { type: String, required: true },
phone : String,
address : String,
state: String,
conference: String,
email : { type: String, required: true, index: { unique : true } },
teams: [teamSchema]
});
因此,嵌入了这些数据,您只需要检索user
,并且“团队”数据已存在:
User.findById( req.user, function(err,user) {
res.render("coach", user);
});
所以这些是一些方法。所有这些都表明不需要循环数组,并且实际发出的查询数量可以大大减少,如果你可以使用它,甚至可以减少到1。