Reputation: 525
I'm modelling a google datastore with objectify and part of my datastore has these 3 elements:
User post a new Post which can be liked by many Users (typical social network).
So far I have User:
@Entity
public class UserMW {
@Id
private Long id;
@Index
private String email;
...
}
Post:
@Entity
public class PostMW{
@Id
private Long id;
@Load
private Ref<UserMW> owner;
...
}
And Like:
@Entity
public class LikeMW {
@Id
private Long id;
@Load
private Ref<UserMW> user;
private Key likedObject;
...
}
It works perfectly and meets all my needs till now. The problem is now I don't know for which way I should go to unlike a post (delete an entity from kind Likes).
I have the User and the likedObject Key to delete it so if it was in a relational database would be very simple (just a delete with a "where" by userID and likedObjectID) but on Objectify...
I could think about 2 ways:
1 - @index on both attributes of Likes entity then query it and delete (but @Index is so expensive and table Likes is gonna be giant!! Doesn't sound as a good idea)
2 - @Parent on user attribute of Likes entity and @Index on likedObject then query by ancestor then filter by likedObject Key and delete (but if I use @Parent, I understood all the time I load 1 user I'll load ALL HIS LIKES and as I said, table Likes is gonna be giant!! Doesn't sound as a good idea either)
Any suggestion to resolve my problem?
Thank you guys!
Upvotes: 1
Views: 136
Reputation: 13556
Yes, you can simply index the user
and likedObject
fields and run queries like a relational database. However, this has two disadvantages:
Strong consistency is desirable so that if a user likes/unlikes something and reloads the page, they're guaranteed to see the effect immediately.
Here's what I would do:
@Entity
@Cache
public class Like {
@Parent
private Ref<User> user;
@Id
private String likedObjectKey;
public <T> Key<T> getLiked() { return Key.create(likedObjectKey); }
}
Use the toWebSafeString()
key of the thing being liked as the string id of the Like
entity. You can add some syntactic sugar to hide the stringification/destringification.
This means that fetching Like
s for {user, thing} tuples is always a get-by-key operation. You can batch-fetch them and fetches will take advantage of memcache (both positive and negative hits will be cached). The result will always be strongly consistent. You can fetch and modify these in transactions easily.
Note this doesn't let you ask the question "who liked this thing?" You may also want to store the liked object key as a normal indexed field in the Like
. I would strongly recommend it even if you don't immediately plan to use it; you will eventually want to run this query even if just for debugging. Indexes don't cost quite as much as everyone seems to fear.
Upvotes: 1
Reputation: 2536
I would go with adding another index to the Like
entity as that is the simplest working solution, especially considering datastore's newish pricing which also makes it a bit cheaper:
...writing a single entity only costs 1 write regardless of indexes and will now cost $0.18 per 100,000. This means writes are more affordable for people using multiple indexes. You can use as many indexes as your application needs without increases in write costs.
Note that indexes still take storage and likely affect performance but you can be a bit more liberal when using them...
Upvotes: 2