【问题标题】:Sort with results matching condition on top排序结果匹配条件在顶部
【发布时间】:2017-05-18 05:27:22
【问题描述】:

我已经在 php 中编写了 mongodb 聚合查询,如下面的代码行。

         $orrollno= array('$or' => array(array("student.roll_no" => new MongoRegex("/$arg/i"))));

            $orlastname= array('$or' => array(array("student.last_name" => new MongoRegex("/$arg/i"))));

            $oremail= array('$or' => array(array("student.email" => new MongoRegex("/$arg/i"))));

            $orguardian= array('$or' => array(array("student.guardian_name" => new MongoRegex("/$arg/i"))));

            $orphone= array('$or' => array(array("student.phone1" => new MongoRegex("/$arg/i"))));

            $orfullname= array('$or' => array(array("fullname" => new MongoRegex("/$arg/i"))));

            $orfirstmiddle= array('$or' => array(array("firstmiddle" => new MongoRegex("/$arg/i"))));

            $orfirstlast= array('$or' => array(array("firstlast" => new MongoRegex("/$arg/i"))));

            $query = array( '$or' => array($orrollno,$orlastname,$oremail,$orguardian,$orphone,$orfullname,$orfirstmiddle,$orfirstlast));


         $outputTotalResults= $this->db->studentTbl->aggregate(
          array(
                 array(
                    '$project' => array(
                      'fullname' => array('$concat' => array('$first_name',  ' ',  '$middle_name', ' ', '$last_name')),
                      'firstmiddle' => array('$concat' => array('$first_name',  ' ',  '$middle_name')),
                      'firstlast' => array('$concat' => array('$first_name',  ' ',  '$last_name')),
                      'student' => '$$ROOT'
                       )
                ),
                  array(
                      '$match' => $query
                       ),
                    )
                 );

我正在尝试对来自 $match => $query 的结果进行排序。 例如 $arg 包含“William David”,那么结果应该首先包含名称为 Willian David 的记录,然后是其余结果。

任何帮助将不胜感激!!!

根据你的建议,我现在尝试了以下

            $outputTotalResults= $this->db->studentTbl->aggregate(
          array(
                 array(
                    '$project' => array(
                      'fullname' => array('$concat' => array('$first_name',  ' ',  '$middle_name', ' ', '$last_name')),
                      'firstmiddle' => array('$concat' => array('$first_name',  ' ',  '$middle_name')),
                      'firstlast' => array('$concat' => array('$first_name',  ' ',  '$last_name')),
                      'student' => '$$ROOT',
                       'weight' => array(
                          '$cond' => array(
                                       array( 
                                         '$or' => array( 
                                          array('$eq' => array('$fullname' => $arg )),
                                          array('$eq' => array('$firstmiddle' => $arg)),
                                          array('$eq' => array('$firstlast' => $arg)),
                                          )
                                        ),
                                       10,
                                        0
                                      )
                                   ),
                        array(
                             '$sort' => array( 'weight'=> -1 )
                              ),
                        array(
                             '$match' => $query
                             ),
                         )
                      )
                   )
               );

【问题讨论】:

  • 我认为你在这里做了完全错误的操作。完全忘记您的代码,那么实际上您打算将first_name 中的“Willam”和last_name 中的“David”的结果“浮动”到结果的“顶部”,之后还有其他结果吗?如果是这样,那么有一种方法可以做到这一点,但 $group 不是完成该工作的运营商。
  • 哪个算子适合做这个需求。我对 mongodb 很陌生...请帮助

标签: mongodb mongodb-query aggregation-framework mongodb-php


【解决方案1】:

您想要在这里实现的是“加权排序”,您实际上是想根据条件计算一个字段,然后将$sort 管道阶段应用于该结果。

一般情况是将$cond 与逻辑条件一起应用,并返回一个值或不返回一个值,可能以级联方式用于多个条件。

最好使用 MongoDB 3.4 及更高版本,使用$addFields

array(
  array(
    '$addFields' => array(
      'weight' => array(
        '$cond => array(
          array( 
            '$and' => array( 
              array( '$eq' => array( '$first_name', 'Willam' ) )
              array( '$eq' => array( '$last_name', 'David' ) )
            )
          ),
          10,
          0
        )
      )
    )
  ),
  array(
    '$sort' => array( 'weight'=> -1 )
  )
)

或者在以前的版本中,您不能简单地将新字段“附加”到您使用 $project 的现有文档结构中,要么指定您想要的每个字段,要么通过 $$ROOT 更改在一个属性下返回的结构:

array(
  array(
    '$project' => array(
      'first_name' => 1,
      'last_name' => 1,
      'weight' => array(
        '$cond => array(
          array( 
            '$and' => array( 
              array( '$eq' => array( '$first_name', 'Willam' ) )
              array( '$eq' => array( '$last_name', 'David' ) )
            )
          ),
          10,
          0
        )
      )
    )
  ),
  array(
    '$sort' => array( 'weight'=> -1 )
  )
)

所以在这种简单的情况下,只要满足“两个”条件(通过$and),weight 属性就会被赋值为10,否则它会得到0weight 属性的后续排序按“降序”顺序排列,因此条件匹配的所有 10 值将位于“顶部”,而所有其他结果将在所有匹配之后出现。


这就是您为您的具体实施而设计的结构。首先你$match你的查询条件,因为这减少了要处理的整体文档,这是唯一聚合管道可以实际使用索引的时间。

然后你 $project 比较匹配短语是否在首选字段中的字段,最后 $sort 在该计算字段上。

array(
  array( '$match' => $query ),
  array(
    '$addFields' => array(
      'weight' => array(
        '$cond => array(
          array( 
            '$or' => array( 
              array( 
                '$eq' => array( 
                  array('$concat' => array('$first_name',  ' ',  '$middle_name', ' ', '$last_name')),
                  $arg
                )
              ),
              array( 
                '$eq' => array(
                  array('$concat' => array('$first_name',  ' ',  '$middle_name')), 
                  $arg 
                )
              ),
              array(
                '$eq' => array(
                  array('$concat' => array('$first_name',  ' ',  '$last_name')),
                  $arg 
                )
              )
            )
          ),
          10,
          0
        )
      )
    )
  ),
  array(
    '$sort' => array( 'weight'=> -1 )
  )
)

所以总是 $match 首先或以其他方式使用将使用索引并“优化”您的结果的管道阶段。然后操作并记住,您不能在“单一”$project 阶段使用计算字段进行比较。如果您确实需要它,那么您可以重复计算或在一个阶段进行计算,然后在下一阶段比较值。

老实说,一旦你达到这些长度,你基本上就是在复制text search,你可以在其中:

  1. 将索引分布到您要搜索的所有字段中。这将大量的$or 条件消除为一个简单的查询操作。

  2. 指定匹配更重要的特定字段的权重。

“文本搜索”不是最佳解决方案的唯一情况是,如果您希望“更多权重”的字段定期更改。由于文本索引具有“设置”权重值,并且每个集合只能有 一个,因此您无法轻松更改字段组合以分配更多权重。显示聚合过程后,更改字段和权重分配相当简单。

【讨论】:

  • 我们的要求是 $arg 可能包含全名(first +middle+last)或名字和中间名的组合,甚至是名字和姓氏。而且我不能将名称字段分成两个单独的数组并对它们执行“和”操作。这就是为什么我将它们连接到 $project 中。$query 变量中还有很多其他条件。我的代码工作正常,但它没有首先获取更接近的结果。另外我不允许在数据库中添加字段...请更改上述解决方案
  • @user2179026 您的代码不起作用,因为$group 没有按照您的想法执行。我在这里演示的只是将“权重”置于第一个名称与给定参数匹配且第二个名称与给定参数匹配的条件上。您可以轻松地使条件成为针对给定字符串的各种连接组合的测试,并相应地应用权重。你的尝试离题太远了,这也没有帮助。
  • 我已经更新了我的实际代码,它正在工作并且没有组
  • @user2179026 因此,查看您的编辑,您正在寻找匹配包含多个可能字段之一中的值的文档。但是您要做的是放置某些字段的组合与值匹配的字段。这意味着只需将 $cond 中的条件替换为构造字符串以匹配平衡参数的任何表达式。
  • @user2179026 第三次。那是因为$group 与此无关。如果您想要“权重”,那么您的 $project 它们按条件显示。
猜你喜欢
  • 1970-01-01
  • 2014-10-01
  • 2021-10-02
  • 2021-09-20
  • 1970-01-01
  • 2011-03-18
  • 2017-09-01
  • 2016-11-04
  • 1970-01-01
相关资源
最近更新 更多