【问题标题】:Faceted filters in Mongo DB with for products with multiple optionsMongo DB 中的多面过滤器,用于具有多种选项的产品
【发布时间】:2017-02-09 15:50:39
【问题描述】:

我们正在尝试创建对 MangoDB 的调用以接收所有可能的产品过滤器。

我将尝试创建我们产品的示例

第一个产品是阿迪达斯鞋,它有两种选择 - 颜色和尺码。但是对于不同的颜色,您有不同的尺寸。

{
    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 改进它?

非常感谢!!!

编辑 02/10/2017

示例响应

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 和颜色:白色时如何搜索可能的过滤器。

谢谢

【问题讨论】:

  • 您能否展示您想要获得的示例响应?我试图了解颜色和大小之间的依赖关系将如何影响响​​应以及它的外观。
  • @AntonioNarkevich 我添加了对原始响应的示例

标签: mongodb aggregation-framework faceted-search


【解决方案1】:

这是一个可能对您有用的聚合。

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

您需要的数据来自 slightly different format,因为没有简单的方法可以将分组值数组转换为对象属性。

如果已经选择了一些过滤器,则在第一个 $unwind 之后需要另一个 $match 阶段。 它还支持多选。假设我想要 Reebook/Adidas 的白色/黑色鞋子。

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

最后一件事是这样的依赖行为: 选择 Nike => 尺寸和颜色按品牌过滤,但您仍然可以选择所有品牌。 选择 Nike + 42 码 => 您只能选择 42 码的品牌、颜色和有 42 码鞋子的品牌。 以此类推。

您可以利用 $facet 来实现它。事实上,当这个想法是下一个。 如果我们正在计算品牌 - 我们应该根据在大小和颜色下拉列表中选择的内容来过滤记录。 如果我们正在计算尺寸 - 应用颜色和品牌。颜色的逻辑相同。

这是在 mongo shell 中工作的代码:

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

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

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

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

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


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

【讨论】:

  • 正如您在 Adidas 品牌的结果中看到的那样 - 有 5 种产品不正确 - 只有一种产品(有 5 个选项)。而且如果您两次展开过滤器,您将失去相关选项之间的联系颜色和大小。
  • @Rastislav 我更新了查询。第一个查询正在获取所有可能的过滤器,所以我认为可以放松两次。第二个过滤器($match)在第一次展开后过滤记录,因此考虑了大小和颜色之间的联系。
  • @Anthonio 谢谢!看起来不错!一个小小的想法 - 应该有可能在一个过滤器中选择更多值。例如,当您选择阿迪达斯时,应该可以多选其他品牌。你明白吗?
  • @Rastislav 给你。再次更新。
  • 我们很接近 :) 我会尝试写下它应该如何工作。当您选择白色时,您应该像这样交换其他过滤器:您应该显示所有品牌哪些产品包含白色,您应该显示所有尺寸哪些产品包含白色并且您应该显示所有颜色(不仅仅是白色)。然后您选择 41 号 - 所以您选择了 41 号和白色。您应该向所有品牌展示哪些产品包含白色和尺寸 41,您应该展示所有尺寸哪些产品只包含白色,您应该展示所有颜色哪些产品包含尺寸 41
猜你喜欢
  • 2013-10-20
  • 2014-11-26
  • 2023-03-05
  • 1970-01-01
  • 2014-12-04
  • 1970-01-01
  • 2017-04-16
  • 2020-10-15
  • 1970-01-01
相关资源
最近更新 更多