2012-01-18 50 views
3

我有一個集合,我只需要查看整個數組的文檔;我想不出任何我想通過該數組的一個值查找文檔的場景。不幸的是,總是爲數組值激活的多鍵功能顯然無法停用。針對MongoDB集合上的精確數組匹配進行優化

documentation它說:「該索引將用於查找值的一個子集(目前是第一個),然後文件將被檢查完全匹配。」我認爲這大大降低了我的表現案件。儘管有索引,一些查找需要70毫秒和幾分鐘,因爲根據第一個元素,MongoDB有時必須搜索幾千或幾十萬個文檔。至少這是我的理論。

有沒有辦法避免這個問題,還是應該序列化我的數組並將它們存儲爲字符串?

在此先感謝!

+0

我知道你已經打上了這個問題的回答但也有一些注意事項。你不明白的一件事是數組排序是'[1,2,3]'與搜索的'[1,3,2]'相同嗎?如果這兩個陣列是相同的,那麼下面的解決方案將失敗。如果這兩者不相同,那麼您可能需要測試序列化版本,因爲速度差異可能很大。 –

+0

@GatesVP是的,那是真的。但在我看來,這不是一個精確的數組匹配。爲了匹配這些數組,您需要使用原始數組值索引來執行「$ in」查詢。 –

+0

@GatesVP對不起,我的意思是全部。它看起來像只有一個方向匹配 - 並不是真正的使用索引來比較(排序不敏感)的好方法。您可以使用$ all和$ size獲得非常接近的值,但如果數組中存在重複項,則不會檢查出現次數。 –

回答

1

也許你可以使用一個子文檔,如:

{ 
    array_sub_doc: { arr: [1,2,3,4,5] } 
} 

所以,你可以做比賽,如:

db.coll.ensureIndex({array_sub_doc:1}); 
db.coll.find({array_sub_doc: {arr:[1,2,3,4,5]}}) 

更新我發現了什麼導致了失敗的大陣列。大於800字節的索引鍵不會編入索引。所以,如果你有一個大的子文檔,並且你在其上放置了一個索引,如果它大於800字節,並且你試圖搜索它,你就不會找到它。如果您關閉索引並重新搜索相同的子文檔,您會發現它(儘管它將是完整的集合掃描)。

這在這裏是作爲一種限制,並會在以後的版本中刪除:https://jira.mongodb.org/browse/SERVER-3372

所以,這將在通用工作了小數組。

下面是一些測試代碼的情況下,任何人想嘗試一下:

var randomArray = function() { 
    var len = 80; 
    var randomarr = new Array(); 
    for (var i=0; i<len; i++) { 
    randomarr.push(Math.floor(Math.random() *10000)); 
    } 
    return randomarr; 
} 

var insert = function() { 
    db.Test2.ensureIndex({array_sub_doc:1}); 
    for(var i=0;i<10000;i++) { 
    db.Test2.save({array_sub_doc: {arr: randomArray()}}); 
    } 
} 

db.Test2.remove(); 
insert(); 

var one = db.Test2.findOne(); 
db.Test2.findOne({array_sub_doc:one.array_sub_doc}); 

//... 

db.Test2.find({array_sub_doc:one.array_sub_doc}).explain(0); 
/* outputs: 
{ 
    "cursor" : "BtreeCursor array_sub_doc_1", 
    "nscanned" : 1, 
    "nscannedObjects" : 1, 
    ... 
*/ 
+0

我剛剛爲此寫了一個更徹底的測試,並且當我使用更大的數組(如100或1000個元素)時,似乎沒有找到匹配項 - 但對於5個元素可以正常工作。奇怪的;也許我做錯了什麼。我稍後再看一遍。 –

+0

根據我自己對短陣列的快速測試,這種解決方法似乎正確地解決了我的問題。謝謝!如果沒有(甚至)更簡單的解決方案,我會接受你的答案作爲一個通用的解決方案,但在我的情況下,序列化解決方案似乎更直觀,因爲我的字符串不能包含空格,並且空格也非常直觀。 –

+0

你的「真實」陣列有什麼樣的價值?只是好奇。 –