這回在開發 api 時,遇到一個有趣的 Case:依照 Model 的某欄位的陣列長度由大到小排序。這看似簡單的一個請求,實質卻讓我繞了不少彎路。先說結論:使用 aggregation 是唯一解,若要取得 reference id 的內容,需要再額外處理。
內容
情境要求
1. 排序、分頁都由後端負責處理,前端僅傳遞搜尋條件和目標頁數
2. 回傳值不能有因 aggregation 而生成的 key-value
3. 所有的 refrence 需要取得其部分資訊,有些內容還需要排序
注意事項
1. vitrual 的欄位是不能當做排序、搜尋條件的
virtual 的欄位因為在實際的 DB 中並不存在該欄位,你無法以這些虛構的欄位做排序。不過你如果下以 virtual 為主的排序條件,mongoose 也不會回報錯誤給你,就直接略過該條件。
2. 如何按照陣列長度回傳結果
唯一解就是使用 aggregation。$project 是決定最後回傳的欄位;$unwind 則可以解構 array 物件的內容;$lookup 可以進行如同 SQL 的 JOIN。
3. 若要做到後端排序、條件搜尋、分頁,該在哪段進行處理
在 aggregation 的階段完成。排序用 $sort;分頁用 $skip 搭配 $limit;條件搜尋用 $match
4. 如何取得 aggregation 的條件總量
進行相同的 aggregation 搜尋條件,僅搭配 { $count: “Total” } 即可
5. 如何取得 aggregation 的結果的 reference 相關內容
如果僅有一層,你可以在 aggregation 的時候就用 $lookup + $unwind。如果太複雜的話,用 Model.populate(<aggregationResult>, {你的 reference 條件}) 即可
6. 如何隱藏 aggregation 的結果欄位
你可以在 $project 的時候隱藏,不過要當作搜尋、排序條件或是新造的欄位都是不可隱藏的。你可以在最後要回傳給前端時,用 Array.map(({deleteKey, …keepAttrs})=> keepAttrs) 的寫法即可
程式碼
參考資料
1. metaShare_backend/controllers/post.js
2. Sorting by virtual field in mongoDB (mongoose)
3. How to sort documents based on length of an Array field