Error when executing CosmosDB stored procedure - azure-cosmosdb

I am learning to write CosmosDB stored procedures following the following information
Stored procedure docs
What I am trying to do is loop through a number of documents returned by a query and find the one that has the most exact match.
The flow is as follows
Check Start and End Date to make sure its a valid documents
1.5 Check that the input VariantNo is included in the Variants array in the document
Check if a user is included in the user array of the document OR if ALL is specified as a string in the array
Check if a store is included in the stores array OR if ALL is specified as a string in the stores array
The document looks as follows
{
"id": "12345",
"brand": "XXX",
"PromotionName": "Test Promo 1",
"PromotionType": "Deal",
"PromotionSticker": "Sticker 1",
"StartDate": "2020-05-14T00:00:00.1212122Z",
"EndDate": "2020-05-30T00:00:00.1212122Z",
"Variants": [
"0628462008001",
"0628462008002",
"0644324003002"
],
"Stores": [
"SE0623"
],
"Users": [
"ALL"
],
"DiscountInPercent": "30",
"RedPriceStores": null,
"CreatedDate": "20200515",
"CreatedBy": "SLAPI Promotions API ClientId: 123",
"UpdatedDate": null,
"UpdatedBy": null,
"Consumer": "YYYYY_V2",
"_rid": "HwVmAIFaOoEBAAAAAAAAAA==",
"_self": "dbs/HwVmAA==/colls/HwVmAIFaOoE=/docs/HwVmAIFaOoEBAAAAAAAAAA==/",
"_etag": "\"11005859-0000-0c00-0000-5ebe0f7e0000\"",
"_attachments": "attachments/",
"_ts": 1589514110
}
The beginnings of my stored procedure looks like this based on the template in CosmosDB
// SAMPLE STORED PROCEDURE
function getFinalPromotionPrice(item, store, user) {
var collection = getContext().getCollection();
// Query documents and take 1st item.
var isAccepted = collection.queryDocuments(
collection.getSelfLink(),
'SELECT * FROM c WHERE c.StartDate <= (SELECT VALUE GetCurrentDateTime()) AND c.EndDate >= (SELECT VALUE GetCurrentDateTime())',
function (err, feed, options) {
if (err) throw err;
// Check the feed and if empty, set the body to 'no docs found', 
// else take 1st element from feed
if (!feed || !feed.length) {
var response = getContext().getResponse();
response.setBody('no docs found');
}
else {
var response = getContext().getResponse();
var body = { prefix: prefix, feed: feed[0] };
response.setBody(JSON.stringify(body));
}
});
if (!isAccepted) throw new Error('The query was not accepted by the server.');
}
but I am getting this error when executing the stored procedure:
{"code":400,"body":{"code":"BadRequest","message":"Message: {\"Errors\":[\"Encountered exception while executing function. Exception = ReferenceError: 'prefix' is not defined\r\nStack trace: ReferenceError: 'prefix' is not defined\n at Anonymous function (script.js:20:13)\n at A

As you can check the error its expecting the "prefix" :
Exception = ReferenceError: 'prefix' is not defined
In the below line you are setting the value of prefix as "prefix" but you have not declared prefix anywhere in the code.
var body = { prefix: prefix, feed: feed[0] };
Change the above line to this if you don't require prefix in your SP body:
var body = { feed: feed[0] };

Related

how to normalize with redux tool and creactentityAdapter

i using redux tool kit to build react native app and i try to normalize my data like this
const postEntitiy = new schema.Entity('post');
const postAdapter = createEntityAdapter({
selectId: (post) => post._id,
});
const normalized = normalize(response.data, postEntitiy);
this is my resopose.data
Array [
Object {
"__v": 5,
"_id": "6020b367cb94a91c9cd48c34",
"comments": Array [],
"date": "2021-02-08T03:43:35.742Z",
"likes": Array [
Object {
"_id": "60216bd341b3744ce4b13bee",
"user": "601f2d46017c85357800da96",
},
],",
},
]
and this is the error it throw
The entity passed to the `selectId` implementation returned undefined., You should probably provide
your own selectId implementation.,
The entity that was passed:, Object {
"undefined": Object {
"0": Object {
"__v": 5,
"_id": "6020b367cb94a91c9cd48c34",
"comments": Array [],
"date": "2021-02-08T03:43:35.742Z",
"likes": Array [
Object {
"_id": "60216bd341b3744ce4b13bee",
"user": "601f2d46017c85357800da96",
},
],
},
]
As this is written the postAdapter and the normalize are independent of each other. What's happening here is that normalize is attempting the map the data into entities, but it ends up with the key as undefined because it is using the default key of id which has an undefined value. Additionally you have an array of entities rather than just one.
The postAdapter then tries to find the selectId on this data which has already been normalized. But the postAdapter is not looking at a single post object -- it is looking at the nested data created by normalize. It is looking at the data structure Object { "undefined": Object { "0": Object {... from your error. The "undefined": is caused by the bad id attribute and the "0": is because it is an array.
How you do write this correctly kind of depends on what it is that you are trying to do. It's possible that normalize is not really necessary here. It looks like the likes in response.data are already returning a string id for the like and the user instead of the deeply-nested data that the normalizr package is designed to flatten. If you want to use normalize and have it work properly then you need to set the idAttribute on the options argument (docs link).
const postEntity = new schema.Entity('post', {}, {idAttribute: '_id'});
const normalized = data.map( o => normalize(o, postEntity));
idAttribute also accepts a function. You can create a reusable id extractor function that pulls the _id property from any object with {_id: string;}.
const myIdExtractor = (object : {_id: string;}) => object._id;
const postEntity = new schema.Entity('post', {}, {idAttribute: myIdExtractor});
const normalized = data.map( o => normalize(o, postEntity));
That same id extractor function also works with createEntityAdapter.
const postAdapter = createEntityAdapter({
selectId: myIdExtractor,
});

Facing issue while trying to run the updateitem in dynamo db

I am able to fetch the record from dynamo db and view the response successfully. I need to modify the fetched 'ACCOUNTNAME' attribute in the 'items' array and update the json and also update in dynamo db. Now when I try to update the fetched records I end up with the Invalid attribute value type exception.
I was trying to update it using the key with Array of Strings which is provided with code snippet also tried to update inside for loop using the individual string but both failed with same exception as
"statusCode": 400,
"body": {
"message": "Invalid attribute value type",
"error": {
"errorMessage": "ValidationException"
}
}
I tried to create params and update the call inside the for loop by setting the key as below,
Key: {
"UUID": {
"S": usersOfAccountFromDB.body.Items[key].UUID
}
,
"TYPE": {
"S": user
}
}
but also failed with the same exception.
Fetched Json from dynamo db
[
{
"DEFINITION": "914ba44a-8c26-4b60-af0f-96b6aa37efe6",
"UUID": "830a49cb-4ed3-41ae-b111-56714a71ab98",
"TYPE": "USER",
"RELATION": "01efd131-6a5d-4068-889e-9dba44262da5",
"ACCOUNTNAME": "Wolff LLC"
},
{
"DEFINITION": "1f60fded-323d-40e1-a7f8-e2d053b0bed0",
"UUID": "47db3bbe-53ac-4e58-a378-f42331141997",
"TYPE": "USER",
"RELATION": "01efd131-6a5d-4068-889e-9dba44262da5",
"ACCOUNTNAME": "Wolff LLC"
},
{
"DEFINITION": "05ddccba-2b6d-46bd-9db4-7b897ebe16ca",
"UUID": "e7290457-db77-48fc-bd1a-7056bfce8fab",
"TYPE": "USER",
"RELATION": "01efd131-6a5d-4068-889e-9dba44262da5",
"ACCOUNTNAME": "Wolff LLC"
},
.
.
.
.]
Now I tried to iterate the Json and setup UUID which is the key as the String array as below,
var userUUIDArray : string[] = [];
for (let key in usersOfAccountFromDB.body.Items) {
userUUIDArray.push(usersOfAccountFromDB.body.Items[key].UUID);
}
for (var uuid of userUUIDArray) {
console.log("UUID : " +uuid); // prints all the uuid
}
// Creating a parameter for the update dynamo db
var params = {
TableName: <tableName>,
Key: {
"UUID": {
"SS": userUUIDArray
}
,
"TYPE": {
"S": user
}
},
UpdateExpression: 'SET #ACCOUNTNAME = :val1',
ExpressionAttributeNames: {
'#ACCOUNTNAME': 'ACCOUNTNAME' //COLUMN NAME
},
ExpressionAttributeValues: {
':val1': newAccountName
},
ReturnValues: 'UPDATED_NEW',
};
//call the update of dynamodb
const result = await this.getDocClient().update(param).promise();
I get the error as below,
"body": {
"message": "Invalid attribute value type",
"error": {
"errorMessage": "ValidationException"
}
}
All the approaches failed with same above exception
The update operation which your code currently uses only allow a single item to be updated.
IIUC, you want to update multiple items with one API call. For this you need to use batchWrite operation. Keep in mind that you cannot update more than 25 items per invocation.
The origin of the error you are getting
Your code fails due to the use of "SS" in the UUID field. This field is of type string so you must use "S". Note however that since you're using the document client API you do not need to pass values using this notation. See this answer for further details.
I have resolved the issue now by running the update statement one by one using loop
for (let key in usersOfAccountFromDB.body.Items) {
var updateParam = {
TableName: process.env.AWS_DYNAMO_TABLE,
Key: {
UUID: usersOfAccountFromDB.body.Items[key].UUID,
TYPE: user
},
UpdateExpression: "SET #ACCOUNTNAME = :val1",
ExpressionAttributeNames: {
'#ACCOUNTNAME': 'ACCOUNTNAME'
},
ExpressionAttributeValues: {
":val1": newAccountName
},
ReturnValues: "UPDATED_NEW",
};
const result = await this.getDocClient().update(updateParam).promise();
}

AWS AppSync GraphQL query a record by a field value

I have an user table, which consists of email, phone etc., and I would like to query a record based on its email or phone value (instead of #Id). Having not-adequate knowledge to do this - I wrote a schema like this:
type Query {
...
getUser(id: ID!): User
getUserByEmail(input: GetUserByEmailInput!): User
...
}
input GetUserByEmailInput {
email: String!
}
In resolver against getUserByEmail(..), I tried to experiment but nothing worked so far, so its remain to default state:
So when I ran a query like this to the Queries console:
query GetUserByEmail {
getUserByEmail(input: {email: "email#email.com"}) {
id
name
email
image
}
}
this returns an error like this:
{
"data": {
"getUserByEmail": null
},
"errors": [
{
"path": [
"getUserByEmail"
],
"data": null,
"errorType": "DynamoDB:AmazonDynamoDBException",
"errorInfo": null,
"locations": [
{
"line": 41,
"column": 5,
"sourceName": null
}
],
"message": "The provided key element does not match the schema (Service: AmazonDynamoDBv2; Status Code: 400; Error Code: ValidationException; Request ID: xxx)"
}
]
}
How can I query a record by non-Id field value?
If you use the Create Resources flow in the console, it will create a listUsers query that looks like the following for example. Note that the DynamoDb operation will be a Scan that has a DynamoDb filter expression where you can use any field to query DynamoDb. See below for the mapping template.
{
"version": "2017-02-28",
"operation": "Scan",
"filter": #if($context.args.filter) $util.transform.toDynamoDBFilterExpression($ctx.args.filter) #else null #end,
"limit": $util.defaultIfNull($ctx.args.limit, 20),
"nextToken": $util.toJson($util.defaultIfNullOrEmpty($ctx.args.nextToken, null)),
}
You can find more details about Scans and filter expressions in the AWS AppSync documentation:
https://docs.aws.amazon.com/appsync/latest/devguide/tutorial-dynamodb-resolvers.html

Getting 'query in command must target a single shard'

I have a CosmosDB setup using the Mongo API. I have a collection with a hashed shard on one of the field of the document. When I run commands like db.collection.remove or db.collection.deleteMany I get the following error.
Command deleteMany failed: query in command must target a single shard key.: {"message":"Command deleteMany failed: query in command must target a single shard key."}
I'm not sure how can I mention a shard key as part of the query considering I want the query to run across all the shards.
You need to provide shard key when you want to run commands like db.collection.remove or db.collection.deleteMany.
For example :
My data source as below:
[
{
"id" : "2",
"name" : "b"
},
{
"id" : "1",
"name" : "a"
}
]
And my shared key is "/name". Use db.coll.deleteMany({"name":"a"}) to delete specific shard.
Hope it helps you.
It should be ShardKey which you have chosen when you created cosmosDb collection.
FilterDefinition<Product> filter = Builders<Product>.Filter.Eq("id", 2);
=== 2 is my shardKey
await this._dbContext.GetProducts.DeleteOneAsync(filter);
return RedirectToAction("Index");
Kindly refer an image below , how does it look like in CosmosDB
Shard Key(Partition Key) has to be provided during specification of schema model in the code. Once its provided, we can perform regular operation like save, update and delete as usual.
Example:
const mySchema = new Schema({
requestId: { type: String, required: true },
data: String,
documents: [{ docId: String, name: String, attachedBy: String }],
updatedBy: {
type: {
name: { type: String, required: true },
email: { type: String, required: true },
}, required: true
},
createdDate: { type: Date, required: true },
updatedDate: { type: Date },
}, { shardKey: { requestId: 1 } }
);
In the above code we specified requestId as Shard Key, now we can perform any mongo operations
Example:
let request:any = await myModel.findById(requestId);
request.data ="New Data";
await request.save();
Hope that helps.
This works with all Mongo operations

400 error when upsert using Cosmos SP

I'm trying to execute the below SP
function createMyDocument() {
var collection = getContext().getCollection();
var doc = {
"someId": "123134444",
};
var options = {};
options['PartitionKey'] = ["someId"];
var isAccepted = collection.upsertDocument(collection.getSelfLink(), doc, options, function (error, resources, options) {
});
}
and cosmos keeps on complaining that there's something wrong with the partition key
{ code: 400,
body: '{"code":"BadRequest","message":"Message: {\\"Errors\\":
[\\"PartitionKey extracted from document doesn\'t match the one specified in the header\\"]}
}
Does anyone have any idea how to pass in the partion key in options so it gets pass this validation ?
Figured it out. The error was with how we call the stored proc.
How we were doing it
client.executeStoredProcedure('dbs/db1/colls/coll-1/sprocs/createMyDocument',
{},
{} //Here you have to pass in the partition key
);
How it has to be
client.executeStoredProcedure('dbs/db1/colls/coll-1/sprocs/createMyDocument',
{},
{"partitionKey": "43321"}
);
I think you misunderstand the meaning of partitionkey property in the options[].
For example , my container is created like this:
The partition key is "name" for my collection here. You could check your collection's partition key.
And my documents as below :
{
"id": "1",
"name": "jay"
}
{
"id": "2",
"name": "jay2"
}
My partitionkey is 'name', so here I have two paritions : 'jay' and 'jay1'.
So, here you should set the partitionkey property to '123134444' in your question, not 'someId'.
More details about cosmos db partition key.
Hope it helps you.

Resources