我想我对我的音乐应用程序的模式设计有一个问题。
我有3个集合:Artists
, Tracks
和Albums
。3类:artists
、albums
、tracks
document from artists
:
[_id] => MongoId Object
(
[$id] => 4ee5bbfd615c219a07000000
)
[freeze] => false,
[genres] => Array,
[hits] => 0,
[name] => Sarya Al Sawas,
[pictures] => Array,
document from albums
:
[_id] => MongoId Object
(
[$id] => 4ee88308615c218128000000
)
[name] => Sabia
[slug] => wafiq-habib-ft-sarya-al-sawas-sabia
[year] => 1999
[genres] => Array,
[pictures] => Array,
[artists] => Array
(
[0] => MongoId Object
(
[$id] => 4ee34a3b615c21b624010000
)
[1] => MongoId Object
(
[$id] => 4ee5bbfd615c219a07000000
)
)
document from tracks
[_id] => MongoId Object
(
[$id] => 4ee8a056615c21542a000000
)
[name] => Bid Ashok
[slug] => wafiq-habib-ft-sarya-al-sawas-bid-ashok
[genres] => Array,
[file] => /m/tracks/t.4ee8a05540c624.04707814.mp3,
[freeze] => false,
[hits] => 0,
[duration] => 303,
[albums] => Array
(
[0] => MongoId Object
(
[$id] => 4ee5cbc3615c216509000000
)
)
[artists] => Array
(
[0] => MongoId Object
(
[$id] => 4ee5bbfd615c219a07000000
)
[1] => MongoId Object
(
[$id] => 4ee34a3b615c21b624010000
)
)
首先,这是好的模式设计吗??!我这样设计这个图式是因为多对多关系有时歌曲有两个艺术家,专辑有两个艺术家。
无论如何,我在查询附加到特定曲目的专辑时遇到了问题。
假设我在艺术家页面
我需要得到所有的艺术家专辑和曲目,所以我这样做:
$cursors = array( 'albums' => $this->albums->find(array('artists' => $artist->_id))->sort(array('_id' => -1)), 'tracks' => $this->tracks->find(array('artists' => $artist->_id))->sort(array('_id' => -1)), 'clips' => $this->clips->find(array('artists' => $artist->_id))->sort(array('_id' => -1)) ); foreach($cursors as $key => $cursor) { foreach($cursor as $obj) { $obj['name'] = ($this->lang->get() != 'ar' ? $obj['translated']['name'] : $obj['name']); $obj['by'] = $this->artists()->get($obj['artists'])->toString('ft'); ${$key}[] = $obj; } }
我需要循环所有曲目并获得他们的专辑名称,让我们说这个艺术家有3000首曲目我觉得会很慢....
所以我的问题是:这是一个好的Schema设计吗
嗯,这是一个非常相关的问题,使用非关系数据库来解决这样的问题需要一些努力。总的来说,我认为你的模式设计是好的。
你所描述的被称为"N+1问题",因为你必须对N个对象进行N+1次查询(在你的情况下,它更复杂,但我想你明白了)。
一些补救措施:
-
您可以使用
$in
操作符来查找例如某个艺术家的所有曲目:db.tracks.find({"artists" : { $in : [artist_id_1, artist_id_2, ...] } });
如果美工人数太多,这就行不通了,但是几百个,也许1000个就可以了。确保
artists
被索引 -
您可以对一些经常需要的信息进行反规范化。例如,您可能希望经常显示曲目列表,因此将艺术家的名字复制到每个曲目中是有意义的。反规格化主要取决于您想从最终用户的角度实现什么。你可能不希望存储每个美工的完整姓名,但只存储前50个字符,因为UI不会在概述中显示更多内容。
事实上,您已经对一些数据进行了反规范化,例如专辑中的艺术家id(这是冗余的,因为您也可以通过曲目获得它们)。这使得查询更容易,但写量更大。更新是丑陋的,因为你必须确保它们在整个系统中传播。
-
在某些情况下,在客户端(!)而不是服务器上"连接"可能更有意义。这并不能很好地解决您的问题,但值得注意的是:假设您有一个朋友列表。现在服务器必须在显示每个朋友的名字时查找他们。相反,它可以为您提供一个查找表id/friends,服务器只提供这些id。一些JavaScript可以用客户端缓存中的真实名称替换id。