Mongodb Mongoose方案设计多对多
我正试图为任务管理应用程序提出一个mongoose方案架构/设计 所需的功能和型号: 型号:Mongodb Mongoose方案设计多对多,mongodb,mongoose,database-design,Mongodb,Mongoose,Database Design,我正试图为任务管理应用程序提出一个mongoose方案架构/设计 所需的功能和型号: 型号: 用户拥有多个功能板 董事会-有名单 列表-有卡片 纸牌 功能: 用户可以创建一个板 用户可以在板中创建列表 用户可以在列表中创建卡片 用户可以向董事会添加成员 用户可以从董事会成员向卡中添加成员 用户可以删除板,删除所有列表和卡,并删除成员 用户可以删除列表,删除列表中的所有卡片 用户可以删除卡,从卡中删除所有成员 用户可以更新板中的列表位置(排序) 用户可以更新列表中的卡片位置(排序) 附加功能
- 用户拥有多个功能板
- 董事会-有名单
- 列表-有卡片
- 纸牌
- 用户可以创建一个板
- 用户可以在板中创建列表
- 用户可以在列表中创建卡片
- 用户可以向董事会添加成员
- 用户可以从董事会成员向卡中添加成员
- 用户可以删除板,删除所有列表和卡,并删除成员
- 用户可以删除列表,删除列表中的所有卡片
- 用户可以删除卡,从卡中删除所有成员
- 用户可以更新板中的列表位置(排序)
- 用户可以更新列表中的卡片位置(排序)
- 附加功能(可选)
- 用户可以在卡片上添加注释
- 卡片活动记录(移动、编辑、评论)
{
name: 'Bob',
_id: '2626'
}
{
name: 'Board name',
_id: '123456',
members: [
{ type: ObjectID, ref: 'user' } // '2626'
]
}
{
name: 'List name',
_id: '2525',
boardId: { type: ObjectID, ref: 'board' } // '123456'
}
{
name: 'Card name',
boardId: { type: ObjectID, ref: 'board' } // '123456',
listId: { type: ObjectID, ref: 'list' } // '2525'
}
董事会:
{
name: 'Bob',
_id: '2626'
}
{
name: 'Board name',
_id: '123456',
members: [
{ type: ObjectID, ref: 'user' } // '2626'
]
}
{
name: 'List name',
_id: '2525',
boardId: { type: ObjectID, ref: 'board' } // '123456'
}
{
name: 'Card name',
boardId: { type: ObjectID, ref: 'board' } // '123456',
listId: { type: ObjectID, ref: 'list' } // '2525'
}
列表:
{
name: 'Bob',
_id: '2626'
}
{
name: 'Board name',
_id: '123456',
members: [
{ type: ObjectID, ref: 'user' } // '2626'
]
}
{
name: 'List name',
_id: '2525',
boardId: { type: ObjectID, ref: 'board' } // '123456'
}
{
name: 'Card name',
boardId: { type: ObjectID, ref: 'board' } // '123456',
listId: { type: ObjectID, ref: 'list' } // '2525'
}
卡片:
{
name: 'Bob',
_id: '2626'
}
{
name: 'Board name',
_id: '123456',
members: [
{ type: ObjectID, ref: 'user' } // '2626'
]
}
{
name: 'List name',
_id: '2525',
boardId: { type: ObjectID, ref: 'board' } // '123456'
}
{
name: 'Card name',
boardId: { type: ObjectID, ref: 'board' } // '123456',
listId: { type: ObjectID, ref: 'list' } // '2525'
}
我将如何查询这种类型的结构
- 1) 按用户id获取电路板列表
- 2) 按板id获取列表列表
- 3) 按board id和listid获取卡片列表
请不要对我太苛刻,我对mongodb世界真的很陌生,但我真的在尽我最大的努力。您定义的模式非常好,下面是流程 最初,当用户登录时,您需要向他们显示电路板列表。这应该很容易,因为您只需使用board集合上的用户id进行查找查询
Board.find({members: user_id}) // where user_id is the ID of the user
现在,当用户单击某个特定的板时,您可以获得带有板id的列表,类似于上面的查询
List.find({boardId: board_id}) // where board_id is the ID of the board
同样,你可以通过列表id和董事会id获得卡片。
find({boardId:board\u id,listId:list\u id})//其中board\u id是板的id,listId是列表的id
现在,让我们看一看您可能需要同时从2个或多个集合中获取数据的情况。
例如,当用户单击黑板时,您不仅需要黑板上的列表,还需要该黑板上的卡片。
在这种情况下,您需要编写一个聚合
Board.aggregate([
// get boards which match a particular user_id
{
$match: {members: user_id}
},
// get lists that match the board_id
{
$lookup:
{
from: 'list',
localField: '_id',
foreignField: 'boardId',
as: 'lists'
}
}
])
这将返回电路板,在每个电路板中,都有一个与该电路板相关联的列表数组。如果某个板没有列表,那么它将有一个空数组
类似地,如果要将卡片添加到列表和棋盘,聚合查询将是一个更复杂的bot,因此
Board.aggregate([
// get boards which match a particular user_id
{
$match: {members: user_id}
},
// get lists that match the board_id
{
$lookup:
{
from: 'list',
localField: '_id',
foreignField: 'boardId',
as: 'lists'
}
},
// get cards that match the board_id
{
$lookup:
{
from: 'card',
localField: '_id',
foreignField: 'boardId',
as: 'cards'
}
}
])
这将增加一系列的卡以及混合。同样地,你也可以得到名单上的卡片
现在,让我们考虑一下这是否是最好的模式。我个人认为您建议的模式非常好,因为另一种方法是将ID存储在父集合中,这将允许您使用“填充”来获取数据,而不是查找查询
例如,在board collection中存储列表ID。这样做的缺点是,每当添加新列表时,您都需要将该列表添加到列表集合中,并更新列表连接到的板(添加列表ID),我认为这太单调了
最后,对您给出的模式提出一些建议,
我认为您应该在每个集合中添加用户id(创建者id),因为在许多情况下,您需要显示创建特定板或列表或其他任何内容的用户的姓名,而且由于您具有向特定卡添加用户的功能,等等,我认为您应该有两个字段,一个是creator_id,另一个应该是关联的_用户,这将是一个数组(显然您可以选择更好的名称)
您应该在要按位置排序的卡片和其他集合中添加位置字段。此字段应为数字
到目前为止,删除一张卡片或将其从一个列表移动到另一个列表应该是非常简单和不言自明的
编辑1:基于评论您不需要在聚合之后将卡片分配到列表中,您可以在聚合本身中执行此操作,因此类似于这样
Board.aggregate([
// get boards which match a particular user_id
{
$match: { members: user_id }
},
// get lists that match the board_id
{
$lookup:
{
from: 'list',
localField: '_id',
foreignField: 'boardId',
as: 'lists'
}
},
// unwind lists (because it's an array at the moment)
{
$unwind: '$lists'
},
// Now you have object named lists in every board object
// get cards that match the list_id (note that the cards contain list_id)
{
$lookup:
{
from: 'card',
localField: '_id',
foreignField: 'listId',
as: 'cards'
}
},
// now merge back the objects and get a simple object for each boardID
{
$group: {
_id: "$_id",
members: { $addToSet: "$members" },
lists: { $addToSet: "$lists" }
}
}
])
data = {
'_id': '123456',
'members': [
{
name: 'Bob',
_id: '2626'
},
{
name: 'Matthew',
_id: '2627'
}
],
'lists': [
{
name: 'List 1',
_id: '2525',
boardId: '123456',
cards: [
{
name: 'Card 1',
boardId: '123456',
listId: '2525'
},
{
name: 'Card 2',
boardId: '123456',
listId: '2525'
}
]
},
{
name: 'List 2',
_id: '2526',
boardId: '123456',
cards: [
{
name: 'Card 3',
boardId: '123456',
listId: '2525'
},
{
name: 'Card 4',
boardId: '123456',
listId: '2525'
}
]
}
]
}
这会给你类似的东西
Board.aggregate([
// get boards which match a particular user_id
{
$match: { members: user_id }
},
// get lists that match the board_id
{
$lookup:
{
from: 'list',
localField: '_id',
foreignField: 'boardId',
as: 'lists'
}
},
// unwind lists (because it's an array at the moment)
{
$unwind: '$lists'
},
// Now you have object named lists in every board object
// get cards that match the list_id (note that the cards contain list_id)
{
$lookup:
{
from: 'card',
localField: '_id',
foreignField: 'listId',
as: 'cards'
}
},
// now merge back the objects and get a simple object for each boardID
{
$group: {
_id: "$_id",
members: { $addToSet: "$members" },
lists: { $addToSet: "$lists" }
}
}
])
data = {
'_id': '123456',
'members': [
{
name: 'Bob',
_id: '2626'
},
{
name: 'Matthew',
_id: '2627'
}
],
'lists': [
{
name: 'List 1',
_id: '2525',
boardId: '123456',
cards: [
{
name: 'Card 1',
boardId: '123456',
listId: '2525'
},
{
name: 'Card 2',
boardId: '123456',
listId: '2525'
}
]
},
{
name: 'List 2',
_id: '2526',
boardId: '123456',
cards: [
{
name: 'Card 3',
boardId: '123456',
listId: '2525'
},
{
name: 'Card 4',
boardId: '123456',
listId: '2525'
}
]
}
]
}
因此,基本上,您可以在一个查询中获得这些列表的列表和卡片,这是非常有效的
现在来看你提出的两个问题
这将有点乏味,因为无论何时向上移动卡片,都需要更新上面卡片的位置。能否显示预期的输出?这正是我想要了解的。我正在尝试呈现一个类似trello的板,在前端有列表和卡片。这是一个多么精彩的回答,感谢您花时间深入解释一切。许多的