{"record":{"id":"9eb987d74e4852cc","repo":"mongodb/laravel-mongodb","slug":"cannot-sort-by-score-in-ascending-order-atlas-search","errorCode":null,"errorMessage":"Cannot sort by '_score' in ascending order; Atlas Search relevance score always sorts descending.","messagePattern":"Cannot sort by '_score' in ascending order; Atlas Search relevance score always sorts descending\\.","errorType":"exception","errorClass":"InvalidArgumentException","httpStatus":null,"severity":"error","filePath":"src/Scout/ScoutEngine.php","lineNumber":209,"sourceCode":"        $builder->take($perPage);\n\n        return $this->performSearch($builder, $perPage * ($page - 1));\n    }\n\n    /**\n     * Perform the given search on the engine.\n     */\n    private function performSearch(Builder $builder, ?int $offset = null): array\n    {\n        // Validate sort before any I/O so unit tests can assert on these exceptions without a real collection.\n        $columns = array_column($builder->orders, 'column');\n        if (in_array('_score', $columns, true) && in_array('score', $columns, true)) {\n            throw new InvalidArgumentException(\"Cannot sort by a field named 'score' together with Atlas Search's '_score' relevance sort.\");\n        }\n\n        foreach ($builder->orders as $order) {\n            if ($order['column'] === '_score' && $order['direction'] === 'asc') {\n                throw new InvalidArgumentException(\"Cannot sort by '_score' in ascending order; Atlas Search relevance score always sorts descending.\");\n            }\n        }\n\n        $collection = $this->getSearchableCollection($builder->model);\n\n        if ($builder->callback) {\n            $cursor = call_user_func(\n                $builder->callback,\n                $collection,\n                $builder->query,\n                $offset,\n            );\n            assert($cursor instanceof CursorInterface, new LogicException(sprintf('The search builder closure must return a MongoDB cursor, %s returned', get_debug_type($cursor))));\n            $cursor->setTypeMap(self::TYPEMAP);\n\n            return $cursor->toArray();\n        }\n","sourceCodeStart":191,"sourceCodeEnd":227,"githubUrl":"https://github.com/mongodb/laravel-mongodb/blob/0634653039468ceb0268a69192bdac64469ed043/src/Scout/ScoutEngine.php#L191-L227","documentation":"Atlas Search relevance scores are always ranked descending (highest score first). The Scout engine rejects an explicit ascending '_score' sort with an InvalidArgumentException, validated before any I/O.","triggerScenarios":"Calling search()/paginate() with ->orderBy('_score', 'asc') on the Scout builder; any code path that adds an ascending order whose column is '_score'.","commonSituations":"Building dynamic sort UIs where the user can pick asc/desc without excluding the relevance column; defaulting all sorts to asc.","solutions":["Remove the '_score' order or force its direction to 'desc'","For lowest-relevance-first results, sort desc by _score and reverse the page client-side (or use $searchMeta differently)","Filter user-supplied sort directions so '_score' is always mapped to 'desc'","Catch InvalidArgumentException and fall back to relevance-descending order"],"exampleFix":"// before\n$builder->orderBy('_score', $dir); // $dir may be 'asc'\n// after\n$builder->orderBy('_score', 'desc'); // relevance is always descending","handlingStrategy":"validation","validationCode":"foreach ($builder->orders as $o) {\n    if ($o['column'] === '_score' && $o['direction'] === 'asc') {\n        throw new InvalidArgumentException(\"'_score' must sort descending.\");\n    }\n}","typeGuard":null,"tryCatchPattern":"try {\n    $builder->orderBy('_score', $dir);\n} catch (\\InvalidArgumentException $e) {\n    $builder->orderBy('_score', 'desc'); // coerce relevance to descending\n}","preventionTips":["Whitelist 'desc' as the only direction for '_score' in sort-building code","Clamp user-supplied directions instead of passing them through","Write a unit test covering every UI-selectable sort direction"],"tags":["php","laravel-scout","atlas-search","sorting"],"backgroundTag":"invalid-argument-value","analyzedSha":"0634653039468ceb0268a69192bdac64469ed043","analyzedAt":"2026-09-15T02:56:37.067Z","contentChangedAt":"2026-09-15T02:56:37.067Z","schemaVersion":2},"datasetVersion":"2026-09-16T09:17:16.951Z"}