php mongodb full-text search and sort

2019-01-28 05:31发布

问题:

i nead to make a search with full text index and this code work:

$cursor=$collection->find(array('$text'=>(array('$search'=>$s))),
                    array("score"=> array('$meta'=>"textScore"))
        );

i try to sort the cursor with:

$cursor =$cursor->sort(array("score"=>1));

when i try to read

var_dump($cursor->getNext());

i gave me this error Uncaught exception 'MongoCursorException' with message 'localhost:27017: Can't canonicalize query: BadValue can't have a non-$meta sort on a $meta projection'

any idea?

回答1:

You are attempting to sort on a meta field, not a normal fieldname.

The second argument to $collection->find() determines which fields of the document you (do/do not) want to be returned by the query.

This is similar to SELECT *... vs SELECT field1, field2 ... in SQL databases.

Now, in MongoDB 2.6 there is an additional keyword you can use here, $meta. This keyword allows you to "inject" fieldnames into the return document (that otherwise would not actually exist). The value of this injected fieldname would come from some sort of "meta data" of the document or query you are executing.

The $text query operator is an example of an operator that has more information available about the matched document.. Unfortunately, it has no way of telling you about this extra information since doing so would manipulate your document in unexpected way. It does however attach a metadata to the document - and it is up to you to decide if you have need for it or not.

The meta data the $text operator creates uses the keyword "textScore". If you want to include that data, you can do so by assigning it to a field name of your choice:

array("myFieldname" => array('$meta' => 'keyword'))

For example, in the case of $text search (textScore) we can inject the fieldname "score" into our document by passing this array as the 2nd argument to $collection->find():

array("score" => array('$meta' => 'textScore'))

Now we have injected a field called "score" into our return document which has the "textScore" value from the $text search.

But since this is still just meta data of the document, if you want to continue to use this value in any subsequent operations before executing the query, you still have to refer to it as $meta data.

This means, to sort on the field you have to sort on the $meta projection

array('score' => array('$meta' => 'textScore'))

Your full example then becomes:

<?php
$mc = new MongoClient();


$collection = $mc->selectCollection("myDatabase", "myCollection");

$string = "search string";
$cursor = $collection->find(
    array('$text' => array('$search' => $string)),
    array('score' => array('$meta' => 'textScore'))
);

$cursor = $cursor->sort(
    array('score' => array('$meta' => 'textScore'))
);

foreach($cursor as $document) {
    var_dump($document);
}