2016-03-08 31 views
2

我有一個文件,如果我想指望詹姆斯多少書擁有與1990年,看起來像這樣MongoDB:計算一個數組中有多少具有給定值的項目,這是在一個文檔中?

{ 
    name : james, 
    books : [ 
    { 
     title: title1, 
     year: 1990 
    }, 
    { 
     title: title2, 
     year: 1990 
    }, 
    { 
     title: title3, 
     year: 1991 
    } 
    ] 
} 

說,我怎麼會去這樣做?我試過以下。但是我意識到這是行不通的,因爲'書'是一個數組。

db.collection(collectionName).find({name:james, books: {year: 1990}}).count(function(book_count){ 
    console.log(book_count); 
    } 

任何指針將不勝感激。謝謝!

編輯:

我沒有看到另一種答案比你可以用下面這段代碼來獲得整個數組的大小。但我想知道如何獲得具有特定參數的數組中的項的數量。即。而不是看詹姆斯擁有多少本書。我想知道有多少詹姆斯的書發表在1990年

db.mycollection.aggregate({$project: { count: { $size:"$foo" }}}) 
+0

的可能的複製[MongoDB的:計數項的數組中的數](http://stackoverflow.com/questions/21387969/mongodb-count-the-number-of-an-an-an-array) –

+2

嚴重的是,如果只有一個文檔要從這個返回,那麼聚合框架(它可以從數組中獲取大小並將其過濾掉首先也是)在這裏將是一個糟糕的選擇。更好的方法是僅測試匹配條件的文檔中返回的數組大小。 'db.collection(collectionName).findOne({「name」:「james」,「books.year」:1990},function(err,result){console.log(result.books.filter(function(book){ return book.year == 1990})。length)})'。很簡單的東西。只有在您打算使用該數字時纔會彙總,以及使用該數字進行彙總。 –

回答

2

聚合框架是這樣的理想選擇。考慮運行以下管道以獲得所需的結果。

pipeline = [ 
    { 
     "$match": { 
      "name": "james", 
      "books.year": 1990 
     } 
    }, 
    { 
     "$project": { 
      "numberOfBooks": { 
       "$size": {     
        "$filter": { 
         "input": "$books", 
         "as": "el", 
         "cond": { "$eq": [ "$$el.year", 1990 ] } 
        }     
       } 
      } 
     } 
    } 
]; 
db.collection.pipeline(pipeline); 

上述管道使用可用MongoDB的3.2新​​操作者以產生滿足指定條件,即它過濾不滿足標準外元素的數組。流水線初始化爲流水線優化策略,以儘早過濾掉進入聚合流水線的文檔。

The $size運算符接受單個表達式作爲參數,然後給你在結果數組中的元素數,因此你有你想要的書數。


對於不使用早期版本沒有找到​​操作,請考慮以下流水線操作的替代解決方案:

pipeline = [ 
    { 
     "$match": { 
      "name": "james", 
      "books.year": 1990 
     } 
    }, 
    { 
     "$project": { 
      "numberOfBooks": { 
       "$size": {     
        "$setDifference": [ 
         { 
          "$map": { 
           "input": "$books", 
           "as": "el", 
           "in": { 
            "$cond": [ 
             { "$eq": [ "$$el.year", 1990 ] }, 
             "$$el", 
             false 
            ] 
           } 
          } 
         }, 
         [false] 
        ]     
       } 
      } 
     } 
    } 
]; 
db.collection.pipeline(pipeline); 

$project流水線階段涉及fittering書籍數組以便刪除1990年以前的文件。這可以通過$setDifference$map運營商。

$map操作在本質上創建保持值作爲一個子表達式到數組的每個元素的邏輯評價的結果的新的數組字段。 $setDifference運算符然後返回一個集合,其中元素出現在第一個集合中,但不出現在第二個集合中;即執行第二組相對於第一組的相對補償。在這種情況下,它將返回包含1990年元素的最終書籍數組,然後$size計算結果數組中的元素數量,從而爲您提供書籍數量。


對於使用$unwind操作者,銘記(由於從@BlakesSeven評價此見地響應)中的溶液:

由於只有返回單個文檔除了一個空值 鍵和一個計數之外,沒有更多的機會破壞這個限制 比以前的操作具有相同的輸出。這並不是說 $ unwind「打破了限制」,而是它「爲每個陣列條目生成每個 文檔的副本」,其使用更多的存儲器(可能的存儲器 對總存儲器的10%的聚合流水線上限),因此也是 需要「時間」產生以及「時間」來處理。

和作爲最後的手段,運行下面的管道:

pipeline = [ 
    { 
     "$match": { 
      "name": "james", 
      "books.year": 1990 
     } 
    }, 
    { "$unwind": "$books" }, 
    { 
     "$match": { "books.year": 1990 } 
    }, 
    { 
     "$group": { 
      "_id": null 
      "count": { "$sum": 1 } 
     } 
    } 
] 
db.collection.pipeline(pipeline) 
+0

這裏不使用'$ unwind'的原因不包括16MB的BSON限制。由於只有一個文檔只有一個'null'鍵和一個計數返回,所以與前面的操作相同的輸出沒有更多的機會打破這個限制。這並不是說'$ unwind'「打破了限制」,它是*「爲每個數組條目生成每個文檔的副本」*,它使用更多的內存(聚集管線上可能的內存上限爲10%的總內存),因此也是需要「時間」產生以及「時間」來處理。 –

+0

您似乎也複製了另一個帖子中的'$ map' /'$ setDifference'示例,因爲這些字段與此問題無關。 –

+0

@BlakesSeven是的,它來自我的答案http://stackoverflow.com/a/34763109/122005。感謝您的更正。 – chridam

0

您可以使用$elemMatch投影到只與符合條件的圖書檢索文檔。

db.collection(collectionName).findOne({name:james, books: {year: 1990}}, { books: { $elemMatch: { year: 1990 } } }). // returned document will only contains books having the year 1990. 

如果你只想計數,那麼你需要使用aggregation框架。首先匹配文件,然後展開書籍陣列,然後匹配year字段。像下面的東西應該工作:

db.collection(collectionName).aggregate([{$match: {name: "james"}}, {$unwind:"$books"}, {$match:{"books.year":1990}}] 
+0

'$ elemMatch'只會像所有投影算子一樣返回**一個**匹配。 –

相關問題