Does azure cosmosdb have like any operator to find records matching a pattern - azure-cosmosdb

I am just wondering how do I do the below using the SQL API on Azure CosmosDB -
SELECT user_id FROM users WHERE user_id LIKE ANY(contacts);
The above statement works on postgres, wondering if there is anything similar in Azure CosmosDB.
The above statement receives a set of contacts in an array format like this ["4160000000","7780000000"] and finds the corresponding records in Postgres db.
UPDATE #Sajeetharan
Below are the documents I have in Cosmos DB-
{
"users": [
{
"partitionKey": "user",
"userPhoneNumber": "14161231234",
"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"
}
]
}
I will be sending in a set of userPhoneNumbers from javascript in an array format like below and then I need the SQL query to return the corresponding records in cosmos db.
var userPhoneNumbers = ["4161231234","7202228799"];
The above array has two values, which when sent to the cosmosdb should return the first and third record.
The userPhoneNumbers sent in will be sometimes missing the country code, so the search should be performed using CONTAINS or ENDSWITH.
Please advise!

As mentioned in question, need to perform like operation on phone numbers passing as array.
In cosmosDb there is no in-built function helps us to achieve the result. The way to achieve expected result is using cosmosDb UDF. Below is code snippet for the same.
function findUserNameByPhone(users, userPhoneNumbers) {
var s, i, j;
let result = [];
for(j = 0; j < userPhoneNumbers.length; j++)
{
for (i = 0; i < users.length; i++)
{
s = users[i];
if(s.userPhoneNumber.match(userPhoneNumbers[j]))
result = result.concat(s);
}
}
return result;
}
Consume the udf in query :-
SELECT udf.findUserNameByPhone(c.users,["4161231234","7202228799"]) FROM c
Edit as per comment
Use the UDF in select query. Also, as per your latest comment, if you need the result based on specific partition key you can use the self join as shown in the updated query. As partitionKey property is part of users array, hence the self join by passing the partitionKey value in where clause.
SELECT DISTINCT udf.findUserNameByPhone(c.users,["4161231234","7202228799"]) FROM c
JOIN u in c.users WHERE u.partitionKey = "user"
Consuming parameterized queries in Cosmos DB using Node.js check here

Related

How to force the DynamoDB query's ExclusiveStartKey to use exact match?

I'm using DynamoDB for my new Serverless Restful API with nodejs.
The Restful API supports query for resources with the limit and lastKey query parameters for key pagination.
Assume there's a table like below:
PK
SK
School
firstSchool
School
secondSchool
School
thirdSchool
PK is partition key, and SK is sort key.
I use SK for key pagination.
If I call the api with http://somewhere/api/school?limit=1&lastKey=secondSchool, ExclusiveStartKey in query will be {"PK" : "School", "SK" : "secondSchool"}, and the returned item will be {"PK" : "School", "SK" : "thirdSchool"}.
It works well in that case, but the problem is the same result is created with the url like http://somewhere/api/school?limit=1&lastKey=seco.
In this case, ExclusiveStartKey in query will be {"PK" : "School", "SK" : "seco"}
It seems DynamoDB doesn't use exact match for a sk value in ExclusiveStartKey.
Is there any way to force DynamoDB to use exact match for ExclusiveStartKey?
I attach my test code below:
const { DynamoDBClient } = require("#aws-sdk/client-dynamodb");
const { DynamoDBDocument } = require("#aws-sdk/lib-dynamodb");
const ddbClient = new DynamoDBClient({
region: AWS_REGION,
endpoint: AWS_DYNAMODB_END_POINT,
credentials: {
accessKeyId: AWS_ACCESSKEY_ID,
secretAccessKey: AWS_SECRET_ACCESS_KEY,
},
});
const ddbDocClient = DynamoDBDocument.from(ddbClient);
(async () => {
try {
const data = await ddbDocClient.query({
TableName: "Table Name",
KeyConditionExpression: "#pk = :pk",
ExpressionAttributeNames: {
"#pk": "PK",
},
ExpressionAttributeValues: {
":pk": "Test",
},
Limit: 1,
ExclusiveStartKey: { PK: "Test", SK: "Seco" },
});
console.log(data);
} catch (err) {
console.log("Error", err);
}
})();
The ExclusiveKeyStart is used mainly for paging large Scan or Query requests - i.e., retrieving the next page of results after the previous page ended with a LastEvaluatedKey, and you are supposed to give exactly that key (not some subset of it...) as the ExclusiveKeyStart of the next request.
You are trying to do something different, and to achieve you can't use ExclusiveKeyStart, but you can use something else:
The Query request has a KeyConditionExpression. You can specify sk > :value as a key condition expression (don't pass ExclusiveKeyStart), and you'll get this all the sort keys higher than that :value like your string "seco". Please note, however, that because your sort key is truncated, this result may actually include one or more extra results before the first key you want (e.g., the keys "seco" and "secoaaaa" come before "secondSchool") so you may need to drop them yourself from the results.
The KeyConditionExpression is implemented efficiently - DynamoDB knows how to skip directly to that sort key in the partition, and doesn't charge you for reading the entire partition, so in this respect it is just as good as ExclusiveKeyStart.

Cosmos SQL API: filter based an array entry

Given the json
{
"fileIds": [
"171824c7-1485-4b35-9418-2b40aea8fa48",
"b158e946-621f-431e-926c-57577e3e0b6b"
],
"eventType": 8,
"description": "File deleted from original storage account",
"registerTimeUtc": "0001-01-01T00:00:00",
"customerId": "c7b00078-8fd7-49ad-989a-2fabcb767f6e",
"applicationId": "266c6ca3-e77c-4233-b780-b8dd69aa7349"
...
}
I need to check if an entry exists based on a given applicationId, eventType and fileId and I do not know how to write the query based on fileId to check its existence in the array list.
Please try the following query:
SELECT * FROM Root r where
r.applicationId = '266c6ca3-e77c-4233-b780-b8dd69aa7349' and
r.eventType = 8 and
ARRAY_CONTAINS(r.fileIds, '171824c7-1485-4b35-9418-2b40aea8fa48')
To search for an item in an array, you will need to use ARRAY_CONTAINS function.

How to Filter Nested Array Object in DynamoDB

I am very beginner to AWS DynamoDB, I want to scan the DynamoDB with SENDTO.emailAddress = "first#first.com" as FilterExpression.
The DB Structure looks like this
{
ID
NAME
MESSAGE
SENDTO[
{
name
emailAddress
}
]
}
A Sample Data
{
ID: 1,
NAME: "HELLO",
MESSAGE: "HELLO WORLD!",
SENDTO: [
{
name: "First",
emailAddress: "first#first.com"
},
{
name: "Second",
emailAddress: "second#first.com"
}
]
}
I want to retrieve document that match emailAddress. I tried to scan with filter expression and here is my code to retrieve the data. I am using AWS Javascript SDK.
let params = {
TableName : "email",
FilterExpression: "SENDTO.emailAddress = :emailAddress",
ExpressionAttributeValues: {
":emailAddress": "first#first.com",
}
}
let result = await ctx.docClient.scan(params).promise();
In order to find the item by sendto attribute, you need to know both name and emailAddress attribute value. DynamoDB can't find the data by just one of the attributes in an object (i.e. email attribute value alone).
CONTAINS function can be used to find the data in List data type.
CONTAINS is supported for lists: When evaluating "a CONTAINS b", "a"
can be a list; however, "b" cannot be a set, a map, or a list.
Sample code using Contains:-
var params = {
TableName: "email",
FilterExpression: "contains (SENDTO, :sendToVal)",
ExpressionAttributeValues: {
":sendToVal": {
"name" : "First",
"emailAddress" : "first#first.com"
}
}
};
If you don't know the value of name and emailAddress attribute, you may need to remodel the data to fulfill your use case.
I think that you should create two tables for users and for messages.
The user table has partition_key: user_id and sort_key: email and a field with an array of his messages ids.
The message table has partition_key: message_id and a field with an array of users ids.
When you will get the array of users ids you can use BATCH GET query to get all users of one message.
When you will get the array of message ids you can use BATCH GET query to get all messages of one user.
If you want to get one user by email you can use QUERY method.
Docs

How to get the count of the rows matching to the condition specified in SQLITE Ionic app

I am developing a mobile application with Ionic and SQLITE database. I want a scenario where same record should not be inserted twice for Inventory operation. I am checking the database as follows:
var query = "SELECT COUNT(*) FROM productScan WHERE uniqueId = (?) AND sync = \'N\'";
$cordovaSQLite.execute(db, query, [uniqueId]).then(function (res) {
alert(JSON.stringify(res));
}, function (err) {
alert(JSON.stringify(err));
});
what happen here is when i install the app for the first time and run it then after scan operation when this code run for duplication check, it gives me following output even though there is no record in database
{"rows":{"length":1},""rowsAffected":0}
I am new to Structured Query Language(SQL). I am not able to parse the result here. The result is coming wrong. Is the query needs to be reformatted or any different way to achieve the goal?
Thanks for your time.
BEST way to do this is by giving ALIAS to the COUNT(*) .
something like this
db.executeSql("select count(*) AS TOTAL from mydebit where aid = ?", [
parseInt(this.mydata)
]);
and then
console.log(data.rows.item(0).TOTAL)
so we can easily give the name to the return result and use the to get he row value
cheersss.....!!!!!!!!!
I do it in this way
JSON.stringify(data.rows.item(0)['COUNT(*)'])
Somehow i managed to achieve it in a different way
var query = "SELECT * FROM productScan WHERE uniqueId = (?) AND sync = \'N\'";
$cordovaSQLite.execute(db, query, [uniqueId]).then(function (res) {
if (res.rows.length == 0) {
//processed the operation
}else{
alert('You have already scanned this asset!.');
}
var query = "SELECT COUNT(*) FROM productScan WHERE uniqueId = (?) AND sync = \'N\'";
$cordovaSQLite.execute(db, query, [uniqueId]).then(function (res) {
just parse the result,(json.parse(res)) and find by rowsAffected like
if(rowsAffected != 0)
{
//just do the operation
}
else {
//terminate
}

Getting all root properties

Let's say I have a DocumentDB collection populated with documents that have this shape:
[{ "Name": "KT", "Dob": "5/25/1990", "Children": [], "IsMale": false },
{ "Name": "Chris", "Dob": "10/1/1980", "Children": [], "IsMale": true }]
Now let's say I don't the structure of the documents above.
Is there a query I can write that will return me a distinct list of those property names ("Name", "Dob", "Children", "IsMale")?
In other words, is there a way for be to sniff out the schema of those documents?
This might be a duplicate of this question. In any case, the answers there might give you some ideas.
tl;dr; The only way to do it is to read all of the docs. You can pull them all back to your machine or you can read them inside of a stored procedure and only send the calculated schema back to your machine.
You need a dynamic ORM or ODM for Azure DocumentDB like Slazure to do something like this. Example follows:
using SysSurge.Slazure.AzureDocumentDB.Linq;
using SysSurge.Slazure.Core;
using SysSurge.Slazure.Core.Linq.QueryParser;
public void EnumProperties()
{
// Get a reference to the collection
dynamic storage = new QueryableStorage<DynDocument>("URL=https://contoso.documents.azure.com:443/;DBID=DDBExample;TOKEN=VZ+qKPAkl9TtX==");
QueryableCollection<DynDocument> collection = storage.TestCustomers;
// Build collection query
var queryResult = collection.Where("SignedUpForNewsletter = true and Age < 22");
foreach (DynDocument document in queryResult)
{
foreach (KeyValuePair<string, IDynProperty> keyValuePair in document)
{
Console.WriteLine(keyValuePair.Key);
}
}
}

Resources