C++, db.collection.find({}, {})

234 views
Skip to first unread message

Jeff Abrahamson

unread,
Dec 7, 2017, 10:09:46 AM12/7/17
to mongodb-user

I want to find and project.  In the mongo shell, I might write this:

db.spines.find({'group_id': {$gt: 285}}, {'group_id': 1, 'wrapper_version': 1})

In C++ (driver source as 3.1.3 / 002fbaf5), I naively try this, which, of course, doesn't work:

mongocxx::cursor cursor = spines.find(
    document{} << "group_id" << open_document << "$gt" << 285
               << close_document << open_document << "group_id" << true
               << "wrapper_version" << true << close_document << finalize);
for (auto doc : cursor) {
    cout << bsoncxx::to_json(doc) << endl;
}

Any pointers?

Related, I've not found proper documentation on the C++ driver, only the tutorial (which is great for getting started) and the code (which is commented, but doesn't help me get unlost, just explains what each individual function does).  Does it exist yet?  I ask not to nag (I prefer code to documentation, in the end, and the code is really great once I understand it) but because I'd like to help myself as much as possible.  Perhaps I've missed something.

Thanks!

-- 

Jeff Abrahamson
+33 6 24 40 01 57
+44 7920 594 255

http://p27.eu/jeff/

Andrew Morrow

unread,
Dec 7, 2017, 10:18:38 AM12/7/17
to mongod...@googlegroups.com

Hi -

The find method takes an optional options::find argument, which includes setting a projection. I recommend reading this example, and the highlighted line demonstrates setting a projection.


As far as documentation, I'm hoping later today to upload the docs for 3.2.0-rc1, but you can find the 3.1.3 javadocs here:


Check out the "Resources" section of that page for additional links to tutorials and manuals for the driver. I also recommend searching around in the examples directory for when you find you aren't sure how to do something.

Thanks,
Andrew



--
You received this message because you are subscribed to the Google Groups "mongodb-user"
group.
 
For other MongoDB technical support options, see: https://docs.mongodb.com/manual/support/
---
You received this message because you are subscribed to the Google Groups "mongodb-user" group.
To unsubscribe from this group and stop receiving emails from it, send an email to mongodb-user+unsubscribe@googlegroups.com.
To post to this group, send email to mongod...@googlegroups.com.
Visit this group at https://groups.google.com/group/mongodb-user.
To view this discussion on the web visit https://groups.google.com/d/msgid/mongodb-user/8a21d7da-f34a-e998-92c7-00242170cab2%40p27.eu.
For more options, visit https://groups.google.com/d/optout.

Jeff Abrahamson

unread,
Dec 8, 2017, 6:43:08 AM12/8/17
to mongodb-user
On 07/12/17 16:09, Jeff Abrahamson wrote:

I want to find and project.  In the mongo shell, I might write this:

db.spines.find({'group_id': {$gt: 285}}, {'group_id': 1, 'wrapper_version': 1})

In C++ (driver source as 3.1.3 / 002fbaf5), I naively try this, which, of course, doesn't work:

mongocxx::cursor cursor = spines.find(
    document{} << "group_id" << open_document << "$gt" << 285
               << close_document << open_document << "group_id" << true
               << "wrapper_version" << true << close_document << finalize);
for (auto doc : cursor) {
    cout << bsoncxx::to_json(doc) << endl;
}

Any pointers?


I can partially answer my question.  In order to project, I use mongocxx::options::find::projection().  The following code snippet works correctly:

    #define FIND_PROJECT_JUST_ENOUGH 1
    #if FIND_PROJECT_JUST_ENOUGH
        mongocxx::options::find opts{};
        opts.projection(document{} << "group_id" << 1 << "wrapper_version" << 1
                                   << finalize);
        mongocxx::cursor cursor =
            spines.find(document{} << "group_id" << open_document << "$gt" << 285
                                   << close_document << finalize,
                        opts);
    #endif  // FIND_PROJECT_JUST_ENOUGH //////////////////////////////////////

        for (auto doc : cursor) {
            cout << bsoncxx::to_json(doc) << endl;
        }

But something is strange about my understanding of the bsoncxx::builder::stream::document class.  If I declare the document first and then pass it to find(), I get the projection I expect but for all members of the collection, not only those that pass the filter (the first argument to find()).

    #define FIND_PROJECT_TOO_MANY 0
    #if FIND_PROJECT_TOO_MANY
        document doc{};
        doc << "group_id" << open_document << "$gt" << 285 << close_document
            << finalize;

        mongocxx::options::find opts{};
        opts.projection(document{} << "group_id" << 1 << "wrapper_version" << 1
                                   << finalize);
        mongocxx::cursor cursor = spines.find(doc.view(), opts);
    #endif  // FIND_PROJECT_TOO_MANY //////////////////////////////////////////

Could anyone offer some suggestion on what is happening that the filter fails in this case?  I've been tracing through these in the debugger without gaining much insight on why the bsoncxx::builder::stream::document behaves differently in the two cases.  I suspect the implicit conversion to something in the first case vs the explicit conversion to a view is key, but...

Andrew Morrow

unread,
Dec 8, 2017, 8:31:52 AM12/8/17
to mongod...@googlegroups.com

Please see my other reply to you, where I provide some background on the stream builder. The essence of the problem here is that you aren't viewing what you think you are. When you say doc.view in your call to spines.find, you are viewing the ephemeral state of the stream builder. However, you asked the stream builder to finalize and return its state when you streamed in the finalize object, and then you failed to capture the state that it returned. See the following for a version that should work:

        document doc_builder{};
        auto doc = doc_builder << "group_id" << open_document << "$gt" << 285 << close_document
            << finalize;
        mongocxx::options::find opts{};
        opts.projection(document{} << "group_id" << 1 << "wrapper_version" << 1
                                   << finalize);
        mongocxx::cursor cursor = spines.find(doc.view(), opts);

Here, I've renamed doc to doc_builder, since bsoncxx::builder::stream::document is a document builder and not itself a document (e.g. not a bsoncxx::document::value), and I've captured the result of streaming finalize into the document builder into a variable doc, which is a bsoncxx::document::value. Then your call to spines.find(doc.view(), opts) works correctly, because now you are passing in a view of the document you built.

You may well wonder why your call to doc.view even compiled when it was being invoked on the document builder, rather than the document. It compiled and ran because the builder allows you to get a view of the current ephemeral state of the document it is building. However, you had already extracted the state of the builder by that time, so the call to doc.view returned you a view of an empty document, which of course matched everything. 

These sort of subtleties are why we have begun to de-emphasize the stream builder to users. While it is very powerful, the "type stack" idea and conceptual overload of the << operator leads to extensive confusion and difficulty. I recommend working with the basic or core builders, and potentially building your own abstraction above them. The ability of the basic builder to accept arbitrary callables gives it great power and flexibility.

Happy to answer any additional questions.

Thanks,
Andrew


--
You received this message because you are subscribed to the Google Groups "mongodb-user"
group.
 
For other MongoDB technical support options, see: https://docs.mongodb.com/manual/support/
---
You received this message because you are subscribed to the Google Groups "mongodb-user" group.
To unsubscribe from this group and stop receiving emails from it, send an email to mongodb-user+unsubscribe@googlegroups.com.
To post to this group, send email to mongod...@googlegroups.com.
Visit this group at https://groups.google.com/group/mongodb-user.

Jeff Abrahamson

unread,
Dec 8, 2017, 8:34:00 AM12/8/17
to mongodb-user
The solution seems to be to avoid the bsoncxx::builder::stream interface in favour of the bsoncxx::builder::basic interface.  I just did this, which works fine:

    bsoncxx::builder::basic::document doc;
    doc.append(kvp("group_id",
                   [](sub_document subdoc) { subdoc.append(kvp("$gt", 285)); }));

    mongocxx::options::find opts{};
    opts.projection(document{} << "group_id" << 1 << "wrapper_version" << 1
                               << finalize);
    mongocxx::cursor cursor = spines.find(doc.view(), opts);
    for (auto doc : cursor) {
        cout << bsoncxx::to_json(doc) << endl;
    }

I'm a bit mystified why that lambda works.  It has no return statement, so a void return, and the sub_document should go out of scope after each invocation.  But it works...

Some further resources:

https://github.com/mongodb/mongo-cxx-driver/blob/master/examples/bsoncxx/builder_basic.cpp
https://mongodb.github.io/mongo-cxx-driver/mongocxx-v3/working-with-bson/#builders


and very notably, Saghm on stackoverflow, an engineer at mongodb, sagely notes (and explains) that the bsonxcxx stream classes aren't streams and have the peculiar and unexpected property (for the streams that they are not) of sometimes having return values that are not the type of their LHS.  In addition, s/he notes quite correctly that the stream error messages are nearly indecipherable and easily provoked whilst the builder messages tend to be harder to provoke and easier to understand.

https://stackoverflow.com/questions/43770113/mongocxx-array-of-objectid-in-find
https://stackoverflow.com/questions/42952742/how-to-generate-mongodb-documents-recursively-using-mongocxx-c-driver/42987087#42987087
https://stackoverflow.com/questions/42445029/ambiguous-operator-in-mongodb-c-driver-document-builder/42463387#42463387

Jeff Abrahamson

unread,
Dec 9, 2017, 9:34:45 AM12/9/17
to mongodb-user
Andrew (and archive readers ages hence), my apologies for not seeing your response until after posting my own.  A quirk in google groups settings (now fixed, I think) resulted in my not being subscribed to my own email, and so not seeing your response properly.

I'd have been much more productive yesterday if I'd seen your response before taking nearly a full day to figure out nearly the same thing for myself.  :(

Jeff

Andrew Morrow

unread,
Dec 11, 2017, 2:28:40 PM12/11/17
to mongod...@googlegroups.com

Hi Jeff -

No worries about the missed reply. The answer to your question about the lambda is fun. When the basic builder append method is called with a kvp containing a callable, overloads that match based on the argument types of the callable kick in. In the case where the callable accepts an argument of type sub_document, the matched implementation then creates the sub_document and invokes the lambda it:


The effect is that for the scope of the lambda you provide, you have a lifetime limited sub_document or sub_array to work with.

Hope that helps clarify.

Andrew



--
You received this message because you are subscribed to the Google Groups "mongodb-user"
group.
 
For other MongoDB technical support options, see: https://docs.mongodb.com/manual/support/
---
You received this message because you are subscribed to the Google Groups "mongodb-user" group.
To unsubscribe from this group and stop receiving emails from it, send an email to mongodb-user+unsubscribe@googlegroups.com.
To post to this group, send email to mongod...@googlegroups.com.
Visit this group at https://groups.google.com/group/mongodb-user.

Jeff Abrahamson

unread,
Dec 12, 2017, 1:30:12 PM12/12/17
to mongodb-user
Oh, that is very nice.  Thanks for the explanation and code link!

You received this message because you are subscribed to a topic in the Google Groups "mongodb-user" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/mongodb-user/mPDJXBKFn80/unsubscribe.
To unsubscribe from this group and all its topics, send an email to mongodb-user+unsubscribe@googlegroups.com.

To post to this group, send email to mongod...@googlegroups.com.
Visit this group at https://groups.google.com/group/mongodb-user.

Andrew Morrow

unread,
Dec 12, 2017, 1:38:17 PM12/12/17
to mongod...@googlegroups.com

Glad that helped. There is actually a lot of fun stuff in the bsoncxx and mongocxx libraries. It helps if you think of them as tools on which to build higher level abstractions, rather than an end state. The bsoncxx library in particular is multilayered exactly so that it is easy to build new types of builders above the primitive ones we provide. Along those lines I hope that we will find some time to implement this: https://jira.mongodb.org/browse/CXX-1455, ideally augmented with similar lambda games.


Jeff Abrahamson

unread,
Dec 12, 2017, 3:44:23 PM12/12/17
to mongodb-user
Reply all
Reply to author
Forward
0 new messages