我对mongoldb很新。
我有一个我自己无法解决的问题。
这是我的模型(简化了这个问题的范围)
account {
type: String
videos: [video],
images: [image]
}
video {
name: String,
length: Number,
Date: Date
}
image {
name: String,
size: Number
Date: Date
}
所以主模型帐户包含两个嵌入文档:视频和图像。
我想(按此顺序):
是否有可能或更好的改变模型?
示例:
来源
[{
type: 2,
images:
[{ name: 'imagetest1', size: 3, Date: 2011-01-01},
{ name: 'imagetest2', size: 13, Date: 2011-02-02}],
videos:
[{ name: 'videotest1', length: 24, Date: 2011-01-07},
{ name: 'videotest2', length: 15, Date: 2011-03-02}] }
{
type: 2,
images:
[{ name: 'imagetest3', size: 3, Date: 2011-01-03},
{ name: 'imagetest4', size: 15, Date: 2011-01-06}],
videos:
[{ name: 'videotest3', length: 24, Date: 2011-02-05},
{ name: 'videotest4', length: 16, Date: 2011-02-04}]
},
{
type: 1,
images:
[{ name: 'imagetest5', size: 3, Date: 2011-01-03},
{ name: 'imagetest6', size: 15, Date: 2011-01-06}],
videos:
[{ name: 'videotest5', length: 24, Date: 2011-02-05},
{ name: 'videotest6', length: 16, Date: 2011-02-04}]
}]
MongoDB查询:
按类型查询帐户:2,按数据升序对图像和视频进行排序,最后将图像和视频合并为一个数组。
输出
[{ name: 'imagetest1', size: 3, Date: 2011-01-01},
{ name: 'videotest1', length: 24, Date: 2011-01-02},
{ name: 'imagetest3', size: 3, Date: 2011-01-03},
{ name: 'videotest4', length: 16, Date: 2011-02-04},
{ name: 'videotest3', length: 24, Date: 2011-02-05},
{ name: 'imagetest4', size: 15, Date: 2011-01-06},
{ name: 'videotest1', length: 24, Date: 2011-01-07},
{ name: 'imagetest2', size: 13, Date: 2011-02-02},
{ name: 'videotest2', length: 15, Date: 2011-03-02}]
答案 0 :(得分:1)
你可以借助MAP-REDUCE来做到这一点。
地图功能:
var map = function()
{
var doc = this.videos;
for( var i =0 ; i < this.images.length; i++ )
{
doc.push({name: this.images[i].name, length : this.images[i].size, Date: this.images[i].Date});
}
emit(this.type, doc );
}
减少功能
var reduce = function(k,v)
{
var arr = v[0];
for( var j = 1 ; j < v.length; j++ )
{
arr = arr.concat(v[j]);
}
return arr;
}
查询:
db.accounts.mapReduce(
map,
reduce,
{
out : {inline: 1} ,
query : {type : 2} ,
sort : {Date : 1}
}
);
在MongoDb 3.2的未来版本中,您可以使用$concatArrays运算符轻松地合并聚合管道中的数组。
db.accounts.aggregate([
{
$match : { type: 2 }
},
{
$project :
{
newArray:
{ $concatArrays: [ {$ifNull: ["$videos", [] ] }, {$ifNull: ["$images", [] ] } ] } ,
type : 1
}
},
{
$sort : {"$newArray.Date" : 1 }
}
]);
答案 1 :(得分:1)
从我的角度来看,您必须将查询与需要的时间点相关联。
例如:您真的立即需要所有这些信息吗?通常,你不会。因此,让我们剖析这些查询。以下是我想出的内容
我之所以将其拆分,只是用户体验。让我们假设一个给定的用户打开一个显示他的图像和视频的网页。通过拆分问题,您可以通过AJAX独立加载它们 - 但用户至少已经拥有了他或她的页面。通过使查询尽可能简单,他们很可能更快地得到回答。所以,让我们看看实际的查询以及如何加快它们的速度。
我会简单地拆分这些模型,因为a)存在16MB BSON大小限制,b)在默认的mmapv1存储引擎中,扩展大小超过某个阈值的文档会导致昂贵的文档迁移,c)如您所见,复杂的模型导致回答简单问题的复杂查询。
所以,我们拆分我们的模型
{
_id: new ObjectId(),
username: "SomeUserName",
type: "someType"
//… Whatever you deem appropriate
}
{
_id: new ObjectId(),
owner: objectIdOfUser,
name: "cool video",
duration: msecsAsLong,
date: new ISODate()
}
由于我们关于视频的问题是针对给定的用户,我们可以在此使用隐式引用而不是嵌入。
这同样适用于图像:
{
_id: new ObjectId(),
owner: objectIdOfUser,
name: "Some image name",
size: bytesAsLong,
Date: new ISODate()
}
查询非常简单:
db.users.find({"type":typeToLookup})
优化此查询同样容易:
db.users.createIndex({"type":1})
由于我们分开了问题,我们简化了一切。回答这个问题的查询变得相当简单,因为我们有一个已知的用户,我们想要获取的视频:
db.videos.find({ "owner": knownUserId }).sort({ "date":-1 })
降序或
db.videos.find({ "owner":knownUserId }).sort({ "date":-1 })
升序。优化查询取决于您想要升序还是降序:
// For descending order
db.videos.createIndex({ "owner":1, "date":-1 })
// For ascending order
db.videos.createIndex({ "owner":1, "date":1 })
请注意,您可以将两个索引用于反之亦然,但它们不会有效。我倾向于根据默认顺序创建索引,因为用户通常可以在订购时反转时间很短。
图像相应地起作用。
现在,这是微不足道的。我们决定订购,让我们说下降。现在,我们只使用skip和limit。我们假设您的页面大小为10,并且您希望按日期的降序查看用户视频的第二页:
var pageSize = 10
var pageToDisplay = 2
var recordsToSkip = pageSize * (pageToDisplay - 1)
db.videos.find({ "owner": knownUserId }).sort({date:-1}).skip(recordsToSkip).limit(pageSize)
同样,图像相应地起作用。
正如所写,通过嵌入,您可能会达到MongoDB强加的BSON文档大小限制。此外,我们可以防止相当昂贵的文档迁移,并使复杂的查询非常容易,同时仍然可以回答相同的问题。
上面显示的示例可能不完全适合您的用例。但是你得到了“分而治之”的图片。解决问题的方法。