RavenDB中具有空间索引的多地图索引失败

时间:2012-07-12 16:09:39

标签: mapreduce ravendb

我想搜索属于用户的内容(靠近我)。我定义了以下索引,但我没有得到合并的结果集:

public class Things_ByLocation : AbstractMultiMapIndexCreationTask<Things_ByLocation.ThingsByLocationResult>
{
    public class ThingsByLocationResult
    {
        public string ThingId { get; set; }
        public string UserId { get; set; }
        public double Latitude { get; set; }
        public double Longitude { get; set; }
    }

    public Things_ByLocation()
    {
        AddMap<Thing>(things => from t in things
                                select new
                                {
                                    ThingId = t.Id,
                                    UserId = (string)null,
                                    Latitude = 0,
                                    Longitude = 0,
                                    _ = (object)null,
                                });
        AddMap<User>(users => from u in users
                                select new
                                {
                                    ThingId = (string)null,
                                    UserId = u.Id,
                                    Latitude = u.Latitude,
                                    Longitude = u.Longitude,
                                    _ = SpatialIndex.Generate(u.Latitude, u.Longitude)
                                });

        Reduce = results => from result in results
                            group result by result.ThingId into g
                            let userId = g.Select(x => x.UserId).Where(t => !string.IsNullOrWhiteSpace(t)).FirstOrDefault()
                            let lat = g.Select(x => x.Latitude).Where(t => t != 0).FirstOrDefault()
                            let lng = g.Select(x => x.Longitude).Where(t => t != 0).FirstOrDefault()
                            select new
                            {
                                ThingId = g.Key,
                                UserId = userId,
                                Latitude = lat,
                                Longitude = lng,
                                _ = SpatialIndex.Generate(lat, lng)
                            };

        Store(x => x.ThingId, FieldStorage.Yes);
        Store(x => x.UserId, FieldStorage.Yes);
    }
}

结果如下所示:

{
  "ThingId": "Thing/Id26",
  "UserId": null,
  "Longitude": "0",
  "__spatialShape": "0.000000 0.000000"
}

我的模特:

public class User
{
    public string Id { get; set; }
    public double Latitude { get; set; }
    public double Longitude { get; set; }
}

public class Thing
{
    public string Id { get; set; }
    public double Name { get; set; }
    public double Description { get; set; }
    public string UserId { get; set; }
}

关于我做错了什么的任何想法?如果我将组切换到用户,则会填充用户部分并且ThingId为空。因此,似乎合并过程失败了。我只是不确定原因。

为什么结果显示经度而不是纬度属性也很奇怪。

在RAM中使用RavenDB Build 960。

我意识到我可以将位置反规范化为Thing,但这意味着如果用户位置发生了变化,我将不得不更新数百件事。这是首选的NoSql方法吗?

更新

根据Ayende的建议,我现在有以下内容:

public class Things_ByLocation : AbstractMultiMapIndexCreationTask<Things_ByLocation.ThingsByLocationResult>
{
    public class ThingsByLocationResult
    {
        public string ThingId { get; set; }
        public string UserId { get; set; }
        public double Latitude { get; set; }
        public double Longitude { get; set; }
    }

    public Things_ByLocation()
    {
        AddMap<Thing>(things => from t in things
                                select new
                                {
                                    ThingId = t.Id,
                                    UserId = (string)null,
                                    Latitude = 0,
                                    Longitude = 0
                                });
        AddMap<User>(users => from u in users
                                select new
                                {
                                    ThingId = (string)null,
                                    UserId = u.Id,
                                    Latitude = u.Latitude,
                                    Longitude = u.Longitude
                                });

        Reduce = results => from result in results
                            group result by result.ThingId into g
                            select new
                            {
                                ThingId = g.Key,
                                UserId = g.Select(x => x.UserId).Where(x => x != null).FirstOrDefault(),
                                Latitude = g.Select(x => x.Latitude).Where(t => t != 0).FirstOrDefault(),
                                Longitude = g.Select(x => x.Longitude).Where(t => t != 0).FirstOrDefault()
                            };

        Store(x => x.ThingId, FieldStorage.Yes);
        Store(x => x.UserId, FieldStorage.Yes);
    }
}

索引本身:

Map:
docs.Things
    .Select(t => new {ThingId = t.__document_id, UserId = (String)null, Latitude = 0, Longitude = 0})

Map:
docs.Users
    .Select(u => new {ThingId = (String)null, UserId = u.__document_id, Latitude = ((double)u.Latitude), Longitude = ((double)u.Longitude)})

Reduce:
results
    .GroupBy(result => result.ThingId)
    .Select(g => new {ThingId = g.Key, UserId = g
    .Select(x => x.UserId)
    .Where(x => x != null).FirstOrDefault(), Latitude = g
    .Select(x => ((double)x.Latitude))
    .Where(t => t != 0).FirstOrDefault(), Longitude = g
    .Select(x => ((double)x.Longitude))
    .Where(t => t != 0).FirstOrDefault()})

结果投影如下所示:

{
  "ThingId": "Thing/Id26",
  "UserId": null,
  "Latitude": null,
  "Longitude": null
}

我似乎在学术上做错了。

2 个答案:

答案 0 :(得分:0)

尝试:

  • 从地图中删除SpatialIndex.Generate,它应该只在reduce上。
  • 从reduce中删除SpatialIndex.Generate,只看到为什么你没有Latitude&amp;你应该有经度。

答案 1 :(得分:0)

通过反复试验(和Ayende的建议),我得到了以下结论。它有效,但我不明白为什么!

我怀疑我最好更改数据模型并将地理位置数据反规范化为Thing。

public class Things_ByLocation : AbstractMultiMapIndexCreationTask<Things_ByLocation.ThingsByLocationResult>
{
    public class ThingsByLocationResult
    {
        public string Id { get; set; }
        public string UserId { get; set; }
        public double Latitude { get; set; }
        public double Longitude { get; set; }
        public string SKU { get; set; }
        public string Name { get; set; }
    }

    public Things_ByLocation()
    {
        AddMap<Thing>(things => from t in things
                                select new
                                {
                                    Id = t.Id,
                                    UserId = t.UserId,
                                    Latitude = 0,
                                    Longitude = 0,
                                    Name = t.Name,
                                    SKU = t.SKU,
                                    _ = (object)null,
                                });
        AddMap<User>(users => from u in users
                                select new
                                {
                                    Id = (string)null,
                                    UserId = u.Id,
                                    Latitude = u.Latitude,
                                    Longitude = u.Longitude,
                                    Name = (string)null,
                                    SKU = (string)null,
                                    _ = (object)null, 
                                });

        Reduce = results => from result in results
                            group result by result.Id into g
                            let lat = g.Select(x => x.Latitude).Where(x => x != 0).FirstOrDefault()
                            let lng = g.Select(x => x.Longitude).Where(x => x != 0).FirstOrDefault()
                            let userId = g.Select(x => x.UserId).Where(x => x != null).FirstOrDefault()
                            let name = g.Select(x => x.Name).Where(x => x != null).FirstOrDefault()
                            let sku = g.Select(x => x.SKU).Where(x => x != null).FirstOrDefault()
                            select new
                            {
                                Id = g.Key,
                                UserId = userId,
                                Latitude = lat,
                                Longitude = lng,
                                Name = name,
                                SKU = sku,
                                _ = SpatialIndex.Generate(lat, lng)
                            };

        Store(x => x.Id, FieldStorage.Yes);
        Store(x => x.UserId, FieldStorage.Yes);

        TransformResults = (database, results) => from result in results
                                                    let user = database.Load<User>(result.UserId)
                                                    select new
                                                    {
                                                        Id = result.Id,
                                                        UserId = result.UserId,
                                                        Latitude = user.Latitude,
                                                        Longitude = user.Longitude,
                                                        Name = result.Name,
                                                        SKU = result.SKU,
                                                    };
    }
}