DynamoDB查询报TypeError:无法读取undefined的'0'属性,求排查
问题描述
我有一个名为AllowListedItems的DynamoDB表,包含Id、name和ignoreNoTraffic字段,且在name字段上创建了名为name-index的GSI。尝试通过name查询ignoreNoTraffic的值时,AWS SDK V3的TypeScript代码报错,但AWS SDK V2的JavaScript代码可正常执行。
AWS SDK V3 TypeScript代码
业务逻辑代码
import { DynamoDB, GetItemCommand, ScanCommand, QueryCommand } from "@aws-sdk/client-dynamodb"; import { unmarshall } from "@aws-sdk/util-dynamodb"; private async getIgnoreNoTraffic(): Promise<boolean> { const params = { TableName: Tables.ALLOW_LISTED_ITEMS, IndexName: "name-index", KeyConditionExpression: "#nameAttr = :nameValue", ExpressionAttributeNames: { "#nameAttr": "name" }, ExpressionAttributeValues: { ":nameValue": this.name, }, }; try { const items = await dynamodbUtils.queryValuesFromDB(params); if (items.length > 0) { const item = items[0]; console.log("Received item:", item); if (item. ignoreNoTraffic !== undefined) { return item. ignoreNoTraffic.BOOL || false; } else { return false; } } else { console.log("Item could not be fetched from the table using name"); return false; } } catch (error) { console.error("Error fetching ignoreNoTraffic by name:", error); throw error; } }
DynamoDB工具函数
export async function queryValuesFromDB(params: any) { console.log("Querying DynamoDB with params2:", JSON.stringify(params, null, 2)); try { const data = await client.send(new QueryCommand(params)); if (!data) { console.error("DynamoDB returned undefined or null data."); throw new Error("DynamoDB returned undefined or null data."); } console.log("Received raw data from DynamoDB:", JSON.stringify(data, null, 2)); if (!data.Items) { console.log("Items field is missing from the data."); throw new Error("DynamoDB response does not contain 'Items'."); } if (data.Items.length > 0) { console.log("Successfully retrieved items:", data.Items); return data.Items; // Directly return the items as JSON } else { console.log("No items found for the query."); return []; } } catch (error) { console.error("Error executing DynamoDB query:", error); if (error instanceof Error) { console.error("Error message:", error.message); throw new Error(`DynamoDB Query Error: ${error.message}`); } else { console.error("Unknown error occurred:", JSON.stringify(error)); throw new Error("An unknown error occurred during DynamoDB query."); } }
错误信息
ERROR Error while sending query command to DynamoDB: TypeError: Cannot read properties of undefined (reading '0')
日志信息
INFO Querying DynamoDB with params3: { "TableName": "AllowListedItems", "IndexName": "name-index", "KeyConditionExpression": "#nameAttr = :nameValue", "ExpressionAttributeNames": { "#nameAttr": "name" }, "ExpressionAttributeValues": { ":nameValue": "development" } }
可正常运行的AWS SDK V2 JavaScript代码
const AWS = require('aws-sdk'); const dynamodb = new AWS.DynamoDB(); const docClient = new AWS.DynamoDB.DocumentClient(); async function queryValuesFromDB(params) { console.log("Querying DynamoDB with params:", JSON.stringify(params, null, 2)); try { const data = await docClient.query(params).promise(); console.log("Received raw data from DynamoDB:", JSON.stringify(data, null, 2)); if (data && data.Items && data.Items.length > 0) { console.log("Successfully retrieved items:", data.Items); return data.Items; } else { console.log("No items found for the query."); return []; } } catch (error) { console.error("Error executing DynamoDB query:", error.message || error); throw new Error(`DynamoDB Query Error: ${error.message || error}`); } } async function getIgnoreNoTraffic(name) { const params = { TableName: 'AllowListedItems', IndexName: 'name-index', KeyConditionExpression: "#nameAttr = :nameValue", ExpressionAttributeNames: { "#nameAttr": "name" }, ExpressionAttributeValues: { ":nameValue": name, }, }; try { const items = await queryValuesFromDB(params); if (items.length > 0) { const item = items[0]; if (item. ignoreNoTraffic !== undefined) { return item. ignoreNoTraffic; } else { return false; } } else { console.log("Item could not be fetched from the table using name"); return false; } } catch (error) { console.error("Error fetching ignoreNoTraffic by name:", error); throw error; } } exports.handler = async (event) => { const name = event.name || "test-name"; try { const ignoreNoTraffic = await getIgnoreNoTraffic(name); console.log("Ignore result:", ignoreNoTraffic); return { statusCode: 200, body: JSON.stringify({ message: "Success", ignoreNoTraffic: ignoreNoTraffic }), }; } catch (error) { return { statusCode: 500, body: JSON.stringify({ message: "Error", error: error.message, }), }; } };
问题原因
- SDK数据格式不兼容:V3使用的是低阶
DynamoDB客户端,要求ExpressionAttributeValues必须传入带类型标识的DynamoDB原生格式(如{ S: "value" }),但你直接传入了字符串,导致查询失败,工具函数返回的items异常。而V2使用的DocumentClient会自动处理格式转换,所以可正常运行。 - 代码语法错误:业务代码中
item. ignoreNoTraffic的点后多了空格,实际访问的是带前导空格的属性名,与表中实际字段ignoreNoTraffic不匹配,导致读取属性时返回undefined,进而触发读取.BOOL的错误。 - GSI投影属性缺失(潜在风险):如果
name-index的投影属性未包含ignoreNoTraffic,即使查询成功,也不会返回该字段,同样会导致属性读取失败。
解决方案
方案1:改用DynamoDBDocumentClient(推荐)
使用V3高阶客户端DynamoDBDocumentClient,自动处理数据格式转换,和V2的DocumentClient逻辑一致:
修改工具函数
import { DynamoDBDocumentClient, QueryCommand } from "@aws-sdk/lib-dynamodb"; import { DynamoDB } from "@aws-sdk/client-dynamodb"; const client = new DynamoDB({}); const docClient = DynamoDBDocumentClient.from(client); export async function queryValuesFromDB(params: any) { console.log("Querying DynamoDB with params:", JSON.stringify(params, null, 2)); try { const data = await docClient.send(new QueryCommand(params)); return data.Items || []; } catch (error) { console.error("Error executing DynamoDB query:", error); if (error instanceof Error) { throw new Error(`DynamoDB Query Error: ${error.message}`); } else { throw new Error("An unknown error occurred during DynamoDB query."); } } }
修正业务代码的语法错误
if (items.length > 0) { const item = items[0]; console.log("Received item:", item); if (item.ignoreNoTraffic !== undefined) { return item.ignoreNoTraffic || false; } else { return false; } }
方案2:手动处理数据格式(不推荐)
若坚持使用低阶DynamoDB客户端,需手动处理参数格式和数据转换:
修改查询参数的格式
const params = { TableName: Tables.ALLOW_LISTED_ITEMS, IndexName: "name-index", KeyConditionExpression: "#nameAttr = :nameValue", ExpressionAttributeNames: { "#nameAttr": "name" }, ExpressionAttributeValues: { ":nameValue": { S: this.name }, // 字符串类型需用S标识 }, };
工具函数中添加数据转换
import { unmarshall } from "@aws-sdk/util-dynamodb"; // ...其他代码 if (data.Items.length > 0) { return data.Items.map(item => unmarshall(item)); // 转换为普通JSON对象 } else { return []; }
同样修正业务代码的空格问题
和方案1中的修正一致,确保访问正确的字段名。
额外检查
- 确认
name-index的投影属性包含ignoreNoTraffic,可在AWS控制台DynamoDB表的GSI设置中查看修改。 - 验证DynamoDB客户端已正确初始化,且Lambda角色或执行账号具有访问该表和GSI的权限。
内容的提问来源于stack exchange,提问作者AnOldSoul
相关产品推荐
相关产品推荐

