MongoDB查询优化

MongoDB queries optimisation

本文关键字:查询优化 MongoDB      更新时间:2023-09-26

我想从我的用户模型中检索一些信息,如下所示:

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性能/优化至关重要。

感谢

您想要的是一个"分面搜索"结果,其中包含当前结果集中匹配术语的统计信息。随后,虽然有些产品"看起来"可以在一个响应中完成所有工作,但您必须考虑到,大多数通用存储引擎都需要多个操作。

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

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

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

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

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 }

这样的结构很容易遍历和检查离散的"国家"和属于"国家"的"城市",因为这些数据只是用连字符"-"一致地分隔。

尝试在数组中混合文档是个坏主意。BSON的大小限制为16MB,因此将结果混合在一起(尤其是当您试图保留文档内容时(肯定会在响应中被超过。

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

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

正如这里所说,特别是在实现结果的"分页"时,那么获取"Result Count"、"Facet Counts"和实际的"Page of results"的角色都被委派给服务器的"单独"查询。

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


结论

所以,在文档中放一些东西,在一个地方标记各个方面。标记化字符串数组可以很好地实现此目的。对于方面选择组合的"或"或"answers"条件,它也可以很好地与查询表单(如$in$all(配合使用。

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

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

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