简体   繁体   中英

Find best match with Mongo ODM

I would like to find the best match with a mongo odm query on Symfony. I have a function that search for objects with an array of terms.

Here an exemple :

I want to search all the users that are nammed 'Paul', 'Peter' and 'Smith'. So my array $search = array('Paul', 'Peter', 'Smith');

I have 3 users who match this request :

  • Peter Anderson
  • Paul Smith
  • Paul Peter Smith

So I would like that this request order the users like this :

  1. Paul Peter Smith
  2. Paul Smith
  3. Peter Anderson

Here my current method :

    public function search($search) {
    $query = $this->createQueryBuilder('AcmeDataBundle:users');
    $users = $query
        ->field('name')->in($search)
        ->getQuery()->execute();

    return $users;
}

Do you have any clue on how I can do that ?

Thanks

Lets forget about php and see how we can achieve that with MongoDB itself (using js) first.

when you want to have "text search" functionality, you have to create a TEXT INDEX on the field you want to search on. source

that can be achieved by:

db.yourCollectionName.createIndex({ fieldName: "text" })

After this, whenever you do text-searching , you will have some meta data related to your query, which contains some scores that MongoDB generates based on the relevancy of results against keywords.

So, because you want to have most accurate result first, we should only sort by that scores .

In your case, it will be:

db.User.createIndex( { name: "text" } )

db.User.find( 
    {$text:{$search:"Paul Smith Peter"}},
    { score: { $meta: "textScore" } }
).sort( { score: { $meta: "textScore" } } );

Okay. This will give you what you want. but let convert it to Doctrine query style.

For the index:

/**
 * @ODM\Document
 * @ODM\Indexes({
 *   @ODM\Index(keys={"name"="text"})
 * })
 */
class User{    
    /** @ODM\String */
    private $name;

    //...
}

Then run this command to ensure scheme and the index are created:

php app/console doctrine:mongodb:schema:create

and final part:

$search = ['Paul', 'Peter', 'Smith'];

$names = implode(' ', $search);

$queryBuilder = $documentManager->createQueryBuilder('User');

$expr = $queryBuilder->expr()->operator('$text', array('$search' => $names));

$result = $queryBuilder
            ->equals($expr->getQuery())
            ->sortMeta('score', 'textScore')
            ->getQuery();

foreach ($result as $user) {
    var_dump($user);
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM