2015-01-08 76 views
1

我需要你的建议,RavenDB是否适合建立音乐数据库。 我想在C#Windows应用程序中使用嵌入式版本。RavenDB音乐数据库

当前该数据库基于SQL,并且具有正常化,并且具有用于例如艺术家,专辑,流派,分享(音乐收藏的主要文件夹),文件夹,歌曲,然后一堆表格来建立像AlbumArtist,GenreSong,ArtistSong,ComposerSong,ConductorSOng等关系。我想你会得到它。

现在用RavenDB,我可以将每首歌曲存储为包含所有信息的文档,但是之后我会为ArtistNAme,AlbumName和每个歌曲的文件夹进行繁殖。

想象一下,我可以分开艺术家,流派等,并在我的查询中使用包含,但是如何运行查询,然后查询给出所有具有“岩石类型”或某个特定艺术家的所有专辑的歌曲?

我的理解是,我需要一个索引,以便能够使用包含文档中的属性作为查询的一部分。否则,我会得到编译错误。对? 所以基本上我需要构建一个包含用户可能执行查询的所有字段的大型索引。

或者有没有其他方法,我没有看到?

回答

0

尽管您可以从索引中的其他文档“使用LoadDocument”“包含”属性,但由于需要更频繁地重建索引,因此不建议广泛使用它。

在你的情况下,你可以建立你的歌曲文档的模型,以包含id和查询引用艺术家,流派等,然后使用变换器将结果转换为所需的“视图模型”。在变换器中使用LoadDocument来获取艺术家姓名,流派名称等并返回转换后的结果。转换是根据请求在服务器端执行的。

你的歌声实体(简体)可能是这样的:

public class Song 
{ 
    public string Id { get; set; } 
    public string Name { get; set; } 
    public string ArtistId { get; set; } 
} 

而且像这样的指标:一个变压器相结合

public class Song_ByArtist : AbstractIndexCreationTask<Song> 
{ 
    public Song_ByArtist() 
    { 
     Map = songs => from song in songs 
         select new 
         { 
          song.Name, 
          song.ArtistId 
         }; 
    } 
} 

public class Song_Artist_Transformer : AbstractTransformerCreationTask<Song> 
{ 
    public Song_Artist_Transformer() 
    { 
     TransformResults = results => from song in results 
             let artist = LoadDocument<Artist>(song.ArtistId) 
             select new SongArtistViewModel 
             { 
              SongName = song.Name, 
              ArtistName = artist.Name 
             }; 
    } 
} 

您可以查询为艺术家提供歌曲并返回包含艺术家姓名的视图模型:

using (var session = _documentStore.OpenSession()) 
{ 
    var results = session.Query<Song, Song_ByArtist>() 
     .Where(x => x.ArtistId == "artists/1") 
     .TransformWith<Song_Artist_Transformer, SongArtistViewModel>(); 
} 

这将返回所有转换为歌曲名称和艺术家姓名的视图模型的艺术家“artists/1”的歌曲。

因此,底线是:模拟您的歌曲文档,以便在需要时包含对其他文档的引用(如果遵循DDD,则为聚合),然后包含使用变形金刚所需的信息。变形金刚可以看作有点像关系数据库中的“视图”。

注意:为您的歌曲文档制作一个组合索引,将索引所有属性(歌曲属性和引用),然后使用多个变换器根据需要呈现数据。对于相同的文档类型,每个文档使用一个“大”索引而不是几个小索引通常会更好。在这个例子中,我只映射了名称和艺术家ID以保持简单。

希望这会有所帮助!

0

数据手册很便宜。

我会建议复制数据,只要其相对简单,如艺术家姓名,专辑名称和文件夹名称。特别是如果你不认为他们会改变。但是,如果他们改变,你必须在每首歌曲上更新它们。

如果你开始为艺术家名字等简单的东西做包含,那么当你没有必要的时候,你会增加一些荒谬的复杂性。

对于艺术家/专辑/流派/等,您可以创建按艺术家或流派或任何您感兴趣的歌曲进行分组的map-reduce索引。map-reduce的结果可以是任何您想要的,只需一个歌曲ID列表或者您可以包含所有歌曲数据的列表。然后通过分组来查询索引。由于艺术家/专辑/流派与歌曲紧密结合 - 您可以从让歌曲定义库中的艺术家和专辑,而不是为他们分开单独文档中获益。这使得添加/编辑/删除歌曲变得更加容易 - 如果您添加一首歌曲与新的艺术家 - 突然你有一个新的艺术家!如果您删除了某张专辑的所有歌曲 - 突然专辑就没了!

如果您想要实现类似播放列表(应该有自己的文档) - 播放列表文档可能只包含一个歌曲ID列表,并且当您加载播放列表时,您可以轻松地为所有歌曲做一个包含。

对于更复杂的场景 - 如果您想显示用户播放列表的列表以及有关歌曲的一些总体数据(例如,此播放列表中有哪些类型的歌曲?),您可以构建一个索引来加载所有歌曲每个播放列表的相关歌曲并从歌曲中分出一个流派列表。然后只是查询索引。

0

有关文档商店vs关系数据库的良好阅读可以在this博客文章中找到。另外,它展示了如何将电影数据库存储在文档存储中(我认为它与文档关系方面的音乐存储非常相似)。

在RavenDB中,您可以创建Map/Reduce索引来帮助您合并来自不同文档的信息,并且通常比在索引时间(即使用LoadDocument)加载文档更便宜(如@Jaynard所述)。

public class Song 
{ 
    public string Id { get; set; } 
    public string Name { get; set; } 
    public string ArtistId { get; set; } 
} 

public class Artist 
{ 
    public string Id {get;set;} 
    public string Name {get;set;} 
} 

public class SongsByArtist : AbstractMultiMapIndexCreationTask<SongsByArtist.ArtistSongs> 
{ 
    public class ArtistSongs 
    { 
     public string Id { get; set; } 
     public string Name { get; set; } 
     public IEnumerable<object> Songs { get; set; } 
    } 

    public SongsByArtist() 
    { 
     AddMap<Artist>(artists => from artist in artists 
            select new ArtistSongs 
            { 
             Id = artist.Id, 
             Name = artist.Name, 
             Songs = new List<object>() 
            }); 

     AddMap<Song>(songs => from song in songs 
           select new ArtistSongs 
           { 
            Id = song.ArtistId, 
            Name = null, 
            Songs = new List<object> { new { song.Id, song.Name } } 
           }); 

     Reduce = results => from result in results 
          group result by result.Id 
           into g 
           select new ArtistSongs 
           { 
            Id = g.Key, 
            Name = g.First(x => x.Name != null).Name, 
            Songs = g.SelectMany(x => x.Songs) 
           }; 
    } 
} 

和测试来证明这一点:

public class CanGetArtistSongs : RavenTestBase 
{ 
    [Fact] 
    public void WillSupportLast() 
    { 
     using (var store = NewDocumentStore()) 
     { 
      using (var session = store.OpenSession()) 
      { 
       session.Store(new Artist { Id = "artists/1", Name = "Pink Floyd" }); 
       session.Store(new Song { Name = "Shine On You Crazy Diamond Part I", ArtistId = "artists/1"}); 
       session.Store(new Artist { Id = "artists/2", Name = "Metallica" }); 
       session.Store(new Song { Name = "Whiplash", ArtistId = "artists/2"}); 
       session.Store(new Song { Name = "One", ArtistId = "artists/2"}); 
       session.SaveChanges(); 
      } 

      new SongsByArtist().Execute(store); 

      using (var session = store.OpenSession()) 
      { 
       var results = session.Query<SongsByArtist.ArtistSongs, SongsByArtist>() 
            .Customize(customization => customization.WaitForNonStaleResults()) 
            .Where(x => x.Name == "Metallica") 
            .ToList(); 

       Assert.Empty(store.DatabaseCommands.GetStatistics().Errors); 
       Assert.Equal(2, results.First().Songs.Count()); 
      } 
     } 
    } 
}