mongodbmongodb-querybsonmongodb-c

MongoDB using $or and $and in new C driver


I'm trying to emulate the following MongoDB shellcode:

db.collection.find( { $and: [ { $or: [ { document: { field: "X" } }, { field: "X" } ] }, { _id: ObjectId("X") } ] } );

This is what I've tried (with new MongoDB-C-Driver):

  bson_init(&query);
  bson_append_document_begin(&query, "$and", 4, &and);
  bson_append_oid(&and, "_id", 3, oid);
     bson_append_document_begin(&and, "$or", 3, &or);
     bson_append_utf8(&query, "field", 5, "X", 1);
     bson_append_document_end(&and, &or);

     bson_append_document_begin(&and, "$or", 3, &or);
     bson_append_utf8(&query, "document.field", 14, "X", 1);
     bson_append_document_end(&and, &or);
  bson_append_document_end(&query, &and);
  collection = mongoc_client_get_collection (client, "db", "collection");
  cursor = mongoc_collection_find(collection, MONGOC_QUERY_NONE, 0, 1, 0, &query, NULL, NULL);
  if(mongoc_cursor_next(cursor, &doc)){
     printf("> Field found\r\n");
  }

Thank you in advance.

Best regards.


Solution

  • The libbson imperative API for creating nested documents is a little tricky and unfortunately you've stumbled into one of the easy pitfalls. Once a subdocument has been opened with bson_append_document_begin or bson_append_array_begin, you must not write to it until a corresponding _end() call has been performed. In this case, you have append_utf8() calls in your "or" documents that write to "query".

    For an easier approach to bson composition, consider using the BCON api, which provides a more declarative syntax with minimal overhead:

    BCON_APPEND(&other_query,
        "$and", "{",
            "_id", BCON_OID(&oid),
            "$or", "{",
                "field", "X",
            "}",
            "$or", "{",
                "document.field", "X",
            "}",
        "}");
    

    using the bcon api might also have given you a hint that you weren't quite replicating what you thought you were.

    To produce the bson you were looking at in the shell:

    BCON_APPEND(&correct_query,
        "$and",
        "[",
            "{", "$or", "[",
                "{", "document", "{", "field", "X", "}", "}",
                "{", "field", "X", "}",
            "]", "}",
            "{", "_id", BCON_OID(&oid), "}",
        "]"
    );
    

    You can also use the bson_as_json() function to stringify a bson document into json, which should make it easier to see what object you've constructed

    iterative: { "$and" : { "_id" : { "$oid" : "53ff00f4342d8c1c712b4841" }, "$or" : { "field" : "X" }, "$or" : { "document.field" : "X" } } }
    bcon: { "$and" : { "_id" : { "$oid" : "53ff00f4342d8c1c712b4841" }, "$or" : { "field" : "X" }, "$or" : { "document.field" : "X" } } }
    correct: { "$and" : [ { "$or" : [ { "document" : { "field" : "X" } }, { "field" : "X" } ] }, { "_id" : { "$oid" : "53ff00f4342d8c1c712b4841" } } ] }
    

    The relevant documentation: http://api.mongodb.org/libbson/current/