2015-01-08 75 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()); 
      } 
     } 
    } 
}