2

我正在使用 Nancy 和 RavenDB 构建照片库我的模型类如下:

[JsonObject(IsReference=true)]
public class Album
{
    public Album()
    {
        this.Photos = new List<Photo>();
        this.Tags = new List<String>();
    }

    public String Id {get; set;}
    public String Name {get; set;}
    public String Path {get; set;}
    public DateTime ModifiedDate{get; set;}
    public IList<String> Tags {get; set;}
    public IList<Photo> Photos {get; set;}

}

    public class Photo
{
    public String Id {get; set;}
    public String Name {get; set;}
    public String Path {get; set;}
    public IList<String> Tags {get; set;}
    public Album Album {get; set;}
    public DateTime ModifiedDate{get; set;}
    public bool IsPrivate{get; set;}
}

我尝试在照片->标签上减少地图索引:

   public class TaggedPhotosIndex:AbstractIndexCreationTask<Album, TaggedPhotos>
    {
        public TaggedPhotosIndex()
        {
            Map = albums =>
                from a in albums
                from p in a.Photos
                from t in p.Tags
                select new TaggedPhotos
                {
                    Tag = t,
                    PhotoIds = new List<String> {p.Id}
                };
            Reduce = results => 
                from result in results
                group result by result.Tag into agg
                select new TaggedPhotos
                {
                    Tag = agg.Key,
                    PhotoIds = agg.SelectMany(a => a.PhotoIds).ToList()
                };
        }
    }

    public class TaggedPhotos
    {
        public String Tag {get; set;}
        public IList<String> PhotoIds {get; set;}
    }

这是我想要实现的目标:

给定一组标签,我想获取所有具有至少一个匹配标签的 Photo 对象。

RavenDB 不允许在查询中使用 SelectMany 并且我没有想法。

找到了一个解决方案(使用客户端 api 在 ravendb 中使用 LuceneQuery @ Workaround for selectmany),但期待任何其他替代方案。

4

2 回答 2

4

您的文档结构存在一些问题,这将使您的查询变得困难。

  • 查询旨在返回整个文档。您要求从文档中获取部分结果。可以这样做,但前提是您将所有字段存储在索引中并从那里投影结果。您放弃了文档存储的 ACID 保证,这是 RavenDB 最强大的功能之一。

  • 您有一个通常会嵌入的来自Photo后面的引用,从而导致循环引用,除非您设置为避免这种情况。这可能适用于单个文档中的基本序列化,但在从预计的索引值引用整个文档时,它是没有意义的。您已经设置了自己的“鸡和蛋”问题。Album[JsonObject(IsReference=true)]

  • 您仍然缺少人们期望从这个问题域中获得的基本功能。具体来说,您应该能够加载单张照片而无需加载整个相册。

强烈建议你把照片放在自己的文件中。然后你可以有更简单的索引并避免循环引用。在DDD术语中,两者PhotoAlbum都是聚合。还有一个 DDD 聚合 == 一个 RavenDB 文档。您当前正在将照片建模为不是聚合的实体,但随后您要求进行仅对聚合执行的操作 - 例如搜索。

如果您只问“给我所有包含带有这些标签之一的照片的相册”,那么您就可以了。但你不是要专辑,你要的是照片

假设您确实以这种方式建模,布雷特的答案很接近 - 但有一些无关紧要的东西。这是一个显示完整实现的单元测试。在 gist 上发布,因为它没有直接解决原始问题。 https://gist.github.com/4499724

于 2013-01-10T05:42:20.397 回答
1

这花了我一段时间,但我验证了下面的解决方案是有效的。

首先,修改您的索引和索引结果,如下所示。

 public class TaggedPhotosIndex:AbstractIndexCreationTask<Photo, TaggedPhotos>
    {
        public TaggedPhotosIndex()
        {
            Map = photos =>                
                from p in Photos
                from t in p.Tags
                select new TaggedPhotos
                {
                    Tag = t,
                    PhotoId = p.Id
                };
        }
    }

    public class TaggedPhotos
    {
        public string Tag {get; set;}
        public string PhotoId {get; set;}
    }

现在创建了索引,下面是您查询它以获取照片的方法。

var tagsToSearch = new List<string>(){"test1", "test2", "test3"};

var photoIds = documentSession.Query<TaggedPhotos, TaggedPhotoIndex>()
                               .Customize(x => x.Include<Photo>(p => p.Id))
                               .Where(x => x.Tag.In(tagsToSearch))
                               .Select(x => x.PhotoId)
                               .Distinct()
                               .ToArray();

// This doesn't actually make a second call as the photos are already loaded
// in the document session
var photos = documentSession.Load<Photo>(photoIds);

根据要求

这是我想要实现的目标:

给定一组标签,我想获取至少有一个匹配标签的所有照片对象。

我没有看到专辑是如何与这一切联系在一起的

于 2013-01-09T22:51:24.883 回答