How to add new attributes to map in DynamoDB? - amazon-web-services

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"
}

Related

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

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

How to apply custom score to a search filed in Elastic Search

I am making a search query in Elastic Search and I want to treat the fields the same when they match. For example if I search for field field1 and it matches, then the _score is increase by 10(for example), same for the field2.
I was tried function_score but it's not working. It throws an error.
"caused_by": {
"type": "class_cast_exception",
"reason": "class
org.elasticsearch.index.fielddata.plain.SortedSetDVOrdinalsIndexFieldData
cannot be cast to class
org.elasticsearch.index.fielddata.IndexNumericFieldData
(org.elasticsearch.index.fielddata.plain.SortedSetDVOrdinalsIndexFieldData
and org.elasticsearch.index.fielddata.IndexNumericFieldData are in unnamed
module of loader 'app')"
}
The query:
{
"track_total_hits": true,
"size": 50,
"query": {
"function_score": {
"query": {
"bool": {
"must": [
{
"term": {
"field1": {
"value": "Value 1"
}
}
},
{
"term": {
"field2": {
"value": "value 2"
}
}
}
]
}
},
"functions": [
{
"field_value_factor": {
"field": "field1",
"factor": 10,
"missing": 0
}
},
{
"field_value_factor": {
"field": "field2",
"factor": 10,
"missing": 0
}
}
],
"boost_mode": "multiply"
}
}
}
You can use function score with filter function to boost.
assuming that your mapping looks like the one below
{
"mappings": {
"properties": {
"field_1": {
"type": "keyword"
},
"field_2": {
"type": "keyword"
}
}
}
}
with documents
{"index":{}}
{"field_1": "foo", "field_2": "bar"}
{"index":{}}
{"field_1": "foo", "field_2": "foo"}
{"index":{}}
{"field_1": "bar", "field_2": "bar"}
you can use weight parameter to boost the documents matched for each query.
{
"query": {
"function_score": {
"query": {
"match_all": {}
},
"functions": [
{
"filter": {
"term": {
"field_1": "foo"
}
},
"weight": 10
},
{
"filter": {
"term": {
"field_2": "foo"
}
},
"weight": 20
}
],
"score_mode": "multiply"
}
}
}
You can refer below solution if you want to provide manual weight for different field in query. This will always replace highest weight field on top of your query response -
Elasticsearch query different fields with different weight

How to update a nested item in AWS DynamoDB with CLI

In one of my AWS DynamoDB tables I have several items that require an update within in a pipeline.
The following json shows an item in the AWS DynamoDB table:
{
"DeploymentConfigs": {
"L": [
{
"M": {
"Config": {
"M": {
"Name": {
"S": "batch-komo"
},
"Replicas": {
"N": "3"
}
}
}
}
},
{
"M": {
"Config": {
"M": {
"Name": {
"S": "online-komo"
},
"Replicas": {
"N": "3"
}
}
}
}
}
]
},
"environment": {
"S": "komo-claimcenter"
}
}
How can update an object in DeploymentConfigs?
Primary partition key is environment.
E.g. the object
{
"M": {
"Config": {
"M": {
"Name": {
"S": "batch-komo"
},
"Replicas": {
"N": "3"
}
}
}
}
}
shall be updated to
{
"M": {
"Config": {
"M": {
"Name": {
"S": "batch-komo"
},
"Replicas": {
"N": "5"
}
}
}
}
}
I do not know to achieve in the AWS CLI.
To update a nested part inside an item, you use an UpdateExpression with an attribute path. For example, SET DeploymentConfigs[0].Config.Replicas = :val.
The problem, however, is that your top-level attribute DeploymentConfigs is a list, so to modify one of its items, you need to know in index (in this example 0). If you don't know the index you have a problem - there is no way to "modify the list item which has name='batch-komo'" or something like that... If you need to do something like this, you have to read the entire top-level attribute, modify it in the client, and write it (or just the small change, now that you know the index) back.

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
}

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')"}
}
}