Skip to content

Commit aee4367

Browse files
committed
Create Atlas Search example
1 parent f6ce211 commit aee4367

File tree

3 files changed

+187
-0
lines changed

3 files changed

+187
-0
lines changed

examples/atlas-search.php

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
<?php
2+
3+
/**
4+
* This example demonstrates how to create an Atlas Search index and perform a search query.
5+
* It requires a MongoDB Atlas M10+ cluster with Sample Dataset loaded.
6+
*
7+
* Use the MONGODB_URI environment variable to specify the connection string from the Atlas UI.
8+
*/
9+
10+
declare(strict_types=1);
11+
12+
namespace MongoDB\Examples;
13+
14+
use Closure;
15+
use MongoDB\Client;
16+
use RuntimeException;
17+
18+
use function define;
19+
use function getenv;
20+
use function hrtime;
21+
use function iterator_to_array;
22+
use function sleep;
23+
use function str_contains;
24+
25+
require __DIR__ . '/../vendor/autoload.php';
26+
27+
$uri = getenv('MONGODB_URI');
28+
if (! $uri || ! str_contains($uri, '.mongodb.net')) {
29+
echo 'This example requires a MongoDB Atlas cluster.', "\n";
30+
echo 'Make sure you set the MONGODB_URI environment variable.', "\n";
31+
exit(1);
32+
}
33+
34+
// Atlas Search index management operations are asynchronous.
35+
// They usually take less than 5 minutes to complete.
36+
define('WAIT_TIMEOUT_SEC', 300);
37+
38+
$client = new Client($uri);
39+
$collection = $client->selectCollection('sample_airbnb', 'listingsAndReviews');
40+
41+
$count = $collection->estimatedDocumentCount();
42+
if ($count === 0) {
43+
echo 'This example requires the sample_airbnb database with the listingsAndReviews collection.', "\n";
44+
echo 'Load the sample dataset in your MongoDB Atlas cluster before running this example:', "\n";
45+
echo ' https://www.mongodb.com/docs/atlas/sample-data/', "\n";
46+
exit(1);
47+
}
48+
49+
// Delete the index if it already exists
50+
$indexes = iterator_to_array($collection->listSearchIndexes());
51+
foreach ($indexes as $index) {
52+
if ($index->name === 'default') {
53+
echo "The index already exists. Dropping it.\n";
54+
$collection->dropSearchIndex($index->name);
55+
56+
// Wait for the index to be deleted.
57+
wait(function () use ($collection) {
58+
echo '.';
59+
foreach ($collection->listSearchIndexes() as $index) {
60+
if ($index->name === 'default') {
61+
return false;
62+
}
63+
}
64+
65+
return true;
66+
});
67+
}
68+
}
69+
70+
// Create the search index
71+
echo "\nCreating the index.\n";
72+
$collection->createSearchIndex(
73+
/* The index definition requires a mapping
74+
* See: https://www.mongodb.com/docs/atlas/atlas-search/define-field-mappings/ */
75+
['mappings' => ['dynamic' => true]],
76+
// "default" is the default index name, this config can be omitted.
77+
['name' => 'default'],
78+
);
79+
80+
// Wait for the index to be ready.
81+
wait(function () use ($collection) {
82+
echo '.';
83+
foreach ($collection->listSearchIndexes() as $index) {
84+
if ($index->name === 'default') {
85+
return $index->queryable;
86+
}
87+
}
88+
89+
return false;
90+
});
91+
92+
// Perform a text search
93+
echo "\n", 'Performing a text search...', "\n";
94+
$results = $collection->aggregate([
95+
[
96+
'$search' => [
97+
'index' => 'default',
98+
'text' => [
99+
'query' => 'view beach ocean',
100+
'path' => ['name'],
101+
],
102+
],
103+
],
104+
['$project' => ['name' => 1, 'description' => 1]],
105+
['$limit' => 10],
106+
])->toArray();
107+
108+
foreach ($results as $document) {
109+
echo ' - ', $document['name'], "\n";
110+
}
111+
112+
echo "\n", 'Enjoy MongoDB Atlas Search!', "\n\n";
113+
114+
/**
115+
* This function waits until the callback returns true or the timeout is reached.
116+
*/
117+
function wait(Closure $callback): void
118+
{
119+
$timeout = hrtime()[0] + WAIT_TIMEOUT_SEC;
120+
while (hrtime()[0] < $timeout) {
121+
if ($callback()) {
122+
return;
123+
}
124+
125+
sleep(5);
126+
}
127+
128+
throw new RuntimeException('Time out');
129+
}

psalm-baseline.xml

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,30 @@
55
<code><![CDATA[$clientEncryption->decrypt($document->encryptedField)]]></code>
66
</MixedArgument>
77
</file>
8+
<file src="examples/atlas-search.php">
9+
<MixedArgument>
10+
<code><![CDATA[$document['name']]]></code>
11+
<code><![CDATA[$index->name]]></code>
12+
</MixedArgument>
13+
<MixedArrayAccess>
14+
<code><![CDATA[$document['name']]]></code>
15+
</MixedArrayAccess>
16+
<MixedAssignment>
17+
<code>$document</code>
18+
<code>$index</code>
19+
<code>$index</code>
20+
<code>$index</code>
21+
</MixedAssignment>
22+
<MixedPropertyFetch>
23+
<code><![CDATA[$index->name]]></code>
24+
<code><![CDATA[$index->name]]></code>
25+
<code><![CDATA[$index->name]]></code>
26+
<code><![CDATA[$index->queryable]]></code>
27+
</MixedPropertyFetch>
28+
<PossiblyFalseArgument>
29+
<code>$uri</code>
30+
</PossiblyFalseArgument>
31+
</file>
832
<file src="src/ChangeStream.php">
933
<DeprecatedConstant>
1034
<code>self::CURSOR_NOT_FOUND</code>

tests/ExamplesTest.php

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@
33
namespace MongoDB\Tests;
44

55
use Generator;
6+
use MongoDB\Client;
7+
8+
use function getenv;
69

710
/** @runTestsInSeparateProcesses */
811
final class ExamplesTest extends FunctionalTestCase
@@ -179,6 +182,37 @@ public static function provideExamples(): Generator
179182
];
180183
}
181184

185+
/**
186+
* MongoDB Atlas Search example requires a MongoDB Atlas M10+ cluster with MongoDB 7.0+ and sample data loaded.
187+
* Tips for insiders: if using a cloud-dev server, append ".mongodb.net" to the MONGODB_URI.
188+
*/
189+
public function testAtlasSearch(): void
190+
{
191+
$uri = getenv('MONGODB_URI') ?? '';
192+
if (! self::isAtlas($uri)) {
193+
$this->markTestSkipped('Atlas Search examples are only supported on MongoDB Atlas');
194+
}
195+
196+
$this->skipIfServerVersion('<', '7.0', 'Atlas Search examples require MongoDB 7.0 or later');
197+
198+
$client = new Client($uri);
199+
$collection = $client->selectCollection('sample_airbnb', 'listingsAndReviews');
200+
$count = $collection->estimatedDocumentCount();
201+
if ($count === 0) {
202+
$this->markTestSkipped('Atlas Search examples require the sample_airbnb database with the listingsAndReviews collection');
203+
}
204+
205+
// Clean variables to avoid conflict with example
206+
unset($uri, $client, $collection, $count);
207+
208+
require __DIR__ . '/../examples/atlas-search.php';
209+
210+
$output = $this->getActualOutputForAssertion();
211+
$this->assertStringContainsString("\nCreating the index.\n...", $output);
212+
$this->assertStringContainsString("\nPerforming a text search...\n - ", $output);
213+
$this->assertStringContainsString("\nEnjoy MongoDB Atlas Search!\n", $output);
214+
}
215+
182216
public function testChangeStream(): void
183217
{
184218
$this->skipIfChangeStreamIsNotSupported();

0 commit comments

Comments
 (0)