非规范化模式?

Posted

技术标签:

【中文标题】非规范化模式?【英文标题】:Denormalized schema? 【发布时间】:2016-12-14 03:28:57 【问题描述】:

Mongoose 架构:

const postSchema = new mongoose.Schema(
    title:  type: String ,
    content:  type: String ,

    comments: 
        count:  type: Number ,
        data: [
            author: 
                type: mongoose.Schema.Types.ObjectId,
                ref: 'User'
            ,
            content:  type: String ,
            created:  type: Date ,

            replies: [
                author: 
                    type: mongoose.Schema.Types.ObjectId,
                    ref: 'User'
                ,
                content:  type: String ,
                created:  type: Date ,
            ]
        ]
    
)

我不想标准化 cmets 和回复,因为:

我总是需要一起发布 cmets(一口气读完) 我永远不会使用 cmets 来查询或排序任何东西 使用 cmets 创建、更新和删除帖子要容易得多(文档锁定

如果我将其标准化,我将不得不:

从数据库中获取帖子 从数据库中获取 cmets 获取每条评论的回复

这是 MongoDB 的最大优势之一:专门对数据进行分组以满足您的应用程序数据需求,但 GraphQL 和 Relay.js 似乎不支持这一点?

问题:

有没有办法让 cmets 嵌套在单个帖子对象中(或者至少让回复嵌套在单个评论中),以便一次读取整个内容?


GraphQL 架构:

const postType = new GraphQLObjectType(
    name: 'Post',
    fields: () => (
        id: globalIdField('Post'),
        title:  type: GraphQLString ,
        content:  type: GraphQLString ,

        comments: 

            // ?????

        
    ),
    interfaces: [nodeInterface],
)

更新:

const postType = new GraphQLObjectType(
    name: 'Post',
    fields: () => (
        id: globalIdField('Post'),
        title:  type: GraphQLString ,
        content:  type: GraphQLString ,

        commentCount:  type: GraphQLInt ,
        comments:  type: new GraphQLList(commentType) 
    ),
    interfaces: [nodeInterface]
)

const commentType = new GraphQLObjectType(
    name: 'Comment',
    fields: () => (
        content:  type: GraphQLString ,
        created:  type: GraphQLString ,
        author: 
            type: userType,
            resolve: async (comment) => 
                return await getUserById(comment.author)
            
        ,
        replies: 
            type: new GraphQLList(commentType),
            resolve: (comment) => 

                // Log is below
                console.log(comment)

                // Error only occurs if I return it!
                return comment.replies
            
        
    )
)

日志(comment):

 
  author: 'user-1',
  created: '05:35'
  content: 'Wow, this is an awesome comment!',
  replies:
     [ 
       author: 'user-2',
       created: '11:01',
       content: 'Not really..',
     ,
       
       author: 'user-1',
       created: '11:03',
       content: 'Why are you so salty?',
     ]

【问题讨论】:

制作“cmets”对象数组,以便您可以使用一个查询轻松访问它。 【参考方案1】:

这是 MongoDB 的最大优势之一:专门对数据进行分组以满足您的应用程序数据需求,但 GraphQL 和 Relay.js 似乎不支持这一点?

GraphQL 确实支持您正在尝试做的事情。在进入细节之前,我们需要记住 GraphQL 是关于公开数据,而不是关于我们如何存储数据。我们可以以任何我们喜欢的方式存储 - 标准化与否。我们只需要告诉 GraphQL 如何获取我们在 GraphQL 模式中定义的数据。

有没有办法让 cmets 嵌套在单个帖子对象中(或者至少让回复嵌套在单个评论中),以便一次读取整个内容?

是的,这是可能的。您只需将 cmets 定义为列表。但是,GraphQL 不支持 GraphQL 对象类型中的任意类型的字段。因此,我们需要定义单独的 GraphQL 类型。在您的猫鼬模式中,comments 是一个具有 count 的 cmets 和 cmets data 的对象。 data 属性是另一种对象的列表。所以,我们需要定义两个 GraphQL 对象——CommentCommentList。代码如下:

const commentType = new GraphQLObjectType(
  name: 'Comment',
  fields: () => (
    author:  type: GraphQLString ,
    content:  type: GraphQLString ,
    created:  type: GraphQLString ,
    replies:  type: new GraphQLList(commentType) ,
  ),
);

const commentListType = new GraphQLObjectType(
  name: 'CommentList',
  fields: () => (
    count: 
      type: GraphQLInt,
      resolve: (comments) => comments.length,
    ,
    data: 
      type: new GraphQLList(commentType),
      resolve: (comments) => comments,
    ,
  ),
);

const postType = new GraphQLObjectType(
  name: 'Post',
  fields: () => (
    id: globalIdField('Post'),
    title:  type: GraphQLString ,
    content:  type: GraphQLString ,

    comments: 
      type: commentListType,
      resolve: (post) => post.comments,
    
  ),
  interfaces: [nodeInterface],
);

我不想标准化 cmets 和回复,因为:

我总是需要一起发布 cmets(一口气读完) 我永远不会使用 cmets 来查询或排序任何东西 使用 cmets 创建、更新和删除帖子要容易得多(文档锁定)

以上述方式定义 post 和 comment GraphQL 对象类型让您:

一起阅读所有post cmets 不要使用评论查询,因为它甚至没有id字段。 使用您喜欢的数据库以您喜欢的任何方式实现 cmets 操作。 GraphQL 与它无关。

【讨论】:

知道为什么我会收到错误User Error: expected iterable, but did not find one for field Comment.replies. 您是否查看了null 的回复?一条评论可能没有任何回复。在这种情况下,根据您的实现,replies 可以是 null 或空数组。在没有回复的情况下空数组更好恕我直言,因为它避免了检查null的需要。 回复要么是填充的要么是空数组。。仍然不确定是什么问题.. comment.replies 的解析功能如何? type: new GraphQLList(commentType),因为名称匹配。我也试过resolve: (comment) => comment.replies,但还是同样的错误。

以上是关于非规范化模式?的主要内容,如果未能解决你的问题,请参考以下文章

ActiveRecord - 非规范化案例研究

XML文件不可导出或数据非规范化

[学习笔记]--数据库系统

终端模式

终端模式

设计数据仓库/星型模式 - 选择事实