we have a sql container. We’d like to enforce case-insensitive unique constraint on a particular property (say, ‘Name’. if a document's Name is ‘ALICE’, no other document in that logical partition shall have ‘alice’ as Name).
One solution might be introducing another property LowerCaseName and enforce the natively supported unique key config on path /LowerCaseName. Every time Name is updated, we make sure LowerCaseName is always updated as the lower-case version of Name in the same request.
I’m not sure if this is the best practice for this problem? Thanks.
public MyDocumentModel
{
string Name {get; set;}
string LowerCaseName {get; set;}
}
From the documentation here:
In Azure Cosmos DB's SQL (Core) API, items are stored as JSON values.
These JSON values are case sensitive. When you choose a property as a
unique key, you can insert case sensitive values for that property.
For example, If you have a unique key defined on the name property,
"Gaby" is different from "gaby" and you can insert both into the
container.
Based on this, I believe your approach for having a 2nd property to enforce unique key is correct.
Related
Suppose I want to store the data that looks like the following
type Person = {
Name:string
PassportNumber:string
}
type PolicyStatus =
|SaleInProgress
|PolicyActive
|Cancelled
type Policy = {
PincipalPerson: Person
PolicyDependents: Person list
PolicyId:int
PolicyState:PolicyStatus
}
(I've expressed these types as F# records since they look a lot like json but don't let that distract you)
I want to make it so that each person that features in a Policy is unique. This means, for example, if someone is a principal on some policy, they cannot also be a dependent on some other policy.
So if I store these in a Policy collection then I don't think there is a way to specify that the union of dependents and principals should have unique passport ids, is there?
If I was using some RDMS, then I can just keep people on a different table, have a foreign key between Policy and People and a unique constraint on PassportNumber.
So what is the idiomatic "NoSQL" way of doing this in Cosmos DB?
There is no way to enforce the uniqueness constraints you require directly in Cosmos DB, at least not if your data is structured as you proposed it. The uniqueness constraints that Cosmos DB supports are based on (combinations of) whole property values (e.g. first name + last name, or PassportNumber), so you can't look into an array of values.
If you do want to enforce the uniqueness constraints on the DB level and are willing to change your data model, consider embedding the policy inside the person object:
type Person = {
Name: string
PassportNumber: string // with uniqueness constraint
IsPrincipalPerson: Nullable<bool> // specifies if the person is principal or dependent
PolicyId: Nullable<int>
PolicyState: Nullable<PolicyStatus>
}
The downside of this model is that if you need to change the state of a policy, you have to change it in all person objects in which the policy id occurs. So alternatively, you could use the following model:
type Person = {
Name: string
PassportNumber: string // with uniqueness constraint
IsPrincipalPerson: Nullable<bool> // specifies if the person is principal or dependent
PolicyId: Nullable<int> // refers to a policy object
}
type Policy = {
PolicyId: int
PolicyState: PolicyStatus
}
Now several person objects can share a policy object, but the uniqueness constraints is still enforced, since every person can have at most one policy (either as principal or dependent). The downside is that you need to take care of the referential integrity of person.PolicyId yourself, since foreign key relationships are not supported.
Dynamodb Table with an attribute say id(unique value) which is a Range key and it should always be an incremental value. Using DynamoDBAutoGeneratedKey will solve my requirement?
No. auto-generated-keys get a UUID value which is a unique random string (e.g., 0674e9df-0059-4f71-a172-6e2dcb32a92d)
Specifically, the Java Doc says the following:
Only String typed keys can be auto generated, and are given a random UUID.
I have a use case where I want to create a Dynamodb Table which contains only 2 attributes - List of String (for example, Countries) and a Boolean value.
I am extracting this value for each country and implementing different logic in case of true or false.
My question is that, what is a best way (best practice) to create a dynamodb table.
I thought of few of following ways -
Boolean value as a key
Use boolean value as key and List as another attribute.
Add a row for each country.
Create a separate record with Country value as key and flag as an attribute.
Use List of countries as key and boolean value as another attribute. (I don't think this can be a good choice)
What could be the best practice while designing tables like this?
Thank You,
Prasad
From AWS DynamoDB Docs, NamingRulesDataTypes:
When you create a table or a secondary index, you must specify the names and data types of each primary key attribute (partition key and sort key). Furthermore, each primary key attribute must be defined as type string, number, or binary.
There are many options to model your table, but keep in mind you have to respect the rules cited above.
Your second case is a good one:
Add a row for each country. Create a separate record with Country value as key and flag as an attribute.
Partition key: country - string
Some column you do not have to define at creation: flag - boolean
It is specifically mention that WAMS needs a int ID column to work in SQL Azure. However when developing enterprise apps over distributed databases, GUIDs are the preferred Primary key to have. How does one get around avoiding int ID column and support GUID?
If that cannot be done then how does one go about syncing data on the cloud from multiple standalone databases on various tablets/mobile the app using WAMS is running on?
An update on this issue - as of last week, the mobile services now support arbitrary strings as the ids for the column - check out this post for more information. You can now insert data with an 'id' value (which you couldn't before), so you can use a Guid value on insert. Also, if you don't send anything on the Id column on insert (or that value is set to null), the server will by default generate an unique identifier for the column.
At present, I don't think that its possible to use a GUID in the ID column. The documentation for the Mobile Services server side scripts specify that for the Delete function, the ID must be a javascript Number type. As far as I can see, all of the available sample code, and the code that you can download from the portal is quite explicit in using an integer type for the ID.
You'll have to come up with a way of generating a unique integer value whenever a new record is created. The example here uses a tick count in Insert script, which is probably OK for a low volume application, but it would need to be made more robust, perhaps by generating a number based on the user's identity and combining it with the tick count.
I'm a little late to this but I have found you can use a GUid as a primary key to a mobile services table. A couple of points though. Set the JSON property to lower case "id" and use a nullable guid, this allows inserting when there a default on the id column (NewId())
[JsonProperty(PropertyName = "id")]
public Guid? Id { get; set; }
Ash..
I have an objectify entity defined like this:
public class MyEntity1
{
#Id #Indexed String phoneNumber;
#Parent #Indexed Key<MyEntity2> parentEntityKey;
}
When I try to filter by phoneNumber, I get the following error message:
Cannot (yet) filter by #Id fields on entities which have #Parent fields.
The reason for this construction is that I want to be able to get (instead of querying) those records when I know both phoneNumber and parentEntityKey, which I sometimes do. In some other cases, I only know the phoneNumber and wish to query for it.
Is this a shortcoming of Objectify or Datastore and can I find a work-around? Do you have a proposal which solves my two requirements (get instead of query when I know both values and query by phoneNumber when only that is known)?
This is the nature of datastore keys.
You cannot filter by the id part of a key. You can filter by an entire key, or you can use an ancestor() query to "filter" by the key hierarchy, but you cannot filter by just the #Id part of a key. Think about how keys are laid out in BigTable:
/parentkind1/parentid1/parentkind2/parentid2/kind/id
You can't do a range scan on just the id part.
It sounds like what you want to do is create an indexed phoneNumber property on your entity, separate from the #Id field. Yes it's duplicate data but you need the separate index no matter what, so the extra data serialized into the blob is fairly negligible.
Note that you can't query on indexed properties inside of a transaction, and queries will have eventual consistency behavior. If you require phone numbers to be assigned uniquely, you will need to create a separate PhoneNumber entity without a #Parent whose #Id is the phone number itself. XG transactions let you create this uniqueness entity and associate it with your MyEntity1 in a single consistent operation.