MongoDB查询优化

4

我希望从我的User模型中检索几个信息,它看起来像这样:

var userSchema = new mongoose.Schema({
  email: { type: String, unique: true, lowercase: true },
  password: String,

  created_at: Date,
  updated_at: Date,

  genre : { type: String, enum: ['Teacher', 'Student', 'Guest'] },
  role : { type: String, enum: ['user', 'admin'], default: 'user' },
  active : { type: Boolean, default: false },

  profile: {
    name : { type: String, default: '' },
    headline : { type: String, default: '' },
    description : { type: String, default: '' },
    gender : { type: String, default: '' },
    ethnicity : { type: String, default: '' },
    age : { type: String, default: '' }
  },

  contacts : {
    email : { type: String, default: '' },
    phone : { type: String, default: '' },
    website : { type: String, default: '' }
  },

  location : {
    formattedAddress : { type: String, default: '' },
    country : { type: String, default: '' },
    countryCode : { type: String, default: '' },
    state : { type: String, default: '' },
    city : { type: String, default: '' },
    postcode : { type: String, default: '' },
    lat : { type: String, default: '' },
    lng : { type: String, default: '' }
  }
});

在主页上,我有一个筛选器用于地点,您可以浏览来自国家或城市的用户。
所有字段中也包含了该地区的用户数量:
United Kingdom
  All Cities (300)
  London (150)
  Liverpool (80)
  Manchester (70)
France
  All Cities (50)
  Paris (30)
  Lille (20)
Nederland
  All Cities (10)
  Amsterdam (10)
Etc...

这是主页,然后我还有学生和教师页面,我希望在那些国家和城市中只提供关于有多少老师的信息......
我想要做的是创建一个查询来检索所有这些信息并进行单个查询。
目前,查询看起来像这样:
User.aggregate([
    { 
      $group: { 
        _id: { city: '$location.city', country: '$location.country', genre: '$genre' },
        count: { $sum: 1 }
      }
    },
    {
      $group: { 
        _id: '$_id.country',
        count: { $sum: '$count' },
        cities: { 
          $push: { 
            city: '$_id.city', 
            count: '$count'
          }
        },
        genres: {
          $push: {
            genre: '$_id.genre',
            count: '$count'
          }
        }
      }
    }
  ], function(err, results) {
    if (err) return next();
    res.json({ 
        res: results
    });
  });

问题在于我不知道如何获取所需的所有信息。
  • 我不知道如何获取每个国家中总用户数量的长度。
  • 我已经拥有每个国家的用户数量长度。
  • 我已经拥有每个城市的用户数量长度。
  • 我不知道如何获取特定性别的相同信息。

在Mongo中是否可能用单个查询获得所有这些信息?

否则:

使用2、3个不同的Mongo请求创建几个Promise,如下所示:

getSomething
.then(getSomethingElse)
.then(getSomethingElseAgain)
.done

我确信每次指定数据存储会更容易,但是:当数据库中有5000/10000个以上的用户时,这对性能有好处吗?
抱歉,我还在学习过程中,我认为理解MongoDB性能/优化非常关键。
谢谢。
1个回答

8
你想要的是"分面搜索"结果,其中包含有关当前结果集中匹配术语的统计信息。随后,虽然有些产品看起来可以在单个响应中完成所有工作,但您必须考虑到大多数通用存储引擎都需要多个操作。
使用MongoDB,您可以使用两个查询获取结果本身,并使用另一个查询获取分面信息。这将提供类似于专用搜索引擎产品(如SolrElasticSearch)提供的分面结果。
但是为了有效地执行此操作,您希望以可有效使用的方式将其包含在文档中。对于您想要的内容,一种非常有效的形式是使用令牌化数据数组:
 {
     "otherData": "something",
     "facets": [
         "country:UK",
         "city:London-UK",
         "genre:Student"
     ]
 }

所以,“factets”是您文档中的单个字段,而不是多个位置。这使得索引和查询非常容易。然后,您可以有效地聚合结果,并获得每个facet的总数:
User.aggregate(
    [
        { "$unwind": "$facets" },
        { "$group": {
            "_id": "$facets",
            "count": { "$sum": 1 }
        }}
    ],
    function(err,results) {

    }
);

或者更理想的是使用一些$match的条件:

User.aggregate(
    [
        { "$match": { "facets": { "$in": ["genre:student"] } } },
        { "$unwind": "$facets" },
        { "$group": {
            "_id": "$facets",
            "count": { "$sum": 1 }
        }}
    ],
    function(err,results) {

    }
);

最终会给出如下回应:
{ "_id": "country:FR", "count": 50 },
{ "_id": "country:UK", "count": 300 },
{ "_id": "city:London-UK", "count": 150 },
{ "_id": "genre:Student": "count": 500 }

这样的结构易于遍历和检查,例如离散的“国家”以及属于“国家”的“城市”,因为该数据始终由连字符“-”分隔。

尝试在数组内混合文档是一个坏主意。还需要遵守16MB的BSON大小限制,如果试图将结果混合在一起(特别是如果您试图保留文档内容),则最终肯定会超过响应。

对于像这样的查询,只需汇总特定facet类型的元素即可获得“总计数”。或者只需将相同的查询参数发出到.count()操作:

User.count({ "facets": { "$in": ["genre:Student"] } },function(err,count) {

});

如此说来,特别是在实现结果“分页”时,获取“结果计数”、“Facet计数”和实际的“结果页面”的角色都委托给了“单独”的服务器查询。

将每个查询并行提交到服务器并组合结构以提供给模板或应用程序,看起来非常像搜索引擎产品之一提供的这种响应的Faceted搜索结果,这没有任何问题。


总结

因此,在文档中放置一些标记以标识单个位置的方面。一个令牌化字符串数组很适合这个目的。它也可以很好地与查询表单配合使用,如$in$all,用于对方面选择组合的“或”或“和”条件。

不要试图混合结果或嵌套添加来匹配某种感知的分层结构,而是遍历接收到的结果并在标记中使用简单的模式。非常简单的

将内容作为单独的查询运行分页查询,无论是对方面还是总计数。尝试将所有内容推入数组中,然后限制输出以获取计数是没有意义的。同样的方法也适用于RDBMS解决方案,以执行相同的操作,其中分页结果计数和当前页面是单独的查询操作。

在MongoDB博客上有关于使用MongoDB进行分面搜索的更多信息,其中还解释了其他选项。还有关于使用mongoconnector或其他方法与外部搜索解决方案集成的文章。


嗯,我在过去几天里使用了这种技术进行了几次试验。显然一开始很难理解,但经过几次测试后,我发现了一个好的结果。 我只需要深入研究这些“分面搜索”,因为我对此完全是新手。 - Ayeye Brazo

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接