MongoDB搜索和分页聚合性能问题

JON*_*JON 5 pagination mongoose mongodb node.js

我是Node js和MongoDB的新手。我正在努力进行良好的MongoDB搜索和分页,但是性能存在问题。计算和搜索记录花费了太多时间。

如果我使用小字词进行搜索,则它的运行速度会更快;如果我使用“长字符串”或“数据库中无记录”,则它会花费太多时间,即50到186.30秒。(这是太多时间,我希望它是1到2秒)。

我的记录中有超过15,00,000个数据。

如果我不包括搜索词的数量。它花费了0.20到1.5秒,但是当我在搜索单词时对记录进行计数时,它花费了25.0到35.0秒。

我不知道如何减少用搜索词(查询优化)来计数记录的时间。

我尝试了查询优化的最大级别。

我也尝试过

{
  $count: "passing_scores"
}
Run Code Online (Sandbox Code Playgroud)

但时间没有变化。我被卡住了。我必须减少搜索词的计数时间。

以SQL查询为例

  SELECT * FROM `post`
    Left JOIN catagory ON post.catid=catagory.id
    WHERE post_name LIKE '%a%' OR post_data LIKE '%a%' OR tags LIKE '%a%' OR post_url LIKE '%a%'
Run Code Online (Sandbox Code Playgroud)

NODE和MongoDB

PostObj.count({},function(err,totalCount) {
        if(err) {
            response = {"error" : true,"message" : "Error fetching data"}
        }
        PostObj.aggregate([
        { $lookup:
                {
                    from: 'catagories',
                    localField: 'catagory.catagory_id',
                    foreignField: '_id',
                    as: 'catagories_data'
                }
        },
        {

            $match:
                {
                    $or: [
                        {"catagories_data.catagory_name": { $regex: new RegExp(search_data)}},
                        {"postname": { $regex: new RegExp(search_data) }},
                        {"posturl": { $regex: new RegExp(search_data) }},
                        {"postdata": { $regex: new RegExp(search_data) }},
                        {"tags": { $regex: new RegExp(search_data) }}
                    ]
                }
        },            
        { $limit : search_limit },
        { $skip : search_skip },
        { $group : { _id : "$_id", postname: { $push: "$postname" } , posturl: { $push: "$posturl" }  } } 
    ]).exec(function (err, data){  

        //end insert log data        
        if(err) {
            response = {"error" : true,"message" :err};
        } 

        if(search_data != "")
        {
            // count record using search word

            PostObj.aggregate([
                    { $lookup:
                        {
                            from: 'catagories',
                            localField: 'catagory.catagory_id',
                            foreignField: '_id',
                            as: 'catagories_data'
                        }
                },
                {

                    $match:
                        {
                            $or: [
                                {"catagories_data.catagory_name": { $regex: new RegExp(search_data)}},
                                {"postname": { $regex: new RegExp(search_data) }},
                                {"posturl": { $regex: new RegExp(search_data) }},
                                {"postdata": { $regex: new RegExp(search_data) }},
                                {"tags": { $regex: new RegExp(search_data) }}
                            ]
                        }
                },    
                { $group: { _id: null, myCount: { $sum: 1 } } },
                { $project: { _id: 0 } }   
            ]).exec(function (err, Countdata){  
                res.json({
                sEcho : req.body.draw,
                iTotalRecords: Countdata.myCount,
                iTotalDispla,yRecords: Countdata.myCount,
                aaData: data
            });
        }

        res.json({
            sEcho : req.body.draw,
            iTotalRecords: totalPages,
            iTotalDisplayRecords: totalPages,
            aaData: data
        });
    });
});
Run Code Online (Sandbox Code Playgroud)

另外,我必须尝试这种方式,但比第一个代码多了35.0至49.0秒。

PostObj.aggregate([
    { $lookup:
               {
                            from: 'catagories',
                            localField: 'catagory.catagory_id',
                            foreignField: '_id',
                            as: 'catagories_data'
                        }
                },
                {

                    $match:
                        {
                            $or: [
                                {"catagories_data.catagory_name": { $regex: new RegExp(search_data)}},
                                {"postname": { $regex: new RegExp(search_data) }},
                                {"posturl": { $regex: new RegExp(search_data) }},
                                {"postdata": { $regex: new RegExp(search_data) }},
                                {"tags": { $regex: new RegExp(search_data) }}
                            ]
                        }
                }, 
    { '$facet'    : {
        metadata: [ { $count: "total" }, { $addFields: { page: NumberInt(3) } } ],
        data: [ { $skip: 20 }, { $limit: 10 } ] // add projection here wish you re-shape the docs
    } }
] )
Run Code Online (Sandbox Code Playgroud)

如果我不使用搜索词,那就很好。搜索任何单词时出现问题(该工作的记录数没有跳过和限制)

收集资料

发布

 {
   "_id": ObjectId("5d29bd7609f28633f38ccc13"),
   "postname": "this is some data ",
   "tags " : "
   Damita,
   Caro,
   Leontyne,
   Theodosia,
   Vyky ",
   "postdata ": "Berry Samara Kellia Rebekah Linette Hyacinthie Joelly Micky Tomasina Christian Fae Doralynn Chelsea Aurie Gwendolyn Tate
   Cairistiona Ardys Aubrie Damita Olga Kelli Leone Marthena Kelcy
   Cherlyn Molli Pris Ginelle Sula Johannah Hedwig Adelle Editha Lindsey
   Loleta Lenette Ann Heidie Drona Charlena Emilia Manya Ketti Dorthea
   Jeni Lorene Eolanda Karoly Loretta Marylou Tommie Leontyne Winny Cyb
   Violet Pavia Karen Idelle Betty Doloritas Judye Aretha Quinta Billie
   Vallie Fiona Letty Gates Shandra Rosemary Dorice Doro Coral Tove Crin
   Bobbe Kristan Tierney Gianina Val Daniela Kellyann Marybeth Konstance
   Nixie Andeee Jolene Patrizia Carla Arabella Berna Roseline Lira Cristy
   Hedi Clem Nerissa ",
   "catagory " : [
     { "catagory_id " : [ ObjectId("5d29bd7509f28633f38ccbfd")]},
     { "catagory_id": [ ObjectId("5d29bd7509f28633f38ccbfd") ]}],
   "createby": "5d22f712fe481b2a9afda4aa"
 }
Run Code Online (Sandbox Code Playgroud)

类别

{
  "_id": ObjectId("5d29bc271a68fb333531f6a1"),
  "catagory_name": "Katharine",
  "catagory_description": "Katharine"
}
Run Code Online (Sandbox Code Playgroud)

有什么解决办法吗?

Raj*_*oel 7

如果在您的情况下,您的正则表达式只是寻找一个(或几个)单词,那么最好使用$text而不是 $regex。$text 可以使用文本索引,因此速度更快。就 MySQL 而言, $text 是 LIKE , $regex 是 REGEXP。由于在您的示例 mysql 查询中您使用的是 LIKE,因此我非常有信心您也可以在 mongo 查询中使用 $text 而不是 $regex。

您需要(如果还没有)在您的字段上有一个复合“文本”索引 - (postname、tags、postdata 和 posturl)。

db.POST.createIndex(
   {
     postname: "text",
     tags: "text",
     posturl: "text",
     postdata: "text"
   }
 )
Run Code Online (Sandbox Code Playgroud)


Shi*_*hra 4

我可以建议您尝试一些技巧。

1:POST收藏

看来您只存储category_idcategory对象属性数组中,您应该避免这种情况。相反,你应该做的是如下。

post_id在集合中创建新属性,而不是在[高性能方法]中创建category类别对象数组。post collection

或者

category将对象的后收集形式数组的属性转换为简单数组。[平均表现]。 Ex: category: [ ObjectId("5d29bd7509f28633f38ccbfd", ObjectId("5d29bd7509f28633f38ccbfd", ObjectId("5d29bd7509f28633f38ccbfd"];

肯定在这两种情况post_id下都 category必须对属性进行索引。

2:查找

lookup您应该使用pipeline方法而不是使用简单的管道

例如:

不好。

$lookup:{
    from: 'catagories',
    localField: 'catagory.catagory_id', // BAD IDEA //
    foreignField: '_id',
    as: 'catagories_data'
},
Run Code Online (Sandbox Code Playgroud)

好的。

$lookup:{
    from: 'catagories',
    localField: '_id',
    foreignField: 'post_id',  // GOOD IDEA
    as: 'catagories_data'
},
Run Code Online (Sandbox Code Playgroud)

更好


$lookup:{
    let : { post_id: "$_id" },
    from: 'catagories',
    pipeline:[
              {
                    $match: {
                        $expr: {
                            $and: [
                                { $eq: ["$post_id", "$$post_id"], },
                            ]
                        }
                    },
                },
                {
                    $match: {
                        $or: [

                            // AVOID `new` keyword if you can do such;
                            // and create indexes for the same;

                            { "catagory_name": { $regex: `^${search_data}` } },
                            { "postname": { $regex: `^${search_data}` } },
                            { "posturl": { $regex: `^${search_data}` } },
                            { "postdata": { $regex: `^${search_data}` } },
                            { "tags": { $regex: `^${search_data}` } }
                        ]
                    }

                }
    ],
    as: 'catagories_data'
},
Run Code Online (Sandbox Code Playgroud)

毕竟facet pipeline seems fine to me.

'$facet' : {
    metadata: [ { $count: "total" }, { $addFields: { page: NumberInt(3) } } ],
    data: [ { $skip: 20 }, { $limit: 10 } ] // add projection here wish you re-shape the docs
}
Run Code Online (Sandbox Code Playgroud)

减慢查询的其他方面取决于

  • 配置您的后端服务器和数据库服务器。
  • 前端 -> 后端 -> 数据库服务器之间的距离。
  • 每秒传入和传出的请求。
  • 当然有互联网连接

完整的查询将如下所示

PostObj.aggregate([
    {
        $lookup: {
            let: { post_id: "$_id" },
            from: 'categories',
            pipeline: [
                {
                    $match: {
                        $expr: {
                            $and: [
                                { $eq: ["$post_id", "$$post_id"], },
                            ]
                        }
                    },
                },
                {
                    $match: {
                        $or: [

                            // AVOID `new` keyword if you can do such;
                            // and create indexes for the same;

                            { "catagory_name": { $regex: `^${search_data}` } },
                            { "postname": { $regex: `^${search_data}` } },
                            { "posturl": { $regex: `^${search_data}` } },
                            { "postdata": { $regex: `^${search_data}` } },
                            { "tags": { $regex: `^${search_data}` } }
                        ]
                    }

                }
            ],
            as: "catagories_data"
        }
    },
    {
        '$facet': {
            metadata: [{ $count: "total" }, { $addFields: { page: NumberInt(3) } }],
            catagories_data: [{ $skip: 0 }, { $limit: 10 }]
        }
    }
])
Run Code Online (Sandbox Code Playgroud)