MongoDB 比较数组

3
我有一个包含成千上万个用户的集合。每个用户文档都有一些属性,例如姓名、年龄范围和收藏品。
我可以轻松帮助用户找到与他们年龄相同的其他用户,但我也想以某种方式允许他们选择匹配百分比或与其他用户共同喜欢的数量。
例如:
User 1
Name: x
Age Range: 19-25
Favourites: ["Red", "Green", "Blue"]

User 2
Name: y
Age Range: 19-25
Favourites: ["Orange", "Red", "Pink"]

User 3
Name: z
Age Range: 19-25
Favourites: ["Orange", "Red", "Blue"]

这里,如果用户1搜索具有33%匹配度或者至少一个共同匹配项的用户,他们只会得到用户2。如果他们搜索66%或至少两个共同匹配项,他们将得到用户3。
我已经完成了年龄范围的匹配,并尝试使用mongoDB的$all和$in方法,但这不是我想要的。有人能指点我正确的方向吗?

请检查此链接(http://stackoverflow.com/a/37459177/5919473),因为它看起来有点相似 - 如果您使用redact进行操作,您可以只得到匹配项并获得%。 - profesor79
百分比是如何定义的?范围是一个两个元素的数组还是一个字符串? - styvane
3个回答

1

如果你想找到具有某些共同属性的对象,我会采用不同的方法。我会在属性上创建全文索引。在你的特定情况下,它是 Favorites

全文索引在搜索文本时速度更快。它还给出一个 text score,显示给定术语在集合中匹配的程度。

在你的特定情况下,我将测量 text score,以查看其他文档是否符合我的标准。

你需要先创建 full text 索引。

db.collection.createIndex({"Favourites":"text"})

创建完全文本索引后,假设您正在尝试查找所有至少匹配66%的文档。这意味着如果我们有三个文本术语,我们希望所有文档至少匹配其中的两个术语。
var match = 2;
var terms = "Red Green Blue";

db.collection.aggregate([
 { $match: { $text: { $search: terms } } },
 { $project: {User:1, _id:0, Name:1, "Age Range":1, Favourites:1, score: {$meta: "textScore"}}},
 { $sort: { score: 1 }},
 { $match: { score: { $gte: match } } }
])

在上面的示例中,我们希望查找至少具有两个匹配项的所有文档。上面的代码片段将返回:
{ 
    "User" : 3.0, 
    "Name" : "z", 
    "Age Range" : "19-25", 
    "Favourites" : [
        "Orange", 
        "Red", 
        "Blue"
    ], 
    "score" : 2.2
}
{ 
    "User" : 1.0, 
    "Name" : "x", 
    "Age Range" : "19-25", 
    "Favourites" : [
        "Red", 
        "Green", 
        "Blue"
    ], 
    "score" : 3.3000000000000003
}

我们找到了两个至少匹配两个词的文档。
更新:
OP提到术语可以包含多个单词的短语。MongoDB全文搜索允许搜索短语,并要求用字符串引号将短语包裹起来。
例如:var terms = "Red \"Light Blue\""; 使用上面的代码片段,并假设文档包含收藏夹中的Light Blue短语,将返回匹配的文档。
然而,有一个陷阱。如果有任何其他术语,MongoDB总是对短语执行逻辑和操作。在上面的例子中,代码将搜索包含短语Light Blue和术语Red的文档。
请参见https://docs.mongodb.com/manual/reference/operator/query/text/#phrases

谢谢!这是一个简洁的方法,似乎运行速度会更快。但我有一个问题,如果颜色是由两个单词组成的,比如“浅蓝”或“深绿”,该怎么办呢?目前我把所有颜色都存储在一个数组中,这样很容易显示,那么一旦它们被保存为文本,最佳的显示实践是什么?在 UI 方面,这是一个多选。 - SeanWM
你需要用字符串引号进行转义,例如 "Light Blue"。它将搜索整个短语。请参阅 https://docs.mongodb.com/manual/reference/operator/query/text/#phrases - Saleem
我喜欢这个想法,正在努力实现它。我试图将我的多选数组转换为字符串,通过使用空格连接各个颜色。这将给我一个属性,如:Favourites: "Red Blue Light Blue"。在保存字段之前,如何适当地转义它们,使它们像短语一样? - SeanWM
我不建议保存转义字段。然而,在将数据传递给MongoDB查询之前,您应该在应用程序中执行此操作。请参见我的上面更新的内容。 - Saleem
如果您不建议这样做,那么您会推荐做什么?关于一个由多个单词组成的颜色。 - SeanWM
我建议不要更新你的文档,而是在将字符串传递给查询之前转义它们。例如:'var terms = "红色\“浅蓝色\”";` - Saleem

0

这会有所帮助
你期望的结果是这样的吗? 例如:至少有两个常见匹配项['orange','red','pink']与“favourites”相匹配。因此,“User3”是期望的结果。查询如下:

db.test.aggregate([ 
{"$match": 
{ 
    favourites: {"$in" : ['orange','red', 'pink']}
 } 
},
 { "$unwind": "$favourites" },
{ "$match": { favourites: { "$in":  ['orange','red', 'pink'] } }},

{"$group" :
{
    "_id": {
        "_id": {"id":"$_id", "name":"$name"},
        "favourites": "$favourites"
    }
}
},
{ "$group": {
    "_id": "$_id._id",
    "favourites": { "$push": "$_id.favourites" },
    "length": { "$sum": 1 }
}},
{ "$match": { "length":  2  }}
])

结果:

{
"result" : [ 
    {
        "_id" : ObjectId("574cf11b0b3052089fe57605"),
        "favourites" : [ 
            "red", 
            "orange"
        ],
        "length" : 2
    }
],
"ok" : 1
}

0

这篇博客提出了一个简单的建议,即通过逐个列出所需属性并为每个属性分配相似度值,然后将它们相加作为查询的一部分来组合每个文档的匹配。 (此处的示例是从博客中剪切粘贴的,但我相信您可以根据自己的数据结构进行调整);

"$project" : {
  "c" : {"$add" : [
     {"$cond" : [{"$eq" : ["$firstname","John"]},1,0]},
     {"$cond" : [{"$eq" : ["$middleone","Andrew"]},1,0]},
     {"$cond" : [{"$eq" : ["$middletwo","Cartwright"]},1,0]},
     {"$cond" : [{"$eq" : ["$lastname","Smith"]},1,0]}
        ]
  }

你可以通过将搜索发起者列表逐个包含在查询中,来扩展收藏夹数组。

这样做的好处是可以根据相似度对结果进行排序。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接