当前位置: 首页 > 知识库问答 >
问题:

Dynamodb无服务器一对多和多对多关系

鲜于峰
2023-03-14

我对Dynamodb和非关系型数据库世界非常陌生。我正在练习AWS的GSILSI。我使用的是无服务器框架。我拆分我的处理程序多个lambda函数。我想创建我的数据,在那里我可以看到所有的餐馆和他们有什么类型的啤酒的价格。我还想查询所有没有价格的啤酒。我成功创建了餐厅,它的分区键是id。我可以得到所有餐馆的数据。但是我坚持啤酒逻辑。我为啤酒创建了像这样的apiendpoint餐厅/{id}/createBeers,当我发布请求时,我得到了错误的消息:一个或多个参数值无效:缺少项中的键id,因为它要求餐馆的ID。我找不到逻辑,我可以添加餐厅的标识来创建啤酒,以及如何免费获得所有啤酒。

餐厅是一对多。啤酒是多对多(同名不同价格基于餐厅)。这就是我想要达到的目标。

[
      {
        "id": 1,
        "name": "restaurent 1",
        "beers": {
            "tap-beers": [{
                    "name": "beer 1",
                    "price": "2$"
                },
                {
                    "name": "beer 2",
                    "price": "2$"
                }
            ],
            "bottle-beers": [{
                    "name": "beer 3",
                    "price": "2$"
                },
                {
                    "name": "beer 4",
                    "price": "2$"
                }
            ]
        }
    },
    
      {
        "id": 2,
        "name": "restaurent 2",
        "beers": {
            "tap-beers": [{
                    "name": "beer 1",
                    "price": "3$"
                },
                {
                    "name": "beer 2",
                    "price": "3$"
                }
            ],
            "bottle-beers": [{
                    "name": "beer 3",
                    "price": "4$"
                },
                {
                    "name": "beer 4",
                    "price": "6$"
                }
            ]
        }
    }
]

这就是我想把所有啤酒放在桌上的方法

[
  {
    "beername": "beer 1"
  },
   {
    "beername": "beer 2"
  },
   {
    "beername": "beer 3"
  },
   {
    "beername": "beer 4"
  }
]

这是我的餐厅创作

const dynamoDb = new AWS.DynamoDB.DocumentClient();
module.exports.createRestaurant = async event => {
  const resquestBody = JSON.parse(event.body);

  const params = {
    TableName: "table name",
    Item: {
      id: uuid.v1(),
      name: resquestBody.name,
      beers: [] // in here I will add beers when I can create a beer my post method path 
                // is restaurant/{id}/createBeers
    }
  }

  try {
    await dynamoDb.put(params).promise();
    return {
      statusCode: 200,
      body: JSON.stringify(resquestBody),
    };
  } catch (error) {
    return {
      statusCode: 500,
      body: JSON.stringify(error),
    };
  }
};

这是创建啤酒处理程序

const dynamoDb = new AWS.DynamoDB.DocumentClient();
module.exports.createBeers = async event => {
  const requestBody = JSON.parse(event.body);
  const params = {
    TableName: "table name",
    Item: {
      beer_name: requestBody.beer_name,
      beer_type: requestBody.beer_type,
      beer_price: requestBody.beer_price
    }
  };

  try {
    await dynamoDb.put(params).promise();
    return {
      statusCode: 200,
      body: JSON.stringify(requestBody),
    };
  } catch (error) {
    return {
      statusCode: 500,
      body: JSON.stringify(error),
    };
  }
};

这是我的GSI免费获取所有啤酒

module.exports.getBeers = async event => {

  const params = {
    TableName: "beer",
    IndexName: "beers",
    KeyConditionExpression: "beer_name = :beer_name",
    ExpressionAttributeValues: {
      ":beer_name": "beer_name"
    },
    Limit: 1
  }

  try {
    let data = await dynamoDb.query(params).promise();
    return {
      statusCode: 200,
      body: JSON.stringify(data.Items),
    };
  } catch (error) {
    return {
      statusCode: 500,
      body: JSON.stringify(error),
    };
  }

}

这是我的无服务器yml文件

functions:
  createRestaurant:
    handler: handlers/createRestaurant.createRestaurant
    events:
      - http:
          path: restaurant
          method: post
          cors: true
  getRestaurants:
    handler: handlers/getRestaurants.getRestaurants
    events:
      - http:
          path: restaurant/all
          method: get
          cors: true
  createBeers:
    handler: handlers/createBeers.createBeers
    events:
      - http:
          path: restaurant/{id}/beers
          method: post
          cors: true
  getBeers:
    handler: handlers/getBeers.getBeers
    events:
      - http:
          path: beers/all
          method: get
          cors: true
resources:
  Resources:
    table:
      Type: "AWS::DynamoDB::Table"
      DeletionPolicy: Retain
      Properties:
        AttributeDefinitions:
          - AttributeName: id
            AttributeType: S
          - AttributeName: beer_name
            AttributeType: S
        KeySchema:
          - AttributeName: id
            KeyType: HASH
        ProvisionedThroughput:
          ReadCapacityUnits: 1
          WriteCapacityUnits: 1
        GlobalSecondaryIndexes:
          - IndexName: beers
            KeySchema:
              - AttributeName: beer_name
                KeyType: HASH
            Projection:
              ProjectionType: ALL
            ProvisionedThroughput:
              ReadCapacityUnits: 1
              WriteCapacityUnits: 1
        TableName: "tableName"

共有1个答案

姬念
2023-03-14

根据我收集的信息,您有两个实体:

  • 餐厅
    • ID
    • 名字
    • 名字

    每家餐厅可能有多瓶啤酒,每只熊要么是瓶装的,要么是自来水,而且有价格。

    您希望启用以下访问模式:

    1. 获取餐厅列表

    我建议使用一个带有全局二级索引(GSI1)的表,如下所示:

    1. 要获取所有餐厅的列表,请使用PK=restaurants
    import typing
    import boto3
    import boto3.dynamodb.conditions as conditions
    
    def get_all_restaurants() -> typing.List[dict]:
    
        table = boto3.resource("dynamodb").Table("table-name")
    
        response = table.query(
            KeyConditionExpression=conditions.Key("GSI1PK").eq("RESTAURANTS"),
            IndexName="GSI1"
        )
    
        return response["Items"]
    
    import typing
    import boto3
    import boto3.dynamodb.conditions as conditions
    
    def get_beers_in_restaurant(restaurant_id: str) -> typing.List[dict]:
    
        table = boto3.resource("dynamodb").Table("table-name")
    
        response = table.query(
            KeyConditionExpression=conditions.Key("PK").eq(f"R#{restaurant_id}") \
                & conditions.Key("SK").begins_with("B-")
        )
    
        return response["Items"]
    
    def get_tap_beers_in_restaurant(restaurant_id: str) -> typing.List[dict]:
    
        table = boto3.resource("dynamodb").Table("table-name")
    
        response = table.query(
            KeyConditionExpression=conditions.Key("PK").eq(f"R#{restaurant_id}") \
                & conditions.Key("SK").begins_with("B-TB")
        )
    
        return response["Items"]
    
    def get_bottled_beers_in_restaurant(restaurant_id: str) -> typing.List[dict]:
    
        table = boto3.resource("dynamodb").Table("table-name")
    
        response = table.query(
            KeyConditionExpression=conditions.Key("PK").eq(f"R#{restaurant_id}") \
                & conditions.Key("SK").begins_with("B-BB")
        )
    
        return response["Items"]
    
    import typing
    import boto3
    import boto3.dynamodb.conditions as conditions
    
    def get_all_beers() -> typing.List[dict]:
    
        table = boto3.resource("dynamodb").Table("data")
    
        response = table.query(
            KeyConditionExpression=conditions.Key("GSI1PK").eq("BEERS"),
            IndexName="GSI1"
        )
        
        list_with_duplicates = [item["name"] for item in response["Items"]]
        list_without_duplicates = list(set(list_with_duplicates))
    
        return [{"beername": name} for name in list_without_duplicates]
    

    现在向表中添加项时,必须考虑要创建哪种类型的实体。

    餐馆很简单,他们只需要一个ID和一个名字。根据这些信息,我们可以计算关键属性:

    import boto3
    
    def create_restaurant(restaurant_id: str, name: str) -> None:
        table = boto3.resource("dynamodb").Table("data")
    
        item = {
            "PK": f"R#{restaurant_id}",
            "SK": "META",
            "GSI1PK": "RESTAURANTS",
            "GSI1SK": f"R#{restaurant_id}",
            "type": "RESTAURANT",
            "id": restaurant_id,
            "name": name
        }
    
        table.put_item(
            Item=item
        )
    

    啤酒总是属于餐馆的,所以我们需要有它的id——它也总是有价格和名字。

    import boto3
    
    def create_beer_for_restaurant(restaurant_id: str, name: str, price: str, is_tap: bool):
    
        table = boto3.resource("dynamodb").Table("data")
    
        sk = f"B-TB#{name}" if is_tap else f"B-BB#{name}"
    
        item = {
            "PK": f"R#{restaurant_id}",
            "SK": sk,
            "GSI1PK": "BEERS",
            "GSI1SK": f"B#{name}",
            "name": name,
            "price": price
        }
    
        table.put_item(
            Item=item
        )
    

 类似资料:
  • 这是项目迁移 这是时间表 这样用户就可以迁移了 这是我的项目模型 这是我的时间表模型: 这是我的用户模型 现在,我从项目返回我的查询 这是可以的,但user_id用户在timesheets.user_id我不能得到它的时间表,并得到它 此控制器按时间表中的项目id返回项目和时间表,但时间表中的用户id我不知道如何将其输入系统

  • 我正在学习冬眠,只是有点困惑。

  • 我正在努力理解如何处理与JOOQ的一对多和多对多关系的Pojo。 我存储玩家创建的位置(一对多关系)。一个位置可以容纳多个可能访问它的其他玩家(多对多)。数据库布局可归结为以下内容: 在我的java应用程序中,所有这些信息都存储在一个pojo中。请注意,玩家和受邀玩家列表可以从应用程序中更新,也需要在数据库中更新: 我可以使用JOOQ的pojo映射将这三个记录映射到单个pojo吗?我可以使用这个p

  • 问题内容: 好的,所以这可能是一个琐碎的问题,但是我在可视化和理解差异以及何时使用它们方面遇到困难。对于诸如单向和双向映射之类的概念如何影响一对多/多对多关系,我也还不清楚。我现在正在使用Hibernate,因此任何与ORM相关的解释都将有所帮助。 举例来说,我有以下设置: 那么在这种情况下,我将进行哪种映射?对于这个特定示例的答案肯定会受到赞赏,但我也确实希望获得何时使用一对多和多对多以及何时使

  • 本文向大家介绍mybatis关系映射之一对多和多对一,包括了mybatis关系映射之一对多和多对一的使用技巧和注意事项,需要的朋友参考一下 本实例使用用户和订单的例子做说明: 一个用户可以有多个订单, 一个订单只对应一个用户。(其中应用到注释) 1.代码的结构 2. 建表语句: 3. 用户实体: 4. 订单实体: 5.写PersonMapper.java的接口 6. 一对多实体配置: Person