1

我有一個group對象,它包含一個members屬性。 members屬性是一個對象數組。該對象具有以下屬性。如何通過貓鼬中的模式屬性進行過濾

{"id" : "1", "status" : 1} 

我的要求是(由給定ID)來獲得用戶,誰是有狀態1,給定group對象的名單。

這是可能做到的,通過一個簡單的ID查詢和一個foreach得到。但想知道,是否有任何提前查詢來做到這一點。

我的整體Group對象如下。

var UserSchema = new Schema({ 
    user_id:{ 
    type : Schema.ObjectId, 
    ref : 'User', 
    default : null 
    }, 
    status:{ 
    type : Number, 
    default : null /* 1 - pending | 2 - rejected | 3 - accepted*/ 
    }, 
}); 

var GroupSchema = new Schema({ 
    type:{ 
    type : Number, 
    default : 1 /* 1 - group | 2 - community*/ 
    }, 
    name:{ 
    type:String, 
    default:null 
    }, 
    members:[UserSchema] 

},{collection:"groups"}); 

非常感謝您。

+0

您的意思是對於給定的,而不是_group_對象_post_對象? – chridam

+0

除了給出Schema定義之外,分享少量示例文檔和預期輸出將會很好,這樣很容易理解您要完成的任務。 – superUser

+0

@chridam'組'對象。編輯了這個問題。 –

回答

2

可以使用聚合框架由給定組ID和成員陣列狀態字段來篩選組收集的文件。這將是您的初始管道階段,這是運營商驅動的$match

下一個流水線步驟應該是$filter運算符,它根據給定的條件選擇成員數組的子集。這是必要的,因爲前一個流水線只能在文檔級進行過濾,而不能在數組/字段級進行過濾。

一旦你得到了過濾的數組,你可以應用功能作爲「填充」你的成員名單的手段。然而,由於localField是一個數組,要匹配它裏面的元素反對foreignField這是一個單一的元素,你需要$unwind數組作爲聚合管線的一個階段應用$lookup運營商之前。

下面的例子演示瞭如何應用在你的情況下,所有上述步驟:

Group.aggregate([ 
    { 
     "$match": { 
      "_id": groupId, 
      "members.status": 1   
     } 
    }, 
    { 
     "$filter": { 
      "input": "$members", 
      "as": "member", 
      "cond": { "$eq": ["$$member.status", 1] } 
     } 
    } 
    { "$unwind": "$members" }, 
    { 
     "$lookup": { 
      "from": "users" 
      "localField": "members.user_id", 
      "foreignField": "_id", 
      "as": "member" 
     } 
    } 
]).exec(function(err, results) { 
    if (err) throw err; 
    console.log(results); 
}); 

結果將包含具有兩個組和用戶屬性的文件清單。


如果您的MongoDB的版本不支持在3.2版本中引入的$filter$lookup運營商。X和更新,然後考慮使用$setDifference$map操作者的組合來過濾在$project管線數組元素。

$map操作在本質上創建保持值作爲一個子表達式到數組的每個元素的邏輯評價的結果的新的數組字段。然後,運算符返回一個集合,其中的元素出現在第一個集合中,但不出現在第二個集合中;即執行第二組相對於第一組的相對補償。在這種情況下,它將通過status屬性返回包含與父文檔無關的元素的最終成員數組。

$project流水線工序後,執行總運行和自恢復的文件都是普通的JavaScript對象,而不是Mongoose Documents(可以返回文檔的任何形狀),你需要轉換的結果,Mongoose Documents這樣就可以使用用結果在現場填充函數。

下面的例子演示了上述解決辦法:

Group.aggregate([ 
    { 
     "$match": { 
      "_id": groupId, 
      "members.status": 1   
     } 
    }, 
    { 
     "$project": { 
      "type": 1, "name": 1, 
      "members": { 
       "$setDifference": [ 
        { 
         "$map": { 
          "input": "$members", 
          "as": "member", 
          "in": { 
           "$cond": [ 
            { "$eq": [ "$$member.status", 1 ] }, 
            "$$member", 
            false 
           ] 
          } 
         } 
        }, 
        [false] 
       ] 
      } 
     } 
    } 
]).exec(function(err, result) { 
    if (err) throw err; 
    var docs = result.map(function(doc) { return new Group(doc) }); 
    Group.populate(docs, { "path": "members" }, function(err, results) { 
     if (err) throw err; 
     console.log(JSON.stringify(results, undefined, 4)); 
     res.json(results); 
    }); 
}); 
+0

lookup是如何工作的 –

+0

@sacDahal它通過對同一個數據庫中的未加註集合執行「左外部聯接」來過濾來自「已加入」集合的文檔進行處理。 '$ lookup'階段在輸入文檔的字段與「已加入」集合的文檔中的字段之間進行平等匹配。有時'$ lookup'可能會被誤用來將MongoDB當作關係數據庫來對待,但是開發人員應該知道它的使用何時適用,何時它是反模式。 – chridam

+0

看起來像我的mongoDB版本不兼容'$ filter'管道階段。是否有其他可能的階段,我們可以用來代替'$ filter'。 –

0

假設您的集合名稱爲groups,您可以查詢這樣的:

db.groups.find({"members.status":1}, {"members":1}); 

這將獲取所有如果想根據特定USER_ID查詢(假定用戶ID爲具有狀態1.用戶「1A」在這裏),您可以添加在這樣的對象:

db.groups.find({"members.status":1, "members.user_id":"1A"}, {"members":1});