如何在不使用扫描的情况下查询 DynamoDB 中的 n:n 邻接列表映射
How to query an n:n adjacency list map in DynamoDB without using scan
我正在尝试在 DynamodDB 中为编目系统建模。它有 "Catalogs",其中包含 "Collections"。每个 "Collection" 可以被许多 "Tags" 标记。
在 RDBMS 中,我会创建一个 table "Catalogs" 与 "Collections" 的 1:n 关系。 "Collections" 会有一个 n:n 和 "Tags",因为一个集合可以有多个标签,一个标签可以属于多个集合。
我想要运行的查询是:
1) 获取所有目录
2) 通过 ID 获取目录
3) 按目录 ID 获取集合
我在 AWS 上看到我可以使用邻接表映射设计(因为我有 n:n 和 "Tags")。所以这是我的 table 结构:
PK SK name
cat-1 cat-1 Sales Catalog
cat-1 col-1 Sales First Collection
cat-1 col-2 Sales Second Collection
cat-2 cat-2 Finance Catalog
tag-1 tag-1 Recently Added Tag
col-1 tag-1 (collection, tag relationship)
这里的问题是我必须使用我认为效率低下的扫描才能获得所有 "Catalogs" 因为查询的 PK 必须是 '=' 而不是 'Begins With' .
我唯一能想到的是创建另一个属性,如"GSI_PK",并在PK为cat-1且SK为cat-1时添加"Catalog_1",当"Catalog_2"时PK 是 cat-2,SK 是 cat-2。我从来没有真正看到这样做,所以我不确定这是否可行,如果我想更改 ID,它需要一些维护。
有什么想法可以实现吗?
让我们来看看吧。我将使用 GraphQL SDL 来设计数据模型和查询,但您可以直接将相同的概念应用于 DynamoDB。
首先考虑数据模型,我们会有类似的东西:
type Catalog {
id: ID!
name: String
# Use a DynamoDB query on the **Collection** table
# where the **catalogId = $ctx.source.id**. Use a GSI or make catalogId the PK.
collections: [Collection]
}
type Collection {
id: ID!
name: String
# Use a DynamoDB query on the **CollectionTag** table where
# the **collectionId = $ctx.source.id**. Use a GSI or make the collectionId the PK.
tags: [CollectionTag]
}
# The "association map" idea as a GraphQL type. The underlying table has a collectionId and tagId.
# Create objects of this type to associate a collection and tag in the many to many relationship.
type CollectionTag {
# Do a GetItem on the **Collection** table where **id = $ctx.source.collectionId**
collection: Collection
# Do a GetItem on the **Tag** table where **id = $ctx.source.tagId**
tag: Tag
}
type Tag {
id: ID!
name: String
# Use a DynamoDB query on teh **CollectionTag** table where
# the **tagId = $ctx.source.id**. If collectionId is the PK then make a GSI where this tagId is the PK.
collections: [CollectionTag]
}
# Root level queries
type Query {
# GetItem to **Catalog** table where **id = $ctx.args.id**
getCatalog(id: ID!): Catalog
# Scan to **Catalog** table. As long as you don't care about ordering on a filed in particular then
# this will likely be okay at the top level. If you only want all catalogs where "arePublished = 1",
# for example then we would likely change this.
allCatalogs: [Catalog]
# Note: You don't really need a getCollectionsByCatalogId(catalogId: ID!) at the top level because you can
# use `query { getCatalog(id: "***") { collections { ... } } }` which is effectively the same thing.
# You could add another field here if having it at the top level was a requirement
getCollectionsByCatalogId(catalogId: ID!): [Collection]
}
Note: Everywhere I use [Collection]
or [Catalog]
etc above you should use a CollectionConnection
, CatalogConnection
, etc wrapper type to enable pagination.
在那种情况下,您可以将 PK 设为对象的类型,将 SK 设为 uuid。一条记录看起来像这样 { PK: "Catalog", SK: "uuid", ...other catalog fields }
。然后,您可以通过对 PK = Catalog 进行查询来获取所有目录。
要存储关联,您可以在两个字段 sourcePK
和 relatedPK
上设置 GSI,您可以在其中存储关联事物的记录。要关联一个对象,您可以创建一条记录,例如{ PK: "Association", SK: "uuid", sourcePK: "category-1", relatedPK: "collection-1", ... other data on the association }
。要查找与 ID 为 1 的 "Catalog" 关联的对象,您可以在 GSI 上执行查询,其中 sourcePK = catalog-1.
使用此设置时,您需要注意热键,并应确保 table 或索引中同一分区键下的数据永远不会超过 10GB。
我正在尝试在 DynamodDB 中为编目系统建模。它有 "Catalogs",其中包含 "Collections"。每个 "Collection" 可以被许多 "Tags" 标记。
在 RDBMS 中,我会创建一个 table "Catalogs" 与 "Collections" 的 1:n 关系。 "Collections" 会有一个 n:n 和 "Tags",因为一个集合可以有多个标签,一个标签可以属于多个集合。
我想要运行的查询是:
1) 获取所有目录
2) 通过 ID 获取目录
3) 按目录 ID 获取集合
我在 AWS 上看到我可以使用邻接表映射设计(因为我有 n:n 和 "Tags")。所以这是我的 table 结构:
PK SK name
cat-1 cat-1 Sales Catalog
cat-1 col-1 Sales First Collection
cat-1 col-2 Sales Second Collection
cat-2 cat-2 Finance Catalog
tag-1 tag-1 Recently Added Tag
col-1 tag-1 (collection, tag relationship)
这里的问题是我必须使用我认为效率低下的扫描才能获得所有 "Catalogs" 因为查询的 PK 必须是 '=' 而不是 'Begins With' .
我唯一能想到的是创建另一个属性,如"GSI_PK",并在PK为cat-1且SK为cat-1时添加"Catalog_1",当"Catalog_2"时PK 是 cat-2,SK 是 cat-2。我从来没有真正看到这样做,所以我不确定这是否可行,如果我想更改 ID,它需要一些维护。
有什么想法可以实现吗?
让我们来看看吧。我将使用 GraphQL SDL 来设计数据模型和查询,但您可以直接将相同的概念应用于 DynamoDB。
首先考虑数据模型,我们会有类似的东西:
type Catalog {
id: ID!
name: String
# Use a DynamoDB query on the **Collection** table
# where the **catalogId = $ctx.source.id**. Use a GSI or make catalogId the PK.
collections: [Collection]
}
type Collection {
id: ID!
name: String
# Use a DynamoDB query on the **CollectionTag** table where
# the **collectionId = $ctx.source.id**. Use a GSI or make the collectionId the PK.
tags: [CollectionTag]
}
# The "association map" idea as a GraphQL type. The underlying table has a collectionId and tagId.
# Create objects of this type to associate a collection and tag in the many to many relationship.
type CollectionTag {
# Do a GetItem on the **Collection** table where **id = $ctx.source.collectionId**
collection: Collection
# Do a GetItem on the **Tag** table where **id = $ctx.source.tagId**
tag: Tag
}
type Tag {
id: ID!
name: String
# Use a DynamoDB query on teh **CollectionTag** table where
# the **tagId = $ctx.source.id**. If collectionId is the PK then make a GSI where this tagId is the PK.
collections: [CollectionTag]
}
# Root level queries
type Query {
# GetItem to **Catalog** table where **id = $ctx.args.id**
getCatalog(id: ID!): Catalog
# Scan to **Catalog** table. As long as you don't care about ordering on a filed in particular then
# this will likely be okay at the top level. If you only want all catalogs where "arePublished = 1",
# for example then we would likely change this.
allCatalogs: [Catalog]
# Note: You don't really need a getCollectionsByCatalogId(catalogId: ID!) at the top level because you can
# use `query { getCatalog(id: "***") { collections { ... } } }` which is effectively the same thing.
# You could add another field here if having it at the top level was a requirement
getCollectionsByCatalogId(catalogId: ID!): [Collection]
}
Note: Everywhere I use
[Collection]
or[Catalog]
etc above you should use aCollectionConnection
,CatalogConnection
, etc wrapper type to enable pagination.
在那种情况下,您可以将 PK 设为对象的类型,将 SK 设为 uuid。一条记录看起来像这样 { PK: "Catalog", SK: "uuid", ...other catalog fields }
。然后,您可以通过对 PK = Catalog 进行查询来获取所有目录。
要存储关联,您可以在两个字段 sourcePK
和 relatedPK
上设置 GSI,您可以在其中存储关联事物的记录。要关联一个对象,您可以创建一条记录,例如{ PK: "Association", SK: "uuid", sourcePK: "category-1", relatedPK: "collection-1", ... other data on the association }
。要查找与 ID 为 1 的 "Catalog" 关联的对象,您可以在 GSI 上执行查询,其中 sourcePK = catalog-1.
使用此设置时,您需要注意热键,并应确保 table 或索引中同一分区键下的数据永远不会超过 10GB。