dynamodb query keyConditionExpression sort by sortkey - javascript

I am trying to sort my dynamo db results by the sort and key and also get a range of values based on the sort key and i keep getting invalid key condition expression error. basically im trying to get a range of values based on my sort key and also sort them.
example of saved db item:
{
id: ORDER_287d6df3-bd9f-472d-87ef-ee9b810b5874
issued_at: 2021-05-07T15:09:11.894Z
base_cur: NGN
orderRate(GSI): SELL#500
quantity: 300
quantityRemaining: 300
quantityRemoved: 0
quote_cur: GBP
rate: 500
side: SELL
ticker(GSI): GBP#NGN
}
what i have tried:
const params = {
TableName: process.env.ORDERS_TABLE_NAME,
IndexName: 'ticker_orderRate',
KeyConditionExpression: `ticker = :ticker and orderRate between :maxRate and :baseRate`,
ExpressionAttributeValues: {
':ticker': `${quote_cur}#${base_cur}`,
':maxRate': `${side === 'BUY' ? 'SELL' : 'BUY'}#${rate}`,
':baseRate': `${side === 'BUY' ? 'SELL' : 'BUY'}#0.00`,
}
};
the rate is a number that comes from an SNS topic.
Error message:
ERROR: ValidationException: Invalid KeyConditionExpression: The BETWEEN operator requires upper bound to be greater than or equal to lower bound; lower bound operand: AttributeValue: {S:BUY#620}, upper bound operand: AttributeValue: {S:BUY#0.00}
is there a way i can transform the rate and the base rate to numbers while querying the database? and also how do i sort by ascending order?
Thanks for the help

The issue is in your BETWEEN clause. As the erorr message states, the upper bound (the second value) must be greater than or equal to the lower bound (the first value).
There are a few things to consider here.
Remember, this is a string that you are sorting, not a number. You'll need to make some adjustments to handle that.
You need to make sure the length of the number is always the same. This can be accomplished by zero-padding the value (e.g. 1.23 becomes 001.23). Make sure you zero pad to a value that will always be as long or longer than the biggest number you expect.
If you support negative values then you need to be explicit on positive values too. Include a plus (+) on positive numbers.
Dynamically adjust your upper and lower bounds to be in the right order. If the first value is less than the second leave them, if the first value is greater than the second then swap their order.
Key Condition Expression

Related

How to sort using multiple orderBy queries in Firebase Firestore?

If I want to sort docs according to this scenario -
Select products which is in stock, (by checking field named "count",
which specifies the total number of items of a product)
Sort elements according to another field called "price".
It might seem simple, just use this code -
DBRef.collection("col")
.where("count", isNotEqual: 0)
.orderBy("price")
.get();
But this will throw an error,
due to the limitations of firebase firestore. So, we'll have to use another orderBy("count") query and also before any other orderBy query.
DBRef.collection("col")
.where("count", isNotEqual: 0)
.orderBy("count")
.orderBy("price")
.get();
And that'll sort the docs according to the count field, and not according to the price field. How can I sort docs list according to the price field at the same time.
I even tried a work around,
databaseReference
.collection("products")
.where("tags", arrayContains: tag)
.where("sellingPrice", isGreaterThan: 0)
.orderBy("sellingPrice", descending: isDesc)
.where("totalUnit", isGreaterThan: 0)
.orderBy("totalUnit")
.limit(limit)
.get()
I added another where statement for the "sellingPrice" field, so that I would have to add orderBy("sellingPrce") the first orderBy statement.
But I'm still getting the error
Failed assertion: line 421 pos 16: 'field == orders[0][0]': The
initial orderBy() field '[[FieldPath([sellingPrice]), false]][0][0]'
has to be the same as the where() field parameter
'FieldPath([totalUnit])' when an inequality operator is invoked.
The results of your second query will be sorted by count first, and then by price. There is no way to get them from the database without first sorting on count, so you will have to reorder them in your application code.
What you could consider is not giving the documents where the count is equal to 0 a count field at all. This will result at them being excluded from the index, and that also means you can put the orderBy("count") at the end of the query.
Alternatively, you can add a isInStock type field that you update in sync with the count field. Once you do that, you can do an equality condition (where("isInStock", "==", true)), which doesn't require an orderBy clause.

AWS Dynamodb Query - get items from table with condition

There is Dynamo table with fields:
email (primary)
tenant
other stuff
I want to get all the items where email contains 'mike'
In my nodejs server, I have this code
const TableName= 'UserTable';
const db = new aws.DynamoDB();
const email = 'mike.green#abc.com'
params = {
TableName: userTableName,
KeyConditionExpression: '#email = :email',
ExpressionAttributeNames: {
'#email': 'email',
},
ExpressionAttributeValues: {
':email': { S: email },
},
};
db.query(params, (err, data) => {
if (err) {
reject(err);
} else {
const processedItems = [...data.Items].sort((a, b) => a.email < b.email ? -1 : 1);
const processedData = { ...data, Items: processedItems };
resolve(processedData);
}
this works ^^ only if I search entire email mike.green#abc.com
Question 1 -
But, if i want to search mike, and return all items where email contains mike, How can i get that?
Question 2
If I want to get all the rows where email contains mike and tenant is Canada. How can i get that?
I'm not a NodeJS user but hope it will be helpful.
Question 1 - But, if i want to search mike, and return all items where
email contains mike, How can i get that?
Key expressions are reserved to equality constraints. If you want to have more querying flexibility, you need to use a filter expression. Please notice that you won't be able to use filter expression on your partition key. You can find more information on https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Query.html but the most important is:
Key Condition Expression
To specify the search criteria, you use a key condition expression—a
string that determines the items to be read from the table or index.
You must specify the partition key name and value as an equality
condition.
You can optionally provide a second condition for the sort key (if
present). The sort key condition must use one of the following
comparison operators:
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.
......
Question 2 If I want to get all the rows where email contains mike and
tenant is Canada. How can i get that?
You can use a filter expression to do that and use one of available functions https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Expressions.OperatorsAndFunctions.html#Expressions.OperatorsAndFunctions.Syntax. A filter expression is:
If you need to further refine the Query results, you can optionally
provide a filter expression. A filter expression determines which
items within the Query results should be returned to you. All of the
other results are discarded.
A filter expression is applied after a Query finishes, but before the
results are returned. Therefore, a Query will consume the same amount
of read capacity, regardless of whether a filter expression is
present.
A Query operation can retrieve a maximum of 1 MB of data. This limit
applies before the filter expression is evaluated.
A filter expression cannot contain partition key or sort key
attributes. You need to specify those attributes in the key condition
expression, not the filter expression.
https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Query.html
To wrap-up:
if e-mail is your partition key, you cannot apply contains on it - you have to query it directly.
eventually you can do a scan over your table and apply filter on it (https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_Scan.html) but I wouldn't do that because of consumed capacity of the table and response time. Scan involves operating over all rows in the table, so if you have kind of hundreds of GB, you will likely not get the information in real-time. And real-time serving is one of purposes of DynamoDB.

Fetch and sort mixed character from firebase javascript

Firebase Structure
Periods
+Period1
+Period10
+Period2
+Period3
+Period4
I am getting the value as it is. But i want to be sorted like Period1, Period2, Period3..,Period10
Referring to :
https://firebase.google.com/docs/database/web/lists-of-data#data-order
When using orderByKey() to sort your data, data is returned in ascending order by key.
Children with a key that can be parsed as a 32-bit integer come first, sorted in ascending order.
Children with a string value as their key come next, sorted lexicographically in ascending order.
You could avoid the "Period" as a redundancy as it is implied from the table and use the numeric id directly (not recommended by firebase) or use the push to generate the keys (ids) native to firebase.
The Firebase Database sorts strings lexicographically. And in alphabetical order Period10 comes before Period2.
To ensure the lexicographical order also matches the numerical order that you're looking for, you can consider padding the numbers:
Periods
+Period001
+Period010
+Period002
+Period003
+Period004
With this padding, the lexicographical order matches the numerical ordering that you're looking for. Of course the amount of padding will depend on the maximum number you realistically expect.
Alternatively you can simply store a Period property with a numeric value in each child node and order on that. In that case you can also use push IDs for the keys, as Alex said:
Periods: {
-Laaaaaa: {
Period: 1
},
-Laaaaab: {
Period: 10
},
-Laaaaac: {
Period: 2
},
-Laaaaad: {
Period: 3
},
-Laaaaae: {
Period: 4
}
}
Now you can query ref.orderByChild("Period") and the children will be returned in the wanted numerical order.

How do I select a record by matching an index based on a partial string that contains '-' characters?

I'm using YDN-DB (an abstraction on top of IndexedDB) as a local database. I have an object store called 'conversations', and in that store, there's an index called 'participants' where there is a string containing id's for different users in the conversation. For example:
Example Conversation #1:
id: 1234343434353456,
participants: '171e66ca-207f-4ba9-8197-d1dac32499db,82be80e2-2831-4f7d-a8d7-9223a2d4d511'
Example Conversation #2:
id: 4321343434356543,
participants: 'd7fa26b3-4ecc-4f84-9271-e15843fcc83f,171e66ca-207f-4ba9-8197-d1dac32499db'
To try to perform a partial match on an index, I tried using ydn-db-fulltext as a solution. The full text catalog looks like this:
{
name: 'participants',
lang: 'en',
sources: [
{
storeName: 'conversations',
keyPath: 'participants',
weight: 1
}
]
}
I see that the catalog is generated, but there seems to be a problem doing exact matches. For example, if I query using only part of the key in the participants index, I get back a primary key from the catalog:
db.search('participants', 'd7fa26b3').done(function(results) {
if(results.length == 0) console.debug('No results found...');
console.debug(results); // there is 1 object here!
var primaryKey = results[0].primaryKey; // primaryKey exists!
});
However, when using any value past the '-', the search request returns 0 results:
db.search('participants', 'd7fa26b3-4ecc-4f84-9271-e15843fcc83f').done(function(results) {
if(results.length == 0) console.debug('No results found...');
console.debug(results); // there are 0 objects in the array
var primaryKey = results[0].primaryKey; // primaryKey throws undefined since there are 0 results!
});
This makes sense, when reading the documentation, in that '-' and '*' are reserved characters that remove a phrase and match a prefix respectively:
Query format is free text, in which implicit and/or/near logic operator apply for each token. Use double quote for exact match, - to subtract from the result and * for prefix search.
I tried putting double quotes inside the single quotes, using only double quotes, and also escaping all of the '-' characters with a backslash, but none of these seem to work.
So the question is how does one perform a match in an index where the string contains '-' characters?
Have you try db.search('participants', '"d7fa26b3"').
BTW, you are using full text search that is not suppose to do. You have to tokenize your string and index them manually.
If you store the participants field of your object as an array, then you can use the multi-entry flag to the createIndex method called on the participants field, and probably do what you want.
The number of items in the participants property of the object is mutable. When you update an object in the store and it has a different number of items in the partic property, then the index is automatically updated as a result (just like any other index). If you add an item to the prop, then restore (put/override/cursor.update) the object in the store, the index updates.
It helps to review the basics of how a multi-entry index works. You can do this with vanilla js, without a framework, and certainly without full-text searching.

Sorting javascript arrays by [field] values gives wrong order

I'm implementing SlickGrid and I want to allow sorting of values by columns, which is supported by SlickGrid by sorting arrays, however, when I sort the columns (arrays) they are not put in the "correct" order.
The order in which they are returned is 1,10,100,11,199,2,20,200,3,30,300....
The problem is displayed very clearly when trying to sort the tasks in this grid by title:
http://mleibman.github.com/SlickGrid/examples/example-multi-column-sort.html
Although I use my own sorting rule, instead of the one used in the example:
data.sort(function(a, b){
var result =
a[field] === b[field] ? 0 :
a[field] > b[field] ? 1 : -1
;
return args.sortAsc ? result : -result;
});
The problem persists.
My question is merely how to sort the array, so that the title (and other data) will be displayed in the correct order: 1,2,3,100,200,300...
Your numbers are actually strings and will be compared as such. To prevent this, use parseInt(a[field],10) > parseInt(b[field],10)
if you are just trying to sort objects of the same type, then there is no problem... your algorithm seems right.
but it seems that your numbers might be actually strings, so they are sorted as strings.
you can parse them into numbers .. ie parseInt() , parseFloat() etc.
But if the field is a string followed by a number like the example in the link, then it will not work
"data 20" will always be less than "data 3", so you might want to extract the number then sort by both the string and the number.

Categories