Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit d6d8004

Browse files
authored
PHPORM-275 PHPORM-276 Add Query\Builder::search() and autocomplete() (#3232)
1 parent 5bed82c commit d6d8004

File tree

3 files changed

+162
-1
lines changed

3 files changed

+162
-1
lines changed

‎src/Eloquent/Builder.php‎

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,10 @@
55
namespace MongoDB\Laravel\Eloquent;
66

77
use Illuminate\Database\Eloquent\Builder as EloquentBuilder;
8+
use Illuminate\Database\Eloquent\Collection;
9+
use Illuminate\Database\Eloquent\Model;
810
use MongoDB\BSON\Document;
11+
use MongoDB\Builder\Type\SearchOperatorInterface;
912
use MongoDB\Driver\CursorInterface;
1013
use MongoDB\Driver\Exception\WriteException;
1114
use MongoDB\Laravel\Connection;
@@ -21,7 +24,10 @@
2124
use function iterator_to_array;
2225
use function property_exists;
2326

24-
/** @method \MongoDB\Laravel\Query\Builder toBase() */
27+
/**
28+
* @method \MongoDB\Laravel\Query\Builder toBase()
29+
* @template TModel of Model
30+
*/
2531
class Builder extends EloquentBuilder
2632
{
2733
private const DUPLICATE_KEY_ERROR = 11000;
@@ -49,6 +55,7 @@ class Builder extends EloquentBuilder
4955
'insertusing',
5056
'max',
5157
'min',
58+
'autocomplete',
5259
'pluck',
5360
'pull',
5461
'push',
@@ -69,6 +76,31 @@ public function aggregate($function = null, $columns = ['*'])
6976
return $result ?: $this;
7077
}
7178

79+
/**
80+
* Performs a full-text search of the field or fields in an Atlas collection.
81+
*
82+
* @see https://www.mongodb.com/docs/atlas/atlas-search/aggregation-stages/search/
83+
*
84+
* @return Collection<int, TModel>
85+
*/
86+
public function search(
87+
SearchOperatorInterface|array $operator,
88+
?string $index = null,
89+
?array $highlight = null,
90+
?bool $concurrent = null,
91+
?string $count = null,
92+
?string $searchAfter = null,
93+
?string $searchBefore = null,
94+
?bool $scoreDetails = null,
95+
?array $sort = null,
96+
?bool $returnStoredSource = null,
97+
?array $tracking = null,
98+
): Collection {
99+
$results = $this->toBase()->search($operator, $index, $highlight, $concurrent, $count, $searchAfter, $searchBefore, $scoreDetails, $sort, $returnStoredSource, $tracking);
100+
101+
return $this->model->hydrate($results->all());
102+
}
103+
72104
/** @inheritdoc */
73105
public function update(array $values, array $options = [])
74106
{

‎src/Query/Builder.php‎

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,13 +23,16 @@
2323
use MongoDB\BSON\ObjectID;
2424
use MongoDB\BSON\Regex;
2525
use MongoDB\BSON\UTCDateTime;
26+
use MongoDB\Builder\Search;
2627
use MongoDB\Builder\Stage\FluentFactoryTrait;
28+
use MongoDB\Builder\Type\SearchOperatorInterface;
2729
use MongoDB\Driver\Cursor;
2830
use Override;
2931
use RuntimeException;
3032
use stdClass;
3133

3234
use function array_fill_keys;
35+
use function array_filter;
3336
use function array_is_list;
3437
use function array_key_exists;
3538
use function array_map;
@@ -1490,6 +1493,68 @@ public function options(array $options)
14901493
return $this;
14911494
}
14921495

1496+
/**
1497+
* Performs a full-text search of the field or fields in an Atlas collection.
1498+
* NOTE: $search is only available for MongoDB Atlas clusters, and is not available for self-managed deployments.
1499+
*
1500+
* @see https://www.mongodb.com/docs/atlas/atlas-search/aggregation-stages/search/
1501+
*
1502+
* @return Collection<object|array>
1503+
*/
1504+
public function search(
1505+
SearchOperatorInterface|array $operator,
1506+
?string $index = null,
1507+
?array $highlight = null,
1508+
?bool $concurrent = null,
1509+
?string $count = null,
1510+
?string $searchAfter = null,
1511+
?string $searchBefore = null,
1512+
?bool $scoreDetails = null,
1513+
?array $sort = null,
1514+
?bool $returnStoredSource = null,
1515+
?array $tracking = null,
1516+
): Collection {
1517+
// Forward named arguments to the search stage, skip null values
1518+
$args = array_filter([
1519+
'operator' => $operator,
1520+
'index' => $index,
1521+
'highlight' => $highlight,
1522+
'concurrent' => $concurrent,
1523+
'count' => $count,
1524+
'searchAfter' => $searchAfter,
1525+
'searchBefore' => $searchBefore,
1526+
'scoreDetails' => $scoreDetails,
1527+
'sort' => $sort,
1528+
'returnStoredSource' => $returnStoredSource,
1529+
'tracking' => $tracking,
1530+
], fn ($arg) => $arg !== null);
1531+
1532+
return $this->aggregate()->search(...$args)->get();
1533+
}
1534+
1535+
/**
1536+
* Performs an autocomplete search of the field using an Atlas Search index.
1537+
* NOTE: $search is only available for MongoDB Atlas clusters, and is not available for self-managed deployments.
1538+
* You must create an Atlas Search index with an autocomplete configuration before you can use this stage.
1539+
*
1540+
* @see https://www.mongodb.com/docs/atlas/atlas-search/autocomplete/
1541+
*
1542+
* @return Collection<string>
1543+
*/
1544+
public function autocomplete(string $path, string $query, bool|array $fuzzy = false, string $tokenOrder = 'any'): Collection
1545+
{
1546+
$args = ['path' => $path, 'query' => $query, 'tokenOrder' => $tokenOrder];
1547+
if ($fuzzy === true) {
1548+
$args['fuzzy'] = ['maxEdits' => 2];
1549+
} elseif ($fuzzy !== false) {
1550+
$args['fuzzy'] = $fuzzy;
1551+
}
1552+
1553+
return $this->aggregate()->search(
1554+
Search::autocomplete(...$args),
1555+
)->get()->pluck($path);
1556+
}
1557+
14931558
/**
14941559
* Apply the connection's session to options if it's not already specified.
14951560
*/

‎tests/AtlasSearchTest.php‎

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,10 @@
22

33
namespace MongoDB\Laravel\Tests;
44

5+
use Illuminate\Database\Eloquent\Collection as EloquentCollection;
6+
use Illuminate\Support\Collection as LaravelCollection;
57
use Illuminate\Support\Facades\Schema;
8+
use MongoDB\Builder\Search;
69
use MongoDB\Collection as MongoDBCollection;
710
use MongoDB\Driver\Exception\ServerException;
811
use MongoDB\Laravel\Schema\Builder;
@@ -43,13 +46,15 @@ public function setUp(): void
4346

4447
$collection = $this->getConnection('mongodb')->getCollection('books');
4548
assert($collection instanceof MongoDBCollection);
49+
4650
try {
4751
$collection->createSearchIndex([
4852
'mappings' => [
4953
'fields' => [
5054
'title' => [
5155
['type' => 'string', 'analyzer' => 'lucene.english'],
5256
['type' => 'autocomplete', 'analyzer' => 'lucene.english'],
57+
['type' => 'token'],
5358
],
5459
],
5560
],
@@ -135,4 +140,63 @@ public function testGetIndexes()
135140

136141
self::assertSame($expected, $indexes);
137142
}
143+
144+
public function testEloquentBuilderSearch()
145+
{
146+
$results = Book::search(
147+
sort: ['title' => 1],
148+
operator: Search::text('title', 'systems'),
149+
);
150+
151+
self::assertInstanceOf(EloquentCollection::class, $results);
152+
self::assertCount(3, $results);
153+
self::assertInstanceOf(Book::class, $results->first());
154+
self::assertSame([
155+
'Database System Concepts',
156+
'Modern Operating Systems',
157+
'Operating System Concepts',
158+
], $results->pluck('title')->all());
159+
}
160+
161+
public function testDatabaseBuilderSearch()
162+
{
163+
$results = $this->getConnection('mongodb')->table('books')
164+
->search(Search::text('title', 'systems'), sort: ['title' => 1]);
165+
166+
self::assertInstanceOf(LaravelCollection::class, $results);
167+
self::assertCount(3, $results);
168+
self::assertIsArray($results->first());
169+
self::assertSame([
170+
'Database System Concepts',
171+
'Modern Operating Systems',
172+
'Operating System Concepts',
173+
], $results->pluck('title')->all());
174+
}
175+
176+
public function testEloquentBuilderAutocomplete()
177+
{
178+
$results = Book::autocomplete('title', 'system');
179+
180+
self::assertInstanceOf(LaravelCollection::class, $results);
181+
self::assertCount(3, $results);
182+
self::assertSame([
183+
'Operating System Concepts',
184+
'Database System Concepts',
185+
'Modern Operating Systems',
186+
], $results->all());
187+
}
188+
189+
public function testDatabaseBuilderAutocomplete()
190+
{
191+
$results = $this->getConnection('mongodb')->table('books')
192+
->autocomplete('title', 'system');
193+
194+
self::assertInstanceOf(LaravelCollection::class, $results);
195+
self::assertCount(3, $results);
196+
self::assertSame([
197+
'Operating System Concepts',
198+
'Database System Concepts',
199+
'Modern Operating Systems',
200+
], $results->all());
201+
}
138202
}

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /