寫在前面
最近項(xiàng)目需使用mongoose分頁操作五垮,通過度娘發(fā)現(xiàn)扎附,很多都是直接用skip+limit實(shí)現(xiàn)分頁操作抠忘。但這樣做有個(gè)弊端撩炊,對(duì)于少量的數(shù)據(jù)來說,完全可行崎脉,當(dāng)數(shù)據(jù)量大了后拧咳,這種方案就不怎么可取了。因?yàn)閟kip需從頭開始查找囚灼,對(duì)于數(shù)據(jù)量大的數(shù)據(jù)庫骆膝,這樣的操作很費(fèi)時(shí)間。當(dāng)然對(duì)于mysql等數(shù)據(jù)庫來說灶体,分頁操作還有其他的實(shí)現(xiàn)方式阅签,但單就mongodb數(shù)據(jù)庫來說,有個(gè)簡單的優(yōu)化方式就是通過ObjectId來實(shí)現(xiàn)蝎抽。因?yàn)镺bjectId是可以比較大小的(不清楚的可自行查看ObjectId的構(gòu)成方式)愉择,因此我們可以操作gt,lt來優(yōu)化我們的分頁效果。為什么說這樣會(huì)快一點(diǎn)呢织中?這樣操作锥涕,我們的查找是從_id數(shù)據(jù)開始查詢,減少遍歷數(shù)據(jù)的時(shí)間狭吼。
實(shí)現(xiàn)原理
首先层坠,我們需要前端傳遞一個(gè)當(dāng)前頁數(shù)據(jù)的最后一個(gè)或第一個(gè)_id字段到服務(wù)器,服務(wù)器通過_id字段查找到數(shù)據(jù)所在位置刁笙,然后從當(dāng)前位置開始查找數(shù)據(jù)破花,如果是向后翻頁,那么我們向后查詢limit個(gè)數(shù)據(jù)出來疲吸,如果向前翻頁座每,我們向前查limit個(gè)數(shù)據(jù)出來。
整個(gè)原理其實(shí)應(yīng)該不是很難的摘悴,但是在實(shí)際操作過程中峭梳,有個(gè)很蛋疼的問題~~~
實(shí)現(xiàn)過程
當(dāng)我們想明白了原理后,開始用代碼實(shí)現(xiàn):
因?yàn)椴攘吮容^多的坑蹂喻,我就直接記錄下最后一次正確的思路葱椭,其他的,不如為外人道也(啰嗦)口四。首先我們說前端需要上傳的參數(shù)(如圖1所示):id(記錄的本組數(shù)據(jù)第一個(gè)id或最后一個(gè)id), preNum(當(dāng)前所在頁 from), nextNum(將要前往的頁 to), limit(每組數(shù)據(jù)的數(shù)據(jù)量 2)孵运,至于為什么傳遞這幾個(gè)參數(shù)?請(qǐng)看下文蔓彩。
假設(shè)數(shù)據(jù)庫中???5條數(shù)據(jù)治笨,編號(hào)為1-5驳概,前端分頁,每頁limit(2)條數(shù)據(jù)旷赖,那么我們的數(shù)據(jù)分組應(yīng)該是這樣的:
54,32,1
也就是說需要分三頁顺又,為什么說需要傳遞第一個(gè)數(shù)據(jù)的id或者最后一條數(shù)據(jù)的_id呢?因?yàn)樵谖覀儚牡谝豁撉袚Q到第二頁的時(shí)候杠愧,拿到編號(hào)為2的數(shù)據(jù)id開始向后查找,若相鄰頁數(shù)跳轉(zhuǎn)逞壁,就可直接向后拿到limit(2)條數(shù)據(jù)流济,也就是我們的34數(shù)據(jù)。當(dāng)然腌闯,如果是第一頁直接跳第三頁绳瘟,同理,我們可以直接從編號(hào)位2的數(shù)據(jù)開始向后跳過limit(2)條數(shù)據(jù)姿骏,然后拿到編號(hào)為5的數(shù)據(jù)糖声。正向跳轉(zhuǎn)解決完成。(ps:數(shù)據(jù)是反向獲取的分瘦,也就是最新的數(shù)據(jù)在最前面)
但是反向跳轉(zhuǎn)呢蘸泻?我們還是可以使用類似的思想來操作。但是要注意嘲玫,反向跳轉(zhuǎn)時(shí)悦施,我們需要獲取每組數(shù)據(jù)的第一條id數(shù)據(jù)!也就是說假如我們要從第三頁跳轉(zhuǎn)到第二頁去团,我們就可以從1開始查找比1的_id大的數(shù)據(jù)抡诞,那肯定會(huì)返回(5,4土陪,3昼汗,2)四條數(shù)據(jù),這個(gè)時(shí)候如果我們按照從大到小的順序排列鬼雀,我們的數(shù)據(jù)是這樣的:
1,54,32
和上面的數(shù)據(jù)比較會(huì)發(fā)現(xiàn)顷窒,想要從第三頁跳轉(zhuǎn)到第二頁需要先跳過54兩條數(shù)據(jù),也就是說如果數(shù)據(jù)多了源哩,其實(shí)還是從頭開始查詢的蹋肮,對(duì)我們的優(yōu)化不友好。然后我們按照從小到大的順序排列呢璧疗?數(shù)據(jù)是這樣的:
1,23,45
我們?cè)俸烷_始的數(shù)據(jù)對(duì)比坯辩,會(huì)發(fā)現(xiàn)我們的數(shù)據(jù)是反著的,我們想要的數(shù)據(jù)是:1崩侠,32漆魔,54。這個(gè)時(shí)候我想到了一個(gè)解決辦法:假設(shè)從第三頁跳轉(zhuǎn)第二頁。我們拿到從前端傳過來的每組數(shù)據(jù)的第一條_id字段改抡,這個(gè)時(shí)候我們拿到數(shù)據(jù)1的_id矢炼,然后向后查找limit(2)條數(shù)據(jù),這個(gè)時(shí)候?qū)⒎祷厍岸说臄?shù)據(jù)是23阿纤,這個(gè)時(shí)候我們可以在返回前做個(gè)處理句灌,將獲取到的數(shù)據(jù)重新按照從大到小的順序排列數(shù)據(jù),將數(shù)據(jù)變?yōu)?2欠拾,這個(gè)時(shí)候前端拿到的第一個(gè)數(shù)據(jù)就是3胰锌,符合我們的數(shù)據(jù)要求,當(dāng)從第二頁向第一頁切換時(shí)藐窄,我們拿到的將是數(shù)據(jù)3的_id字段资昧,重復(fù)前面的操作,從數(shù)據(jù)3的_id開始查找荆忍,會(huì)找到45兩條數(shù)據(jù)格带,返回前都經(jīng)過重新排序。當(dāng)然刹枉,如果是跳過多頁叽唱,我們就只需要計(jì)算從_id開始跳過相關(guān)數(shù)據(jù)就行了,這樣就會(huì)大大減小數(shù)據(jù)的查詢等操作(代碼實(shí)現(xiàn)請(qǐng)看圖2)微宝。
相關(guān)說明
- query對(duì)象是mongoose的方法尔觉,若使用mongodb類似,只需要將參數(shù)寫到find()內(nèi)就行
- ObjectId 重新排序是根據(jù)時(shí)間的高低重排芥吟≌焱可以看到其實(shí)_id字段其實(shí)是有時(shí)間戳在里面的,我們使用getTimestamp()方法就可以解析出每個(gè)_id的生成時(shí)間钟鸵。
收獲:
本次探究讓我更加深入的了解了mongodb做分頁時(shí)的過程钉稍,對(duì)于query參數(shù)的運(yùn)作有了深入的理解,而在數(shù)組排序方面棺耍,學(xué)習(xí)到了自定義字段排序的方法贡未,對(duì)數(shù)組排序有了更深入的了解。
ps:
以上內(nèi)容僅為自己的學(xué)習(xí)過程蒙袍,歡迎大家取其精華俊卤,丟其糟粕。若對(duì)以上內(nèi)容有不同理解或更好的想法害幅,歡迎一起探討消恍。
企鵝號(hào):1041415167 郵箱地址:zth1041415167@outlook.com