You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
I'm using mongocxx and bsoncxx libraries in c++. How can i pass find query result (or any native type) to nodejs with node-addon-api ?
ValueFind(Callbackinfo&info{
mongocxx::instanceinst{};
mongocxx::clientconn{mongocxx::uri{}};
auto db=conn["test"];
auto cursor=db["testcollection"].find({});
returncursor ??
}
Take a look at ObjectWrap. It allows you to "wrap" any native C++ object to be used within Node. This will basically create a new JS class that you can use with constructor, prototype properties, etc. If you want to have some functionality that the JS APIs can access (eg. perhaps this mongocxx::cursor has some APIs like next() that you would want to access within JS), ObjectWrap would be a good choice for you.
If you just want to have simple NAPI Value that represents the underlying C++ mongocxx::cursor data, you could also use External. This basically creates a Napi::Value that has any arbitrary void* data associated with it. You can only access this data within your C++ native module however. This object will be presented to your JS code as a simple external object that has no real properties etc. (of course, you can provide Napi::Functions that use an Napi::External, but might be easier for the ObjectWrap approach depending on your use cases)
Hi @emreyalvac,
when you return a value like Napi::External it will be marked like external object and you can access and manipulate it only in the C++ side. The Napi::Externalis used to allocate a JavaScript value with external data attached to it, data that you can use later on the native side. On the JavaScript side when you try to print the properties of Napi::External object you will get the output you reported.
You can use Napi::External to return your native object, but you need to create a native function if you want use it:
Your question is really vague. Really, anything is possible, you just have to use the correct wrappers. Can you be more specific with what you are trying to accomplish? I am not familiar with the mongocxx library.
I'm using mongocxx library for use mongodb in c++. I'm running Find function in c++. It's returning mongocxx::cursor type but i can't returning this type to nodejs.
ValueFind(constCallbackInfo&info) {
std::stringcollection=info[0].As<Napi::String>();
mongocxx::uriuri{"mongodb://localhost:27017/?minPoolSize=100&maxPoolSize=150"};
mongocxx::poolpool{uri};
auto client=pool.acquire();
auto cursor= (*client)["testdb"][collection].find({});
returnExternal<mongocxx::cursor>::New(info.Env(), &cursor);
}
You are returning an External to JS. You need to also create C++ methods that will take this Value as an External<mongocxx:cursor> and get its .Data() pointer (which would be this address &cursor). This is what @NickNaso 's example does with an External<std::string>: his C++ method CreateExternal returns to JS a Value (specifically, this External<std::string>) and his C++ method ToString takes the first argument in passed in JS as External, gets its .Data(), and performs actions).
Note: with your implementation of Find() you are creating values on the stack (these uri and pool values), and then passing this stack-allocated pointer to External::New. I do not think this will work as you intend it to -- this value is no longer valid once this function returns.
With External, you will need to use proper memory management to the allocate memory in this method and (generally) free it once the External is no longer used inside JavaScript. The External::New constructor takes a callback that is ran on the main thread when the External is about to be garbage collected.
I would highly look into ObjectWrap because this will handle a lot of the memory management for you. For example, your C++ class' constructor correlates to the JS constructor, and here you could create the mongocxx::uri uri{"mongodb://localhost:27017/?minPoolSize=100&maxPoolSize=150"}; which would be stored on the instance being constructed, if that makes sense. Then in your class' destructor, do whatever cleanup necessary (again, destructor is ran when your object is being GC'd)
data.Data() will give you the pointer mongocxx::cursor* that you defined via External<mongocxx::cursor>::New(info.Env(), &cursor); in Find() , but see my comment regarding the validity of this pointer's data.
Then, you will need to use whatever mongocxx APIs there are to get whatever values you want (strings, numbers, etc) and create JavaScript values via Object::New, Number::New, String::New, etc... See Basic Types
So, for example, if there was some function on mongocxx::cursor like int numRows(), you would return from your GetFind() function something like return Number::New(info.Env(), data.Data()->numRows());
Hi @emreyalvac,
when you return a value like Napi::External it will be marked like external object and you can access and manipulate it only in the C++ side. The Napi::Externalis used to allocate a JavaScript value with external data attached to it, data that you can use later on the native side. On the JavaScript side when you try to print the properties of Napi::External object you will get the output you reported.
You can use Napi::External to return your native object, but you need to create a native function if you want use it:
Hello! I am currently trying to do moreless the same. The problem is that I cannot integrate the mongocxx drivers in the addon; when I run node-gyp rebuild, I get an error message that says "cannot open source file", with all the mongocxx driver libraries that I include. Could you please tell me how you did it? @emreyalvac I would be really grateful
Activity
legendecas commentedon Nov 1, 2019
Any types returned to JavaScript has to be bridged to Napi::Value type in node-addon-api.
Though it depends on your usage case: if the value was expected to be manipulated in JavaScript.
If not, attaching it to an arbitrary JavaScript object just works.
KevinEady commentedon Nov 1, 2019
Hi @emreyalvac ,
Take a look at ObjectWrap. It allows you to "wrap" any native C++ object to be used within Node. This will basically create a new JS class that you can use with constructor, prototype properties, etc. If you want to have some functionality that the JS APIs can access (eg. perhaps this
mongocxx::cursorhas some APIs likenext()that you would want to access within JS),ObjectWrapwould be a good choice for you.If you just want to have simple NAPI Value that represents the underlying C++
mongocxx::cursordata, you could also use External. This basically creates aNapi::Valuethat has any arbitraryvoid* dataassociated with it. You can only access this data within your C++ native module however. This object will be presented to your JS code as a simple external object that has no real properties etc. (of course, you can provideNapi::Functions that use anNapi::External, but might be easier for theObjectWrapapproach depending on your use cases)Does this help?
NickNaso commentedon Nov 1, 2019
I want add that her you can find some simple examples on how to use
Napi::ObjectWrap:emreyalvac commentedon Nov 1, 2019
I used it like this, but the return was like that
js:
//Output
NickNaso commentedon Nov 1, 2019
Hi @emreyalvac,
when you return a value like
Napi::Externalit will be marked like external object and you can access and manipulate it only in the C++ side. TheNapi::Externalis used to allocate a JavaScript value with external data attached to it, data that you can use later on the native side. On the JavaScript side when you try to print the properties ofNapi::Externalobject you will get the output you reported.You can use
Napi::Externalto return your native object, but you need to create a native function if you want use it:emreyalvac commentedon Nov 2, 2019
@NickNaso
Thank you for your answer but i can't use it with mongocxx lib find type ?
KevinEady commentedon Nov 2, 2019
Hi @emreyalvac ,
Your question is really vague. Really, anything is possible, you just have to use the correct wrappers. Can you be more specific with what you are trying to accomplish? I am not familiar with the mongocxx library.
emreyalvac commentedon Nov 2, 2019
Hi @KevinEady ,
Sorry for the uncertainty.
I'm using mongocxx library for use mongodb in c++. I'm running Find function in c++. It's returning mongocxx::cursor type but i can't returning this type to nodejs.
KevinEady commentedon Nov 2, 2019
Hi @emreyalvac ,
You are returning an
Externalto JS. You need to also create C++ methods that will take thisValueas anExternal<mongocxx:cursor>and get its.Data()pointer (which would be this address&cursor). This is what @NickNaso 's example does with anExternal<std::string>: his C++ methodCreateExternalreturns to JS aValue(specifically, thisExternal<std::string>) and his C++ methodToStringtakes the first argument in passed in JS asExternal, gets its.Data(), and performs actions).Note: with your implementation of
Find()you are creating values on the stack (theseuriandpoolvalues), and then passing this stack-allocated pointer toExternal::New. I do not think this will work as you intend it to -- this value is no longer valid once this function returns.With
External, you will need to use proper memory management to the allocate memory in this method and (generally) free it once theExternalis no longer used inside JavaScript. TheExternal::Newconstructor takes a callback that is ran on the main thread when theExternalis about to be garbage collected.I would highly look into
ObjectWrapbecause this will handle a lot of the memory management for you. For example, your C++ class' constructor correlates to the JS constructor, and here you could create themongocxx::uri uri{"mongodb://localhost:27017/?minPoolSize=100&maxPoolSize=150"};which would be stored on the instance being constructed, if that makes sense. Then in your class' destructor, do whatever cleanup necessary (again, destructor is ran when your object is being GC'd)Does this all help?
emreyalvac commentedon Nov 2, 2019
Hi @KevinEady ,
I already did that way. How can i return ?
KevinEady commentedon Nov 2, 2019
data.Data()will give you the pointermongocxx::cursor*that you defined viaExternal<mongocxx::cursor>::New(info.Env(), &cursor);inFind(), but see my comment regarding the validity of this pointer's data.KevinEady commentedon Nov 2, 2019
Then, you will need to use whatever mongocxx APIs there are to get whatever values you want (strings, numbers, etc) and create JavaScript values via
Object::New,Number::New,String::New, etc... See Basic TypesSo, for example, if there was some function on
mongocxx::cursorlikeint numRows(), you would return from yourGetFind()function something likereturn Number::New(info.Env(), data.Data()->numRows());KevinEady commentedon Nov 5, 2019
Hi @emreyalvac ,
Does this solve your question? Can we close this issue?
emreyalvac commentedon Nov 6, 2019
@KevinEady
Unfortunately, I couldn't resolve.
KevinEady commentedon Nov 6, 2019
Hi @emreyalvac ,
Is there something we can help with then? Do you have more questions? What can we do so we can close this issue?
alexisfrjp commentedon Jan 31, 2020
@NickNaso
That should be an official example and your explanation in the readme.
I'm glad I found this post just before opening a ticket.
Thanks!
jmquilez commentedon Mar 31, 2021
Hello! I am currently trying to do moreless the same. The problem is that I cannot integrate the mongocxx drivers in the addon; when I run node-gyp rebuild, I get an error message that says "cannot open source file", with all the mongocxx driver libraries that I include. Could you please tell me how you did it? @emreyalvac I would be really grateful