2017-02-04 34 views
14

對於具有如下文檔的集合,我需要找到特定字段的文檔 - 例如。 lev3_field2(在下面的文檔中)存在。當父字段未知時,在嵌套文檔中查找字段的記錄

我嘗試了以下操作,但是這不會返回任何結果,但字段lev3_field2存在於某些文檔中。

db.getCollection('some_collection').find({"lev3_field2": { $exists: true, $ne: null } })

{ 
    "_id" : ObjectId("5884de15bebf420cf8bb2857"), 
    "lev1_field1" : "139521721", 
    "lev1_field2" : "276183", 
    "lev1_field3" : { 
     "lev2_field1" : "4", 
     "lev2_field2" : { 
      "lev3_field1" : "1", 
      "lev3_field2" : { 
       "lev4_field1" : "1", 
       "lev4_field2" : "1" 
      }, 
      "lev3_field3" : "5" 
     }, 
     "lev2_field3" : { 
      "lev3_field3" : "0", 
      "lev3_field4" : "0" 
     } 
    } 
} 

UPDATE1:這是一個例子,然而真實文檔中它不知道什麼父字段是外地尋找。所以,而不是lev3_field2,我會尋找`levM_fieldN'。

update2:速度對我來說不是主要關心的問題,我可以使用相對較慢的選項,因爲主要功能是查找具有所討論條件的文檔,並且一旦找到文檔並且架構據瞭解,可以通過包含父密鑰來重新編寫查詢以提高性能。

+0

如果您想查找包含字段而不知道字段嵌套位置的文檔,則需要(a)遞歸JavaScript(s)表達式,並且速度非常慢。我建議使用另一種模式,記住易於查詢。 – Lex

+0

@RyanBach - 爲我探索mongodb的原因是由於其無模式本質。在這種特殊情況下,我不太在意速度。 – user3206440

回答

6

要搜索的嵌套文檔中的關鍵,你需要遞歸遍歷文件字段,您可以在JavaScript中通過的$幫助做到這一點,其中如果一個鍵名中存在方法MongoDB中 下面的查詢將搜索一份文件及其子文件。

我已經檢查了你給出的例子,它工作得很好。

db.getCollection('test').find({ $where: function() { 
    var search_key = "lev3_field2"; 

    function check_key(document) { 
     return Object.keys(document).some(function(key) { 
     if (typeof(document[key]) == "object") { 
      if (key == search_key) { 
       return true; 
      } else { 
       return check_key(document[key]); 
      } 
     } else { 
      return (key == search_key); 
     } 
     }); 
    } 
    return check_key(this); 
    }} 

); 
+0

你可以詳細說明'$在MongoDB中的方法嗎' - 這裏是如何使用的? – user3206440

+1

@ user3206440由於習慣而感到抱歉,我沒有在查詢中寫入$關鍵字(可選),就好像查詢只包含$ where操作符一樣,只能傳入JavaScript表達式或JavaScript函數。 我編輯了答案,如果它適合您,請選擇它作爲正確答案 –

0

這是因爲您試圖查看是否存在嵌套字段。這是你想要的查詢:

db.some_collection.find({"lev1_field3.lev2_field2.lev3_field2": { $exists: true, $ne: null } }) 
+2

幾乎發佈了這個,但我想他想知道如何找到沒有給出父字段的給定字段的文檔。 – Lex

+0

啊,你可能是對的,從 – masterforker

+0

看他的模式,這是我的猜測,但你沒有回答他發佈的示例問題 – Lex

2

沒有內置函數來遍歷在MongoDB中的文件鍵,但你可以用MapReduce的實現這一目標。主要優點是,所有的代碼是在MongoDB的數據庫中直接執行,而不是在JS客戶端,所以沒有網絡開銷,因此它應該比客戶端JS更快

這裏是腳本:

var found; 

    // save a function in MongoDB to iterate over documents key and check for 
    // key name. Need to be done only once 
    db.system.js.save({ 
     _id: 'findObjectByLabel', 
     value: function(obj, prop) { 
      Object.keys(obj).forEach(function(key) { 
       if (key === prop) { 
        found = true 
       } 
       if (!found && typeof obj[key] === 'object') { 
        findObjectByLabel(obj[key], prop) 
       } 
      }) 
     } 
    }) 

// run the map reduce fonction 
    db.ex.mapReduce(
     function() { 
      found = false; 
      var key = this._id 
      findObjectByLabel(this, 'lev3_field2') 
      value = found; 
      if (found) { 
       // if the document contains the key we are looking for, 
       // emit {_id: ..., value: true } 
       emit(key, value) 
      } 
     }, 
     function(key, values) { 
      return values 
     }, { 
      'query': {}, 
      'out': {inline:1} 
     } 
    ) 

此輸出(4樣品DOC運行,只用一種含「lev3_field2」)

{ 
    "results" : [ 
     { 
      "_id" : ObjectId("5884de15bebf420cf8bb2857"), 
      "value" : true 
     } 
    ], 
    "timeMillis" : 18, 
    "counts" : { 
     "input" : 4, 
     "emit" : 1, 
     "reduce" : 0, 
     "output" : 1 
    }, 
    "ok" : 1 
} 

運行腳本,將其複製到一個文件名「的script.js」爲例,然後從你的外殼運行

mongo databaseName < script.js 
0

您可以使用mongodb「模式分析器」variety

混帳克隆它,並打印所有收集字段maximum depth

mongo db_name --eval "var collection = 'collection_name'" variety.js

用grep的輸出,你在找什麼。執行時間將類似於收集計數。

相關問題