【问题标题】:Using weights for searching in mongoose在猫鼬中使用权重进行搜索
【发布时间】:2015-03-06 11:40:35
【问题描述】:

所以我已经阅读了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"}
]

【问题讨论】:

  • 好吧,$text 是“文本搜索”运算符的一个相当贴切的名称,因为它就是这样做的。但实际上,您对“最近”到布尔值或数值的期望是什么?它们几乎是绝对的。如果您想从它们那里得到“模糊”的东西,请使用“范围”运算符,例如 $gte$lte,并且可能省略布尔值,除非您实际上想要返回满足这些条件的结果。跨度>
  • 好吧,给定输入字段,它们都应该是 or'd 在一起,所以如果文档的字段 petsAllowed 为 true,那么它应该包含在结果中。然后所有这些结果都应该根据与输入的结果匹配的结果进行排序。
  • 因此您希望作为示例:"petsAllowed": true 位于结果的“顶部”,但还包括其下方的 false 值。听起来像你在问什么?或多或少地为每个完全匹配的条件添加权重分数,然后首先对具有最高分数的结果进行排序。所以,戴上你的思考帽。您将如何计算 MongoDB 查询中的值,然后使用计算结果进行排序?有一个答案,但这样做并不是很有效。
  • 只有当该文件有 4 个房间或房屋类型是联排别墅时才会包含错误。是的,这正是我要问的。我知道 mongoose 有一个 sort() 函数,但同样,我不确定如何去做(这就是我问的原因)。很抱歉,如果这不是您要找的东西,但我对如何使用 mongoose 进行搜索有点迷茫。
  • 我最初的部分观点是你的问题在你真正想要的可能解释中“过于宽泛”。与其与我来回聊天,不如尝试编辑您的问题以准确解释在给定一组条件下您期望达到的结果。以您当前拥有的形式发布会产生“精确”匹配而不是“模糊”搜索。您可以编写模糊逻辑,但这意味着首先定义条件。你目前没有。用所有相关内容更新您的问题。

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


【解决方案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  }

把它分解成这里发生的事情,第一件事是我自己的决定,我可能想要“类型”中包含“房子”的任何东西,以及所选类型的任何“精确匹配”。这是确定这一点的任意逻辑,但重点是我们将在此示例中同时考虑两者。

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

还有一个条件是寻找房间的数量。这里的一个任意决定是我会考虑任何有四个或更多房间的东西,因此**$gte** 条件。我还想考虑比要求的房间少一个的东西。再次武断的逻辑,但只是为了证明你在需要时做什么。

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

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

这是一个“三元”运算符,因为它评估逻辑“if”条件作为第一个参数,然后返回true 第二个参数或false 第三个参数。与任何三元组一样,当您想要测试不同的条件以“嵌套”false 参数中的运算符以便“流过”它们时。

一旦确定了“分数”,$sort 就会按照最大“分数”的顺序排列结果。

可以通过在管道末尾添加$skip$limit 管道阶段以传统形式实现分页,或者通过保留最后看到的值并排除更多涉及的“前向分页”来完成那些从结果中寻找“分数”$lte 看到的最后一个“分数”的人。这本身就是另一个主题,但这完全取决于哪种分页概念最适合您的应用程序。

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

这些是原则,但当然一切都是有代价的,“即时”计算这些权重不仅仅是一个可以在索引中查找值的简单查询。

【讨论】:

  • 非常感谢您!它肯定会清除很多东西。
  • @user1883614 我有一些空闲的午餐时间。当然,您的加权规则会有所不同,但至少这应该是在实现您的实际逻辑时要遵循的基本模板。
  • 唯一的问题是最多大约有 6 个字段,但并非每次我调用搜索时都会使用所有字段,所以我会看看如何去做。
  • @user1883614 这正是我所说的“构建数据结构”在stackoverflow上有这样的例子(我知道,因为我已经写了一些答案)或者你总是可以问另一个如果遇到困难,如何根据您的输入“构建”您的条件。
  • 我会看看我能不能得到一些东西来处理这一切,否则我很快就会问另一个问题。
猜你喜欢
  • 2014-09-03
  • 2021-10-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-08-05
  • 2017-08-16
  • 2021-01-05
  • 1970-01-01
相关资源
最近更新 更多