【问题标题】:Raven DB: What's wrong with this multi-map/reduce index?Ravendb:这个多 mapreduce 索引有什么问题?
【发布时间】:2012-05-16 01:10:27
【问题描述】:

我有一个应用程序来跟踪网站的页面访问。 这是我的模型:

public class VisitSession {
    public string SessionId { get; set; }
    public DateTime StartTime { get; set; }
    public string UniqueVisitorId { get; set; }
    public IList<PageVisit> PageVisits { get; set; }
}

当访问者访问网站时,访问会话开始。一次访问会话有许多页面访问。当访问者第一次访问网站时,跟踪器将写入一个 UniqueVisitorId (GUID) cookie。这样我们就可以知道访客是否是回访者。

现在我想编写一个视图来显示每天的 TotalVisitSessions、TotalPageVisits、TotalUniqueVisitors。所以我写了这个多图reduce:

public class VisitSummaryByDateIndex : AbstractMultiMapIndexCreationTask<VisitSummaryByDate>
{
    public VisitSummaryByDateIndex()
    {
        AddMap<VisitSession>(sessions => from s in sessions
                                            select new VisitSummaryByDate
                                            {
                                                Date = s.StartTime.Date,
                                                TotalVisitSessions = 1,
                                                TotalPageVisits = 0,
                                                TotalNewVisitors = s.IsNewVisit ? 1 : 0,
                                                TotalUniqueVisitors = 0,
                                                UniqueVisitorId = s.UniqueVisitorId
                                            });

        AddMap<PageVisit>(visits => from v in visits
                                    select new VisitSummaryByDate
                                    {
                                        Date = v.VisitTime.Date,
                                        TotalVisitSessions = 0,
                                        TotalPageVisits = 1,
                                        TotalNewVisitors = 0,
                                        TotalUniqueVisitors = 0,
                                        UniqueVisitorId = String.Empty
                                    });

        Reduce = results => from result in results
                            group result by result.Date into g
                            select new VisitSummaryByDate
                            {
                                Date = g.Key,
                                TotalVisitSessions = g.Sum(it => it.TotalVisitSessions),
                                TotalPageVisits = g.Sum(it => it.TotalPageVisits),
                                TotalNewVisitors = g.Sum(it => it.TotalNewVisitors),
                                TotalUniqueVisitors = g.Select(it => it.UniqueVisitorId).Where(it => it.Length > 0).Distinct().Count(),
                                UniqueVisitorId = String.Empty
                            };
    }
}

问题是在“TotalUniqueVisitors”计算中,有时索引结果的TotalUniqueVisitors是1,有时是2。但是我查了数据,绝对不会这么少。我的 Map/Reduce 语法有问题吗?

相关帖子: Raven DB: How to create "UniqueVisitorCount by date" index

可以在此处找到带有示例数据的代码: https://gist.github.com/2702071

【问题讨论】:

    标签: c# mapreduce ravendb


    【解决方案1】:

    Reduce 实际上对结果进行了多次处理。 您的索引假定这只发生一次,并且可以访问整个结果集。

    您的索引需要如下所示:

    public class VisitSummaryByDateIndex : AbstractMultiMapIndexCreationTask<VisitSummaryByDate>
    {
        public VisitSummaryByDateIndex()
        {
            AddMap<VisitSession>(sessions => from s in sessions
                                             select new VisitSummaryByDate
                                             {
                                                 Date = s.StartTime.Date,
                                                 TotalVisitSessions = 1,
                                                 TotalPageVisits = 0,
                                                 TotalNewVisitors = s.IsNewVisit ? 1 : 0,
                                                 TotalUniqueVisitors = 1,
                                                 UniqueVisitorId = new[]{s.UniqueVisitorId}
                                             });
    
            AddMap<PageVisit>(visits => from v in visits
                                        select new VisitSummaryByDate
                                        {
                                            Date = v.VisitTime.Date,
                                            TotalVisitSessions = 0,
                                            TotalPageVisits = 1,
                                            TotalNewVisitors = 0,
                                            TotalUniqueVisitors = 0,
                                            UniqueVisitorId = new string[0]
                                        });
    
            Reduce = results => from result in results
                                group result by result.Date into g
                                select new VisitSummaryByDate
                                {
                                    Date = g.Key,
                                    TotalVisitSessions = g.Sum(it => it.TotalVisitSessions),
                                    TotalPageVisits = g.Sum(it => it.TotalPageVisits),
                                    TotalNewVisitors = g.Sum(it => it.TotalNewVisitors),
                                    TotalUniqueVisitors = g.Sum(it => it.TotalUniqueVisitors),,
                                    UniqueVisitorId =  g.Select(x=>x.UniqueVisitorId).Distinct()
                                 };
        }
    }
    

    【讨论】:

    • (不敢相信我在质疑你!)但这不起作用。每个会话不一定是一个新的唯一 ID,因此总和不正确。另外,我假设 UniqueVisitorId 现在应该是IEnumerable&lt;string&gt;,所以它不会编译。但是,查看这个问题基于 (stackoverflow.com/questions/10597359/…) 的帖子,我认为该字段无论如何都不重要,因此我的回答只是将其设置为 FirstOrDefault。
    • 您实际上应该从此处删除TotalUniqueVisitors,并使用UniqueVisitorId.Count 获取实际的唯一身份访问者数量。
    • @Simon 同意你的看法。但我认为将其设置为 FirstOrDefault 是不正确的。因为reduce函数将被处理不止一次(可能会将自身的输出作为输入)。所以如果你使用 FirstOrDefault,你有时会得到 1 TotalUniqueVisits 的结果。我想我根据 Ayende 的回答制定了最终解决方案。但我现在正在考虑表现。因为 reduce 函数中的 SelectMany 使得生成的文档非常大。你们怎么看?这是新的要点:gist.github.com/2702071
    • 谢谢艾扬德。您如何看待性能问题? (见我上面对西蒙的回复)
    • @DylanLin 我无法重现您获得 1 TotalUniqueVisits 的问题 - 您有重现它的示例吗?关于性能问题,我认为通常您希望从索引中返回小文档 - 所以这实际上取决于您认为会有多少唯一 ID。
    【解决方案2】:

    正确的索引是:

    public class VisitSummaryByDateIndex : AbstractMultiMapIndexCreationTask<VisitSummaryByDate>
    {
        public VisitSummaryByDateIndex()
        {
            AddMap<VisitSession>(sessions => from s in sessions
                                             select new VisitSummaryByDate
                                             {
                                                 Date = s.StartTime.Date,
                                                 TotalVisitSessions = 1,
                                                 TotalPageVisits = 0,
                                                 TotalNewVisitors = s.IsNewVisit ? 1 : 0,
                                                 TotalUniqueVisitors = 0,
                                                 UniqueVisitorId = s.UniqueVisitorId
                                             });
    
            AddMap<PageVisit>(visits => from v in visits
                                        select new VisitSummaryByDate
                                        {
                                            Date = v.VisitTime.Date,
                                            TotalVisitSessions = 0,
                                            TotalPageVisits = 1,
                                            TotalNewVisitors = 0,
                                            TotalUniqueVisitors = 0,
                                            UniqueVisitorId = string.Empty,
                                        });
    
            Reduce = results => from result in results
                                group result by result.Date into g
                                select new VisitSummaryByDate
                                {
                                    Date = g.Key,
                                    TotalVisitSessions = g.Sum(it => it.TotalVisitSessions),
                                    TotalPageVisits = g.Sum(it => it.TotalPageVisits),
                                    TotalNewVisitors = g.Sum(it => it.TotalNewVisitors),
                                    TotalUniqueVisitors = g.Select(it => it.UniqueVisitorId).Where(x => x.Length > 0).Distinct().Count(),
                                    UniqueVisitorId = g.FirstOrDefault().UniqueVisitorId,
                                };
        }
    }
    

    不同的是在reduce中设置了UniqueVisitorId。我还不能 100% 确定为什么需要这样做,我必须承认。

    【讨论】:

    • 啊,感谢 Ayende 的回答,我们现在知道为什么需要它了。我不确定使用像 Ayende 这样的数组会给你带来什么好处,但根据我的回答,它也可以使用字符串。
    猜你喜欢
    • 2019-07-29
    • 1970-01-01
    • 1970-01-01
    • 2017-06-01
    • 2020-05-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多