asp.net-mvc - 在 asp.net 和 ravendb 中处理基于事件的提要的最佳方法

标签 asp.net-mvc database ravendb

我将为每个用户提供一个事件提要,显示与用户订阅的事件相关的所有事件,并且该提要将提取最近的 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 可供使用。如果有很多事件,单个集合可能会增长到数百万个,并且当有成千上万的用户使用无穷无尽的订阅列表组合时,我可能会在缓存中存储数千个 key 。这些提要位于用户登录页面上,因此它受到了很多打击,我不想仅仅投入更多硬件来解决这个问题。

所以我真正想要的答案是,这是否是最好的查询,或者当我可以使用 list.Contains 查询数百万个文档时,是否有更好的方法在 Raven 中执行此操作。

这是一个使用 ravendb 的 asp.net 4.5 mvc 4 项目。

最佳答案

下面是我将如何处理它。这是基于 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);

关于asp.net-mvc - 在 asp.net 和 ravendb 中处理基于事件的提要的最佳方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13594816/

相关文章:

c# - 从 C# MVC 模型设置 AngularJS $provide.constant 值的最佳方法是什么?

c# - 将多个属性的验证消息组合到一条消息中 asp.net mvc

c# - 如何让一个 <div> 响应另一个 <div>?

c# - MVC Controller 将整个模型发布到远程 api 操作

sql - 检查多列约束

ravendb - 如何使用版本控制包导入 raven db

.net - RavenDb 在没有过滤条件时跳过项目

c# - RavenDb 4.0 - 来自 Raven.Abstractions 的 RavenJValue

android - 删除元素时 Firestore 快照监听器触发

php - 使用 PHP 和 MySQL 从数据库删除或编辑表单数据时出错