使用权重在猫鼬中搜索

时间:2015-03-06 01:56:29

标签: node.js mongodb mongoose mongodb-query aggregation-framework

所以我通读了this,但对于如何解决这个问题仍然有些困惑。

我的模型包含各种字段,包括字符串,数字和布尔值。

$ text似乎只能接受字符串。

如果我想进行如下搜索,该怎么办?

model.find({petsAllowed:true, rooms:4, house:"townhouse"}).sort()

因此,它会搜索mongodb中与我输入内容相匹配的所有不同条目,并根据条目与输入字段的接近程度对其进行排序。

我知道mongoose支持这个,所以我不想依赖插件。

这是我想要的结果:

[ 
Document 1 (most closely matched with the input): 
    {petsAllowed:true, rooms:4, house:"townhouse"},
Document 2: {petsAllowed:false, rooms:4, house:"townhouse"},
Document 3: {petsAllowed:true, rooms:5, house:"townhouse"},
Document 4: {petsAllowed:false, rooms:3, house:"townhouse"}
]

1 个答案:

答案 0 :(得分:1)

为了“加权”回答,那么基本原则是您必须确定结果的哪些部分对您正在执行的搜索更重要,并且基本上按照您的结果的重要性顺序提供适当的分数规则。

这实际上是一个MongoDB的东西,而不是外部编码的东西,因为你需要分析服务器上的结果,特别是当你考虑像有很多这样的“分页”加权结果时。要在服务器上执行此操作,您需要.aggregate()方法。

完成此操作后,我在等待您的输入时已经拥有了自己的数据样本,但它仍然是一个例子。考虑这个初始样本。

{ "petsAllowed" : true,  "rooms" : 5, "type" : "townhouse" }
{ "petsAllowed" : false, "rooms" : 4, "type" : "house"     }
{ "petsAllowed" : true,  "rooms" : 4, "type" : "townhouse" }
{ "petsAllowed" : false, "rooms" : 4, "type" : "townhouse" }
{ "petsAllowed" : true,  "rooms" : 2, "type" : "townhouse" }
{ "petsAllowed" : true,  "rooms" : 3, "type" : "townhouse" }
{ "petsAllowed" : true,  "rooms" : 4, "type" : "house"     }

所以这也包括一个“类型”,我们在比赛中也将“模糊”,而不仅仅是确定“精确”匹配。使用聚合管道并根据输入设置逻辑基本上是这样的:

 var roomsWanted = 4,
     exact = "townhouse",
     types = [];

 // Some logic to get the "fuzzy" values
 var fuzzy = [/house/]

 // Combine exact and fuzzy    
 types.push(exact);
 fuzzy.forEach(function(fuzz) {
     types.push(fuzz);
 });

 // Perform the query
 db.houses.aggregate([
     // Match items you want and exclude others
     { "$match": { 
         "type": { "$in": types }, 
         "$or": [
             { "rooms": { "$gte": roomsWanted } },
             { "rooms": roomsWanted - 1 }
         ]
     }},

     // Calculate a score
     { "$project": {
         "petsAllowed": 1,
         "rooms": 1,
         "type": 1,
         "score": {
             "$add": [
                 // Exact match is higher than the fuzzy ones
                 // Fuzzy ones score lower than other possible matches
                 { "$cond": [
                     { "$eq": [ "$type", "townhouse" ] },
                     20,
                     2
                 ]},
                 // When petsAllowed is true you want a weight
                 { "$cond": [
                     "$petsAllowed",
                     10,
                     0
                 ]},
                 // Score depending on the roomsWanted
                 { "$cond": [
                     { "$eq": [ "$rooms", roomsWanted ] },
                     5,
                     { "$cond": [
                         { "$gt": [ "$rooms", roomsWanted ] },
                         4,
                         { "$cond": [
                             { "$eq": [ "$rooms", roomsWanted - 1 ] },
                             3,
                             0
                         ]}
                     ]}
                 ]}
             ]
         }
     }},
     { "$sort": { "score": -1 } },
 ])

然后,您获得的结果将按生成的“得分”进行排序,如下所示:

{ "petsAllowed" : true,  "rooms" : 4, "type" : "townhouse", "score" : 35 }
{ "petsAllowed" : true,  "rooms" : 5, "type" : "townhouse", "score" : 34 }
{ "petsAllowed" : true,  "rooms" : 3, "type" : "townhouse", "score" : 33 }
{ "petsAllowed" : false, "rooms" : 4, "type" : "townhouse", "score" : 25 }
{ "petsAllowed" : true,  "rooms" : 4, "type" : "house",     "score" : 17 }
{ "petsAllowed" : false, "rooms" : 4, "type" : "house",     "score" : 7  }

打破这里发生的事情,首先是我自己的决定,我可能想要在“类型”中包含“house”的任何内容以及所选类型的任何“完全匹配”。这是确定这一点的任意逻辑,但重点是我们将在这个例子中考虑两者。

当然搜索会想要过滤掉你真正不想要的东西,所以有一个$match管道阶段来做这件事。 $in运算符用于将“type”与精确的“联排别墅”术语或/house/的可能正则表达式匹配进行匹配。那是因为我也想要它,你的里程可能会因你想要真正做的而有所不同。

还有条件来查找房间数量。在这里,一个任意的决定是我会考虑四个房间或更大的房间,因此**$gte**条件。我还想考虑比所要求的空间少一些的东西。再次使用任意逻辑,但只是为了证明你在想要的时候所做的事情。

$match完成“过滤”后,您将结果移至$project阶段。这里的要点是您想要一个计算的“得分”值,但是在使用此管道阶段时,您还必须指定要返回的所有字段。

您可以在这里选择适用于条件的“重量”。 $add运算符将“求和”作为参数给出的结果,而这些结果又由$cond或“条件”运算符生成。

这是一个“三元”运算符,因为它将逻辑“if”条件作为第一个参数进行求值,然后返回true第二个参数或false第三个参数。像任何三元一样,当你想测试不同的条件来“嵌套”false参数中的运算符以便“流过”它们时。

确定“得分”后,您$sort的结果将按照最大“得分”的顺序排列。

通过在管道末端添加$skip$limit管道阶段,或者通过保留最后一个值(s)进行更多参与“前向分页”,可以以传统形式实现分页。 )看到并排除结果中的那些寻找“得分”$lte所见的最后“得分”。这本身就是另一个话题,但这一切都取决于哪种分页概念最适合您的应用。

当然对于像“petsAllowed”这样的一些逻辑,你只希望这些条件在它实际对你想要的选择标准有效时计算一个权重。汇总管道语法的一部分,实际上所有MongoDB查询语法都是无论语言实现它基本上只是“数据结构”的表示。因此,您可以根据输入的需要“构建”管道阶段,就像代码中的任何数据结构一样。

这些是原则,但当然一切都需要付出代价并且“动态”计算这些权重不仅仅是一个简单的查询,其中的值可以在索引中查找。