【问题标题】:Doctrine QueryBuilder sort results by field priorityDoctrine QueryBuilder 按字段优先级排序结果
【发布时间】:2017-03-23 10:41:34
【问题描述】:

我有带有titletagsdescription 字段的Service 实体。使用 QueryBuilder 搜索服务时,如何获得按字段优先级排序的结果。例如,当我搜索术语php 时,我希望在列表顶部的标题中获取带有php 的服务,然后在其标签中使用php 的服务和在其描述中使用搜索术语的服务最后。

这是我的 Querybuilder 的一部分:

    $qb = $this->createQueryBuilder('service');
    $qb->leftJoin('service.tags', 'tag');

    $conditions = array($conditions[] = $qb->expr()->eq('service.enabled', true));
    $conditions[] = $qb->expr()->eq('service.category', $categoryId);
    $term = '%' . $term . '%';
    $conditions[] = $qb->expr()->orX(
            $qb->expr()->like('service.title', "'$term'"),
            $qb->expr()->like('service.description',  "'$term'"),
            $qb->expr()->like('tag.name', "'$term'")
        );

    $conditions = call_user_func_array(array($qb->expr(), 'andX'), $conditions);

    $qb->where($conditions);

【问题讨论】:

    标签: php mysql symfony doctrine-orm


    【解决方案1】:

    最好的方法是执行一系列UNION 语句,然后同时剔除重复的语句。

    (未经检查的伪 SQL 给你的想法):

    SELECT id,title,tag,SUM(weight) score
    FROM (
       SELECT id,title,tag, 100 as weight FROM service WHERE title LIKE '%foo%'
       UNION ALL
       SELECT id,title,tag, 10 as weight FROM service WHERE tags LIKE '%foo%'
       UNION ALL
       SELECT id,title,tag, 1 as weight FROM service WHERE description LIKE '%foo%'
    ) t
    GROUP BY id
    ORDER BY score DESC /* This sort by probably won't work; might need to do it a different way, but you get the idea */
    

    【讨论】:

      【解决方案2】:

      您可以为此使用本机查询。例如:

          $em = $this->get('doctrine')->getManager();
      
          $sql = "
              select *
              from service s
              where
                  s.title like '%xxx%'
                  or s.tags like '%xxx%'
                  or s.description  like '%xxx%'
              order by 
                  s.title like '%xxx%' desc,
                  s.tags like '%xxx%' desc,
                  s.description  like '%xxx%' desc
          ";
      
          $rsm = new \Doctrine\ORM\Query\ResultSetMappingBuilder($em);
          $rsm->addRootEntityFromClassMetadata('\You\Entity\Service\Class', 's');
      
          $query = $em->createNativeQuery($sql, $rsm);
      
          $data = $query->getResult();
      
          dump($data);
      

      http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/native-sql.html如何在orm中使用sql insted of dql

      【讨论】:

      • 谢谢,这很有帮助。但是我的初始查询非常复杂。现在我在将您的解决方案与之前的查询结构集成时遇到问题
      【解决方案3】:

      不会尝试在只有一个查询中获得所需的结果。这太复杂了,你会花很多时间来尝试得到你想要的。

      首先,我假设您希望得到一个 排序的 数组 Service 实体,如下所示:

      /** @var Service[] $results **/
      $results = $this->getDoctrine()->getManager()->getRepository('YourBundle:YourServiceRepo')->findSerivesByGivenSerchCriteria($yourConditions);
      

      所以你可以简单地迭代(foreach)它

      如果是这样,请考虑以下几点:

      • 在您的服务存储库中创建三个单独的方法以获取
        1. 标题中包含搜索条件的服务 - 第一优先
        2. 标签中包含搜索条件的服务 - 第二优先
        3. 描述中包含 serach-criteria 的服务 - 第三优先级

      在你的findSerivesByGivenSearchCriteria()method 中,你调用了所有三个,并且可以按照我想要的任何顺序组合找到的结果

      例如:

      public function findSerivesByGivenSerchCriteria( $searchTirm ) {
          $foundServicesByTitle = $this->findServicesBySearchCriteriaInTitle( $searachTirm );
          $foundServicesByTag = $this->findServicesBySearchCriteriaInTags( $searchTirm );
          $foundServicesByDesc = $this->findServicesBySearchCriteriaInDesc( $searchTirm );
      
          // Hier you can combine found results in any order
          $results = [];
          if( false === empty($foundServicesByTitle ) )
          {
          // for example with array_merge ...
          // NOTE: If you choose array_merge() you have to make sure your $foundServicesByTitle, $foundServicesByTag, $foundServicesByDesc have unique array-indexes
          //       And you get Results like
          //       array( 'unique_id_1' => ServiceEntity, 'unique_id_2' => ServiceEntity ... ) 
           $results = array_merge($results, $foundServicesByTitle);
          }
          // .. do the same with other two
          return $results;
      }
      

      要获得“独特性”,请尝试结合 Doctrine 的 INDEX BYHIDDEN

      INDEX BY -> http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/dql-doctrine-query-language.html#using-index-by

      INDEX BY 在 QueryBuilder -> https://stackoverflow.com/a/15120793/348193

      HIDDEN -> https://michelsalib.com/2012/03/04/doctrine2-ninja-with-the-hidden-keyword/

      为什么我不会选择UNION的方式?

      由于您使用 symfony 和教义 UNION 不是最好的方法。见How to write UNION in Doctrine 2.0

      如您所见,UNION Doctrine 不支持,您必须使用 NativeQuery(这可能会令人沮丧 ResultSetMappingBuilder 和修正映射并将原始结果转换为到所需的实体)

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2018-05-15
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多