我有一個包含一個createdAt欄位的集合。當我從這個集合中獲得多個檔案時,我使用這個欄位將它們從最新到最舊排序。問題是,可以以某種方式在同一毫秒創建多個檔案,因此具有完全相同的日期。正因為如此,我需要我的排序在這些情況下有一些決勝局(我不在乎它們的順序是什么,但它必須是一致的)。我嘗試為此使用 _id 并使用 排序{createdAt: -1, _id: -1},但它會在某些查詢中使用索引產生問題,從而使一些非常常見的查詢變得更慢且效率低下。
然后,我認為與其保存createdAt為 Date 物件,不如將其保存為字串(ISO 8601),然后在末尾添加一個隨機字串。這樣,即使多個檔案具有相同的日期,由于隨機字串,它們仍然具有一致的順序。它就像一個內置的決勝局,使得排序或獲取在其他檔案之前創建的所有檔案變得非常簡單(我經常使用它進行分頁)。它添加到我的代碼中的唯一一件事是,當我想獲取實際日期時,我需要洗掉該隨機字串并將其轉換為Date,但這很容易用new Date(createdAt.split('Z')[0] 'Z').
這種方法有什么缺點嗎?我需要考慮什么?
uj5u.com熱心網友回復:
正如您在解釋計劃中看到的那樣,索引范圍不是最佳的:
indexBounds: {
tags: [ '["some_tag", "some_tag"]' ],
createdAt: [ '[MaxKey, MinKey]' ],
_id: [ '[MaxKey, MinKey]' ]
}
重寫查詢并嘗試這個:
db.questions.find(
{
$or: [
{ tags: "some_tag", createdAt: { $lt: ISODate('2022-10-12T17:39:49.774Z') } },
{ tags: "some_tag", createdAt: ISODate('2022-10-12T17:39:49.774Z'), _id: 'F3zcf4Ll0R' }
]
}
).limit(20).sort({ createdAt: -1, _id: -1 }).explain("allPlansExecution")
當您創建索引時,db.questions.createIndex({ tags: 1, createdAt: -1 })
您將獲得以下計劃:
{
stage: 'OR',
inputStages: [
{
stage: 'FETCH',
filter: {
'$and': [
{ createdAt: { '$eq': ISODate("2022-10-12T17:39:49.774Z") } },
{ tags: { '$eq': 'some_tag' } }
]
},
inputStage: {
stage: 'IXSCAN',
keyPattern: { _id: 1 },
indexName: '_id_',
isMultiKey: false,
multiKeyPaths: { _id: [] },
isUnique: true,
isSparse: false,
isPartial: false,
indexVersion: 2,
direction: 'forward',
indexBounds: { _id: [ '["F3zcf4Ll0R", "F3zcf4Ll0R"]' ] }
}
},
{
stage: 'IXSCAN',
keyPattern: { tags: 1, createdAt: -1 },
indexName: 'tags_1_createdAt_-1',
isMultiKey: false,
multiKeyPaths: { tags: [], createdAt: [] },
isUnique: false,
isSparse: false,
isPartial: false,
indexVersion: 2,
direction: 'forward',
indexBounds: {
tags: [ '["some_tag", "some_tag"]' ],
createdAt: [ '(new Date(1665596389774), new Date(-9223372036854775808)]' ]
}
}
]
}
欄位始終是唯一的(并且始終具有索引),因此 MongoDB 查詢該欄位并使用第二個條件運行OR_id是有意義的。
因此,您還可以撰寫:
db.questions.find(
{
$or: [
{ tags: "some_tag", createdAt: { $lt: ISODate('2022-10-12T17:39:49.774Z') } },
{ _id: 'F3zcf4Ll0R' }
]
}
).limit(20).sort({ createdAt: -1, _id: -1 }).explain("allPlansExecution")
uj5u.com熱心網友回復:
聽起來您可能需要考慮使用Universally Unique Lexicographically Sortable Identifier (ULID)。
它將根據時間戳創建一個隨機 ID。它對時間戳進行編碼并附加一個隨機部分以避免完全相同的日期發生沖突。
const {ulid} = require('ulid');
ulid(new Date('2022-01-01').valueOf()); // 01FR9EZ700AT3P6EA9PW5K9NQ8
ulid(new Date('2022-01-01').valueOf()); // 01FR9EZ700NQ1RP84E34S1W6X1
請注意,如果我再次為相同的資料生成 ulid,則字串的前 10 個位元組將保持不變,其余的只會更改。這是因為 ULID 格式:
01AN4Z07BY 79KA1307SR9X4MV3
|----------| |----------------|
Timestamp Randomness
48bits 80bits
{createdAt: -1, _id:1}因此,在您的情況下,您將擁有一個基于存盤 ULID 的 1 個欄位的簡單索引,而不是 的復合索引。
但是等等,你如何根據某個日期進行搜索呢?你如何解碼原始日期?
搜索大于日期的檔案:
const {encodeTime} = require('ulid');
const datePrefix = encodeTime(new Date('2022-01-01').valueOf(), 10);
db.collection.find({ulidField: {$gte: datePrefix}})
回傳原始日期:
const {decodeTime} = require('ulid');
new Date(decodeTime('01FR9EZ700AT3P6EA9PW5K9NQ8')); // Sat Jan 01 2022 01:00:00 GMT 0100 (Central European Standard Time)
轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/514771.html
上一篇:排序未回傳正確排序的值
