4

我将为每个用户提供一个活动提要,其中显示与用户订阅的事件相关的所有活动,并且提要将引入最新的 20 个左右的活动。我设置它的方式是所有活动,无论其相关的事件是否存储在一个集合中,并且文档本身具有我查询和索引的“事件”属性。基本查询只是从集合中选择活动,其中事件在按日期排序的用户事件订阅列表中。我存储用户事件订阅列表的哈希,并使用哈希作为键缓存查询结果 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 回答