Skip to content

Commit 7eb657d

Browse files
CDRIVER-3063 add support for pipelines in the update command
1 parent 8fd9f17 commit 7eb657d

9 files changed

+140
-11
lines changed

src/libmongoc/doc/mongoc_collection_update.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ Parameters
2222
* ``collection``: A :symbol:`mongoc_collection_t`.
2323
* ``flags``: A bitwise or of :symbol:`mongoc_update_flags_t`.
2424
* ``selector``: A :symbol:`bson:bson_t` containing the query to match documents for updating.
25-
* ``update``: A :symbol:`bson:bson_t` containing the update to perform.
25+
* ``update``: A :symbol:`bson:bson_t` containing the update to perform. If updating with a pipeline, a :symbol:`bson:bson_t` array.
2626
* ``write_concern``: A :symbol:`mongoc_write_concern_t`.
2727
* ``error``: An optional location for a :symbol:`bson_error_t <errors>` or ``NULL``.
2828

src/libmongoc/doc/mongoc_collection_update_many.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ Parameters
2121

2222
* ``collection``: A :symbol:`mongoc_collection_t`.
2323
* ``selector``: A :symbol:`bson:bson_t` containing the query to match documents for updating.
24-
* ``update``: A :symbol:`bson:bson_t` containing the update to perform.
24+
* ``update``: A :symbol:`bson:bson_t` containing the update to perform. If updating with a pipeline, a :symbol:`bson:bson_t` array.
2525
* ``reply``: Optional. An uninitialized :symbol:`bson:bson_t` populated with the update result, or ``NULL``.
2626
* ``error``: An optional location for a :symbol:`bson_error_t <errors>` or ``NULL``.
2727

src/libmongoc/doc/mongoc_collection_update_one.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ Parameters
2121

2222
* ``collection``: A :symbol:`mongoc_collection_t`.
2323
* ``selector``: A :symbol:`bson:bson_t` containing the query to match the document for updating.
24-
* ``update``: A :symbol:`bson:bson_t` containing the update to perform.
24+
* ``update``: A :symbol:`bson:bson_t` containing the update to perform. If updating with a pipeline, a :symbol:`bson:bson_t` array.
2525
* ``reply``: Optional. An uninitialized :symbol:`bson:bson_t` populated with the update result, or ``NULL``.
2626
* ``error``: An optional location for a :symbol:`bson_error_t <errors>` or ``NULL``.
2727

src/libmongoc/src/mongoc/mongoc-collection.c

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1753,7 +1753,6 @@ mongoc_collection_insert_many (mongoc_collection_t *collection,
17531753
RETURN (ret);
17541754
}
17551755

1756-
17571756
/*
17581757
*--------------------------------------------------------------------------
17591758
*
@@ -3066,7 +3065,11 @@ mongoc_collection_find_and_modify_with_opts (
30663065
}
30673066

30683067
if (opts->update) {
3069-
BSON_APPEND_DOCUMENT (&command, "update", opts->update);
3068+
if (_mongoc_document_is_pipeline (opts->update)) {
3069+
BSON_APPEND_ARRAY (&command, "update", opts->update);
3070+
} else {
3071+
BSON_APPEND_DOCUMENT (&command, "update", opts->update);
3072+
}
30703073
}
30713074

30723075
if (opts->fields) {

src/libmongoc/src/mongoc/mongoc-util-private.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,9 @@ void
131131
_mongoc_bson_init_with_transient_txn_error (const mongoc_client_session_t *cs,
132132
bson_t *reply);
133133

134+
bool
135+
_mongoc_document_is_pipeline (const bson_t *document);
136+
134137
BSON_END_DECLS
135138

136139
#endif /* MONGOC_UTIL_PRIVATE_H */

src/libmongoc/src/mongoc/mongoc-util.c

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -363,6 +363,10 @@ _mongoc_validate_update (const bson_t *update,
363363
return false;
364364
}
365365

366+
if (_mongoc_document_is_pipeline (update)) {
367+
return true;
368+
}
369+
366370
if (!bson_iter_init (&iter, update)) {
367371
bson_set_error (error,
368372
MONGOC_ERROR_BSON,
@@ -377,7 +381,8 @@ _mongoc_validate_update (const bson_t *update,
377381
bson_set_error (error,
378382
MONGOC_ERROR_COMMAND,
379383
MONGOC_ERROR_COMMAND_INVALID_ARG,
380-
"Invalid key '%s': update only works with $ operators",
384+
"Invalid key '%s': update only works with $ operators"
385+
" and pipelines",
381386
key);
382387

383388
return false;
@@ -521,3 +526,47 @@ _mongoc_bson_init_with_transient_txn_error (const mongoc_client_session_t *cs,
521526
bson_append_array_end (reply, &labels);
522527
}
523528
}
529+
530+
bool
531+
_mongoc_document_is_pipeline (const bson_t *document)
532+
{
533+
bson_iter_t iter;
534+
bson_iter_t child;
535+
const char *key;
536+
int i = 0;
537+
char *i_str;
538+
539+
if (!bson_iter_init (&iter, document)) {
540+
return false;
541+
}
542+
543+
while (bson_iter_next (&iter)) {
544+
key = bson_iter_key (&iter);
545+
i_str = bson_strdup_printf ("%d", i++);
546+
547+
if (strcmp (key, i_str)) {
548+
bson_free (i_str);
549+
return false;
550+
}
551+
552+
bson_free (i_str);
553+
554+
if (BSON_ITER_HOLDS_DOCUMENT (&iter)) {
555+
if (!bson_iter_recurse (&iter, &child)) {
556+
return false;
557+
}
558+
if (!bson_iter_next (&child)) {
559+
return false;
560+
}
561+
key = bson_iter_key (&child);
562+
if (key[0] != '$') {
563+
return false;
564+
}
565+
} else {
566+
return false;
567+
}
568+
}
569+
570+
/* should return false when the document is empty */
571+
return i != 0;
572+
}

src/libmongoc/src/mongoc/mongoc-write-command.c

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,11 @@ _mongoc_write_command_update_append (mongoc_write_command_t *command,
118118

119119
bson_init (&document);
120120
BSON_APPEND_DOCUMENT (&document, "q", selector);
121-
BSON_APPEND_DOCUMENT (&document, "u", update);
121+
if (_mongoc_document_is_pipeline (update)) {
122+
BSON_APPEND_ARRAY (&document, "u", update);
123+
} else {
124+
BSON_APPEND_DOCUMENT (&document, "u", update);
125+
}
122126
if (opts) {
123127
bson_concat (&document, opts);
124128
}

src/libmongoc/tests/json/crud/v2/updateWithPipelines.json

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@
2222
"tests": [
2323
{
2424
"description": "UpdateOne using pipelines",
25-
"skipReason": "blocked on CDRIVER-3063",
2625
"operations": [
2726
{
2827
"name": "updateOne",
@@ -101,7 +100,6 @@
101100
},
102101
{
103102
"description": "UpdateMany using pipelines",
104-
"skipReason": "blocked on CDRIVER-3063",
105103
"operations": [
106104
{
107105
"name": "updateMany",
@@ -175,7 +173,6 @@
175173
},
176174
{
177175
"description": "FindOneAndUpdate using pipelines",
178-
"skipReason": "blocked on CDRIVER-3063",
179176
"operations": [
180177
{
181178
"name": "findOneAndUpdate",

src/libmongoc/tests/test-mongoc-collection.c

Lines changed: 74 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1286,6 +1286,73 @@ test_update (void)
12861286
mongoc_client_destroy (client);
12871287
}
12881288

1289+
static void
1290+
test_update_pipeline (void *ctx)
1291+
{
1292+
mongoc_collection_t *collection;
1293+
mongoc_database_t *database;
1294+
mongoc_client_t *client;
1295+
bson_error_t error;
1296+
bson_t *b;
1297+
bson_t *pipeline;
1298+
bson_t *replacement;
1299+
bool res;
1300+
1301+
client = test_framework_client_new ();
1302+
ASSERT (client);
1303+
1304+
database = get_test_database (client);
1305+
ASSERT (database);
1306+
1307+
collection = get_test_collection (client, "test_update_pipeline");
1308+
ASSERT (collection);
1309+
1310+
b = tmp_bson ("{'nums': {'x': 1, 'y': 2}}");
1311+
res = mongoc_collection_insert_one (collection, b, NULL, NULL, &error);
1312+
ASSERT_OR_PRINT (res, error);
1313+
1314+
/* format: array document with incrementing keys
1315+
(i.e. {"0": value, "1": value, "2": value}) */
1316+
pipeline = tmp_bson ("{'0': {'$replaceRoot': {'newRoot': '$nums'}},"
1317+
" '1': {'$addFields': {'z': 3}}}");
1318+
res = mongoc_collection_update_one (
1319+
collection, b, pipeline, NULL, NULL, &error);
1320+
ASSERT_OR_PRINT (res, error);
1321+
1322+
res = mongoc_collection_insert_one (collection, b, NULL, NULL, &error);
1323+
ASSERT_OR_PRINT (res, error);
1324+
1325+
/* ensure that arrays sent to mongoc_collection_replace_one are not
1326+
treated as pipelines */
1327+
replacement = tmp_bson ("{'0': 0, '1': 1}");
1328+
res = mongoc_collection_replace_one (
1329+
collection, b, replacement, NULL, NULL, &error);
1330+
ASSERT_OR_PRINT (res, error);
1331+
1332+
/* ensure that pipeline updates sent to mongoc_collection_replace_one
1333+
receive a client-side error */
1334+
res = mongoc_collection_replace_one (
1335+
collection, b, pipeline, NULL, NULL, &error);
1336+
ASSERT (!res);
1337+
ASSERT_ERROR_CONTAINS (error,
1338+
MONGOC_ERROR_COMMAND,
1339+
MONGOC_ERROR_COMMAND_INVALID_ARG,
1340+
"invalid argument for replace");
1341+
1342+
/* ensure that a pipeline with an empty document is considered invalid */
1343+
pipeline = tmp_bson ("{ '0': {} }");
1344+
res = mongoc_collection_update_one (
1345+
collection, b, pipeline, NULL, NULL, &error);
1346+
ASSERT (!res);
1347+
ASSERT_ERROR_CONTAINS (error,
1348+
MONGOC_ERROR_COMMAND,
1349+
MONGOC_ERROR_COMMAND_INVALID_ARG,
1350+
"Invalid key");
1351+
1352+
mongoc_collection_destroy (collection);
1353+
mongoc_database_destroy (database);
1354+
mongoc_client_destroy (client);
1355+
}
12891356

12901357
static void
12911358
test_update_oversize (void *ctx)
@@ -6175,6 +6242,12 @@ test_collection_install (TestSuite *suite)
61756242
NULL,
61766243
skip_unless_server_has_decimal128);
61776244
TestSuite_AddLive (suite, "/Collection/update", test_update);
6245+
TestSuite_AddFull (suite,
6246+
"/Collection/update_pipeline",
6247+
test_update_pipeline,
6248+
NULL,
6249+
NULL,
6250+
test_framework_skip_if_max_wire_version_less_than_8);
61786251
TestSuite_AddLive (suite, "/Collection/update/multi", test_update_multi);
61796252
TestSuite_AddLive (suite, "/Collection/update/upsert", test_update_upsert);
61806253
TestSuite_AddFull (suite,
@@ -6346,4 +6419,4 @@ test_collection_install (TestSuite *suite)
63466419
NULL,
63476420
NULL,
63486421
test_framework_skip_if_not_replset);
6349-
}
6422+
}

0 commit comments

Comments
 (0)