Re: [RavenDB] document unique keys, approaches

100 views
Skip to first unread message
Message has been deleted

Ayende Rahien

unread,
Jun 30, 2010, 1:42:48 PM6/30/10
to rav...@googlegroups.com
inline

On Fri, Jun 25, 2010 at 2:26 PM, Daniel Steigerwald <dan...@steigerwald.cz> wrote:
I would like to summarize my investigations and show approach I chose.
Problem definition: we need unique keys, for example: email and phone
number.

public class User
{
       public string Id { get; set; }
       public string EmailAddress { get; set; }
       public string PhoneNumber { get; set; }
       public string Password { get; set; }
}

How ensure unique key in RavenDB? Straightforward approach would be to
chose native unique key, for example email or phone number. But, which
one?
Personally I suppose this approach is actually anti-pattern, unless
such key is absolute unique immutable value, like social security
number (I hope they are really immutable in your country, in my
country it's not always true ;)


Agreed on that. 
_every single time_ that I tried to use a natural key I got bitten by it.
 
Second approach was suggested by Ayende, he calls it reverse lookup.
emails/f...@bla.com = { "user_id": "users/132" }
phoneNumberts/420123456789 = { "user_id": "users/132" }

Maybe I overlooked something, but I don't like this "nosql
normalization" approach, because it prevents aggregation.

I am sorry, I don't follow your logic here.
 
Also I would
have to maintain two separated groups of models. One for my
application and second for ravendb.

My current approach is to use separate types, and use them only for
uniqueness ensuring.

[HttpPost]
public ActionResult SignUp(AccountSignUpViewModel signup)
{
       if (ModelState.IsValid)
       {
               var email = session.Load<Email>("emails/" + signup.Email);
               if (email != null)
               {
                       ModelState.AddModelError("Email", "Email is used.");
               }
               else
               {
                       session.Store(new Email
                       {
                               Id = "emails/" + signup.Email
                       });
                       session.Store(new User
                       {
                               Email = signup.Email,
                               Password = signup.Password
                       });
                       session.SaveChanges();
                       return Redirect("/");
               }
       }
       return View(signup);
}

I am still newbie, but this approach seems to be best. Yes, we are
duplicating emails, but it should not be problem. It's trade off. What
do you think?



I like that approach, but I would add a reference to the user in the email document, so we can go from an email to the user.

Daniel Steigerwald

unread,
Jun 30, 2010, 4:59:46 PM6/30/10
to ravendb
I should correct my previous post. I wrote it during process of
learning things :)
Rewamped version:

1) I would put somehere basic recommendations for beginners. Rule
about immutable identity keys, as I wrote above.

2) What I wanted to say. Reverse lookup is good. Actually it is the
only solution we have. But it smells like workaround. We are splitting
documents into chunks.. why? Because we need more than one unique
keys. Why document simply can't have more identity keys, therefore
unique keys?

public class User
{
[RavenKey]
public string Id { get; set; }
[RavenKey]
public string EmailAddress { get; set; }
[RavenKey]
public string PhoneNumber { get; set; }
public string Password { get; set; }
}

I do not see any pitfall.





On 30 čvn, 19:42, Ayende Rahien <aye...@ayende.com> wrote:
> inline
>
> On Fri, Jun 25, 2010 at 2:26 PM, Daniel Steigerwald
> <dan...@steigerwald.cz>wrote:
>
>
>
>
>
> > I would like to summarize my investigations and show approach I chose.
> > Problem definition: we need unique keys, for example: email and phone
> > number.
>
> > public class User
> > {
> >        public string Id { get; set; }
> >        public string EmailAddress { get; set; }
> >        public string PhoneNumber { get; set; }
> >        public string Password { get; set; }
> > }
>
> > How ensure unique key in RavenDB? Straightforward approach would be to
> > chose native unique key, for example email or phone number. But, which
> > one?
> > Personally I suppose this approach is actually anti-pattern, unless
> > such key is absolute unique immutable value, like social security
> > number (I hope they are really immutable in your country, in my
> > country it's not always true ;)
>
> Agreed on that.
> _every single time_ thatI tried to use a natural key I got bitten by it.
>
> > Second approach was suggested by Ayende, he calls it reverse lookup.
> > emails/...@bla.com = { "user_id": "users/132" }

Ayende Rahien

unread,
Jun 30, 2010, 5:38:18 PM6/30/10
to rav...@googlegroups.com
inline

On Wed, Jun 30, 2010 at 11:59 PM, Daniel Steigerwald <dan...@steigerwald.cz> wrote:
I should correct my previous post. I wrote it during process of
learning things :)
Rewamped version:

1) I would put somehere basic recommendations for beginners. Rule
about immutable identity keys, as I wrote above.

 
Good idea, I have been trying to figure out what the common questions are.
would you care to join our docs contributers?
 
2) What I wanted to say. Reverse lookup is good. Actually it is the
only solution we have. But it smells like workaround.
 
I don't see it that way.
 
We are splitting
documents into chunks.. why?
 
No, you aren't. The document is still stored in only one place. You are using this as a way to reserve additional information.
 
Because we need more than one unique
keys.
 
The problem here is a bit more complex. What happen when you run in a sharded environment? You can't have a unique index there.
 
Why document simply can't have more identity keys, therefore
unique keys?

public class User
 {
       [RavenKey]
       public string Id { get; set; }
       [RavenKey]
       public string EmailAddress { get; set; }
       [RavenKey]
       public string PhoneNumber { get; set; }
       public string Password { get; set; }
 }

I do not see any pitfall.

 
 
Sharding.
Message has been deleted

Daniel Steigerwald

unread,
Jun 30, 2010, 6:09:31 PM6/30/10
to ravendb
1) Sure I would like to contribute, if I could.
2) Hmm, didn't tried sharding yet. But, if one document identity key
works, why two should not works? On same document?

Nick Aceves

unread,
Jun 30, 2010, 11:34:07 PM6/30/10
to rav...@googlegroups.com
Daniel,

In theory you're right, having "multiple" keys would work, but it would really just be an abstraction in the database's interface. Each document has identity (in an abstract sense, that is: two documents with the exact same fields and values are still considered to be unique), which probably means that any such "multiple keys" implementation is going to be implemented by simply concatenating the keys together to form the identity (exactly how you would handle multiple "keys" in a key-value store). Because document identities are immutable (a good thing) this would imply that each "key" on the document would also be immutable, so you don't really gain anything. You could do this now if you wanted to, by combining the various properties you want to be "keys" into some computable and unique value (concatenate them, hash them, whatever) and manually setting that as the ID (instead of letting raven generate the ID).

I think your problem is that you are thinking of the "Emails" (in this case) as separate documents, which they (technically) are, but the don't function as such in your model. You store the actual data for each email as part of the original aggregate, so you don't lose anything in terms of your models, and you treat each separate email "document" as more of an entry in a unique index.

I'm actually considering writing some infrastructure code to handle this for me like so:

class Post {
   public string Id { get; set; }

   [Unique(Collection="PostSlugs")]
   public string Slug { get; set; }

   /* ... the usual blog post example stuff ... */
}

You would save this like usual:

session.Store(post);

Then, inside the session (which would obviously be my own custom implementation that further abstracts the default IDocumentSession) the infrastructure would handle creating a "PostSlugs" document /PostSlugs/[slug] that looks like { "Post": <ID of post> }. Handling updates is trivial, just delete the old PostSlug and create a new one with the new value. When you delete a Post, you also delete its PostSlug.

Now that I'm thinking about it, could this be handled automatically on the server with a trigger?

Thoughts?

Nick
Reply all
Reply to author
Forward
0 new messages