【问题标题】:MongoDB Closest Match on propertiesMongoDB 属性的最接近匹配
【发布时间】:2018-07-28 09:17:20
【问题描述】:

假设我在 MongoDB 中有一个用户集合,其架构如下所示:

{
    name: String,
    sport: String,
    favoriteColor: String
}

假设我传递了这样的值来匹配用户:

{ name: "Thomas", sport: "Tennis", favoriteColor:"blue" }

我想做的是根据所有这些属性匹配用户。但是,如果没有用户回来,我想仅在以下属性上匹配用户:

{sport: "Tennis", favoriteColor:"blue" }

如果没有用户回来,我想只在这个属性上匹配一个用户:

{ favoriteColor: "blue" }

是否可以使用 Mongo 在一个查询中执行类似的操作?我在 Mongo 中看到了 $switch 条件,它将匹配一个案例,然后立即返回,但问题是我无法访问它在 then 块中检索到的文档。好像只能在里面写字符串。

关于如何完成我正在寻找的任何建议?

只执行多个User.find({...}) 查询是最好的(也是唯一的方法)吗?

【问题讨论】:

  • mongodb没有内置这样的功能。您是否有特定原因在服务器端执行此类操作?如果 else 阻止执行此类请求,您应该使用客户端。您可以运行所有查询,尽管在 $facet 阶段内的单独管道中并不理想。

标签: mongodb mongoose mongodb-query aggregation-framework


【解决方案1】:

这是一个使用 MongoDB 文本索引的好案例:

首先您需要在这些字段上创建text 索引:

db.users.ensureIndex({ name: "text", sport: "text", favoriteColor: "text" });

然后您可以搜索与“$text”的最佳匹配,并限制显示:

db.users.find( { $text: { $search: "Tennis blue Thomas" } } ).limit(10)

【讨论】:

  • 我认为这是最适合您的情况@Thomas(因为您正在执行搜索查询),尤其是您甚至可以Control Search Results with Weights,这样您就可以通过返回的文档来使用您的结果scores .
  • 您好,我该如何使用正则表达式?就像我在搜索“网球”一样,但我是一个字母一个字母地写。我试过 db.users.find( { $text: { $search: {$regex:"ten"} } })
  • @tatuck,文本索引是用于语义搜索,而不是词法搜索。如果你想使用正则表达式的词法搜索,创建一个普通索引
【解决方案2】:

尝试在聚合管道中为所有具有权重的文档添加排名,并将排名相加,$sort 降序得到最匹配的文档

  1. 姓名 -> 1
  2. 运动 -> 2
  3. 最喜欢的颜色 -> 4

通过这种匹配的喜爱颜色总是比运动和名称或两者的组合具有更高的权重

聚合管道

db.col.aggregate([
    {$match : {
        $or : [
            {"name" : {$eq :"Thomas"}},
            {"sport" : {$eq : "Tennis"}},
            {"favoriteColor" : {$eq : "blue"}}
        ]
    }},
    {$addFields : {
        rank : {$sum : [
            {$cond : [{$eq : ["$name", "Thomas"]}, 1, 0]},
            {$cond : [{$eq : ["$sport", "Tennis"]}, 2, 0]},
            {$cond : [{$eq : ["$favoriteColor", "blue"]}, 4, 0]}
        ]}
    }},
    {$match : {rank :{$gt : 0}}},
    {$sort : {rank : -1}}
])

【讨论】:

    【解决方案3】:

    希望此查询能满足您的要求,您可以在单个 db hit 中获得相关结果。只需在aggregate 管道中创建查询

    db.collection.aggregate([
    {$match : {
            $or : [
                {"name" : {$eq :"Thomas"}},
                {"sport" : {$eq : "Tennis"}},
                {"favoriteColor" : {$eq : "blue"}}
            ]
    }},
     {$addFields : {
            rank : {$sum : [
                {$cond : [{$and:[{$eq : ["$name", "Thomas"]},{$eq : ["$sport", "Tennis"]},{$eq : ["$favoriteColor", "blue"]}]  }  , 1, 0]},
                {$cond : [{$and:[{$eq : ["$name", "Thomas"]},{$eq : ["$sport", "Tennis"]}]  }  , 2, 0]},
                {$cond : [{$and:[{$eq : ["$name", "Thomas"]}]  } , 3, 0]},
            ]}
    }},
    
    {$group:{
        _id:null,
        doc:{$push:'$$ROOT'},
        rank:{$max:'$rank'}
    }},
    {$unwind:'$doc'},
    {$redact: {
        $cond: {
          if: { $eq: [ "$doc.rank", '$rank' ] },
          then: "$$KEEP",
          else: "$$PRUNE"
        }
      }},
      {
          $project:{
                name:'$doc.name',
                sport:'$doc.sport',
                favoriteColor:'$doc.favoriteColor',
        }}
    ])
    

    【讨论】:

      【解决方案4】:

      只需在 mongoDB 聚合管道中为 $match 管道创建一个查询构建器,或者也将其用于查找,创建 JavaScript 对象变量并动态构建您的查询。

      var query={};
      
      if(name!=null){
              query['name']={ '$eq': name};
      }
      if(sport!=null){
                  query['sport']={ '$eq': sport};
      }
      if(favoriteColor!=null){
                  query['favoriteColor']={ '$eq': favoriteColor};
      }
      
      db.collection.find(query)
      

      它将在动态基础上给出完全匹配的结果

      【讨论】:

        【解决方案5】:

        你试过$or:https://docs.mongodb.com/manual/reference/operator/query/or/

        当我想检查用户名或电子邮件是否存在时使用它..

        【讨论】:

        • 是的,但问题是,它总是会返回满足最基本$or条件的集合,即{ favoriteColor: blue }
        • 我想我可以用$and 做一个复杂的`$or` 来解决这个问题
        • 我认为它可能会先查询第一个条件,然后再查询第二个,可能不是这个选项。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2011-03-01
        • 2015-03-18
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-03-29
        • 1970-01-01
        相关资源
        最近更新 更多