2017-04-25 22 views
2

我一直在玩弄MongoDB,並希望通過域名鍵入項目。問題是使用特殊字符如''''。對於密鑰中斷與Mongo錯誤:什麼是習慣用法'。' (或其他特殊字符)在MongoDB密鑰?

錯誤:密鑰www.google.com不能包含'。'

例如,我希望能夠存儲:

stupidObject = { 
    'www.google.com': { 
     '8.8.8.8': 'Other info', 
     '8.8.4.4': ['item1', 'item2', ... , 'itemN'] 
     }, 
    'www.othersite.com': { 
     '8.8.8.8': 'Other info', 
     '8.8.4.4': ['item1', 'item2', ... , 'itemN'] 
     }, 
    } 

所有我見過的解決方案是一些變種:更改保存前的關鍵,使用Unicode表示,哈希保存前的關鍵。例如查看回答:MongoDB dot (.) in key name

所有這些解決方案都會導致他們自己的問題,並且很難維護代碼。程序員有責任記住要做這種過濾並始終如一地執行。這是一個可怕的解決方案。

我雖然關於散列,但碰撞是一種風險(這幾乎不可能調試),並再次對程序員負責。想象一下這些解決方案對國際開發團隊的影響。

我的問題很簡單:在MongoDB中這樣做的正確方法是什麼?

我結束了一個自定義解決方案,我遞歸地(警鈴!)導航結構並替換特殊字符。這通過利用pre('save')和post('find')鉤子在Mongoose Schema中完成。

這意味着程序員不必關心他們使用密鑰的特殊字符以及他們保存的域名,數據庫層將透明地處理所有內容。這對我來說似乎是一個更好的解決方案。

但是......這需要一些混亂的代碼來解決使用hasOwnProperty時Mongoose對象行爲不當的問題,並且需要首先運行'.toObject()',然後通過引用傳遞原始'this'指針。

該解決方案的工作原理,但我認爲必須有更好的方法!任何想法或指導正確的方式來做到這一點是感激地接受!當你看到下面的代碼時,你會明白爲什麼我認爲必須有更好的方法!

我應該提到我不想安裝任何庫或有其他依賴項來解決此問題。

這裏使用的代碼示例:

// Recursive function to replace character||string in keys that may cause violations 
// Same code can be used to reverse the change 
// 
var replaceStringInKeys = function (stringToReplace, newString, regExp, thisObj, thisPtr) { 
    for(property in thisObj) { 
     if (thisObj.hasOwnProperty(property)) { 
      if(property.indexOf(stringToReplace) > -1) { 
       // Replace the '.'s with URL escaped version. Delete old object. 
       var newproperty = property.replace(regExp, newString); 
       thisObj[newproperty] = thisObj[property]; 
       thisPtr[newproperty] = thisPtr[property]; 
       delete thisObj[property]; 
       delete thisPtr[property]; 
       // Pass the new property too 
       if (thisObj[newproperty].constructor === Object) { 
        thisObj[newproperty] = replaceStringInKeys(stringToReplace, newString, regExp, thisObj[newproperty], thisPtr[newproperty]); 
        thisPtr[newproperty] = thisObj[newproperty]; 
       } 
       continue; 
      } 
      if (thisObj[property].constructor === Object) { 
       thisObj[property] = replaceStringInKeys(stringToReplace, newString, regExp, thisObj[property], thisPtr[property]); 
       thisPtr[property] = thisObj[property]; 
      } 
     } 
    } 
    return thisObj; 
}; 

testSchema.pre('save', function(next) { 
    // Calling '.toObject' allows for hasOwnProperty to work 
    var thisObj = this.toObject(); 
    console.log('Pre save record...'); 
    // Duplicate the this pointer as mongo is too shit to use hasOwnProperty properly 
    replaceStringInKeys('.', '[whateveryouwantinsteadofdot]', /\./g, thisObj, this); 
    next(); 
}); 

testSchema.post('find', function(results) { 
    console.log('post find record...'); 
    // Undo the changes made by the pre-save hook 
    var i; 
    for(i = 0; i < results.length; i++) { 
     var thisObj = results[i].toObject(); 
     replaceStringInKeys('[whateveryouwantinsteadofdot]', '.', /\[whateveryouwantinsteadofdot\]/g, thisObj, results[i]); 
    } 
}); 

注:請小心使用此解決方案(如果你夠瘋狂),因爲可能會有安全問題。例如,如果一個壞人知道你取代''。與%2E,他們可以強制使用例如

hxxp://www.vulnerablesitethatdoesntexist.com/%2E%2E/%2E%2E/%2E%2E/%2E%2E/%2E%2E/%2E%2E/%2E%2E/% 2E%2E /%2E%2E /%2E%2E /%2E%2E/etc/passwd中

其被正確轉義,但會被透明地轉換爲一個目錄遍歷類型字符串:
hxxp:// WWW。 weakrablesitethathatesntexist.com/../../../../../../../../../../../etc/passwd

+0

看到這個答案http://stackoverflow.com/questions/40542336/mongodb-insert-key-with-dollar/40542724#40542724 – sergiuz

+0

嗨的Sergiu,感謝您的鏈接。我之前讀過這個答案。此解決方案需要批量更改後端數據,這很少是一個好主意。你可以通過前面和後面的鉤子來完成這個替換,但是解決方案和上面一樣。而且你冒着將數據改變成看起來像角色但不是角色即單應性的風險。這可能會導致以下問題:如果開發人員通過呈現Unicode的終端查看數據,則無法確定查詢失敗的原因。 – misterfitzy

回答

0

您應該更改文檔的結構使用點作爲關鍵字無效。幾年前我遇到了同樣的問題。

yourStupidObject = { 
    'www.google.com': [ 
    {'ip': '8.8.8.8', more: 'Other info', 
    {'ip': '8.8.4.4', more: ['item1']} 
    ] 
} 
+0

嗨托馬斯,感謝您的評論。在思考你的答案之後,它可能是做正確事情的「正確」方式。然而,我想要檢查的一部分是保留JSON結構,即前端對象和MongoDB後端相同。在某些情況下,如果可能,簡單地保存前端對象會更方便。 – misterfitzy

+0

試想一下,您已經沉浸了多少小時,並且比較前端的工作量會有多大 –

+0

您是對的,但它正在工作,因爲此刻我需要它。我只是想知道正確的做法是什麼?對於我的用例來說,有必要弄清楚這一點,因爲它使端到端更容易一些。 – misterfitzy

相關問題