Match non-partitionKey fields (in nested json) in both tables and retrieve data in DynamoDB table - amazon-web-services

I have 2 tables, with 1 matching data in which I want to utilize that as a matching field and retrieve some data.
First table is this:
{
"mainFieldName": {
"S": "someString"
},
"fieldA": {
"L": [
{
"M": {
"AccountId": {
"S": "12345"
},
"PrincipalId": {
"S": "randomIdString"
},
"PrincipalType": {
"S": "GROUP"
}
}
},
{
"M": {
"AccountId": {
"S": "12345"
},
"PrincipalId": {
"S": "secondRandomString"
},
"PrincipalType": {
"S": "GROUP"
}
}
}
]
},
"fieldC": {
"L": [
{
"M": {
"name": {
"S": "xxx"
},
"final_json": {
"S": "some json data"
}
}
}
]
}
}
Second table:
{
"userId": {
"S": "randomString"
},
"group": {
"L": [
{
"M": {
"GroupId": {
"S": "randomGroupId"
}
}
}
]
}
}
I want to find the matched field for first table's fieldA.PrincipalId and second table's group.GroupId, if match, returning data is first table's fieldC.final_json
My params i tried is this, it's executed successfully but no results returned. I have confirmed there should be some matched inputs.
response = table1.scan(
TableName=TABLE_1,
FilterExpression="#gid.#pid = :id",
ExpressionAttributeValues={
':id': {'S': groupId}
},
ExpressionAttributeNames={
'#gid': 'groupId',
'#pid': 'PrincipalId'
}
)
It always return empty results

I managed to find a resolution to this. To simplify, I changed to a flatter table structure by pre-processing the Json to appending to a list.
My first table becomes:
{
"id": {
"S": "randomString"
},
"fieldA": {
"S": "randomString"
},
"table_1_groupId": {
"L": [
{
"S": "randomGroupIdString"
}
]
},
"fieldB": {
"S": "asdfsafd"
},
"fieldC": {
"L": [
{
"M": {
"name": {
"S": "randomString"
},
"jsonData": {
"S": "randomJsonData"
},
"type": {
"S": "type_a"
}
}
}
]
}
}
Second table stays the same.
With that i am able to use DynamoDB query which is more efficient as well, with FilterExpressions
My query is:
response = table1.query(
TableName=TABLE_1,
KeyConditionExpression="id = :id",
FilterExpression="contains(groupId, :gid)",
ExpressionAttributeValues={
':id': defaultId,
':gid': groupId
},
)
My output returns list of all data (since I haven't added any filter to the output) once they have the field 'GroupId' in Table2 match with table_1_groupId in table 1

Related

How to filter data by an object in array in DynamoDb

I want to filter data using dynamoDB QueryInput approach, the problem is in filtering data with an object in Array.
I have a data with below structure
[
{
"Pk": "mimzeslami#gmail.com",
"Sk": "social-shared-goal-2022-09-27 12:29:27",
"Gsi1Pk": "social-shared-goal",
"Gsi1Sk": "2022-09-27 12:29:27",
"Username": "test#gmail.com",
"Goals": [
{
"M": {
"Gsi2Sk": {
"S": "goal-end-2022-11-26 20:30:00"
},
"Gsi1Sk": {
"S": "goal-start-2022-09-27 12:28:47"
},
"Pk": {
"S": "mimzeslami#gmail.com"
},
"Gsi1Pk": {
"S": "mimzeslami#gmail.com"
},
"BaseCategoryId": {
"S": "85j85nachallll9idja"
},
"SubCategoryId": {
"S": "49023842874xhhiayx"
},
"Gsi2Pk": {
"S": "mimzeslami#gmail.com"
}
}
}
]
}
]
I filtered data with Username in this way:
keyCondition := map[string]*dynamodb.Condition{
Gsi1Pk: {
ComparisonOperator: aws.String(Eq),
AttributeValueList: []*dynamodb.AttributeValue{
{
S: aws.String(constants.SocialSharedGoal),
},
},
},
}
var queryFilter = map[string]*dynamodb.Condition{}
if maybeUserName != "" {
queryFilter["Username"] = &dynamodb.Condition{
ComparisonOperator: aws.String("CONTAINS"),
AttributeValueList: []*dynamodb.AttributeValue{
{
S: aws.String(maybeUserName),
},
},
}
}
params := &dynamodb.QueryInput{
KeyConditions: keyCondition,
TableName: aws.String(myTable),
IndexName: aws.String(Gsi1PkGsi1SkIndex),
Limit: &limit,
ScanIndexForward: &ascSort,
QueryFilter: queryFilter,
}
Now I want to filter data by BaseCategoryId and SubCategoryId that are in a Golas array and I don't know how to do that.
I am looking for a way like this to filter data
for example
if maybeBaseCategoryId != "" {
queryFilter[""] = &dynamodb.Condition{
ComparisonOperator: aws.String("CONTAINS"),
AttributeValueList: []*dynamodb.AttributeValue{
{S: aws.String(maybeBaseCategoryId),
},
},
}
}

How to add new attributes to map in DynamoDB?

My database structure,
{
"email":"example#mail.com",
"products": {
"product1":{
"price":"$10",
"details":"detail"
},
"product2":{
"price":"$20",
"details":"detail"
}
}
}
I want to add new attributes to "products" map and expected output as follow,
{
"email":"example#mail.com",
"products": {
"product1":{
"price":"$10",
"details":"detail"
},
"product2":{
"price":"$20",
"details":"detail"
},
"product3":{
"price":"$10",
"details":"detail"
}
}
}
I am using API Gateway and UpdateItem action. Here is my mapping template,
{
"TableName": "tableName",
"Key": {
"email": {
"S": "$input.path('$.email')"
}
},
"UpdateExpression": "SET #pr = :vals",
"ExpressionAttributeNames": {
"#pr": "products"
},
"ExpressionAttributeValues": {
":vals": {
"M": {
"$input.path('$.productId')": {
"M": {
"price": {
"N": "$input.path('$.price')"
},
"details": {
"S": "$input.path('$.details')"
}
}
}
}
}
},
"ReturnValues": "NONE"
}
Using above template will replace all my attributes. Actual output,
{
"email":"example#mail.com",
"products": {
"product3":{
"price":"$10",
"details":"detail"
}
}
}
How I can add new attributes to map instead of replace it?
Thanks.
In your request you are SETting the entire Products map, but you only want to add a nested map.
{
"TableName": "tableName",
"Key": {
"email": {
"S": "$input.path('$.email')"
}
},
"UpdateExpression": "SET #pr.product3 = :vals",
"ExpressionAttributeNames": {
"#pr": "products"
},
"ExpressionAttributeValues": {
":vals": {
"M": {
"$input.path('$.productId')": {
"M": {
"price": {
"N": "$input.path('$.price')"
},
"details": {
"S": "$input.path('$.details')"
}
}
}
}
}
},
"ReturnValues": "NONE"
}

AWS API Gateway as proxy to dynamo DB HTTP Get mapping template

I have a API gateway which does a get to the tables stored in dynamo DB.
The table stored looks like as JSON as show below
{
"photos": {
"page": 1,
"pages": "1234",
"perpage": 100,
"photo": [
{
"farm": 1,
"id": "12345678901",
"isfamily": 0,
"isfriend": 0,
"ispublic": 1,
"owner": "23456789#A12",
"secret": "abc123d456",
"server": "1234",
"title": "Sample photo 1"
},
{
"farm": 2,
"id": "23456789012",
"isfamily": 0,
"isfriend": 0,
"ispublic": 1,
"owner": "34567890#B23",
"secret": "bcd234e567",
"server": "2345",
"title": "Sample photo 2"
}
],
"total": "123398"
},
"srini": "srini"
}
With out integration response mapping template I get the table as shown below
{
"Count": 1, "Items": [
{
"photos": {
"M": {
"photo": {
"L": [
{
"M": {
"owner": {
"S": "23456789#A12"
},
"server": {
"S": "1234"
},
"ispublic": {
"N": "1"
},
"isfriend": {
"N": "0"
},
"farm": {
"N": "1"
},
"id": {
"S": "12345678901"
},
"secret": {
"S": "abc123d456"
},
"title": {
"S": "Sample photo 1"
},
"isfamily": {
"N": "0"
}
}
},
{
"M": {
"owner": {
"S": "34567890#B23"
},
"server": {
"S": "2345"
},
"ispublic": {
"N": "1"
},
"isfriend": {
"N": "0"
},
"farm": {
"N": "2"
},
"id": {
"S": "23456789012"
},
"secret": {
"S": "bcd234e567"
},
"title": {
"S": "Sample photo 2"
},
"isfamily": {
"N": "0"
}
}
}
]
},
"perpage": {
"N": "100"
},
"total": {
"S": "123398"
},
"pages": {
"S": "1234"
},
"page": {
"N": "1"
}
}
},
"srini": {
"S": "srini"
}
} ], "ScannedCount": 1
}
I am trying to retrieve in the JSON format so that web client takes the table from Dynamo in JSON format .The mapping template I am trying to write is as follows
#set($inputRoot = $input.path('$'))
{
#foreach($elem in $inputRoot.Items) {
"srini": "$elem.srini.S",
"pages": "$elem.photos.pages.S",
#foreach($elemnext in $elem.photos.photo) {
"id": "$elemnext.id.S"
}#if($foreach.hasNext),#end
#end
}#if($foreach.hasNext),#end
#end
}
I only can retrieve srini as show below
Response Body
{
{
"srini": "srini",
"pages": ""
}
}
All other data is not retreived ,What is the right way to write mapping template ,Can any one let me know please?
#set($inputRoot = $input.path('$'))
{
#foreach($elem in $inputRoot.Items) {
"srini": "$elem.srini.S",
"pages": "$elem.photos.M.pages.S",
#foreach($elemnext in $elem.photos.M.photo.L)
{
"id": "$elemnext.M.id.S"
} #if($foreach.hasNext),#end
#end
}#if($foreach.hasNext),#end
#end
}

Unable to map a list of numbers in API-Gateway

How do I create a mapping for a list of numbers object in API gateway? I am trying to post a list of integers using POST request. I tried working with NS attribute but got the Error.
Error:
{
"__type": "com.amazon.coral.service#SerializationException"
}
However, it works well when I have N attribute and post a single integer value.
Is there any way to resolve this issue?
I believe you are trying to map your request payload to DynamoDB JSON String. You can apply a velocity template like this one,
{
"TableName":"ABC",
"Item": {
"id": {
"S": "$context.requestId"
},
"name": {
"S": "$input.path('$.name')"
},
"price": {
"L": [
#set($prices=$input.path('$.price'))
#foreach($p in $prices)
{
"N": "$p"
}#if ($velocityCount < $prices.size()), #end
#end
]
}
}
}
Method Request Body:
{
"name":"Test",
"price": [1, 2, 3]
}
Endpoint Request Body:
{
"TableName": "ABC",
"Item": {
"id": {
"S": "test-invoke-request"
},
"name": {
"S": "Test"
},
"price": {
"L": [
{
"N": "1"
},
{
"N": "2"
},
{
"N": "3"
}
]
}
}
}

How do I configure an UpdateExpression with nested data

I have an AWS API Gateway that stores data in a DynamoDB instance. My table structure looks like this:
{
"TableName": "stuff",
"Item": {
"stuffId": {
"S": "02b4e004-1132-4b87-a855-20e7d1bd1840"
},
"clients": {
"M": {
"company_inc": {
"M": {
"prod": {
"S": "null"
},
"qa": {
"S": "null"
},
"stage": {
"S": "null"
}
}
}
}
}
}
}
I'm trying to figure out how to configure my Body Mapping Template so that given an HTTP PATCH request I can update company_inc.prod. For example; given this query string:
?stuffId=02b4e004-1132-4b87-a855-20e7d1bd1840&client=company_inc&location=prod&locationIsSet=true
I would update the record to look like this:
{
"TableName": "stuff",
"Item": {
"stuffId": {
"S": "02b4e004-1132-4b87-a855-20e7d1bd1840"
},
"clients": {
"M": {
"company_inc": {
"M": {
"prod": {
"S": "true"
},
"qa": {
"S": "null"
},
"stage": {
"S": "null"
}
}
}
}
}
}
}
What should an "UpdateExpression" look like to achieve that?
I think I found the answer. You have to use attribute names like #client and #location as placeholders in the path for company_inc.prod.
{
"TableName": "stuff",
"Key": {
"alterId": {
"S": "$input.params('stuffId')"
}
},
"UpdateExpression": "set clients.#attrClientName.#attrLocation = :locationIsSet",
"ExpressionAttributeNames" : {
"#attrClientName" : "$input.params('client')",
"#attrLocation" : "$input.params('location')"
},
"ExpressionAttributeValues": {
":locationIsSet": {"S": "$input.params('location')"}
}
}