diff --git a/examples/atlas-search.php b/examples/atlas-search.php
new file mode 100644
index 000000000..3bcfc7cbc
--- /dev/null
+++ b/examples/atlas-search.php
@@ -0,0 +1,129 @@
+selectCollection('sample_airbnb', 'listingsAndReviews');
+
+$count = $collection->estimatedDocumentCount();
+if ($count === 0) {
+ echo 'This example requires the sample_airbnb database with the listingsAndReviews collection.', "\n";
+ echo 'Load the sample dataset in your MongoDB Atlas cluster before running this example:', "\n";
+ echo ' https://www.mongodb.com/docs/atlas/sample-data/', "\n";
+ exit(1);
+}
+
+// Delete the index if it already exists
+$indexes = iterator_to_array($collection->listSearchIndexes());
+foreach ($indexes as $index) {
+ if ($index->name === 'default') {
+ echo "The index already exists. Dropping it.\n";
+ $collection->dropSearchIndex($index->name);
+
+ // Wait for the index to be deleted.
+ wait(function () use ($collection) {
+ echo '.';
+ foreach ($collection->listSearchIndexes() as $index) {
+ if ($index->name === 'default') {
+ return false;
+ }
+ }
+
+ return true;
+ });
+ }
+}
+
+// Create the search index
+echo "\nCreating the index.\n";
+$collection->createSearchIndex(
+ /* The index definition requires a mapping
+ * See: https://www.mongodb.com/docs/atlas/atlas-search/define-field-mappings/ */
+ ['mappings' => ['dynamic' => true]],
+ // "default" is the default index name, this config can be omitted.
+ ['name' => 'default'],
+);
+
+// Wait for the index to be ready.
+wait(function () use ($collection) {
+ echo '.';
+ foreach ($collection->listSearchIndexes() as $index) {
+ if ($index->name === 'default') {
+ return $index->queryable;
+ }
+ }
+
+ return false;
+});
+
+// Perform a text search
+echo "\n", 'Performing a text search...', "\n";
+$results = $collection->aggregate([
+ [
+ '$search' => [
+ 'index' => 'default',
+ 'text' => [
+ 'query' => 'view beach ocean',
+ 'path' => ['name'],
+ ],
+ ],
+ ],
+ ['$project' => ['name' => 1, 'description' => 1]],
+ ['$limit' => 10],
+])->toArray();
+
+foreach ($results as $document) {
+ echo ' - ', $document['name'], "\n";
+}
+
+echo "\n", 'Enjoy MongoDB Atlas Search!', "\n\n";
+
+/**
+ * This function waits until the callback returns true or the timeout is reached.
+ */
+function wait(Closure $callback): void
+{
+ $timeout = hrtime()[0] + WAIT_TIMEOUT_SEC;
+ while (hrtime()[0] < $timeout) {
+ if ($callback()) {
+ return;
+ }
+
+ sleep(5);
+ }
+
+ throw new RuntimeException('Time out');
+}
diff --git a/psalm-baseline.xml b/psalm-baseline.xml
index f1db3e9fa..c26c4f6bf 100644
--- a/psalm-baseline.xml
+++ b/psalm-baseline.xml
@@ -5,6 +5,30 @@
decrypt($document->encryptedField)]]>
+
+
+
+ name]]>
+
+
+
+
+
+ $document
+ $index
+ $index
+ $index
+
+
+ name]]>
+ name]]>
+ name]]>
+ queryable]]>
+
+
+ $uri
+
+
self::CURSOR_NOT_FOUND
diff --git a/tests/ExamplesTest.php b/tests/ExamplesTest.php
index 4c58bcc71..1ba3ceaab 100644
--- a/tests/ExamplesTest.php
+++ b/tests/ExamplesTest.php
@@ -3,6 +3,9 @@
namespace MongoDB\Tests;
use Generator;
+use MongoDB\Client;
+
+use function getenv;
/** @runTestsInSeparateProcesses */
final class ExamplesTest extends FunctionalTestCase
@@ -179,6 +182,37 @@ public static function provideExamples(): Generator
];
}
+ /**
+ * MongoDB Atlas Search example requires a MongoDB Atlas M10+ cluster with MongoDB 7.0+ and sample data loaded.
+ * Tips for insiders: if using a cloud-dev server, append ".mongodb.net" to the MONGODB_URI.
+ */
+ public function testAtlasSearch(): void
+ {
+ $uri = getenv('MONGODB_URI') ?? '';
+ if (! self::isAtlas($uri)) {
+ $this->markTestSkipped('Atlas Search examples are only supported on MongoDB Atlas');
+ }
+
+ $this->skipIfServerVersion('<', '7.0', 'Atlas Search examples require MongoDB 7.0 or later');
+
+ $client = new Client($uri);
+ $collection = $client->selectCollection('sample_airbnb', 'listingsAndReviews');
+ $count = $collection->estimatedDocumentCount();
+ if ($count === 0) {
+ $this->markTestSkipped('Atlas Search examples require the sample_airbnb database with the listingsAndReviews collection');
+ }
+
+ // Clean variables to avoid conflict with example
+ unset($uri, $client, $collection, $count);
+
+ require __DIR__ . '/../examples/atlas-search.php';
+
+ $output = $this->getActualOutputForAssertion();
+ $this->assertStringContainsString("\nCreating the index.\n...", $output);
+ $this->assertStringContainsString("\nPerforming a text search...\n - ", $output);
+ $this->assertStringContainsString("\nEnjoy MongoDB Atlas Search!\n", $output);
+ }
+
public function testChangeStream(): void
{
$this->skipIfChangeStreamIsNotSupported();