我在用Mongodb做查询的时候遇到的问题,求大神帮忙看下
发布于 8 年前 作者 wenyuking 2814 次浏览 来自 问答

我设计了两个collection,一个是分类的collection,另一个是分类下对应图片的collection,分类有‘推荐’这个字段,每张图片都有一个对应的分类字段。现在我需要将所有分类为推荐状态下的图片查询出来,并在页面上显示为列表,每个列表显示分类名和其所对应的图片,我是这样做的。但现在遇到的问题是,经常刷新页面的时候,显示的图片跟分类名称对应不上,总是串位,想知道这是怎么一回事?

‘’’'Picture.prototype.getByRecommondCate = function(callback){ var piclist = []; mongodb.open(function(err,db){ if(err){ return callback(err); } db.collection(‘categorys’,function(err,collection){ if(err){ mongodb.close(); return callback(err); }

        collection.find({
            isRecommend:"true"
        }).toArray(function(err,cates){

            if(err){
                mongodb.close();
                return callback(err);
            }

            db.collection('pictures',function(err,collection){
                if(err){
                    mongodb.close();
                    return callback(err);
                }

                for(var i = 0;i<cates.length;i++){
                    collection.find({
                        category:cates[i].name
                    }).limit(20).toArray(function(err,pics){

                        if(err){
                            mongodb.close();
                            return callback(err);
                        }

                        piclist.push(pics);

                        if(piclist.length == cates.length){
                            callback(null,cates,piclist);
                        }

                    })
                }
            })
        })
    })

})

}’’’’

E2CB9B38-34DE-47FA-9DF4-AE39504C2554.png

4 回复

代码太多了,看不清楚。另外建议重新设计下你得数据模型,你这个需求用一张collection就能搞定

@jingsam 请问下怎么设计会比较好呢?

首先你的数据存储结构(many to many)符合你的需求(注1),其次我不确定你上面贴的逻辑有返回值或正确的返回值,最后是一个for + 异步 的一个小误区;ok, 问题1:先确认下你的callback函数只有在piclist的长度等于类型长度时才返回,同时类型限制条件是被推荐,可以是很多很多。。。而图片列表限制不能超过20,换句话说只有恰巧你的类比表中有20个被推荐的,而且恰好该类别有20张图片才会有返回,其余情况下都是无返回的状态; 问题2:其实是一个经典的for + 异步的坑,建议通过下面的列子说明:

var arr = [1,2,3];

for(var i in arr) {
	setImmediate(function () {
		console.log('item is', arr[i]);
	});
}

for是同步执行,在遇到setImmediate等异步函数时并不会等待异步执行完,实际上是打印了三次arr[i]而由于异步的原因,并不会立即输出,而是等待for同步任务全部完成后才会进入下一个事件循环,而此时arr[i]已经是数组的最后一个元素了,所以可以看到打印出来的是三个3;当然还有一个坑是js数据类型引起的,比如

var arr = [1,2,3];

for(var i in arr) {
	var foo = arr[i];

	setImmediate(function () {
		console.log('item is', foo);
	});
}

有的同学会认为这样就可以达到遍历输出arr的效果了,其实这里要引入js的基础数据类型的概念,arr,Object等都不属于js基础数据类型,所以在内存中均以引用的形式存在,略懂c的同学都知道,将一个引用的地址传递给一个变量名,实质上仍旧是最后一次修改后的数据,仍旧是最后一个元素; 为了解决该问题,可以引入异步控制流的方式(当然并不一定使用async,这里只是一个demo),比如这样:

var arr = [1,2,3],
	async = require('async');

async.each(arr, function (item, cb) {
	setImmediate(function () {
		console.log('item is', item);
	});
}, function (err) {
	if (err) console.error('err is', err);
})

注1:考虑到后期可能出现很多分类对应很多图片的场景,不建议使用聚合的存储结构,虽然一张表可以同时存储分类以及下属图片,但可能出现很多问题,比如后期图片太多时超过16M限制;比如同一张图片可能会有不同的分类属性,冗余度就很高;执行更新操作时因为聚合结构过深效率很低等;

@haozxuan 后来我用一个递归的方式替代了for循环。但是隐约感觉还会有坑的存在,准备替换成async这种方式去处理了

回到顶部