How to fix disable validation in Dynamo-db against null / empty string? - amazon-dynamodb

I am trying to push the data to Dynamodb from node JS code.
I am getting the issue like
DynamoDB.DocumentClient should support empty string properties
does anyone knows how to disable validation in DynamoDB ?

By adding this we will be able to insert empty values to Dynamodb.
var dynamoDocClient = new AWS.DynamoDB.DocumentClient({ region : 'us-west-2', convertEmptyValues : true });

2020 May 18 Update:
DynamoDB now supports empty string values for non-key attributes: https://aws.amazon.com/about-aws/whats-new/2020/05/amazon-dynamodb-now-supports-empty-values-for-non-key-string-and-binary-attributes-in-dynamodb-tables/
In order to have consistent behavior, make sure that the client does not have { convertEmptyValues: true }, or else these attributes will be saved as NULL as opposed to an empty string.

Related

AWS Java SDK DynamoDB, how to get attribute values from ExecuteStatementRequest response?

I'm using Java AWS SDK to query a DynamoDB table using ExecuteStatementRequest, but I'm don't know how to fetch the returned attribute values from the response.
Given I have the following query:
var response2 = client.executeStatement(ExecuteStatementRequest.builder()
.statement("""
UPDATE "my-table"
SET thresholdValue= thresholdValue + 12.5
WHERE assignmentId='item1#123#item2#456#item3#789'
RETURNING ALL NEW *
""")
.build());
System.out.println(response2.toString());
System.out.println(response2.getValueForField("Items", Collections.class)); // Doesn't cast to
This query executes fine and returns as part of the response attributes, however I can't find a way to get these values out of the response object using Java.
How can I do that?
I have found how to do that, however I'm not sure it this is the indicated way as the documentation doesn't provide any examples.
List items = response2.getValueForField("Items", List.class).get();
for (Object item : items) {
var values = (Map<String, AttributeValue>) item;
System.out.println(values.get("assignmentId").s());
System.out.println(values.get("thresholdValue").n());
}

Global Secondary Index DynamoDB empty value error

I have a string attribute that can be an empty value. And I want to set it as a Global Secondary Index. But it showed an error when I tried to perform UpdateItemRequest or SaveTable Context:
Amazon.DynamoDBv2.AmazonDynamoDBException: One or more parameter values are not valid. A value specified for a secondary index key is not supported. The AttributeValue for a key attribute cannot contain an empty string value. IndexName: .... IndexKey: ...
What is wrong with my mindset or my settings? I'm new to DynamoDB and had a MongoDB base. If I don't use GSI for this attribute, how to perform a query on that attribute?
I tried
[DynamoDBIgnore] string property;
var operationConfig = new DynamoDBOperationConfig() { };
operationConfig.IsEmptyStringValueEnabled = true;
operationConfig.Conversion = DynamoDBEntryConversion.V2;
but it does not work.
This states that you are setting the value for your GSI PK to an empty string "" which is not supported.
In order to overcome this, you simply remove any value from that attribute, i.e don't set it at all. This allows your index to become sparse in that it will only store data that has a key associated with it.

findAll() returns empty with WHERE option

First question on StackOverflow, long time reader first time poster or whatever people say.
I'm developing a Discord bot in my free time using Discord.js, and I'm using Sequelize to interface with a local SQLite database. I can insert data into it just fine-- however, I can't seem to delete any of the records I add. Relevant piece of code is below, which I believe to be self-contradictory:
const query3 = await Towers.findAll({
attributes: ['channelID']
});
console.log(JSON.stringify(query3)); //returns the one Tower
console.log(query3[0].channelID === channel); //returns true(!)
const query2 = await Towers.findAll({
attributes: ['channelID'],
where: {channelID: channel}
});
console.log(JSON.stringify(query2)); //returns empty
//DELETE FROM Towers WHERE channelID = channel;
const query = await Towers.destroy({
where: {channelID: channel}
});
console.log(query); //returns 0, expected behavior given query2 returns empty
I'm attempting to delete a record from a table named Towers by passing a channel ID to it, which is expected to be unique. However, when I make any query on the database with a WHERE clause, the query returns an empty set-- even when, in this example, I sanity-checked and verified that the value I'm attempting to remove is present in the table. This occurs for both findAll() and findOne() as long as a WHERE clause is present.
(For posterity, I've double and triple checked that channelID was spelled correctly and with the correct capitalization in all instances.)
I'm happy to provide any more information if needed!
EDIT: As requested, the model definition...
const Towers = sequelize.define('Towers', {
serverID: {
type: Sequelize.INTEGER,
allowNull: false,
},
channelID: {
type: Sequelize.INTEGER,
unique: true,
allowNull: false,
},
pattern: Sequelize.STRING,
height: Sequelize.INTEGER,
delay: Sequelize.BOOLEAN,
});
channel in the snippet in the original post is defined as parseInt(interaction.options.getChannel('channel').id).
To anyone who happens to have the same issue I did, the answer is a doozy.
I wanted to store Discord server and channel ID's as integers, even though they're returned to you as strings when calling the API. As it turns out, Discord snowflakes are higher than float64 precision, which JS uses. When parsing the strings into integers to insert them into my table, the value changed from the intended number, and I was creating erroneous records.
In my case (with the actual numbers obfuscated) interaction.options.getChannel('channel').id returned "837512533934092340", while parseInt(interaction.options.getChannel('channel').id returned 837512533934092300. The number I was adding to the table was somehow 40 less!
I'm not sure if this could be fixed by using BigInt, but since it's going into a different structure anyway, I just shrugged and changed the serverId and channelId types to Sequelize.STRING in the model definition and removed the parseInt calls. Works like a charm now.
Good opportunity to shake my fist at JS though.

DynamoDb - .NET Object Persistence Model - LoadAsync does not apply ScanCondition

I am fairly new in this realm and any help is appreciated
I have a table in Dynamodb database named Tenant as below:
"TenantId" is the hash primary key and I have no other keys. And I have a field named "IsDeleted" which is boolean
Table Structure
I am trying to run a query to get the record with specified "TenantId" while it is not deleted ("IsDeleted == 0")
I can get a correct result by running the following code: (returns 0 item)
var filter = new QueryFilter("TenantId", QueryOperator.Equal, "2235ed82-41ec-42b2-bd1c-d94fba2cf9cc");
filter.AddCondition("IsDeleted", QueryOperator.Equal, 0);
var dbTenant = await
_genericRepository.FromQueryAsync(new QueryOperationConfig
{
Filter = filter
}).GetRemainingAsync();
But no luck when I try to get it with following code snippet (It returns the item which is also deleted) (returns 1 item)
var queryFilter = new List<ScanCondition>();
var scanCondition = new ScanCondition("IsDeleted", ScanOperator.Equal, new object[]{0});
queryFilter.Add(scanCondition);
var dbTenant2 = await
_genericRepository.LoadAsync("2235ed82-41ec-42b2-bd1c-d94fba2cf9cc", new DynamoDBOperationConfig
{
QueryFilter = queryFilter,
ConditionalOperator = ConditionalOperatorValues.And
});
Any Idea why ScanCondition has no effect?
Later I also tried this: (throw exception)
var dbTenant2 = await
_genericRepository.QueryAsync("2235ed82-41ec-42b2-bd1c-d94fba2cf9cc", new DynamoDBOperationConfig()
{
QueryFilter = new List<ScanCondition>()
{
new ScanCondition("IsDeleted", ScanOperator.Equal, 0)
}
}).GetRemainingAsync();
It throws with: "Message": "Must have one range key or a GSI index defined for the table Tenants"
Why does it complain about Range key or Index? I'm calling
public AsyncSearch<T> QueryAsync<T>(object hashKeyValue, DynamoDBOperationConfig operationConfig = null);
You simply cant query a table only giving a single primary key (only hash key). Because there is one and only one item for that primary key. The result of the Query would be that still that single item, which is actually Load operation not Query. You can only query if you have composite primary key in this case (Hash (TenantID) and Range Key) or GSI (which doesn't impose key uniqueness therefore accepts duplicate keys on index).
The second code attempts to filter the Load. DynamoDBOperationConfig's QueryFilter has a description ...
// Summary:
// Query filter for the Query operation operation. Evaluates the query results and
// returns only the matching values. If you specify more than one condition, then
// by default all of the conditions must evaluate to true. To match only some conditions,
// set ConditionalOperator to Or. Note: Conditions must be against non-key properties.
So works only with Query operations
Edit: So after reading your comments on this...
I dont think there conditional expressions are for read operations. AWS documents indicates they are for put or update operations. However, not being entirely sure on this since I never needed to do a conditional Load. There is no such thing like CheckIfExists functionality as well in general. You have to read the item and see if it exists. Conditional load will still consume read throughput so your only advantage would be only NOT retrieving it in other words saving the bandwith (which is very negligible for single item).
My suggestion is read it and filter it in your application layer. Dont query for it. However what you can also do is if you very need it you can use TenantId as hashkey and isDeleted for range key. If you do so, you always have to query when you wanna get a tenant. With the query you can set rangeKey(isDeleted) to 0 or 1. This isnt how I would do it. As I said, would just read it and filter it at my application.
Another suggestion thing could be setting a GSI on isDeleted field and writing null when it is 0. This way you can only see that attribute in your table when its only 1. GSI on such attribute is called sparse index. Later if you need to get all the tenants that are deleted (isDeleted=1) you can simply scan that entire index without conditions. When you are writing null when its 0 dynamoDB wont put it in the index at the first place.

How to check if SimpleList or SimpleRecord is empty

I got the following issue. I am trying to use the With or WithMany instruction
to retrieve a linked list of roles of an business relation via an
outer join. The referential integrity is in place on the database but
the primary key on the roles table is a composite key. That's the
reason i use an OuterJoin clause because I get an exception
otherwise .
When the query gets executed the results are exactly as I expected and
nicely filled with data. Nevertheless there are certain cases where
there are not yet roles available in the database. So I would expect
that in those cases the returned SimpleList (Roles below) would be
null, cause there is not data available. Instead Simple.Data returns a
SimpleLIst and if i expand the Dynamic View in debug then it says
'Empty: No further information on this object could be discovered".
Even if i traverse further down and i retrieve the first object in the
SimpleList, it even returns a SimpleRecord with the same information
as above in debug. Only after I request a property of the SimpleRecord
I get some information that the record was empty because then it
returns null.
To come to the bottom line... is there anybody who can tell me how to
check if a SimpleList or SimpleRecord is empty or null without
traversing down the hierarchy?
I am using Simple.Data 0.16.1.0 (due to policy i can't use the
beta yet)
Thanks in advance for reading the whole story...
Below is the code sample:
dynamic businessRelationRoles;
var query = db.Zakenrelaties.As("BusinessRelations")
.All()
.OuterJoin(db.Zakenrelaties_Rollen.As("Roles"), out businessRelationRoles)
.On(zr_ID: db.Zakenrelaties.zr_ID)
.With(businessRelationRoles);
foreach (var relation in query)
{
//Get the SimpleList as IEnumerable
IEnumerable<dynamic> roles = relation.Roles;
//Get the first available SimpleRecord
var role = roles.First();
//Check if any record was returned..This passes always?? Even if the SimpleList was empty
if (role != null)
{
//Get the id of the role. returns null if SimpleRecord was empty
var roleId = role.zrro_id;
}
}
Is there anybody who can help me out?
Belatedly, and for information purposes only, this was a bug and got fixed in the 0.17 (aka 1.0-RC0) release.

Resources