【问题标题】:Unable to write item(s) to DynamoDB table utilizing DocumentClient - Nodejs无法使用 DocumentClient - Nodejs 将项目写入 DynamoDB 表
【发布时间】:2020-03-01 13:06:11
【问题描述】:

我对 DynamoDb 绝对是全新的,我正在尝试简单地从 NodeJS Lambda 编写一个对象。根据我阅读和研究的内容,我可能应该使用来自aws-sdkDocumentClient。关于 DocumentClient 的问题,我还发现了以下question here,但它似乎并没有解决我的具体问题......不幸的是,我无法真正找到/确定。我已经设置了一个调试器来帮助进行 SAM 本地开发,但它似乎只提供了一些错误。

这里显示了代码的实现。

var params = {
     TableName: "March-Madness-Teams",
     Item: {
        "Id": {"S": randstring.generate(9)},
        "School":{"S": team_name},
        "Seed": {"S": seed},
        "ESPN_Id": {"S": espn_id}
      }
    }
  console.log(JSON.stringify(params))

   dynamodb.put(params, (error,data) => {
      if (error) {
         console.log("Error ", error)
      } else {
         console.log("Success! ", data)
      }
   })

基本上,我正在使用cheerio 库和从DOM 中挑选值并将它们保存到如下所示的json 对象中来清理网站。

{
  "TableName": "March-Madness-Teams",
  "Item": {
    "Id": {
      "S": "ED311Oi3N"
    },
    "School": {
      "S": "BAYLOR"
    },
    "Seed": {
      "S": "1"
    },
    "ESPN_Id": {
      "S": "239"
    }
  }
}

当我尝试将此 json 对象推送到 Dynamo 时,出现错误提示

Error  MultipleValidationErrors: There were 2 validation errors:
* MissingRequiredParameter: Missing required key 'TableName' in params
* MissingRequiredParameter: Missing required key 'Item' in params

上面的错误一切都很好......我认为它不喜欢我将这些键包装在字符串中的事实,所以我删除了引号并发送了以下内容

{
  TableName: "March-Madness-Teams",
  Item: {
    "Id": {
      "S": "ED311Oi3N"
    },
    "School": {
      "S": "BAYLOR"
    },
    "Seed": {
      "S": "1"
    },
    "ESPN_Id": {
      "S": "239"
    }
  }
}

但是,当我这样做时……我什么也得不到。

这是一个更大的代码sn-p。

return new Promise((resolve,reject) => {
            axios.get('http://www.espn.com/mens-college-basketball/bracketology')
            .then(html => {

                const dynamodb = new aws.DynamoDB.DocumentClient()

                let $ = cheerio.load(html.data)
                $('.region').each(async function(index, element){
                    var preregion = $(element).children('h3,b').text()
                    var region = preregion.substr(0, preregion.indexOf('(') - 1)

                    $(element).find('a').each(async function(index2, element2){
                        var seed = $(element2).siblings('span.rank').text()
                    if (seed.length > 2){
                        seed = $(element2).siblings('span.rank').text().substring(0, 2)
                    }

                    var espn_id = $(element2).attr('href').split('/').slice(-2)[0]
                    var team_name = $(element2).text()
                    var params = {
                        TableName: "March-Madness-Teams",
                        Item: {
                            "Id": randstring.generate(9),
                            "School":team_name,
                            "Seed": seed,
                            "ESPN_Id": espn_id
                        }
                    }
                    console.log(JSON.stringify(params))

                    // dynamodb.put(params)
                    //     .then(function(data) {
                    //         console.log(`Success`, data)
                    //     })
                })
              })
            })
        })

【问题讨论】:

  • 看起来不错,只需再次检查您的调试点和aws文档示例docs.aws.amazon.com/amazondynamodb/latest/developerguide/…
  • 你能显示更多的代码吗?根据您的其中一个 cmets,听起来您可能正在将异步与回调结合起来。
  • 好了,@JasonWadsworth。用更多的源代码编辑了原始帖子。
  • 这是在网页上运行,还是 NodeJS 服务器?
  • 它现在只是一个 lambda 函数,但将支持网页。

标签: node.js amazon-dynamodb aws-sdk


【解决方案1】:

你可以尝试不使用类型吗?

代替

    "School":{"S": team_name},

例如,使用

    "School": team_name,

【讨论】:

  • 不走运.....调试时它会转到我开始调用 put API 的位置,但是当我尝试遍历时,它只是完全跳过回调。即使我在整个 dynamodb api 代码部分都有断点:(
【解决方案2】:

从您的代码中,我可以看到 dynamodb 请求中的错误承诺。试着改变你的台词:

dynamodb.put(params).then(function(data) {
    console.log(`Success`, data)
})

成为:

dynamodb.put(params).promise().then(function(data) {
    console.log(`Success`, data)
})

您也可以与 await 结合使用:

await dynamodb.put(params).promise().then(function(data) {
    console.log(`Success`, data)
})

【讨论】:

  • 就像 Jason 说的我错过了一些严肃的等待......我花了一些时间并重组了很多代码,使其更具可读性并且可能更有意义。我将更新帖子以反映更改并接受它。如果您或任何人看到可以/应该改变的东西,请告诉我!
【解决方案3】:
exports.lambdaHandler = async (event, context) => {
    const html = await axios.get('http://www.espn.com/mens-college-basketball/bracketology')
    let $ = cheerio.load(html.data)
    const schools = buildCompleteSchoolObject(html, $)
    try {
        await writeSchoolsToDynamo(schools)
        return { statusCode: 200 }
    } catch (error) {
        return { statusCode: 400, message: error.message }
    }
}

const writeSchoolsToDynamo = async (schools) => {
        const promises = schools.map(async school => {
            await dynamodb.put(school).promise()
        })

        await Promise.all(promises)
}

const buildCompleteSchoolObject = (html, $) => {
    const schools = []

    $('.region').each(loopThroughSubRegions(schools, $))
    return schools
}
const loopThroughSubRegions = (schools, $) => {
    return (index, element) => {
        var preregion = $(element).children('h3,b').text()
        var region = preregion.substr(0, preregion.indexOf('(') - 1)
        $(element).find('a').each(populateSchoolObjects(schools, $))
    }
}
const populateSchoolObjects = (schools, $) => {
    return (index, element) => {
        var seed = $(element).siblings('span.rank').text()
        if (seed.length > 2) {
            seed = $(element).siblings('span.rank').text().substring(0, 2)
        }
        var espn_id = $(element).attr('href').split('/').slice(-2)[0]
        var team_name = $(element).text()
        schools.push({
            TableName: "March-Madness-Teams",
            Item: {
                "Id": randstring.generate(9),
                "School": team_name,
                "Seed": seed,
                "ESPN_Id": espn_id
            }
        })
    }
}

我知道这与我开始的方式截然不同,但我做了更多的挖掘和某种工作......我不确定这是否是最好的方法,但我似乎明白了工作...如果有什么需要改变,请告诉我!

【讨论】:

    【解决方案4】:

    哦,我明白你想要什么。

    也许您可以看到上面的代码有效,但这里有一个关于异步的概念需要改进 - 等待和承诺,尤其是在 lambda 函数上。

    我在上面的代码中有一些注释,也许你可以考虑改进你的 lambda:

    1. 对 lambda 中的每个 promise 使用 await 并不是最好的方法,因为我们知道 lambda 时间限制。但有时我们可以在其他情况下这样做。

    2. 也许您可以将 dynamodb.put 方法更改为 dynamodb.batchWriteItem :

    The BatchWriteItem operation puts or deletes multiple items in one or more tables.

    1. 或者,如果您必须使用 dynamodb.put,请尝试将代码改进为:
        const writeSchoolsToDynamo = async (schools) => {
            const promises = schools.map(school => {
                dynamodb.put(school).promise()
            })
    
            return Promise.all(promises)
        }
    

    【讨论】:

    • 嘿,马赫迪,我尝试了你的代码 sn-p,但它不起作用。该函数成功完成,但是实际上没有数据写入表中....想法?
    • 老实说,我没有测试我上面的代码。我只是提供了一个运行异步/并行多进程的想法(这里你运行多个 dynamodb.put)。在您的代码中,您通过等待运行同步过程。这意味着如果我们有 5 个 put 需要 1 个,您的代码执行它们序列化并需要 5 个。通过并行,它们可以处理 avg +- 1s for 5 put。也许您会尝试更改我的代码: const promises =schools.map(school => { dynamodb.put(school).promise() }) 成为 const promises =schools.map(school => { return dynamodb.put(school) ).promise() })
    猜你喜欢
    • 2016-12-07
    • 2017-03-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-09-12
    • 1970-01-01
    • 1970-01-01
    • 2017-07-21
    相关资源
    最近更新 更多