Dynamo db query using contains operator - node.js

My table items are of the form of
function addDoc(movie,cb){
var params = {
TableName: "Movies",
Item: {
"year": movie.year,
"title": movie.title,
"info": movie.info,
"genres" : movie.info.genres || []
}
};
docClient.put(params, function(err, data) {
bar.tick(1)
i++;
cb(err);
});
}
async.eachLimit(allMovies,50,addDoc,function (err) {
console.log(err)
console.log("done inserting " + i + " movies");
});
I'm running this code :
var params = {
TableName : "Movies",
//ProjectionExpression:"#yr, title, genres, info.actors[0]",
KeyConditionExpression: "#yr = :yyyy and contains(genres, :g1)",
ExpressionAttributeNames:{
"#yr": "year"
},
ExpressionAttributeValues: {
":yyyy":1992,
":g1" : "Drama"
},
//Select : "COUNT"
};
var start = Date.now()
docClient.query(params, function(err, data) {
if (err) {
console.error("Unable to query. Error:", JSON.stringify(err, null, 2));
} else {
console.log("time elapsed :",Date.now()-start);
console.log("Query succeeded.");
console.log(data)
}
});
and I'm getting this error
"Invalid operator used in KeyConditionExpression: contains"
any idea?

There are few things that need to be clarified here.
1) The Key attributes of DynamoDB has to be scalar data type. So, I believe the attribute genres can't be defined as SET or LIST data type
2) KeyConditionExpression - can refer to Hash and Sort key only. So, I presume the attribute genres is defined as SORT key of the table
3) contains can be used on FilterExpression on data types STRING, SET or LIST. It can't be used on KeyConditionExpression
Conclusion - Refer point 3 for straight forward answer

Based on the SDK documentation, KeyConditionExpression supports the following expressions:
a = b — true if the attribute a is equal to the value b
a < b — true if a is less than b
a <= b — true if a is less than or equal to b
a > b — true if a is greater than b
a >= b — true if a is greater than or equal to b
a BETWEEN b AND c — true if a is greater than or equal to b, and less than or equal to c.
The following function is also supported:
begins_with (a, substr)— true if the value of attribute a begins with a particular substring.
see documentation page:
https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Query.html

Related

How update (using Nodejs) a second table (DynamoDB) into a lambda function using some values from 2nd table?

I'm writing a lambda function that update a second table when there is an insert on the first table. Both tables are in DynamoDB. When an insert occurs in the first table a lambda function is triggered to update another table based on some values from the new record.
The lambda function is on Nodejs
I do not know exactly how to write the update statement since I have to use the value store in the second table and sum with the new value and update. What I want to do is something like this:
update table1 set campo1 = campo1 + newValue
I am not sure how to do it in NodeJs + DynamoDB.
This is the function:
const AWS = require('aws-sdk');
const documentClient = new AWS.DynamoDB.DocumentClient();
exports.getCalculos = function(event, context, callback){
let columnName = "";
let numPersonas = 0;
let domainIn = "";
event.Records.forEach((record) => {
// I just one the new records inserted
if (record.eventName == 'INSERT') {
// I read all the values and storage into newRecord
const newRecord = record.dynamodb.NewImage;
// I get the key from the new record to be
// used in the second table
domainIn = newRecord.Domain;
// I check some conditions from the new record
// to see which column I will update in the second table
// columns to update are alwayd different
if((newRecord.puerta1.S == "pass") && (newRecord.puerta2.S == "pass")){
columnName = "escape";
}
if((newRecord.puerta1.S == "close") && (newRecord.puerta2.S == "close")){
columnName = "atrapado";
}
// I get the number from the new record
numPersonas = newRecord.count;
// Then base on an ID from the new record,
// I want to update another field into another table
// It means, sum both values
const params = {
TableName :"SummaryByDomain",
Key:{
"Domain": domainIn
},
UpdateExpression: "set #field2 = :numPersonas",
ExpressionAttributeNames:{
"#field2":columnName
},
ExpressionAttributeValues: {
":numPersonas": numPersonas
},
ReturnValues:"UPDATED_NEW"
};
documentClient.update(params, function(err, data) {
if (err) {
console.error("Unable to update item. Error JSON:", JSON.stringify(err, null, 2));
} else {
console.log("UpdateItem succeeded:", JSON.stringify(data, null, 2));
}
});
}
});
};
Thanks #LostJon for your answer. callback wasn't the issue.
I just need how to update the filed using the same value.
I found it and solved it.
I used:
UpdateExpression: "ADD #field2 :numPersonas",
In this case the ADD works as a math operator if values are numeric.
now it's working good

Using aws sdk for javascript to write condition expressoins in DynamoDB when a new attribute is greater than the origin attribute

I want to update an item in DynamoDB when a new attribute is greater than the origin one.
for example, a sample dynamodb structure given below
id(primary key) value count
--------------- ----- -----
a 1 10
b 2 10
-------------------------------------------
// When input this item, I wish not to update
-------------------------------------------
a 5 9 (9 is smaller than 10)
-------------------------------------------
// When input this item, I wish to update
-------------------------------------------
b 5 15 (15 is greater than 10)
Here is my code:
const DynamoDB = require('aws-sdk').DynamoDB;
const db = new DynamoDB.DocumentClient();
const TableName = "sample_table";
function update(id, value, count)
{
return db.update({
TableName,
Key: {
id
},
UpdateExpression: "SET #value = :value, #count = :count",
ConditionExpression: "#count > :count",
ExpressionAttributeNames:
{
"#value": "value",
"#count": "count"
},
ExpressionAttributeValues:
{
":value": value,
":count": count
}
}).promise();
}
update("a", 5, 9);
update("b", 5, 15);
But after I run this code, it will throw error
UnhandledPromiseRejectionWarning: ConditionalCheckFailedException: The
conditional request failed
How to use ConditionExpression parameter to meet my need, how can I fixed it?
It is expected. Because the first update - update("a", 5, 9) will be rejected as it fails to satisfy your condition. DynamoDB rejects the promise if Conditional Expressions fails. You should handle this in your code.
Maybe something like:
update('a', 5, 9)
.then(data => {
console.log(data);
})
.catch((err) => {
if (err.code === 'ConditionalCheckFailedException') {
return 'Ok I know this';
}
throw err;
});

Aggregated Query in elastic search is not returning output

This is my code snippet :
if (aggfunction1=="sum")
{
var my_query="{\"size\": 0 ,\"query\": {\"bool\":
{\"must\": {\"match\": { \""+ field1 +"\":\""+ value1 +"\"}}}},
\"aggs\":{\"sum_fld\":{\"sum\": {\"field\":\""+ aggfield1 +"\"}}}}"
console.log(my_query);
client.count({
index: 'paypal',
body:my_query
},
function (err, response)
{
//countM = response.count;
aggs= res.aggregations;
console.log("\nAggregated Value is : " + aggs);
res.send("\nAggregated Value is : " + aggs);
});
};
I am running this in nodejs and i am getting an error ? I tried it in elasticsearch, but it is returning me documents ?
What I want :
Lets say I have a field SentMails. I want the average SentMails in the table where a field equals a value ?

Counting number of rows matching criteria between two dates

I am trying to query a postgres db table for a count of rows matching some criteria between a time range. I am using the sequelize orm with nodejs.
In my controller method i have a query
var query = 'SELECT o.ordered_at, COUNT(o.id) AS order_count FROM "Orders" o WHERE status = :status AND o.ordered_at >= :lower_limit AND o.ordered_at <= :upper_limit GROUP BY o.id ORDER BY o.ordered_at DESC';
models.sequelize.query(query, queryOptions).
then(function (orders) {
res.status(200).json(response);
}).
catch(function (err) {
logger.error(err);
res.status(500).json(err.message);
})
this yields a response like this
[
{
"ordered_at": "2016-12-10T16:05:46.525Z",
"order_count": "1"
},
{
"ordered_at": "2016-12-10T16:03:46.429Z",
"order_count": "1"
},
{
"ordered_at": "2016-12-10T16:01:46.440Z",
"order_count": "1"
},
{
"ordered_at": "2016-12-05T12:20:18.714Z",
"order_count": "1"
},
{
"ordered_at": "2016-12-05T12:18:18.650Z",
"order_count": "1"
},
{
"ordered_at": "2016-12-03T12:16:18.658Z",
"order_count": "1"
}
]
You can see the query returns the order_count for each row instead of for each order, so i am try to transform the data here
var data = [],
lower_time_limit = t(new Date());
var range = _.range(30),
month_lower_limit = t(lower_time_limit).subtract(1, 'M').startOf('day');
var lower_limit,
limit,
upper_limit;
_.forEach(range, function (day, key) {
limit = t(month_lower_limit);
lower_limit = t(limit.add(day, 'd'));
upper_limit = t(limit.add(1, 'd'));
_.forEach(orders, function (order, index) {
var count = 0;
if (t(order.ordered_at).isBetween(lower_limit, upper_limit, 'second', '[)')) {
var orderCount = count + parseInt(order.order_count);
data.push({date: lower_limit, count: orderCount})
}
});
});
return data;
I know am missing something, just can't figure it out.
GROUP BY o.id will produce one group (i.e. one row in the output) per o.id. You want one group for all matching rows, which is what you get if you omit the GROUP BY entirely.
Including o.ordered_at in your output doesn't make a whole lot of sense; if you're aggregating multiple matching rows into a single output record, it's not clear whose ordered_at time you'd be expecting to see.
And once you've got one row of output, an ORDER BY won't do you much good.
With all of that in mind:
SELECT COUNT(o.id) AS order_count
FROM "Orders" o
WHERE status = :status
AND o.ordered_at >= :lower_limit
AND o.ordered_at <= :upper_limit

how to use in operator in dynamo db

I have a user table with a field username. I need to write something equivalent to this in dynamo db: Select * from user where username in('a','b','c');
Adding more from code prosepective i have usernames in an array say var arr=['a','b','c'];
I so far tried this which is giving me zero result
this.dynamo.client.scanAsync({
TableName: this.dynamo.table('users'),
FilterExpression: 'username IN (:list)',
ExpressionAttributeValues: {
':list': arr.toString()
}
}).then((response) => {
console.log(response);
return {
userFriends: result.Item.friends
};
});
When I pass one element in array it give me result searching passed single element in user table but its not working with more than one element in array.
The individual users should be given as comma separated String variables. JavaScript array is equivalent to List in AWS DynamoDB data type. The DynamoDB can't compare the String data type in database with List attribute (i.e. Array in JavaScript).
var params = {
TableName : "Users",
FilterExpression : "username IN (:user1, :user2)",
ExpressionAttributeValues : {
":user1" : "john",
":user2" : "mike"
}
};
Construct the object from array for FilterExpression:-
Please refer the below code for forming the object dynamically based on Array value.
var titleValues = ["The Big New Movie 2012", "The Big New Movie"];
var titleObject = {};
var index = 0;
titleValues.forEach(function(value) {
index++;
var titleKey = ":titlevalue"+index;
titleObject[titleKey.toString()] = value;
});
var params = {
TableName : "Movies",
FilterExpression : "title IN ("+Object.keys(titleObject).toString()+ ")",
ExpressionAttributeValues : titleObject
};
Note:-
I don't think IN clause with 1000s of usernames is a good idea in terms of performance.

Resources