Mongodb Mongoose方案设计多对多

Mongodb Mongoose方案设计多对多,mongodb,mongoose,database-design,Mongodb,Mongoose,Database Design,我正试图为任务管理应用程序提出一个mongoose方案架构/设计 所需的功能和型号: 型号: 用户拥有多个功能板 董事会-有名单 列表-有卡片 纸牌 功能: 用户可以创建一个板 用户可以在板中创建列表 用户可以在列表中创建卡片 用户可以向董事会添加成员 用户可以从董事会成员向卡中添加成员 用户可以删除板,删除所有列表和卡,并删除成员 用户可以删除列表,删除列表中的所有卡片 用户可以删除卡,从卡中删除所有成员 用户可以更新板中的列表位置(排序) 用户可以更新列表中的卡片位置(排序) 附加功能

我正试图为任务管理应用程序提出一个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获取卡片列表
因此,对于board视图,我会去抓取所有列表,但随后我必须去获取每个列表的所有卡片,这似乎不是很有效

也许在进入board view时,我应该只通过board id查询卡片,但是如何获取列表,并将每张卡片放入自己的列表中呢

如何处理删除卡片或将卡片从一个列表移动到另一个列表


请不要对我太苛刻,我对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'
                }
            ]
        }
    ]
}
因此,基本上,您可以在一个查询中获得这些列表的列表和卡片,这是非常有效的

现在来看你提出的两个问题

  • 卡片从一个列表移动到另一个列表,只需将卡片文档中的listId字段编辑为新的listId(实际上很简单)

  • 卡片在列表中向上移动了一个位置

  • 正如我所说的,如果您想要位置,您需要在文档中添加一个名为position的字段,然后每当移动卡时,您都需要更改这些卡的“position”值。 在聚合中,您只需要添加另一个名为“$sort”的阶段,并根据位置值对其进行排序。
    这将有点乏味,因为无论何时向上移动卡片,都需要更新上面卡片的位置。

    能否显示预期的输出?这正是我想要了解的。我正在尝试呈现一个类似trello的板,在前端有列表和卡片。这是一个多么精彩的回答,感谢您花时间深入解释一切。许多的