When I am running a DeleteItemRequest on a dynamoDB table, I am getting an exception which says "the provided key size doesn't match with that of the schema".
All I am doing is
DeleteItemRequest deleteRequest = new DeleteItemRequest().withTableName(dynamoDbTableName).withKey(key);
client.deleteItem(deleteRequest);
Do I need to specify something more? Am I missing something?
It could mean that the key passed to the method does not match the type of the primary key in the table. For example, you are passing a numerical key but the table uses a string key. The type of the key depends on the method used when creating the AttributeValue. The method withN() creates a numerical key, while the method .withS() creates a string key.
Numerical key example:
Key key = new Key().withHashKeyElement(new AttributeValue().withN("120"));
String key example:
Key key = new Key().withHashKeyElement(new AttributeValue().withS("johndoe"));
There are methods for other types as well, like binary types and sets. See the javadoc for the AttributeValue class for more details.
Related
I need to pass a UniqueIdentifier as a string between two services. Unfortunately UniqueIdentifier#toString and UniqueIdentifier.fromString do not work well if an externalId is set.
What is the canonical way of serializing and deserializing a UniqueIdentifier?
If you're using Corda's library to create a UniqueIdentifier set with a custom external id, you can see that the toString() will generate a pattern of ${externalId}_$id i.e dummyExternalId_10ed0cc3-7bdf-4000-b610-595e36667d7d.
So to covert it back to UniqueIdentifier from that string, just split by delimiter of _
if (p.text.contains("_")) {
val ids = p.text.split("_")
//Create UUID object from string.
val uuid: UUID = UUID.fromString(ids[1])
//Create UniqueIdentifier object using externalId and UUID.
return UniqueIdentifier(ids[0], uuid)
}
Link here
If you have underscore in external id, you'll probably need your own function.
val potentialIds = input.split("_")
// Drop last one and stitch back the external id
val externalIdString = potentialIds.dropLast(1).joinToString("_")
// Last one is the UUID
val uuid = UUID.fromString(potentialIds.last())
val finalUniqueIdentifier = UniqueIdentifier(externalIdString, uuid)
Wait a sec, do you have to create an UniqueIdentifier for some business of yours? In that case you can just create a UniqueIdentifier from scratch and pass it as a variable between services (transactions accept external attachment).
If you have to "extract" the uniqueIdentifier of your State I don't think that is possible.
If I missed the point I apologize in advance :D
Currently I have a dynamodb table with hash key marked as DynamoDBAutoGeneratedKey. That means whenever I will save in dynamodb table hashkey will be generated automatically as UUID. I dont need to specify one by myself.
Now I would like to know if there is a way that I can have this hashkey once record is saved in the table. Is there a way that save method returns the record object from which I can get this hashkey?
Thank you,
Prasad
You can get the auto generated id from the model object after the save() method is executed successfully. Please note that DynamoDBMapper class save() method is defined as void. However, it does populate the auto generated id in the model object.
Order id is defined as auto generated key:-
#DynamoDBHashKey(attributeName = "orderId")
#DynamoDBAutoGeneratedKey
public String getOrderId() {
return orderId;
}
Order id is available in the "order" object as soon as the save() method is executed successfully:-
dynamoDBMapper.save(order);
System.out.println("Order id : " + order.getOrderId());
Output:-
Order id : f8b63e5b-eeff-43aa-bcaf-fdc245f43a7c
This question completely follows on from a related question I had asked (and was answered) here: Error when trying to retrieve a single entity
As I understand, to retrieve a single entity from the datastore using a property other than helper methods already provided (e.g. 'id') requires turning a simple data property into an EndpointsAliasProperty? If yes, how would I go about doing that? Or is it that we can only use 'id' (helper methods provided by EndpointsModel) and we cannot use any of the properties that we define (in this case 'title')?
The distinction between the custom EndpointsAliasPropertys and one of the data properties you defined is how they are used. They are all used to create a protorpc message, and that message is then converted into an EndpointsModel with your custom data in it. THIS is where the magic happens.
Breaking it down into steps:
1. You specify your data
from google.appengine.ext import ndb
from endpoints_proto_datastore.ndb import EndpointsModel
class MyModel(EndpointsModel):
my_attr = ndb.StringProperty()
2. You pick your fields for your method
class MyApi(...):
#MyModel.method(request_fields=('id', 'my_attr'), ...)
def my_method(self, my_model_entity):
...
3. A protorpc message class is defined from your fields
>>> request_message_class = MyModel.ProtoModel(fields=('id', 'my_attr'))
>>> request_message_class
<class '.MyModelProto_id_my_attr'>
>>> for field in request_message_class.all_fields():
... print field.name, ':', field.variant
...
id : INT64
my_attr : STRING
This happens every time a request is handled by a method decorated with #MyModel.method.
4. A request comes in your application and a message is created
Using the protorpc message class, a message instance is parsed from the JSON which gets passed along to your Endpoints SPI (which is created by endpoints.api_server).
When the request comes in to your protorpc.remote.Service it is decoded:
>>> from protorpc import remote
>>> protocols = remote.Protocols.get_default()
>>> json_protocol = protocols.lookup_by_content_type('application/json')
>>> request_message = json_protocol.decode_message(
... request_message_class,
... '{"id": 123, "my_attr": "some-string"}'
... )
>>> request_message
<MyModelProto_id_my_attr
id: 123
my_attr: u'some-string'>
5. The protorpc message is cast into a datastore model
entity = MyModel.FromMessage(request_message)
THIS is the step you really care about. The FromMessage class method (also provided as part of EndpointsModel) loops through all the fields
for field in sorted(request_message_class.all_fields(),
key=lambda field: field.number):
and for each field with a value set, turns the value into something to be added to the entity and separates based on whether the property is an EndpointsAliasProperty or not:
if isinstance(value_property, EndpointsAliasProperty):
alias_args.append((local_name, to_add))
else:
entity_kwargs[local_name] = to_add
After completing this loop, we have an ordered list alias_args of all key, value pairs and a dictionary entity_kwargs of the data attributes parsed from the message.
Using these, first a simple entity is created
entity = MyModel(**entity_kwargs)
and then each of the alias property values are set in order:
for name, value in alias_args:
setattr(entity, name, value)
The extended behavior happens in setattr(entity, name, value). Since EndpointsAliasProperty is a subclass of property, it is a descriptor and it has a setter which can perform some custom behavior beyond simply setting a value.
For example, the id property is defined with:
#EndpointsAliasProperty(setter=IdSet, property_type=messages.IntegerField)
def id(self):
and the setter performs operations beyond simply setting data:
def IdSet(self, value):
self.UpdateFromKey(ndb.Key(self.__class__, value))
This particular method attempts to retrieve the entity stored in the datastore using the id and patch in any values from the datastore that were not included in the entity parsed from the request.
If you wanted to do this for a field like my_attr, you would need to construct a custom query which could retrieve the item with that unique my_attr value (or fail if not exactly one such entity exists).
This is problematic and you'd be better off using a unique field like the key or ID used to store the entity in the datastore.
The keys with ancestors sample gives a great example of creating your own custom properties.
If you REALLY insist on using my_attr to retrieve an entity, you could do so using a different property name (since my_attr is already used for the data property) such as fromMyAttr:
class MyModel(EndpointsModel):
def MyAttrSet(self, value):
...
#EndpointsAliasProperty(setter=MyAttrSet)
def fromMyAttr(self):
...
Here, the MyAttrSet instance method would form the query:
def MyAttrSet(self, value):
query = MyModel.query(MyModel.my_attr == value)
results = query.fetch(2)
reject results that aren't unique for my_attr:
if len(results) == 0:
raise endpoints.NotFoundException('Not found.')
if len(results) == 2:
raise endpoints.BadRequestException('Colliding results.')
and copy over the values for the already stored entity if we do find a unique one:
matching_entity = results[0]
self._CopyFromEntity(matching_entity)
self._from_datastore = True
I am new to VB in general. I am going through some old VB code and I see statements like -
Addr.AddrType(CStr(0)).A_Type = " "
Why does the integer 0 have to be converted to a string?
Note that Addr is defined as
Public Addr As clsAddressDetail
AddrType is defined as a collection
Public AddrType As New Collection
The Collection class being used here has what is effectively an overloaded indexer. My emphases:
Returns a specific element of a Collection object either by position
or by key. Read-only.
Default Public ReadOnly Property Item( _
ByVal { Key As String | Index As Integer | Index As Object } _ ) As Object
Parameters
Key
A unique String expression that specifies a key string that can be used, instead of a positional index, to access an element of the
collection. Key must correspond to the Key argument specified when the
element was added to the collection.
Index
(A) A numeric expression that specifies the position of an element of the collection. Index must be a number from 1 through the value of
the collection's Count Property (Collection Object). Or (B) An Object
expression that specifies the position or key string of an element of
the collection.
So, if you ask for AddrType(0), you are asking for the zeroth member of the collection, which for this 1-based collection is an error. But if you ask for AddrType("0"), you are asking for that member which was added with Key "0". Any string can be used as a key - it's just that the particular string used here is the string representation of a number.
Incidentally, stylistically I would say writing CStr(0) rather than "0" isn't particularly nice...
The CStr() function there is used to access a specific member of the AddrType collection. Collections can be referenced either by a numeric index or by a string key value. So the short answer is, integers don't have to be converted to strings to access collection members. Why this particular coder chose to use "0" for a key value is unknown; it's certainly no more descriptive than using a numeric index, which would be the only advantage to the string key value.
In the Simple.Data examples, there is an example of 'Magic Casting':
// When you assign the dynamic record to a static type, any matching properties are auto-mapped.
var db = Database.Open();
Customer customer = db.Customers.FindByCustomerId(1);
Does Simple.Data also magically cast if there are multiple records returned? Something like this:
var db = Database.Open();
IEnumerable<Customer> customers = db.Customers.FindBySurname("Smith");
Obviously I have tried the above and it doesn't work ("Cannot implicitly convert type" from SimpleQuery to my concrete type). Any advice would be welcome.
FindBySurname returns a single record. If you use FindAllBySurname you'll get an enumerable, which should magic cast OK. (If for some reason it doesn't, you can call .Cast() on it.)