首页 文章

RavenDB音乐数据库

提问于
浏览
1

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

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

现在使用RavenDB,我可以将每首歌曲存储为包含所有信息的文档,但是我会为每首歌曲增加ArtistNAme,AlbumName甚至文件夹 .

弄清楚我可以分离艺术家,流派等,并在我的查询中使用包含,但我如何运行一个查询,它给了我所有歌曲的流派“摇滚”或特定艺术家的所有专辑?

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

还是有其他方式,我没看到?

3 回答

  • 0

    虽然您可以在索引中“包含”其他文档中的属性(使用LoadDocument),但建议不要广泛使用,因为索引需要更频繁地重建 .

    在您的情况下,您可以对您的Song文档进行建模,以通过id和查询对Artist,Genre等进行引用,然后使用Transformer将结果转换为所需的“视图模型” . 在变换器中使用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 缩小索引,按艺术家或流派或任何您感兴趣的内容对歌曲进行分组 . Map 缩减的结果可以是您想要的任何内容,只是歌曲ID的列表或者您可以包含所有歌曲数据的列表 . 然后按照您要分组的内容查询索引 .

    因为艺术家/专辑/流派与歌曲紧密相连 - 您可以通过让您的歌曲定义图书馆中的艺术家和专辑来获益,而不是为他们提供单独的文档 . 这样可以更轻松地添加/编辑/删除歌曲 - 如果您使用新艺术家添加歌曲 - 突然间您会有一位新艺术家!如果删除给定专辑的所有歌曲 - 突然相册就不见了!

    如果你想实现播放列表(应该有自己的文档)之类的东西 - 播放列表文档可能只有一个歌曲ID列表,当你加载播放列表时,你可以轻松地为所有歌曲做一个包含 .

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

  • 0

    关于文件的好读物商店与关系数据库可以在this博客文章中找到 . 另外,它展示了一个如何在文档存储中存储Movie数据库的方式(我觉得它在文档关系方面非常类似于音乐商店) .

    在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());
                }
            }
        }
    }
    

相关问题