2017-02-09 20 views
2

我們正在嘗試創建對MangoDB的調用以接收產品的所有可能過濾器。Mongo DB中的分面過濾器帶有多個選項的產品

我將嘗試創建例如我們的產品

第一款產品是有兩個選擇,選擇阿迪達斯鞋 - 顏色和大小。但對於不同的顏色,你有不同的尺寸。

{ 
    id: 1 
    name: "Adidas Shoes", 
     filters: [ 
     [ 
      { 
       code: "brand", 
       value: "Adidas" 
      }, 
      { 
       code: "colour", 
       value: "white" 
      }, 
      { 
       code: "size", 
       value: 41 
      } 
     ], 
     [ 
      { 
       code: "brand", 
       value: "Adidas" 
      }, 
      { 
       code: "colour", 
       value: "white" 
      }, 
      { 
       code: "size", 
       value: 42 
      } 
     ], 
     [ 
      { 
       code: "brand", 
       value: "Adidas" 
      }, 
      { 
       code: "colour", 
       value: "white" 
      }, 
      { 
       code: "size", 
       value: 43 
      } 
     ], 
     [ 
      { 
       code: "brand", 
       value: "Adidas" 
      }, 
      { 
       code: "colour", 
       value: "blue" 
      }, 
      { 
       code: "size", 
       value: 41 
      } 
     ], 
     [ 
      { 
       code: "brand", 
       value: "Adidas" 
      }, 
      { 
       code: "colour", 
       value: "blue" 
      }, 
      { 
       code: "size", 
       value: 44 
      } 
     ] 
    ] 
} 

第二個產品是耐克鞋。

{ 
    id: 2 
    name: "Nike Shoes", 
     filters: [ 
    [ 
     { 
      code: "brand", 
      value: "Nike", 
     }, 
     { 
      code: "colour", 
      value: "white", 
     }, 
     { 
      code: "size", 
      value: 41, 
     } 
    ], 
    [ 
     { 
      code: "brand", 
      value: "Nike", 
     }, 
     { 
      code: "colour", 
      value: "white", 
     }, 
     { 
      code: "size", 
      value: 42, 
     } 
    ], 
    [ 
     { 
      code: "brand", 
      value: "Nike", 
     }, 
     { 
      code: "colour", 
      value: "green", 
     }, 
     { 
      code: "size", 
      value: 41, 
     } 
    ] 
] 
} 

而且Reebook鞋

{ 
    id: 3 
    name: "Reebook Shoes", 
    filters: [ 
    [ 
     { 
      code: "brand", 
      value: "Reebook", 
     }, 
     { 
      code: "colour", 
      value: "black", 
     }, 
     { 
      code: "size", 
      value: 41, 
     } 
    ] 
    ] 
} 

,你可以看到選項大小取決於顏色和顏色取決於大小。

我們如何創建MongoDb.aggregate以擁有所有可能的過濾器?

品牌:阿迪達斯(1),耐克(1),Reebook(1)

尺寸: 41(3),42(2),43(1),44(1)

顏色:白(2),藍色(1),綠(1),黑色(1)

,並呼籲應是獨立的多少和過濾器,我們有(產品一種選擇,產品有更多的選擇和不同的過濾器)。你能解釋一下在這種情況下如何使用$ group,$ unwind嗎?以後我們如何通過$ facet改進它?

非常感謝!

編輯2017年2月10日

樣本響應

facets: [ 
    { 
     code: "brand", 
     values: [ 
      { 
       name: "Adidas", 
       count: 1 
      }, 
      { 
       name: "Nike", 
       count: 1 
      }, 
      { 
       name: "Reebook", 
       count: 1 
      } 
     ] 
    }, 
    { 
     code: "size", 
     values: [ 
      { 
       name: 41, 
       count: 3 
      }, 
      { 
       name: 42, 
       count: 2 
      }, 
      { 
       name: 43, 
       count: 1 
      }, 
      { 
       name: 44, 
       count: 1 
      } 
     ] 
    }, 
    { 
     code: "colour", 
     values: [ 
      { 
       name: "White", 
       count: 2 
      }, 
      { 
       name: "Blue", 
       count: 1 
      }, 
      { 
       name: "Green", 
       count: 1 
      }, 
      { 
       name: "Black", 
       count: 1 
      } 
     ] 
    } 
] 

facets: { 
    "brand": { 
     "Adidas": 1, 
     "Nike":1, 
     "Reebook":1 
    }, 
    "size": { 
     "41": 3, 
     "42":2, 
     "43":1, 
     "44":1 
    }, 
    "colour": { 
     "White": 2, 
     "Blue":1, 
     "Green":1, 
     "Black":1 
    } 
} 

這是我們的第一個階段。下一步將是如何在我選擇尺寸:41和顏色:白色時搜索可能的濾鏡。

謝謝

+0

能告訴你,你想獲得一個樣本響應?我試圖瞭解顏色和大小之間的依賴關係如何影響響​​應以及它的外觀。 –

+0

@AntonioNarkevich 我添加了對原始 – Rastislav

回答

1

這是一個聚合,可能會爲你工作。

db.getCollection('test').aggregate([ 
 
\t {$unwind: '$filters'}, 
 
\t {$unwind: '$filters'}, 
 
\t { 
 
\t \t $group: { 
 
\t \t \t _id: {code: '$filters.code', value: '$filters.value'}, 
 
\t \t \t products: {$addToSet: '$_id'} 
 
\t \t } 
 
\t }, 
 
\t { 
 
\t \t $project: { 
 
\t \t \t 'filter.value': '$_id.value', 
 
\t \t \t 'filter.count': {$size: '$products'} 
 
\t \t } 
 
\t }, 
 
\t { 
 
\t \t $group: { 
 
\t \t \t _id: '$_id.code', 
 
\t \t \t filters: {$push: '$filter'} 
 
\t \t } 
 
\t } 
 
]);

您所需要的數據是在一個slightly different format因爲有轉換分組值數組對象屬性沒有簡單的方法。

如果已經選擇了一些過濾器,則需要在第一個$展開之後進行另一個$匹配階段。 它也支持多選。假設我想要由Reebook/Adidas製作的白色/黑色鞋子。

db.getCollection('test').aggregate([ 
 
\t {$unwind: '$filters'}, 
 
\t { 
 
\t \t $match: { 
 
\t \t \t $and: [ 
 
\t \t \t \t //Add objects here fo everything that is selected already 
 
\t \t \t \t {'filters': {$elemMatch: {code: 'colour', value: {$in: ['black', 'white']}}}}, 
 
\t \t \t \t {'filters': {$elemMatch: {code: 'brand', value: {$in: ['Adidas', 'Reebook']}}}} 
 
\t \t \t ] 
 
\t \t } 
 
\t }, 
 
\t {$unwind: '$filters'}, 
 
\t { 
 
\t \t $group: { 
 
\t \t \t _id: {code: '$filters.code', value: '$filters.value'}, 
 
\t \t \t products: {$addToSet: '$_id'} 
 
\t \t } 
 
\t }, 
 
\t { 
 
\t \t $project: { 
 
\t \t \t 'filter.value': '$_id.value', 
 
\t \t \t 'filter.count': {$size: '$products'} 
 
\t \t } 
 
\t }, 
 
\t { 
 
\t \t $group: { 
 
\t \t \t _id: '$_id.code', 
 
\t \t \t filters: {$push: '$filter'} 
 
\t \t } 
 
\t } 
 
]);

的最後一件事是因行爲是這樣的: 選擇耐克=>大小和顏色由品牌過濾,但你仍然可以選擇所有的品牌。 選擇Nike + 42尺碼=>您只能選擇尺碼爲42,顏色和品牌的尺碼爲42尺碼的品牌。 依此類推。

您可以利用$ facet。事實上,當這個想法是下一個。 如果我們正在計算品牌 - 我們應該根據尺寸和顏色下拉列表中的內容來過濾記錄。 如果我們正在計算尺寸 - 應用顏色和品牌。顏色相同的邏輯。

下面是在蒙戈外殼工作代碼:

//This hash is going to by dynamic 
 
//Add and remove properties, change $in arrays 
 
//Depending on what user does 
 
var conditionsForUserSelect = { 
 
\t 'colour': {'filters': {$elemMatch: {code: 'colour', value: {$in: ['green']}}}}, 
 
\t 'brand': {'filters': {$elemMatch: {code: 'brand', value: {$in: ['Nike']}}}}, 
 
\t 'size': {'filters': {$elemMatch: {code: 'size', value: {$in: [41]}}}} 
 
}; 
 

 
var getFacetStage = function (code) { 
 
\t //Empty object, accept all filters if nothing is selected 
 
\t var matchStageCondition = {}; 
 

 
\t var selectedFilters = Object.keys(conditionsForUserSelect); 
 
\t if (selectedFilters && selectedFilters.length) { 
 
\t \t //Take all queries EXCEPT for the passed 
 
\t \t //E.g. if we are counting brand filters then we should apply color and size. 
 
\t \t //Because for example if no size/colour selected we should 
 
\t \t //allow all brands even if Reebok is selected 
 
\t \t var conditionsToApply = selectedFilters 
 
\t \t \t .filter(function (key) { 
 
\t \t \t \t return key !== code 
 
\t \t \t }) 
 
\t \t \t .map(function (key) { 
 
\t \t \t \t return conditionsForUserSelect[key] 
 
\t \t \t }); 
 

 
\t \t if (conditionsToApply && conditionsToApply.length) { 
 
\t \t \t matchStageCondition = { 
 
\t \t \t \t $and: conditionsToApply 
 
\t \t \t }; 
 
\t \t } 
 
\t } 
 

 
\t return [ 
 
\t \t {$unwind: '$filters'}, 
 
\t \t { 
 
\t \t \t $match: matchStageCondition 
 
\t \t }, 
 
\t \t {$unwind: '$filters'}, 
 
\t \t { 
 
\t \t \t $group: { 
 
\t \t \t \t _id: {code: '$filters.code', value: '$filters.value'}, 
 
\t \t \t \t products: {$addToSet: '$_id'} 
 
\t \t \t } 
 
\t \t }, 
 
\t \t { 
 
\t \t \t $project: { 
 
\t \t \t \t 'filter.value': '$_id.value', 
 
\t \t \t \t 'filter.count': {$size: '$products'} 
 
\t \t \t } 
 
\t \t }, 
 
\t \t { 
 
\t \t \t $group: { 
 
\t \t \t \t _id: '$_id.code', 
 
\t \t \t \t filters: {$push: '$filter'} 
 
\t \t \t } 
 
\t \t }, 
 
\t \t { 
 
\t \t \t $match: {_id: code} 
 
\t \t } 
 
\t ]; 
 
}; 
 

 

 
db.getCollection('test').aggregate([ 
 
\t { 
 
\t \t $facet: { 
 
\t \t \t colour: getFacetStage('colour'), 
 
\t \t \t size: getFacetStage('size'), 
 
\t \t \t brand: getFacetStage('brand') 
 
\t \t } 
 
\t } 
 
]);

+0

的樣品響應正如您在品牌阿迪達斯的結果中看到的 - 有5種產品不是真的 - 只有一種產品(有5種選擇)。而且**如果您放鬆過濾器兩次,則會丟失相關選項**顏色和大小之間的連接。 – Rastislav

+0

@Rastislav好點。讓我想一下。 –

+0

@Rastislav我更新了查詢。 冷杉查詢獲取所有可能的過濾器,所以我認爲可以放鬆兩次。 第一次在第一次展開後過濾($匹配)記錄,以便考慮尺寸和顏色之間的連接。 –