Doctrine search a string in multiple columns

时间:2015-12-10 01:39:49

标签: php mysql doctrine-orm

I hope you can help :)

This is how the table looks:

+------------+----------------+------------------+---------+
| firstName  | lastName       | email            | etc...  |
+------------+----------------+------------------+---------+
| John       | Doe            | john@doe.com     | etc...  |
+------------+----------------+------------------+---------+
| John       | Michaels       | john@michaels.es | etc...  |
+------------+----------------+------------------+---------+

This is how the code looks:

if($_GET['search-customers'] != '') {
    $busqueda = $_GET['search-customers'];
    $query->andWhere("(c.firstName LIKE '%$busqueda%' OR c.lastName LIKE '%$busqueda%' OR c.email LIKE '%$busqueda%')"); 
}

With that QUERY:

  • If I input: John in the search box, it gives me the 2 results. OK
  • If I input: John D in the search box, it doesn't give me any result. FAIL

All right, I understand, When I type "John D", it try to find first in firstName (doesn't match) and also it doesn't match lastName or email.

How can I combine them?

The idea its to find the complete string in all possibilities.

Thanks!

4 个答案:

答案 0 :(得分:5)

我会使用MySQL's Full-Text Search Functions为您提供另一种选择。让我们开始准备桌子:

ALTER TABLE persons ADD FULLTEXT (`firstname`, `lastname`);

现在,firstnamelastname是全文使用的列,用于搜索匹配项:

SELECT * FROM persons
WHERE MATCH (firstname,lastname)
AGAINST ('John D' IN NATURAL LANGUAGE MODE);

结果将是:

+------------+----------------+------------------+---------+
| firstName  | lastName       | email            | etc...  |
+------------+----------------+------------------+---------+
| John       | Doe            | john@doe.com     | etc...  |
+------------+----------------+------------------+---------+
| John       | Michaels       | john@michaels.es | etc...  |
+------------+----------------+------------------+---------+

两个为什么?由于John(作为单词)被找到,但John Doe位于第一行,因为它与搜索词有很多相似之处。

说,让我们将此工具应用于Doctrine。我假设你的模型看起来像这样:

class Person{

    /** @column(type="string", name="firstname")*/
    protected $firstName;

    /** @column(type="string", name="lastname")*/
    protected $lastName;

    /** @column(type="string")*/
    protected $email;
}

让我们创建搜索功能:

public function search($term){
    $rsm = new ResultSetMapping();

    // Specify the object type to be returned in results
    $rsm->addEntityResult('Models\Person', 'p');

    // references each attribute with table's columns 
    $rsm->addFieldResult('p', 'firstName', 'firstName');
    $rsm->addFieldResult('p', 'lastName', 'lastname');
    $rsm->addFieldResult('p', 'email', 'email');

    // create a native query
    $sql = 'select p.firstName, p.lastname, p.email from persons p
            where match(p.firstname, p.lastname) against(?)';

    // execute the query
    $query = $em->createNativeQuery($sql, $rsm);
    $query->setParameter(1, $term);

    // getting the results
    return $query->getResult();
}

Finnally,例如:

$term = 'John D';
$results = search($term);
// two results
echo count($results);

附加说明:

  1. 在mysql 5.7之前,只能将全文添加到MyISAM表。
  2. 只能将CHARVARCHARTEXT列编入索引。
  3. 在搜索中使用IN NATURAL LANGUAGE MODE时,如果结果代表记录的< 50%,则mysql返回空结果。

答案 1 :(得分:0)

也许你可以像这样使用explode函数:

$busqueda = $_GET['search-customers'];
$names = explode(' ',$busqueda);
if(count($names)>1){
  $query->andWhere("(c.firstName LIKE '%{$names[0]}%' AND c.lastName LIKE '%{$names[1]}%')"); 
}else{
  $query->andWhere("(c.firstName LIKE '%$busqueda%' OR c.lastName LIKE '%$busqueda%' OR c.email LIKE '%$busqueda%')"); 
}

但是,使用like %word%效率很低,因为它无法使用索引。

答案 2 :(得分:0)

最后,我决定连接firstName和lastName。我排除了电子邮件,然后查询看起来像:

    $busqueda = $_GET['search-customers'];
    $names = explode(' ',$busqueda);
    $hasemail = strpos('@', $busqueda);

    if ( $hasemail ) {
        $query->andWhere("c.email LIKE '%$busqueda%'");
    } else {
      $query->andWhere("( CONCAT(c.firstName,' ',c.lastName) LIKE '%$busqueda%' OR c.email LIKE '%$busqueda%')"); 
    }

答案 3 :(得分:0)

在您的存储库中,您可以执行以下操作:

yaml.Marshal

查询构建器能够生成可根据您的需求进行扩展的复杂查询