【问题标题】:MongoDB queries optimisationMongoDB查询优化
【发布时间】:2015-03-06 09:24:33
【问题描述】:

我希望从我的用户模型中检索如下所示的几个信息:

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...

这在主页中,然后我也有学生和教师页面,我希望只知道这些国家和城市有多少教师......

我要做的是创建一个对 MongoDB 的查询,以通过单个查询检索所有这些信息。

目前查询如下所示:

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 创建几个承诺:

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

我确信每次指定数据时存储会更容易,但是:当数据库中有超过 5000 / 10000 个用户时,这对性能有好处吗?

抱歉,我仍在学习中,我认为这些内容对于了解 MongoDB 性能/优化至关重要。

谢谢

【问题讨论】:

    标签: javascript node.js mongodb mongoose aggregation-framework


    【解决方案1】:

    您想要的是一个"faceted search" 结果,您可以在其中保存有关当前结果集中匹配术语的统计信息。随后,虽然有些产品“似乎”在一个响应中完成所有工作,但您必须考虑到大多数通用存储引擎都需要多个操作。

    使用 MongoDB,您可以使用两个查询来获取结果本身,另一个查询来获取方面信息。这将提供与 SolrElasticSearch 等专用搜索引擎产品提供的分面结果相似的结果。

    但是为了有效地做到这一点,您希望以一种可以有效使用的方式将其包含在您的文档中。您想要的一种非常有效的形式是使用标记化数据数组:

     {
         "otherData": "something",
         "facets": [
             "country:UK",
             "city:London-UK",
             "genre:Student"
         ]
     }
    

    因此,“事实”是文档中的单个字段,而不是多个位置。这使得索引和查询变得非常容易。然后,您可以有效地汇总结果并获得每个方面的总数:

    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 大小限制也需要遵守,由此产生的混合结果(尤其是如果您试图保留文档内容)肯定会在响应中被超过。

    对于像从这样的查询中获取结果的“总计数”这样简单的事情,那么只需总结特定方面类型的元素即可。或者只是向.count() 操作发出相同的查询参数:

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

    正如这里所说,特别是在实现结果的“分页”时,获取“结果计数”、“构面计数”和实际“结果页面”的角色都被委派给服务器的“单独”查询。

    将这些查询中的每一个并行提交到服务器,然后组合一个结构以提供给您的模板或应用程序,看起来很像来自提供这种响应的搜索引擎产品之一的分面搜索结果,这并没有错.


    结束

    因此,在您的文档中添加一些内容以在一个地方标记各个方面。一个标记化的字符串数组可以很好地用于此目的。对于构面选择组合上的“或”或“与”条件,它也适用于诸如$in$all 之类的查询表单。

    不要试图混合结果或嵌套添加只是为了匹配一些感知的层次结构,而是遍历收到的结果并在标记中使用简单的模式。这很简单

    将内容的分页查询作为对方面或总体计数的单独查询运行。尝试将所有内容推送到数组中,然后仅仅为了获得计数而进行限制是没有意义的。这同样适用于执行相同操作的 RDBMS 解决方案,其中分页结果计数和当前页面是单独的查询操作。

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

    【讨论】:

    • 嗯,这几天我用这个技术做了几次试验。显然一开始不太容易理解,但经过几次测试,我发现了一个很好的结果。我只需要深入了解这些“受感染的搜索”,因为我对此完全陌生。
    猜你喜欢
    • 2015-01-21
    • 2015-01-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-11-13
    • 2021-09-03
    • 2019-10-25
    • 2020-07-05
    相关资源
    最近更新 更多