Reputation: 414
Consider a web-based system where multiple users might be creating accounts at the same time. The system requires that users have an unique username as well as an unique e-mail address. In an RDBMS, this would be simple: the "email" field would be marked as UNIQUE. How could I approach this the datastore way?
This was my initial attempt:
// Create entity using the username as key
String username = ... // provided by the user
String email = ... // provided by the user
Entity entity = new Entity("Users", KeyFactory.createKey("User", username));
entity.setProperty("email", email);
// Create a query that matches the email property
Query q = new Query();
q.setFilter(new FilterPredicate("email", FilterOperator.EQUAL, email));
// Start a transaction
Transaction txn = datastore.beginTransaction();
try {
// Try to get an entity with that username
datastore.get(KeyFactory.createKey("User", username);
}
catch(EntityNotFoundException e) {
// No entity with that username, make sure the e-mail
// is not taken either
PreparedQuery pq = datastore.prepare(q);
if (pq.countEntities(FetchOptions.Builder.withLimit(1)) == 0) {
// The e-mail isn't taken either, all good
datastore.put(entity);
txn.commit();
... handle success here ...
return;
}
}
finally {
if (txn.isActive())
txn.rollback();
}
... handle failure here ...
But after a few simple tests I noticed that the query doesn't always "see" the "puts" that were made a short time before (eventual consistency, I should have guessed). To try to work around that, I tried to turn that query into a "dummy" ancestor query.
So this "dummy" ancestor query works like this. I create an entity of kind RootUser with a named key. I get the key from this root user, and make it the ancestor key for the query in the code above. Now that didn't work either, I'm still getting duplicate e-mail addresses. I also tried to configure the transaction so it's a cross-group transaction, but that didn't help either.
So, any tips on how to get this working? Is it possible at all?
Upvotes: 3
Views: 468
Reputation: 5707
quick version: You want to use another entity "kind" and store your unique values as the id for objects in this kind. eg. "User.username:myusername", "User.email:[email protected]". By creating these entries first, you'll know when a value isn't unique.
see this for an example implementation.. http://webapp-improved.appspot.com/_modules/webapp2_extras/appengine/auth/models.html#Unique
Upvotes: 2