Skip to content

Commit

Permalink
Add Atlas Search example (#1147)
Browse files Browse the repository at this point in the history
* Create Atlas Search example
* Add fixtures to run atlas-search example in CI
  • Loading branch information
GromNaN committed Sep 13, 2023
1 parent e043d43 commit 7589840
Show file tree
Hide file tree
Showing 3 changed files with 211 additions and 0 deletions.
133 changes: 133 additions & 0 deletions examples/atlas-search.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
<?php

/**
* This example demonstrates how to create an Atlas Search index and perform a search query.
* It requires a MongoDB Atlas M10+ cluster with Sample Dataset loaded.
*
* Use the MONGODB_URI environment variable to specify the connection string from the Atlas UI.
*/

declare(strict_types=1);

namespace MongoDB\Examples;

use Closure;
use MongoDB\Client;
use RuntimeException;

use function define;
use function getenv;
use function hrtime;
use function iterator_to_array;
use function sleep;
use function str_contains;

require __DIR__ . '/../vendor/autoload.php';

$uri = getenv('MONGODB_URI');
if (! $uri || ! str_contains($uri, '.mongodb.net')) {
echo 'This example requires a MongoDB Atlas cluster.', "\n";
echo 'Make sure you set the MONGODB_URI environment variable.', "\n";
exit(1);
}

// Atlas Search index management operations are asynchronous.
// They usually take less than 5 minutes to complete.
define('WAIT_TIMEOUT_SEC', 300);

// The sample dataset is loaded into the "sample_airbnb.listingsAndReviews" collection.
$databaseName = getenv('MONGODB_DATABASE') ?: 'sample_airbnb';
$collectionName = getenv('MONGODB_COLLECTION') ?: 'listingsAndReviews';

$client = new Client($uri);
$collection = $client->selectCollection($databaseName, $collectionName);

$count = $collection->estimatedDocumentCount();
if ($count === 0) {
echo 'This example requires the "', $databaseName, '" database with the "', $collectionName, '" 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');
}
24 changes: 24 additions & 0 deletions psalm-baseline.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,30 @@
<code><![CDATA[$clientEncryption->decrypt($document->encryptedField)]]></code>
</MixedArgument>
</file>
<file src="examples/atlas-search.php">
<MixedArgument>
<code><![CDATA[$document['name']]]></code>
<code><![CDATA[$index->name]]></code>
</MixedArgument>
<MixedArrayAccess>
<code><![CDATA[$document['name']]]></code>
</MixedArrayAccess>
<MixedAssignment>
<code>$document</code>
<code>$index</code>
<code>$index</code>
<code>$index</code>
</MixedAssignment>
<MixedPropertyFetch>
<code><![CDATA[$index->name]]></code>
<code><![CDATA[$index->name]]></code>
<code><![CDATA[$index->name]]></code>
<code><![CDATA[$index->queryable]]></code>
</MixedPropertyFetch>
<PossiblyFalseArgument>
<code>$uri</code>
</PossiblyFalseArgument>
</file>
<file src="examples/persistable.php">
<LessSpecificReturnStatement>
<code><![CDATA[(object) [
Expand Down
54 changes: 54 additions & 0 deletions tests/ExamplesTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,12 @@

use Generator;

use function bin2hex;
use function getenv;
use function putenv;
use function random_bytes;
use function sprintf;

/** @runTestsInSeparateProcesses */
final class ExamplesTest extends FunctionalTestCase
{
Expand Down Expand Up @@ -179,6 +185,54 @@ public static function provideExamples(): Generator
];
}

/**
* MongoDB Atlas Search example requires a MongoDB Atlas M10+ cluster with MongoDB 7.0+
* Tips for insiders: if using a cloud-dev server, append ".mongodb.net" to the MONGODB_URI.
*
* @group atlas
*/
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');

// Generate random collection name to avoid conflicts with consecutive runs as the index creation is asynchronous
$collectionName = sprintf('%s.%s', $this->getCollectionName(), bin2hex(random_bytes(5)));
$databaseName = $this->getDatabaseName();
$collection = $this->createCollection($databaseName, $collectionName);
$collection->insertMany([
['name' => 'Ribeira Charming Duplex'],
['name' => 'Ocean View Bondi Beach'],
['name' => 'Luxury ocean view Beach Villa 622'],
['name' => 'Ocean & Beach View Condo WBR H204'],
['name' => 'Bondi Beach Spacious Studio With Ocean View'],
['name' => 'New York City - Upper West Side Apt'],
]);
putenv(sprintf('MONGODB_DATABASE=%s', $databaseName));
putenv(sprintf('MONGODB_COLLECTION=%s', $collectionName));

$expectedOutput = <<<'OUTPUT'
Creating the index.
%s
Performing a text search...
- Ocean View Bondi Beach
- Luxury ocean view Beach Villa 622
- Ocean & Beach View Condo WBR H204
- Bondi Beach Spacious Studio With Ocean View
Enjoy MongoDB Atlas Search!


OUTPUT;

$this->assertExampleOutput(__DIR__ . '/../examples/atlas-search.php', $expectedOutput);
}

public function testChangeStream(): void
{
$this->skipIfChangeStreamIsNotSupported();
Expand Down

0 comments on commit 7589840

Please sign in to comment.