-
Notifications
You must be signed in to change notification settings - Fork 263
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add Atlas Search example #1147
Add Atlas Search example #1147
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
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'); | ||
jmikola marked this conversation as resolved.
Show resolved
Hide resolved
|
||
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); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Since this is idempotent, I can remove surrounding loop. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In fact no, the error for |
||
|
||
// 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; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is a boolean, but I could not document it in phpdoc. #1097 (comment) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I see there's no Psalm error here. Is it worth casting this to a boolean and using |
||
} | ||
} | ||
|
||
return false; | ||
}); | ||
|
||
// Perform a text search | ||
echo "\n", 'Performing a text search...', "\n"; | ||
$results = $collection->aggregate([ | ||
jmikola marked this conversation as resolved.
Show resolved
Hide resolved
|
||
[ | ||
'$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'); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like the idea of a comment header explaining the purpose of the example. This might be a good place to come up with some header copypasta that notes supported environment variables and/or CLI arguments (down the line, not in this PR).