我已按以下格式获取数据..
{
"_id" : ObjectId("534fd4662d22a05415000000"),
"product_id" : "50862224",
"ean" : "8808992479390",
"brand" : "LG",
"model" : "37LH3000",
"features" : [{
{
"key" : "Screen Format",
"value" : "16:9",
}, {
"key" : "DVD Player / Recorder",
"value" : "No",
},
"key" : "Weight in kg",
"value" : "12.6",
}
... so on
]
}
我需要将一种产品的功能与其他产品进行比较,并根据功能匹配百分比将结果划分为不同的类别(100%匹配,50-99%匹配)。
我最初的想法是为每个功能准备一个动态查询或条件,并在php中执行百分比,但这意味着mongodb将返回我甚至那些只有1个功能匹配的产品。而且我认为几乎所有类别的产品都有一些共同的特性,所以我担心我可能会在php中使用很多产品。
我基本上有两个问题。
答案 0 :(得分:2)
我假设您想要将其余的集合与给定的产品进行比较,这是一个集合的教科书示例:
lookingat = db.products.findOne({product_id:'50862224'})
matches = db.products.aggregate([
{ $unwind: '$features' },
{ $match: { features: { $in: lookingat.features }}},
{ $group: { _id: '$product_id', matchedfeatures: { $sum:1 }}},
{ $sort: { matchedfeatures: -1 }},
{ $limit: 5 },
{ $project: { _id:0, product_id: '$_id',
pctmatch: { $multiply: [ '$matchedfeatures',
100/lookingat.features.length ]}
}}
])
从具有6个功能的集合中的产品的角度简要介绍一下,并将其与具有4个功能的目标产品('lookingat')进行比较,其中3个匹配:
答案 1 :(得分:2)
你的解决方案确实应该是MongoDB特定的,否则你最终会在客户端进行计算和可能的匹配,这对性能不会有好处。
当然,您真正想要的是在服务器端进行处理的方法:
db.products.aggregate([
// Match the documents that meet your conditions
{ "$match": {
"$or": [
{
"features": {
"$elemMatch": {
"key": "Screen Format",
"value": "16:9"
}
}
},
{
"features": {
"$elemMatch": {
"key" : "Weight in kg",
"value" : { "$gt": "5", "$lt": "8" }
}
}
},
]
}},
// Keep the document and a copy of the features array
{ "$project": {
"_id": {
"_id": "$_id",
"product_id": "$product_id",
"ean": "$ean",
"brand": "$brand",
"model": "$model",
"features": "$features"
},
"features": 1
}},
// Unwind the array
{ "$unwind": "$features" },
// Find the actual elements that match the conditions
{ "$match": {
"$or": [
{
"features.key": "Screen Format",
"features.value": "16:9"
},
{
"features.key" : "Weight in kg",
"features.value" : { "$gt": "5", "$lt": "8" }
},
]
}},
// Count those matched elements
{ "$group": {
"_id": "$_id",
"count": { "$sum": 1 }
}},
// Restore the document and divide the mated elements by the
// number of elements in the "or" condition
{ "$project": {
"_id": "$_id._id",
"product_id": "$_id.product_id",
"ean": "$_id.ean",
"brand": "$_id.brand",
"model": "$_id.model",
"features": "$_id.features",
"matched": { "$divide": [ "$count", 2 ] }
}},
// Sort by the matched percentage
{ "$sort": { "matched": -1 } }
])
因为您知道正在应用的 $or
条件的“长度”,那么您只需要找出“features”数组中有多少元素符合这些条件。这就是管道中第二个$匹配的全部内容。
一旦计算完毕,您只需将 $or
传递的条件数除以。这里的美妙之处在于,现在你可以做一些有用的事情,比如按相关性排序,然后甚至“点”结果服务器端。
当然,如果您想要对此进行一些额外的“分类”,您需要做的就是在管道的末尾添加另一个 $project
阶段:
{ "$project": {
"product_id": 1
"ean": 1
"brand": 1
"model": 1,
"features": 1,
"matched": 1,
"category": { "$cond": [
{ "$eq": [ "$matched", 1 ] },
"100",
{ "$cond": [
{ "$gte": [ "$matched", .7 ] },
"70-99",
{ "$cond": [
"$gte": [ "$matched", .4 ] },
"40-69",
"under 40"
]}
]}
]}
}}
或类似的东西。但是$cond
运营商可以在这里为您提供帮助。
架构应该没问题,因为你可以在feature列中的条目的“key”和“value”上有一个复合索引,这对于查询来说应该可以很好地扩展。
当然,如果您确实需要更多内容,例如分面搜索和结果,您可以查看Solr或弹性搜索等解决方案。但是完全实现这一点在这里会有点冗长。