2017-09-19 69 views
0

我正在使用聚合來查詢我的模式計數日期範圍,我的問題是我沒有從服務器得到任何響應(每次超時),其他貓鼬查詢工作正常(查找,保存等) ),當我調用聚合它取決於管道(當我只使用匹配我得到一個響應,當我添加放鬆我沒有得到任何)。聚合時序輸出


連接代碼:

var promise = mongoose.connect('mongodb://<username>:<password>@<db>.mlab.com:<port>/<db-name>', { 
    useMongoClient: true, 
    replset: { 
    ha: true, // Make sure the high availability checks are on 
    haInterval: 5000 // Run every 5 seconds 
    } 
}); 

promise.then(function(db){ 
    console.log('DB Connected'); 
}).catch(function(e){ 
    console.log('DB Not Connected'); 
    console.errors(e.message); 
    process.exit(1); 
}); 

架構:

var ProspectSchema = new Schema({ 
    contact_name: { 
    type: String, 
    required: true 
    }, 
    company_name: { 
    type: String, 
    required: true 
    }, 
    contact_info: { 
    type: Array, 
    required: true 
    }, 
    description:{ 
    type: String, 
    required: true 
    }, 
    product:{ 
    type: Schema.Types.ObjectId, ref: 'Product' 
    }, 
    progression:{ 
    type: String 
    }, 
    creator:{ 
    type: String 
    }, 
    sales: { 
    type: Schema.Types.ObjectId, 
    ref: 'User' 
    }, 
    technical_sales: { 
    type: Schema.Types.ObjectId, 
    ref: 'User' 
    }, 
    actions: [{ 
    type: {type: String}, 
    description: {type: String}, 
    date: {type: Date} 
    }], 
    sales_connect_id: { 
    type: String 
    }, 
    date_created: { 
    type: Date, 
    default: Date.now 
    } 
}); 

聚合代碼:

exports.getActionsIn = function(start_date, end_date) { 
    var start = new Date(start_date); 
    var end = new Date(end_date); 

    return Prospect.aggregate([ 
    { 
     $match: { 
     // "actions": { 
     // $elemMatch: { 
     //  "type": { 
     //  "$exists": true 
     //  } 
     // } 
     // } 
     "actions.date": { 
      $gte: start, 
      $lte: end 
     } 
     } 
    } 
    ,{ 
     $project: { 
     _id: 0, 
     actions: 1 
     } 
    } 
    ,{ 
     $unwind: "actions" 
    } 
    ,{ 
     $group: { 
     _id: "actions.date", 
     count: { 
      $sum: 1 
     } 
     } 
    } 
    // ,{ 
    // $project: { 
    //  _id: 0, 
    //  date: { 
    //  $dateToString: { 
    //   format: "%d/%m/%Y", 
    //   date: "actions.date" 
    //  } 
    //  } 
    //  // , 
    //  // count : "$count" 
    // } 
    // } 
    ]).exec(); 
} 

調用聚合:

router.get('/test',function(req, res, next){ 
    var start_date = req.query.start_date; 
    var end_date = req.query.end_date; 
    ProspectCont.getActionsIn(start_date,end_date).then(function(value, err){ 
    if(err)console.log(err); 
    res.json(value); 
    }); 
}) 

我的主要問題是,我沒有得到任何答覆,我可以與一個錯誤消息的問題是我沒有得到任何,所以我不知道什麼是錯的。

貓鼬版本:4.11.8

P.我試圖聚合管道的多種變化,所以這不是我第一次嘗試,我有一個聚合工作的主要前景架構而不是動作子文檔

回答

1

您在這裏有幾個問題,主要是由缺少概念。懶惰的讀者可以直接跳到底部以獲得完整的管道示例,但這裏的主體是解釋爲什麼事情是按原樣完成的。

  1. 您正試圖選擇日期範圍。檢查任何長時間運行的操作的第一件事是您有一個有效的索引。你可能有一個,或者你可能沒有。但你應該發出:(從殼)

    db.prospects.createIndex({ "actions.date": 1 }) 
    

    只是可以肯定的。您可能真的應該將此添加到架構定義中,以便您知道應該部署它。因此,添加到您定義的架構:

    ProspectSchema.index({ "actions.date": 1 }) 
    
  2. 當對數組的元素的「範圍」查詢,你要明白,這些是「多條件」,你期待以符合「之間」的元素。雖然你通常可以逃脫查詢使用「點表示法」的陣列的「單屬性」,您缺少[$gte][1]$lte的應用就像是幾次$and明確指定屬性。

    每當你有這樣的「多重條件」,你總是的意思是使用$elemMatch。沒有它,你只是在陣列中檢測每一個值,以查看它是否大於(是一些可能更大一些可以是較小的)大於或小於。該$elemMatch運營商可以確保「兩個」被應用到相同的「元素」,而不僅僅是所有數組值「點符號」暴露了他們:

    { "$match": { 
        "actions": { 
        "$elemMatch": { "date": { "$gte": start, "$lte: end } } 
        } 
    }} 
    

    那現在將只匹配文件裏的「數組元素「在指定日期之間。沒有它,你正在選擇和處理更多與選擇無關的數據。

  3. 數組過濾:標記爲粗體,因爲它的突出性不容忽視。任何初始的$match就像任何「查詢」一樣工作,因爲它的「作業」是對錶達式有效的「選擇文檔」。但是,不會對對返回的文檔中的數組內容有任何影響。

    只要你有這樣的文件選擇條件,你幾乎總是打算從數組本身「過濾」這樣的內容。這是一個獨立的過程,確實應該在與內容一起工作的任何其他操作之前執行。特別是[$unwind][4]

    那麼你真的應該在任何一個$addFields$project添加$filter形式,以適合你的打算「立即」下的任何文件的選擇:

    { "$project": { 
        "_id": 0, 
        "actions": { 
        "$filter": { 
         "input": "$actions", 
         "as": "a", 
         "in": { 
         "$and": [ 
          { "$gte": [ "$$a.date", start ] }, 
          { "$lte": [ "$$a.date", end ] } 
         ] 
         } 
        } 
        } 
    }} 
    

    現在陣列的內容,已知道「必須」由於最初的查詢條件,至少包含一個有效項目,「減少」至只有那些實際符合所需日期範圍的項目。這消除了後續處理中的大量開銷。

    請注意在$filter條件下使用的$gte$lte的不同「邏輯變體」。這些計算結果會爲需要它們的表達式返回一個布爾值。

  4. 分組這可能只是因爲在得到結果的嘗試,但你的代碼並沒有真正做到與有關日期什麼。由於典型的日期值應該以毫秒級的精度提供,所以你一般要減少它們。

    評論代碼建議在$project內使用$dateToString。強烈建議您不要那樣做。如果你打算這樣的下降,然後提供該表達式直接在分組鍵中$group代替:

    { "$group": { 
        "_id": { 
        "$dateToString": { 
         "format": "%Y-%m-%d", 
         "date": "$actions.date" 
        } 
        }, 
        "count": { "$sum": 1 } 
    }} 
    

    我個人不喜歡返回一個「串」的時候自然Date對象序列化這些對我來說已經。所以我喜歡用「數學」方針,以「圓」,而不是日期:

    { "$group": { 
        "_id": { 
        "$add": [ 
         { "$subtract": [ 
         { "$subtract": [ "$actions.date", new Date(0) ] }, 
         { "$mod": [ 
          { "$subtract": [ "$actions.date", new Date(0) ] }, 
          1000 * 60 * 60 * 24 
         ]} 
         ], 
         new Date(0) 
        ] 
        }, 
        "count": { "$sum": 1 } 
    }} 
    

    返回一個有效Date對象「四捨五入」到目前的一天。里程可能會根據首選方法而有所不同,但這是我喜歡的。它只需傳輸最少的字節。

    Date(0)的使用代表「時代日期」。因此,當$subtract一個BSON來自另一個日期時,兩者之間的毫秒差將以整數形式結束。當$add爲BSON日期的整數值時,您將得到一個新的BSON日期,表示兩者之間的毫秒值之和。這是轉換爲數字的基礎,四捨五入到最接近的一天的開始,然後將數字轉換回日期值。

    通過直接使$group而不是$project內的語句,你基本上保存什麼是真正被解釋爲「經過的所有數據並返回這個計算值,然後去做......」。就像在一堆物體上工作一樣,首先用鋼筆標記它們,然後將它們作爲單獨的步驟進行計數。

    作爲一個單一的流水線階段,它可以節省大量的資源,因爲您可以在計算積累的值的同時進行累加。當你認爲它很像所提供的類比時,它就很有意義。


作爲一個完整的管道例子中,你會把上面在一起:

Prospect.aggregate([ 
    { "$match": { 
     "actions": { 
     "$elemMatch": { "date": { "$gte": start, "$lte: end } } 
     } 
    }}, 
    { "$project": { 
     "_id": 0, 
     "actions": { 
     "$filter": { 
      "input": "$actions", 
      "as": "a", 
      "in": { 
      "$and": [ 
       { "$gte": [ "$$a.date", start ] }, 
       { "$lte": [ "$$a.date", end ] } 
      ] 
      } 
     } 
     } 
    }}, 
    { "$unwind": "$actions" }, 
    { "$group": { 
     "_id": { 
     "$dateToString": { 
      "format": "%Y-%m-%d", 
      "date": "$actions.date" 
     } 
     }, 
     "count": { "$sum": 1 } 
    }} 
]) 

老實說,如果要確保索引到位,並遵循管道後,你仍然有超時問題,然後減少日期選擇,直到您獲得合理的響應時間。

如果它仍然需要太長的時間(或減少日期不合理),那麼你的硬件根本無法完成任務。如果你真的有很多數據,那麼你必須對期望合理。因此,擴大規模或擴大規模,但這些事情超出了這裏任何問題的範圍。

就目前來看,這些改進應該會對迄今爲止顯示的任何嘗試產生重大影響。主要是由於一些基本概念被遺漏。

+0

謝謝你的答案。我仍然有一個問題,當我在$ elematch階段的$匹配之後添加項目階段時,查詢超時,在我的給定集合中,$ match階段本身返回3個結果,因此我懷疑它是一個硬件問題(要查詢的總文檔是42(38.31kb))。我添加了索引,$匹配查詢幾乎是即時的。 –

+1

@AhmedSaad您實際上沒有檢查錯誤。錯誤與承諾的工作方式不同。 'ProspectCont.getActionsIn(start_date,end_date).then(result => {/ * work with result * /})。catch(err =>/*使用err * /)'。所以你使用'.catch()'進行錯誤檢查。 Promises只通過'.then()'返回** one **參數,並且不使用'(err,result)'的「節點回調」風格,但分開錯誤。如果出現錯誤,您正在以不同的方式顯示它的顯示方式。這就是爲什麼我是如此明確,所以你可以完全按照一切。 –

+0

因此,當鏈接承諾我應該使用底部的一個捕獲? –