Azure CosmosDB User Defined Function returns records equal to the total number of records in the table - azure-cosmosdb

I am just no understanding how user defined functions in CosmosDB works.
Why is the my UDF in Cosmos DB returning results equal to the number of records in the table?
My table has 4 records currently.
Here are the records -
{
"users": [
{
"partitionKey": "user",
"userPhoneNumber": "14168000000",
"userDisplayName": "Test User 1"
},
{
"partitionKey": "user",
"userPhoneNumber": "18055678978",
"userDisplayName": "Test User 2"
},
{
"partitionKey": "user",
"userPhoneNumber": "17202228799",
"userDisplayName": "Test User 3"
},
{
"partitionKey": "user",
"userPhoneNumber": "17780265987",
"userDisplayName": "Test User 4"
}
]
}
Here is my UDF -
function findUserByPhoneNumber(users, contactNumbers){
var result;
for (let i = 0; i < contactNumbers.length; i++) {
if (contactNumbers[i] == "14168000000") {
result = contactNumbers[i];
}
return result;
}
Here is my SQL -
SELECT udf.findUserByPhoneNumber(c,["14168000000","17200000000"]) FROM c
And here is the result I am getting -
[
{
"$1": "14168000000"
},
{
"$1": "14168000000"
},
{
"$1": "14168000000"
},
{
"$1": "14168000000"
}
]
I am only expecting one record to be returned.

The SELECT statement in your query will never help you filter items in your query. If you want to filter you should use WHERE in your query. While you could use a UDF in a WHERE statement that returns e.g. a boolean you should typically avoid it as the database can not use it indexes to efficiently retrieve your data and will have to use your UDF on every document to calculate if it should be retrieved.
For your scenario the query could be:
SELECT *
FROM c
WHERE ARRAY_CONTAINS(['14168000000', '17200000000'], c.userPhoneNumber)
The reason your UDF always returns 14168000000 is because you always use the array ['14168000000', '17200000000'] as input in your UDF and check if any of those values equals 14168000000 and return that.

Related

Why do I get empty list when doing filter?

I have a table 'test-table':
id (string) - primaryKey
type (string)
I have items like this in that table, for example:
34 AWC
56 BDE
I want to do scan table and filter by type:
I use:
async getItems(typeInput) {
const params: ScanCommandInput = {
TableName: "test-table",
FilterExpression: "type in (:type)", // also tried with type = :type
ExpressionAttributeValues: { ":type": { "S": typeInput } },
};
return await dynamodbdocumentclient.send(new ScanCommand(params));
}
I get as a result empty Items. Why ?
You appear to be using the DocumentClient, which automatically marshalls attribute values from their native JavaScript type. You do not need to wrap all values in {'S': 'xxx'}, {'N': '999'}, etc. Use ExpressionAttributeValues: { ":type": typeInput }.

group data by same timestamp using cosmos db sql

Question:
I am trying SQL query as the image showed below,I want it to be grouped by the same timestamp
expected output:
[
{
"tag1": {
"TagName": "PV1-input-power-L(10W)",
"Value": 0
},
"tag2": {
"TagName": "Sunshine-Display-Value",
"Value": 0
},
"tag3": {
"TagName": "TotalEnergy-(100kWh)_1",
"Value": 0
},
"timestamp": "2020-03-27T02:40:18Z"
}
]
sample document:
You can use User Defined Functions.
Here is the data from my containers
Here is the function I have created. I named it CustomArray.
function userDefinedFunction(input){
var obj={};
input.forEach(function(element,index){
obj["tag"+index] = {
TagName :element.TagName,
Value: element.Value
};
}); return obj;}
Here, I run the UDF with my select statement
It returns the following data.
Schema is very close to what you are looking for. I think you can make it better by changing some jscript in UDF.
I hope this helps!

Azure Cosmos DB - SQL API range filter in WHERE condition

I am trying to filter data based on "amount" range in SQL API query WHERE condition. The amount value could be an integer value without double quotes or decimal value with double quotes. Basically I need to filter all records between two amount range values
Below is my sample data.
[
{
"customer": "001",
"dt": {
"Transaction": [
{
"Amount": "999.00",
"timestamp": "2019-01-28T15:44:49.215"
}
]
}
},
{
"customer": "002",
"dt": {
"Transaction": [
{
"Amount": "9999.00",
"timestamp": "2019-01-28T15:44:49.215"
}
]
}
},
{
"customer": "003",
"dt": {
"Transaction": [
{
"Amount": 9,
"timestamp": "2019-01-28T15:44:49.215"
}
]
}
}
]
Below is the query that I am trying to run to find records with amount between 99 and 10000
SELECT c.dt[0].Transaction[0].Amount FROM c where c.dt[0].Transaction[0].Amount>"99"
and c.dt[0].Transaction[0].Amount<"10000"
But it is not returning any records
Could you please help me with the query. I need to filter records with amount between 99 and 10000
Please use UDF.
udf:
function test(str){
if(typeof(str)=='string'){
return Number(str)
}else{
return str
}
}
sql:
SELECT c.dt.Transaction[0].Amount FROM c where udf.test(c.dt.Transaction[0].Amount) >99
and udf.test(c.dt.Transaction[0].Amount)<10000
Output:

Querying Cosmos Nested JSON documents

I would like to turn this resultset
[
{
"Document": {
"JsonData": "{\"key\":\"value1\"}"
}
},
{
"Document": {
"JsonData": "{\"key\":\"value2\"}"
}
}
]
into this
[
{
"key": "value1"
},
{
"key": "value2"
}
]
I can get close by using a query like
select value c.Document.JsonData from c
however, I end up with
[
"{\"key\":\"value1\"}",
"{\"key\":\"value2\"}"
]
How can I cast each value to an individual JSON fragment using the SQL API?
As David Makogon said above, we need to transform such data within our app. We can do as below:
string data = "[{\"key\":\"value1\"},{\"key\":\"value2\"}]";
List<Object> t = JsonConvert.DeserializeObject<List<Object>>(data);
string jsonData = JsonConvert.SerializeObject(t);
Screenshot of result:

API Gateway and DynamoDB PutItem for String Set

I can't seem to find how to correctly call PutItem for a StringSet in DynamoDB through API Gateway. If I call it like I would for a List of Maps, then I get objects returned. Example data is below.
{
"eventId": "Lorem",
"eventName": "Lorem",
"companies": [
{
"companyId": "Lorem",
"companyName": "Lorem"
}
],
"eventTags": [
"Lorem",
"Lorem"
]
}
And my example template call for companies:
"companies" : {
"L": [
#foreach($elem in $inputRoot.companies) {
"M": {
"companyId": {
"S": "$elem.companyId"
},
"companyName": {
"S": "$elem.companyName"
}
}
} #if($foreach.hasNext),#end
#end
]
}
I've tried to call it with String Set listed, but it errors out still and tells me that "Start of structure or map found where not expected" or that serialization failed.
"eventTags" : {
"SS": [
#foreach($elem in $inputRoot.eventTags) {
"S":"$elem"
} #if($foreach.hasNext),#end
#end
]
}
What is the proper way to call PutItem for converting an array of strings to a String Set?
If you are using JavaScript AWS SDK, you can use document client API (docClient.createSet) to store the SET data type.
docClient.createSet - converts the array into SET data type
var docClient = new AWS.DynamoDB.DocumentClient();
var params = {
TableName:table,
Item:{
"yearkey": year,
"title": title
"product" : docClient.createSet(['milk','veg'])
}
};

Resources