Skip to content

CDRIVER-4612 document management of search indexes #1366

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

Merged
merged 9 commits into from
Aug 1, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/libmongoc/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1095,6 +1095,7 @@ if (ENABLE_EXAMPLES)
mongoc_add_example (example-command-monitoring ${PROJECT_SOURCE_DIR}/examples/example-command-monitoring.c)
mongoc_add_example (example-command-with-opts ${PROJECT_SOURCE_DIR}/examples/example-command-with-opts.c)
mongoc_add_example (example-manage-collection-indexes ${PROJECT_SOURCE_DIR}/examples/example-manage-collection-indexes.c)
mongoc_add_example (example-manage-search-indexes ${PROJECT_SOURCE_DIR}/examples/example-manage-search-indexes.c)
mongoc_add_example (example-gridfs ${PROJECT_SOURCE_DIR}/examples/example-gridfs.c)
mongoc_add_example (example-gridfs-bucket ${PROJECT_SOURCE_DIR}/examples/example-gridfs-bucket.c)
if (NOT WIN32 AND ENABLE_EXAMPLES)
Expand Down
37 changes: 37 additions & 0 deletions src/libmongoc/doc/manage-collection-indexes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,40 @@ To drop an index, use :symbol:`mongoc_collection_drop_index_with_opts`. The inde
:dedent: 6

For a full example, see `example-manage-collection-indexes.c <https://github.com/mongodb/mongo-c-driver/blob/master/src/libmongoc/examples/example-manage-collection-indexes.c>`_.

Manage Atlas Search Indexes
---------------------------

To create an Atlas Search Index, use the ``createSearchIndexes`` command:

.. literalinclude:: ../examples/example-manage-search-indexes.c
:language: c
:start-after: // Create an Atlas Search Index ... begin
:end-before: // Create an Atlas Search Index ... end
:dedent: 6

To list Atlas Search Indexes, use the ``$listSearchIndexes`` aggregation stage:

.. literalinclude:: ../examples/example-manage-search-indexes.c
:language: c
:start-after: // List Atlas Search Indexes ... begin
:end-before: // List Atlas Search Indexes ... end
:dedent: 6

To update an Atlas Search Index, use the ``updateSearchIndex`` command:

.. literalinclude:: ../examples/example-manage-search-indexes.c
:language: c
:start-after: // Update an Atlas Search Index ... begin
:end-before: // Update an Atlas Search Index ... end
:dedent: 6

To drop an Atlas Search Index, use the ``dropSearchIndex`` command:

.. literalinclude:: ../examples/example-manage-search-indexes.c
:language: c
:start-after: // Drop an Atlas Search Index ... begin
:end-before: // Drop an Atlas Search Index ... end
:dedent: 6

For a full example, see `example-manage-search-indexes.c <https://github.com/mongodb/mongo-c-driver/blob/master/src/libmongoc/examples/example-manage-search-indexes.c>`_.
230 changes: 230 additions & 0 deletions src/libmongoc/examples/example-manage-search-indexes.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,230 @@
// example-manage-search-indexes creates, lists, updates, and deletes an Atlas
// search index from the `test.test` collection.
// Example is expected to be run against a MongoDB Atlas cluster.

#include <mongoc/mongoc.h>
#include <stdlib.h> // abort

#define HANDLE_ERROR(...) \
if (1) { \
fprintf (stderr, __VA_ARGS__); \
fprintf (stderr, "\n"); \
goto fail; \
} else \
(void) 0

#define ASSERT(stmt) \
if (!stmt) { \
fprintf (stderr, \
"assertion failed on line: %d, statement: %s\n", \
__LINE__, \
#stmt); \
abort (); \
} else \
(void) 0

int
main (int argc, char *argv[])
{
mongoc_client_t *client = NULL;
const char *uri_string =
"mongodb://127.0.0.1/?appname=create-search-indexes-example";
mongoc_uri_t *uri = NULL;
mongoc_collection_t *coll = NULL;
bson_error_t error;
bool ok = false;

mongoc_init ();

if (argc > 2) {
HANDLE_ERROR (
"Unexpected arguments. Expected usage: %s [CONNECTION_STRING]",
argv[0]);
}

if (argc > 1) {
uri_string = argv[1];
}

uri = mongoc_uri_new_with_error (uri_string, &error);
if (!uri) {
HANDLE_ERROR ("Failed to parse URI: %s", error.message);
}
client = mongoc_client_new_from_uri_with_error (uri, &error);
if (!client) {
HANDLE_ERROR ("Failed to create client: %s", error.message);
}

// Create a random collection name.
char collname[25];
{
// There is a server-side limitation that prevents multiple search indexes
// from being created with the same name, definition and collection name.
// Atlas search index management operations are asynchronous. Dropping a
// collection may not result in the index being dropped immediately. Use a
// randomly generated collection name to avoid errors.
bson_oid_t oid;
bson_oid_init (&oid, NULL);
bson_oid_to_string (&oid, collname);
}

// Create collection object.
{
// Create the collection server-side to avoid the server error:
// "Collection 'test.<collname>' does not exist."
mongoc_database_t *db = mongoc_client_get_database (client, "test");
coll = mongoc_database_create_collection (
db, collname, NULL /* options */, &error);
if (!coll) {
mongoc_database_destroy (db);
HANDLE_ERROR ("Failed to create collection: %s", error.message);
}
mongoc_database_destroy (db);
}

// Check that $listSearchIndexes pipeline stage is supported.
// This is intended to check that a MongoDB Atlas cluster is used.
{
const char *pipeline_str =
BSON_STR ({"pipeline" : [ {"$listSearchIndexes" : {}} ]});
bson_t pipeline;
ASSERT (bson_init_from_json (&pipeline, pipeline_str, -1, &error));
mongoc_cursor_t *cursor =
mongoc_collection_aggregate (coll,
MONGOC_QUERY_NONE,
&pipeline,
NULL /* opts */,
NULL /* read_prefs */);
const bson_t *got;
while (mongoc_cursor_next (cursor, &got))
;
if (mongoc_cursor_error (cursor, &error)) {
bson_destroy (&pipeline);
mongoc_cursor_destroy (cursor);
HANDLE_ERROR ("Failed to run $listSearchIndexes with error: %s\n"
"Does the URI point to a MongoDB Atlas cluster? %s",
error.message,
uri_string);
}
bson_destroy (&pipeline);
mongoc_cursor_destroy (cursor);
}

{
// Create an Atlas Search Index ... begin
bson_t cmd;
// Create command.
{
char *cmd_str = bson_strdup_printf (
BSON_STR ({
"createSearchIndexes" : "%s",
"indexes" : [ {
"definition" : {"mappings" : {"dynamic" : false}},
"name" : "test-index"
} ]
}),
collname);
ASSERT (bson_init_from_json (&cmd, cmd_str, -1, &error));
bson_free (cmd_str);
}
if (!mongoc_collection_command_simple (
coll, &cmd, NULL /* read_prefs */, NULL /* reply */, &error)) {
bson_destroy (&cmd);
HANDLE_ERROR ("Failed to run createSearchIndexes: %s", error.message);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If this example is run against a non-Atlas server, this example fails on this command attempt and error with a "no such cmd" message. Can we consider adding an earlier "example must be run against Atlas" check and error to make the required conditions a bit more clear?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like that idea. ee78062 adds an earlier check that $listSearchIndexes is supported, and prints an error suggesting the example may not be using Atlas if an error occurred.

}
printf ("Created index: \"test-index\"\n");
bson_destroy (&cmd);
// Create an Atlas Search Index ... end
}

{
// List Atlas Search Indexes ... begin
const char *pipeline_str =
BSON_STR ({"pipeline" : [ {"$listSearchIndexes" : {}} ]});
bson_t pipeline;
ASSERT (bson_init_from_json (&pipeline, pipeline_str, -1, &error));
mongoc_cursor_t *cursor =
mongoc_collection_aggregate (coll,
MONGOC_QUERY_NONE,
&pipeline,
NULL /* opts */,
NULL /* read_prefs */);
printf ("Listing indexes:\n");
const bson_t *got;
while (mongoc_cursor_next (cursor, &got)) {
char *got_str = bson_as_canonical_extended_json (got, NULL);
printf (" %s\n", got_str);
bson_free (got_str);
}
if (mongoc_cursor_error (cursor, &error)) {
bson_destroy (&pipeline);
mongoc_cursor_destroy (cursor);
HANDLE_ERROR ("Failed to run $listSearchIndexes: %s", error.message);
}
bson_destroy (&pipeline);
mongoc_cursor_destroy (cursor);
// List Atlas Search Indexes ... end
}

{
// Update an Atlas Search Index ... begin
bson_t cmd;
// Create command.
{
char *cmd_str = bson_strdup_printf (
BSON_STR ({
"updateSearchIndex" : "%s",
"definition" : {"mappings" : {"dynamic" : true}},
"name" : "test-index"
}),
collname);
ASSERT (bson_init_from_json (&cmd, cmd_str, -1, &error));
bson_free (cmd_str);
}
if (!mongoc_collection_command_simple (
coll, &cmd, NULL /* read_prefs */, NULL /* reply */, &error)) {
bson_destroy (&cmd);
HANDLE_ERROR ("Failed to run updateSearchIndex: %s", error.message);
}
printf ("Updated index: \"test-index\"\n");
bson_destroy (&cmd);
// Update an Atlas Search Index ... end
}

{
// Drop an Atlas Search Index ... begin
bson_t cmd;
// Create command.
{
char *cmd_str = bson_strdup_printf (
BSON_STR ({"dropSearchIndex" : "%s", "name" : "test-index"}),
collname);
ASSERT (bson_init_from_json (&cmd, cmd_str, -1, &error));
bson_free (cmd_str);
}
if (!mongoc_collection_command_simple (
coll, &cmd, NULL /* read_prefs */, NULL /* reply */, &error)) {
bson_destroy (&cmd);
HANDLE_ERROR ("Failed to run dropSearchIndex: %s", error.message);
}
printf ("Dropped index: \"test-index\"\n");
bson_destroy (&cmd);
// Drop an Atlas Search Index ... end
}

// Drop created collection.
{
if (!mongoc_collection_drop (coll, &error)) {
HANDLE_ERROR (
"Failed to drop collection '%s': %s", collname, error.message);
}
}

ok = true;
fail:
mongoc_collection_destroy (coll);
mongoc_client_destroy (client);
mongoc_uri_destroy (uri);
mongoc_cleanup ();
return ok ? EXIT_SUCCESS : EXIT_FAILURE;
}
4 changes: 3 additions & 1 deletion src/libmongoc/tests/bsonutil/bson-match.c
Original file line number Diff line number Diff line change
Expand Up @@ -565,7 +565,9 @@ bson_matcher_match (bson_matcher_t *matcher,

memcpy (&tmp_error, error, sizeof (bson_error_t));
test_set_error (error,
"BSON match failed: %s\nExpected: %s\nActual: %s",
"BSON match failed: %s\n"
"Expected: %s\n"
"Actual: %s",
tmp_error.message,
bson_val_to_json (expected),
bson_val_to_json (actual));
Expand Down
2 changes: 1 addition & 1 deletion src/libmongoc/tests/bsonutil/bson-parser.c
Original file line number Diff line number Diff line change
Expand Up @@ -708,7 +708,7 @@ bson_parser_parse (bson_parser_t *parser, bson_t *in, bson_error_t *error)
{
if (!entry->optional && !entry->set) {
test_set_error (error,
"Required field %s was not found parsing: %s",
"Required field '%s' was not found parsing: %s",
entry->key,
tmp_json (in));
return false;
Expand Down
Loading