4

ユーザーが購読しているイベントに関連するすべてのアクティビティを表示するアクティビティ フィードをユーザーごとに用意し、フィードは最新の約 20 のアクティビティを取り込みます。私が設定した方法は、関連するイベントが 1 つのコレクションに格納されているかどうかに関係なく、すべてのアクティビティであり、ドキュメント自体には、クエリを実行してインデックスを作成する「イベント」プロパティがあります。基本的なクエリは、イベントが日付順に並べられたユーザー イベント サブスクリプション リストにあるコレクションからアクティビティを選択するだけです。ユーザー イベント サブスクリプションのリストのハッシュを保存し、そのハッシュをキーとして xx 秒間使用してクエリの結果をキャッシュします。別のユーザーがまったく同じイベントにサブスクライブしている場合は、代わりにキャッシュから結果を取得できます。結果が xx 秒古いことには関係ありません。

編集:モデルとクエリの例を追加

モデル:

User
{
    // useless properties excluded
    // fixed: hashset not list, can be quite large
    HashSet<string> Subscriptions { get; set; }
    string SubscriptionHash { get; set; } // precomputed hash of all strings in subscriptions
}

Activity
{
    // Useless properties excluded
    string ActivityType { get; set; }
}

クエリ:

if (cache[user.SubscriptionHash] != null)
    results = (HashSet<Activity>)cache[user.SubscriptionHash];
else
    results = session.Query<Activity>().Where(user.Subscriptions.Contains(e => e.ActivityType)).Take(20).ToList();

// add results to cache

私の懸念は、これがこれを処理するための最良のアプローチであるかどうか、または使用するより良い ravendb voodoo があるかどうかです。多数のアクティビティがある場合、単一のコレクションは数百万に成長する可能性があり、サブスクリプション リストの無限の組み合わせを持つユーザーが何千人もいる場合、キャッシュに何千ものキーを格納する可能性があります。これらのフィードはユーザーのランディング ページにあるため、頻繁にアクセスされるため、問題にハードウェアを投入したくありません。

したがって、私が本当に探している答えは、これが使用するのに最適なクエリであるか、または list.Contains を使用して何百万ものドキュメントに対してクエリを実行できる場合に、Raven でそれを実行するためのより良い方法があるかどうかです。

これは、ravendb を使用した asp.net 4.5 mvc 4 プロジェクトです。

4

1 に答える 1

1

これが私がそれにアプローチする方法です。これは、RaccoonBlog PostComments に基づいています。

各ユーザーのイベントを別のドキュメント (つまり、以下の例では UserEvent) に保存し、ユーザーには、それにリンクする追加のプロパティと、ユーザーに関連付けられた最後のイベントのタイムスタンプを追加します。これにより、ユーザードキュメントははるかに小さくなりますが、多くの重要な情報が含まれます

UserEvent では、ID、このドキュメントが参照するユーザー ID へのリンク、「イベント」コレクション、および lasteventid を保持する単純なドキュメントになります。このようにして、各「イベント」は、必要に応じてメンテナンス用のサブドキュメントになります。

最後に、データを簡単にクエリできるようにする UserEvent のインデックス

public class User
{
    public string Id { get; set; }
    // other user properties

    public string UserEventId { get; set; }
    public int NumberOfEvents { get; set; }
    public DateTimeOffset LastEvent { get; set; }
}

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

    public string UserId { get; set; }

    public int LastEventId { get; set; }

    public ICollection<Event> Events { get; protected set; }

    public int GenerateEventId()
    {
        return ++LastEventId;
    }

    public class Event
    {
        public int Id { get; set; }
        public DateTimeOffset CreatedAt { get; set; }
        public string ActivityType { get; set; }
        // other event properties
    }
}

public class UserEvents_CreationDate : AbstractIndexCreationTask<UserEvent, UserEvents_CreationDate.ReduceResult>
{
    public UserEvents_CreationDate()
    {
        Map = userEvents => from userEvent in userEvents
                            from evt in userEvent.Events
                            select new
                            {
                                evt.CreatedAt,
                                EventId = evt.Id,
                                UserEventId = userEvent.Id,
                                userEvent.UserId,
                                evt.ActivityType
                            };
        Store(x => x.CreatedAt, FieldStorage.Yes);
        Store(x => x.EventId, FieldStorage.Yes);
        Store(x => x.UserEventId, FieldStorage.Yes);
        Store(x => x.UserId, FieldStorage.Yes);
        Store(x => x.ActivityType, FieldStorage.Yes);
    }

    public class ReduceResult
    {
        public DateTimeOffset CreatedAt { get; set; }
        public int EventId { get; set; }
        public string UserEventId { get; set; }
        public string UserId { get; set; }
        public string ActivityType { get; set; }
    }
}

public static class Helpers
{
    public static DateTimeOffset AsMinutes(this DateTimeOffset self)
    {
        return new DateTimeOffset(self.Year, self.Month, self.Day, self.Hour, self.Minute, 0, 0, self.Offset);
    }

    public static IList<Tuple<UserEvents_CreationDate.ReduceResult, User>> QueryForRecentEvents(
        this IDocumentSession documentSession,
        Func
            <IRavenQueryable<UserEvents_CreationDate.ReduceResult>, IQueryable<UserEvents_CreationDate.ReduceResult>
            > processQuery)
    {
        IRavenQueryable<UserEvents_CreationDate.ReduceResult> query = documentSession
            .Query<UserEvents_CreationDate.ReduceResult, UserEvents_CreationDate>()
            .Include(comment => comment.UserEventId)
            .Include(comment => comment.UserId)
            .OrderByDescending(x => x.CreatedAt)
            .Where(x => x.CreatedAt < DateTimeOffset.Now.AsMinutes())
            .AsProjection<UserEvents_CreationDate.ReduceResult>();

        List<UserEvents_CreationDate.ReduceResult> list = processQuery(query).ToList();

        return (from identifier in list
                let user = documentSession.Load<User>(identifier.UserId)
                select Tuple.Create(identifier, user))
            .ToList();
    }
}

次に、クエリを実行するために必要なことは、次のようなものです。

 documentSession.QueryForRecentEvents(q => q.Where(x => x.UserId == user.Id &&  x.ActivityType == "asfd").Take(20)).Select(x => x.Item1);
于 2012-11-29T16:14:38.500 に答える