我正在考虑将MongoDB用于我的下一个项目。此应用程序的核心要求之一是提供方面搜索。有没有人尝试使用MongoDB来实现方面搜索?
我有一个产品型号,其中包含大小,颜色,品牌等各种属性。在搜索产品时,此Rails应用程序应在侧栏上显示构面过滤器。 Facet过滤器看起来像这样:
Size:
XXS (34)
XS (22)
S (23)
M (37)
L (19)
XL (29)
Color:
Black (32)
Blue (87)
Green (14)
Red (21)
White (43)
Brand:
Brand 1 (43)
Brand 2 (27)
答案 0 :(得分:18)
我认为使用Apache Solr或ElasticSearch可以获得更大的灵活性和性能,但使用Aggregation Framework支持此功能。
使用MongoDB的主要问题是你需要查询N次:首先获得匹配的结果,然后每组一次;使用全文搜索引擎时,您可以在一个查询中获得所有内容。
示例强>
//'tags' filter simulates the search
//this query gets the products
db.products.find({tags: {$all: ["tag1", "tag2"]}})
//this query gets the size facet
db.products.aggregate(
{$match: {tags: {$all: ["tag1", "tag2"]}}},
{$group: {_id: "$size"}, count: {$sum:1}},
{$sort: {count:-1}}
)
//this query gets the color facet
db.products.aggregate(
{$match: {tags: {$all: ["tag1", "tag2"]}}},
{$group: {_id: "$color"}, count: {$sum:1}},
{$sort: {count:-1}}
)
//this query gets the brand facet
db.products.aggregate(
{$match: {tags: {$all: ["tag1", "tag2"]}}},
{$group: {_id: "$brand"}, count: {$sum:1}},
{$sort: {count:-1}}
)
用户使用构面过滤搜索后,您必须添加此过滤器以查询谓词并匹配谓词,如下所示。
//user clicks on "Brand 1" facet
db.products.find({tags: {$all: ["tag1", "tag2"]}, brand: "Brand 1"})
db.products.aggregate(
{$match: {tags: {$all: ["tag1", "tag2"]}}, brand: "Brand 1"},
{$group: {_id: "$size"}, count: {$sum:1}},
{$sort: {count:-1}}
)
db.products.aggregate(
{$match: {tags: {$all: ["tag1", "tag2"]}}, brand: "Brand 1"},
{$group: {_id: "$color"}, count: {$sum:1}},
{$sort: {count:-1}}
)
db.products.aggregate(
{$match: {tags: {$all: ["tag1", "tag2"]}}, brand: "Brand 1"},
{$group: {_id: "$brand"}, count: {$sum:1}},
{$sort: {count:-1}}
)
答案 1 :(得分:6)
Mongodb 3.4介绍faceted search
$ facet阶段允许您创建多面聚合 表征跨越多个维度或方面的数据 单一聚合阶段。多方面聚合提供多种方式 过滤器和分类,以指导数据浏览和分析。
输入文档只传递给$ facet阶段一次。
现在,您无需查询N次以检索N个组的聚合。
$ facet在同一组输入文档上启用各种聚合, 无需多次检索输入文档。
OP用例的示例查询类似于
db.products.aggregate( [
{
$facet: {
"categorizedByColor": [
{ $match: { color: { $exists: 1 } } },
{
$bucket: {
groupBy: "$color",
default: "Other",
output: {
"count": { $sum: 1 }
}
}
}
],
"categorizedBySize": [
{ $match: { size: { $exists: 1 } } },
{
$bucket: {
groupBy: "$size",
default: "Other",
output: {
"count": { $sum: 1 }
}
}
}
],
"categorizedByBrand": [
{ $match: { brand: { $exists: 1 } } },
{
$bucket: {
groupBy: "$brand",
default: "Other",
output: {
"count": { $sum: 1 }
}
}
}
]
}
}
])
答案 2 :(得分:2)
你可以进行查询,问题是它是否快速。比如:
find( { size:'S', color:'Blue', Brand:{$in:[...]} } )
问题是表现如何。尚未在产品中进行分面搜索的特殊工具。在路上可能会有一些类似交叉点的查询计划,这些计划很好,但这是tbd / future。
如果您的属性是预定义的集合,并且您知道它们是什么,则可以在每个属性上创建索引。在当前的实现中只使用其中一个索引,所以这将有所帮助,但只能让你到目前为止:如果数据集中等大小,可能没问题。
您可以使用可能复合两个或更多属性的复合索引。如果你有一小部分属性,这可能会很好。索引不需要使用所有变量查询,但在上面的一个中,三个中任意两个上的复合索引可能比单个项目上的索引表现更好。
如果你没有太多skus蛮力会起作用;例如如果你是1MM skues,ram中的表扫描可能足够快。在这种情况下,我会创建一个只包含facet值的表,并使其尽可能小,并将完整的sku文档保存在单独的集合中。 e.g:
facets_collection: {SZ:1,品牌:123,CLR: 'B',_ ID:} ...
如果小平面尺寸的数量不是太高,你可以改为制作一个高度复合的facit尺寸索引,你可以得到相当于上述尺寸而无需额外的工作。
如果你创建退出一些索引,最好不要创建那么多它们不再适合ram。
鉴于查询运行并且这是一个性能问题,可能只有mongo,如果它不够快,那么就可以使用solr。
答案 3 :(得分:0)
分面解决方案(基于计数)取决于您的应用程序设计。
db.product.insert(
{
tags :[ 'color:green','size:M']
}
)
但是,如果能够以上述格式提供数据,其中facet及其值连接在一起以形成一致的标记,则使用以下查询
db.productcolon.aggregate(
[
{ $unwind : "$tags" },
{
$group : {
_id : '$tags',
count: { $sum: 1 }
}
}
]
)
请参阅下面的结果输出
{
"_id" : "color:green",
"count" : NumberInt(1)
}
{
"_id" : "color:red",
"count" : NumberInt(1)
}
{
"_id" : "size:M",
"count" : NumberInt(3)
}
{
"_id" : "color:yellow",
"count" : NumberInt(1)
}
{
"_id" : "height:5",
"count" : NumberInt(1)
}
除此步骤外,您的应用程序服务器可以在发送回客户端之前进行颜色/大小分组。
注意 - 组合facet及其值的方法为你提供了聚合的所有方面值,你可以避免 - "使用MongoDB的主要问题是你必须查询N次:首先获得匹配的结果然后一次每组;使用全文搜索引擎时,您可以在一个查询中获得所有内容。"看加西亚的回答