1) Так ли это так? -> Вид Размер списка свойств для нескольких значений ограничен примерно 20K при индексировании (это ваш случай, так как вы будете запускать запросы по идентификаторам подписчиков) What is maximum size/limitation of ListProperty for Google App Engine datastore? Чтобы суммировать его, ограничения, с которыми вы столкнетесь в таком случае использования, - это : - проиндексирован размер многозначным-свойство (20K) - Entity размер (1 МБ), - которые должны быть в порядке, если вы не храните сгустки там
2) разбивка нужно будет обрабатываться вручную, так как я не Не знаю какой-либо постоянной структуры, которая это делает. Objectify - единственная инфраструктура, которая достаточно прочно связана с хранилищем данных GAE, чтобы иметь такую функцию, но я не использую ее, хотя это IDK.
3) Вам необходимо четко понять ограничения, которые побуждают вас моделировать ваш прецедент в хранилище данных GAE. Мне кажется, что вы все еще сильно подвержены влиянию моделирования реляционных баз данных:
Поскольку вы планируете миллионы пользователей, вы создаете приложение для масштабирования и производительности. Эти «соединения» - именно то, чего вам следует избегать, поэтому вы не используете РСУБД в первую очередь. Дело в том, что: DUPLICATE! денормализовать, чтобы ваши данные соответствовали вашим прецедентам.
public class UserEntity {
@Id Key id;
String name;
/** INDEXED : to retrieve a user by display name */
String displayName;
/** For the sake of the example below */
int tweetCount;
/**
* USE CASE : See a user's followers from his "profile" page.
*
* Easily get subscribers data from your user entity.
* Duplicate UserEntity (this object) 's data in the UserSubscriberEntity.
* You just need to run an ancestor query on UserSubscriberEntity using the User id.
*/
List<UserSubscriberChildEntity> subscribers;
}
/** Duplicate user data in this entity, retrieved easily with an ancestor query */
public class UserSubscriberChildEntity {
/** The id of this entity */
@Id Key subscriberId;
/** Duplicate your User Entity data */
String name;
String displayName;
/** The id from the UserEntity referenced */
String userId;
}
public class TweetEntity {
@Id Key id;
/**
* The actual text message
*/
String tweetContent;
/**
* USE CASE : display the tweet maker name alongside the tweet content.
*
* Duplicate user data to prevent an expensive join when not needed.
* You will always need to display this along with the tweet content !
* Model your entity based on what you want to see when you display them
*/
String tweetMakerName;
String tweetMakerDisplayName;
/**
* USE CASE
* 1) to retrieve tweets MADE by a given user
* 2) In case you actually need to access the User entity
* (for example, if you remove this tweet and want to decrease the user tweet counter)
*
* INDEXED
*/
Key tweetMakerId;
/**
* USE CASE : display tweet subscribers from the "tweet page"
*
* Same as "UserSubscriberChildEntity", retrieve data fast by duplicating
*/
List<TweetSubscriberChildEntity> subscribers;
}
Теперь основные вопросы: Как вы получить «Все твиты один пользователь подписался на»?
шардинг подписок accross лиц:
/**
* USE CASE : Retrieve tweets one user subscribed to
*
* Same goes for User subscription
*/
public class TweetSubscriptionShardedEntity {
/** unused */
@Id Key shardKey;
/** INDEXED : Tweet reference */
Key tweetId;
/** INDEXED : Users reference */
List<Key> userKeys;
/** INDEXED : subscriber count, to retrieve shards that are actually under the limitation of 20K */
int subscribersCount = 0;
/**
* Add a subscriber and increment the subscriberCount
*/
public void addSubscriber(Key userId) {
userKeys.add(userId);
subscribersCount++;
}
}
Пример службы чирикать, что провода все вместе:
/**
* Pseudo code
*/
public class TweetService {
public List<TweetEntity> getTweetsSubscribed(Key userId) {
List<TweetEntity> tweetsFollowed = new ArrayList<TweetEntity>;
// Get all the subscriptions from a user
List<TweetSubscriberShardedEntity> shards = datastoreService.find("from TweetSubscriberShardedEntity where userKeys contains (userId)");
// Iterate over each subscription to retrieve the complete Tweet
for (TweetSubscriberShardedEntity shard : shards) {
TweetEntity tweet = datastoreService.get(TweetEntity.class, shard.getTweetId);
tweetsFollowed.add(tweet);
}
return tweetsFollowed;
}
public void subscribeToTweet(Key subscriberId, Key tweetId) {
TweetSubscriberShardedEntity shardToUse = null;
// Only get the first shard with under 20000 subscribers
TweetSubscriberShardedEntity shardNotFull = datastoreService.find("
FROM TweetSubscriberShardedEntity
WHERE tweetId == tweetId
AND userKeys contains (subscriberId)
AND subscribersCount < 20000
LIMIT 1");
if (shardNotFull == null) {
// If no shard exist create one
shardToUse = new TweetSubscriberShardedEntity();
}
else {
shardToUse = shardNotFull;
}
// Link user and tweet
shardToUse.setTweet(tweetId);
shardToUse.getUserKeys().add(subscriberId);
// Save shard
datastoreService.put(shardToUse);
}
/**
* Hard to put in a transaction with so many entities updated !
* See cross entity group docs for more info.
*/
public void createTweet(UserEntity creator, TweetEntity newTweet) {
creator.tweetCount++;
newTweet.tweetMakerName = creator.name;
newTweet.tweetMakerDisplayName = creator.displayName;
newTweet.tweetMakerId = creator.id;
// Duplicate User subscribers to Tweet
for(UserSubscriberChildEntity userSubscriber : creator.subcribers) {
// Create a Tweet child entity
TweetSubscriberChildEntity tweetSubscriber = new TweetSubscriberChildEntity();
tweetSubscriber.name = userSubscriber.name;
// ... (duplicate all data)
newTweet.add(tweetSubscriber);
// Create a shard with the previous method !!
subscribeToTweet(newTweet.id, subscriber.id);
}
// Update the user (tweet count)
datastoreService.put(creator);
// Create the new tweet and child entities (duplicated subscribers data)
datastoreService.put(newTweet);
}
}
Прежде всего, где вы получите ограничения по размеру собственности мультиплексного стоимости?Тогда, чтобы убедиться, что я правильно использую ваш случай: могут ли люди подписаться на Твит с кем-то, с кем они не подписались? Я имею в виду, может ли абонент находиться в 'TweetIndex', а не' User'? –
Хорошо, я получаю его, размер свойства multi value ограничен, когда они индексируются (выглядит скорее как 20K): http://stackoverflow.com/questions/20200307/what-is-maximum-size-limitation-of-listproperty- for-google-app-engine-datastore –