400 error when upsert using Cosmos SP - azure-cosmosdb

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.

Related

Get status for a list of items

I have a dynamodb table with a partition key for client_id and no sort key. The json stored in the table just contains the client_id and compliance_level (which is a string title). I need to query the table for a list of client_id's because there is an application that displays client information in a tabular display. I am trying to use ExpressionFilter but get an "Error ValidationException: Either the KeyConditions or KeyConditionExpression parameter must be specified in the request." exception. I don't however have a keycondition to query on. Any assistance would be appreciated. I can't use batchgetitem as there will be more than 100 items to get the status for.
var AWS = require('aws-sdk');
AWS.config.update({region: 'ap-southeast-2'});
var ddb = new AWS.DynamoDB({apiVersion: '2012-08-10'});
var params = {
TableName : "Client-Compliance",
ProjectionExpression: "username, version",
FilterExpression : "client_id IN (:client1, :client2)",
ExpressionAttributeValues : {
":client1" : { "S": "c1234567" },
":client2" : { "S": "c88888888" }
}
};
ddb.query(params, function(err, data) {
if (err) {
console.log("Error", err);
} else {
console.log(data);
}
});
Many thanks

Do I require a partition key when dealing with the CosmosDB emulator?

Trying to delete a CosmosDB document via DeleteDocumentAsync is giving me a Microsoft.Azure.Documents.DocumentClientException: Message: {"Errors":["Resource Not Found"]} no matter what I try.
I am using the CosmosDB local emulator with a single collection and a single record for now, so I haven't defined any partition key.
This is my document structure:
{
"id": "a1032017-c131-4fe0-a045-1d342bc56410",
"Code": "059058",
"Key": "f9971f3a-9737-4da5-90df-2ab7f93ba679",
"CreatedOn": "2019-09-30T15:50:53.0368614-04:00",
"TTL": 1440,
"PhoneNumber": "1112223333",
"_rid": "35E3AOfSiUUBAAAAAAAAAA==",
"_self": "dbs/35E3AA==/colls/35E3AOfSiUU=/docs/35E3AOfSiUUBAAAAAAAAAA==/",
"_etag": "\"00000000-0000-0000-77c8-620aa5ca01d5\"",
"_attachments": "attachments/",
"_ts": 1569873059
}
Code to delete:
public async Task Delete<T>(T codeKeyPairModel) where T : CodeKeyPairModel
{
var documentLink = UriFactory.CreateDocumentUri(cosmosDBId, collectionId, codeKeyPairModel.Id.ToString());
var result = await cosmosDBClient.DeleteDocumentAsync(documentLink,
new RequestOptions() { PartitionKey = new PartitionKey(Undefined.Value) });
}
documentLink value:
{dbs/CodeCheckerDB/colls/CodeKeyPair/docs/a1032017-c131-4fe0-a045-1d342bc56410}
Does the emulator requires a partition to be set even for smaller DBs? If so, how can I set one?
I did a test for your sample,it works for me. You could remove the PartitionKey settings because you said your collection is single collection, not partitioned collection. No need to pointing any partition key.
My code:
DocumentClient documentClient = new DocumentClient(new Uri(endpointUrl), authorizationKey);
var documentLink = UriFactory.CreateDocumentUri(databaseId, collectionId, "a1032017-c131-4fe0-a045-1d342bc56410");
await documentClient.DeleteDocumentAsync(documentLink, null);

Query works at the console but not in code

My DynamoDB table alexas has this item with key "abc" as seen in the DynamoDB console below:
However, the following query returns no result:
const params = { TableName: "alexas",
KeyConditionExpression: "deviceId = :deviceId",
ExpressionAttributeValues: { ":deviceId": "abc"}
}
const docClient = new AWS.DynamoDB.DocumentClient();
docClient.query(params, (err, data) => {
if (err) {
console.error(err);
} else {
console.log(data);
}
});
The above code returns null for err and in data:
{ Items: [], Count: 0, ScannedCount: 0 }
I am new to the DynamoDB style of expressions. Is there anything wrong with my code which I took from here.
If instead of query, I used the scan method and just have TableName in params, I get the items in my table. This confirms that I am performing the operations on the correct table that has data.
The query returned no data because the key value does not match.
The item's deviceId is the string "abc" and not abc. Note the extra quotation marks.
The item was inserted using the DynamoDB console's Create editor and there is no need to include "" if the value is already expected to be of type string.
DynamoDB's Scan operation doesn't take a KeyConditionExpression - only the Query operation takes this parameter. Scan always scans the entire table, and has a FilterExpression to post-filter these results (however please note that you still pay for scanning the entire table).
For example, here is the official documentation of Scan: https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_Scan.html
Check QueryAPI
const params = { TableName: "alexas",
KeyConditionExpression: "deviceId = :deviceId",
ExpressionAttributeValues: {
":devideId":{
S: "abc", // here
}
}
}
const docClient = new AWS.DynamoDB.DocumentClient();
docClient.query(params, (err, data) => {
if (err) {
console.error(err);
} else {
console.log(data);
}
});
ExpressionAttributeValues needs to be passed in a different manner.
Update:
Try using Exp attribute names, (I'm not sure if this will make a difference)
var params = {
TableName: "alexas",
KeyConditionExpression: "#d = :dId",
ExpressionAttributeNames:{
"#d": "email"
},
ExpressionAttributeValues: {
":dId": "abc"
}
};

convert a dynamodb scan to query

I've written a API gateway to scan a dynamodb table and get values based on the condition and my code is as below.
var params = {
TableName: 'CarsData',
FilterExpression: '#market_category = :market_category and #vehicle_size = :vehicle_size and #transmission_type = :transmission_type and #price_range = :price_range and #doors = :doors',
ExpressionAttributeNames: {
"#market_category": "market_category",
"#vehicle_size": "vehicle_size",
"#transmission_type": "transmission_type",
"#price_range": "price_range",
"#doors": "doors"
},
ExpressionAttributeValues: {
":market_category": body.market_category,
":vehicle_size": body.vehicle_size,
":transmission_type": body.transmission_type,
":price_range": body.price_range,
":doors": body.doors
}
}
dynamodb.scan(params).promise().then(function (data) {
var uw = data.Items;
console.log(data + "\n" + JSON.stringify(data) + "\n" + JSON.stringify(data.Items));
var res = {
"statusCode": 200,
"headers": {},
"body": JSON.stringify(uw)
};
ctx.succeed(res);
}).catch(function (err) {
console.log(err);
var res = {
"statusCode": 404,
"headers": {},
"body": JSON.stringify({ "status": "error" })
};
ctx.succeed(res);
});
when I run this code, I get the result as expected. But when I was going through some online forums, I came to know that scanning is expensive compared to querying. But I'm unable to know on how can I change my query from scan to query. Here my primary key is ID. please let me know on how can I do this.
Thanks
Scan operation is more expensive comparing to query operation, in terms of performance as well as costing. Dynamodb calculates cost based on the number of read capacity units consumed for processing not on number of records returned.
Query operation finds value based on primary key (Hash) or composite primary key (Hash key and Sort Key).
Your schema should be redesigned with composite primary key(Hash key and Sort Key).
Its not neccessary to have column Id as primary Key like old school RDBMS. If you are not using Id effectively remove that column from your schema and redefine it with some other attributes. For an example am using Market Category (market_category ) as Hash Key & Price Range (price_range) as Range Key.
var params = {
"TableName": 'CarsData',
"ConsistentRead": true,
//Composite Primary Key in Key Condition Expression
"KeyConditionExpression": "#market_category = :market_category AND #price_range = :price_range",
//Remaining column in filter expression
"FilterExpression": '#vehicle_size = :vehicle_size and #transmission_type = :transmission_type and #doors = :doors',
"ExpressionAttributeNames": {
"#market_category": "market_category",
"#vehicle_size": "vehicle_size",
"#transmission_type": "transmission_type",
"#price_range": "price_range",
"#doors": "doors"
},
"ExpressionAttributeValues": {
":market_category": body.market_category,
":vehicle_size": body.vehicle_size,
":transmission_type": body.transmission_type,
":price_range": body.price_range,
":doors": body.doors
}
}
dynamodb.query(params).promise()
.then(function (data) {
console.log(data);
}).catch(function (err) {
console.log(err);
});
Hope this example will give you insights about using composite primary key,
Based on your usage choose the widely used columns for Hash & Range key.

Update Multiple Items with same Hash Key in DynamoDb

I have a dynamodb table that stores users videos.
It's structured like this:
{
"userid": 324234234234234234, // Hash key
"videoid": 298374982364723648 // Range key
"user": {
"username": "mario"
}
}
I want to update username for all videos of a specific user. It's possible with a simple update or i have to scan the complete table and update one item a time?
var params = {
TableName: DDB_TABLE_SCENE,
Key: {
userid: userid,
},
UpdateExpression: "SET username = :username",
ExpressionAttributeValues: { ":username": username },
ReturnValues: "ALL_NEW",
ConditionExpression: 'attribute_exists (userid)'
};
docClient.update(params, function(err, data) {
if (err) fn(err, null);
else fn(err, data.Attributes.username);
});
I receive the following error, I suppose the range key is necessary.
ValidationException: The provided key element does not match the schema
Dynamo does not support write operations across multiple items (ie. for more than one item at a time). You will have to first scan/query the table, or otherwise generate a list of all items you'd like to update, and then update them one by one.
Dynamo does provide a batching API but that is still just a way to group updates together in batches of 25 at a time. It's not a proxy for a multi-item update like you're trying to achieve.

Resources