Cosmos SQL API: filter based an array entry - azure-cosmosdb

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.

Related

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

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

Firebase / Firestore - How to manually create indexes

I'm developing a mobile app with react-native.
I am using a third party template to get going.
This templates creates a Firestore database collection named "users".
This template also keeps giving me errors about not having an index on that "users" collection:
[FirebaseError: The query requires an index. You can create it here: https://console.firebase.google.com/v1/r/project/social-marketplace-3c8df/firestore/indexes?create_composite=CmRwcm9qZWN0cy9zb2NpYWwtbWFya2V0cGxhY2UtM2M4ZGYvZGF0YWJhc2VzLyhkZWZhdWx0KS9jb2xsZWN0aW9uR3JvdXBzL1NvY2lhbE5ldHdvcmtfUG9zdHMvaW5kZXhlcy9fEAEaDAoIYXV0aG9ySUQQARoNCgljcmVhdGVkQXQQAhoMCghfX25hbWVfXxAC]
When I copy this "create index" link from the error message and paste it into the browser, it takes me to an empty Firebase console page. No indexes being created as the result of this.
WhenI attempt to manually create such index by navigating to the "indexes" tab on the Firestore page, selecting composite index, and entering "/users" as my collection name, I'm being told that "/users" is reserved, so I am unable to create such index.
Is there a way to create such an index on my "/users" collection?
Can create a file firestore.indexes.json
Leter create your index:
{
"indexes": [
{
"collectionGroup": "collection_name",
"queryScope": "COLLECTION",
"fields": [
{
"fieldPath": "your_field_name",
"order": "ASCENDING"
},
{
"fieldPath": "your_field_name",
"order": "DESCENDING"
}
]
},
]
}
Remember:
CollectionGroup = collection name
queryScope = type (collection | document)
fields = fields for orderBy
singleField = only field for orderBy
As #Renaud Tarnec's comment it fixed by using the Firebase console in :
Cloud Firestore > Indexes panel > Click Add Index button.
Input "users" or value in Collection ID text-box.
In Field to index, add at least two fields - this is required
Scroll down > Query scopes > Select if Collection or Collection Group
And it will enable the Create index button.

How to update a nested object inside an array in DynamoDB

Consider the following document item / syntax in a DynamoDB table:
{
"id": "0f00b15e-83ee-4340-99ea-6cb890830d96",
"name": "region-1",
"controllers": [
{
"id": "93014cf0-bb05-4fbb-9466-d56ff51b1d22",
"routes": [
{
"direction": "N",
"cars": 0,
"sensors": [
{
"id": "e82c45a3-d356-41e4-977e-f7ec947aad46",
"light": true,
},
{
"id": "78a6883e-1ced-4727-9c94-2154e0eb6139",
}
]
}
]
}
]
}
My goal is to update a single attribute in this JSON representation, in this case cars.
My approach
I know all the sensors IDs. So, the easiest way to reach that attribute is to find, in the array, the route which has a sensor with any of the ids. Having found that sensor, Dynamo should know which object in the routes array he has to update. However, I cannot run this code without my condition being rejected.
In this case, update attribute cars, where the route has a sensor with id e82c45a3-d356-41e4-977e-f7ec947aad46 or 78a6883e-1ced-4727-9c94-2154e0eb6139.
var params = {
TableName: table,
Key:{
"id": "0f00b15e-83ee-4340-99ea-6cb890830d96",
"name": "region-1"
},
UpdateExpression: "set controllers.intersections.routes.cars = :c",
ConditionExpression: ""controllers.intersections.routes.sensors.id = :s",
ExpressionAttributeValues:{
":c": 1,
":s": "e82c45a3-d356-41e4-977e-f7ec947aad46"
},
ReturnValues:"UPDATED_NEW"
};
docClient.update(params, ...);
How can I achieve this?
Unfortunately, you can't achieve this in DynamoDB without knowing the array index. You have very complex nested structure. The DynamoDB API doesn't have a feature to handle this scenario.
I think you need the array index for controllers, routes and sensors to get the update to work.
Your approach may work in other databases like MongoDB. However, it wouldn't work on DynamoDB. Generally, it is not recommended to have this complex structure in DynamoDB especially if your use case has update scenario.
TableName : 'tablename',
Key : { id: id},
ReturnValues : 'ALL_NEW',
UpdateExpression : 'set someitem['+`index`+'].somevalue = :reply_content',
ExpressionAttributeValues : { ':reply_content' : updateddata }
For updating nested array element need to fing out array index . Then you can update nested array element in dynamo db.

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

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