Can't add custom object into mongoDB Document Array - javascript

I'm trying to add a new object to my mongodb document inside an array.
I have a NodeJS project using MongoDB which has a collection called "Teste" where i'm saving some random data.
Amongst that data is an array called "ArrayTeste". Currently it is only saving multiple strings because I named my inputs the same thing, so it automatically does it for me.
But I don't want to save each element as an individual string, i need to get these informations, group them in an object and then add it to the array.
Here is my code snippet in NodeJS:
ServicosModel.prototype.Teste = function (req, res) {
console.log("Metodo Teste");
var query =
{
$push:
{
ArrayTeste:
{
Dado1: req.body.Dado1,
Dado2: req.body.Dado2
}
}
}
console.log(query)
this._connection.open(function (errConn, mongoClient) {
console.log("Entrou open")
if (errConn) {
res.end("Deu erro" + errConn);
}
mongoClient.collection("teste", function (errColl, collection) {
if (errColl) {
res.end("Deu erro" + errColl);
}
console.log("Entrou collection")
collection.update(query, function (errUpdate, result) {
console.log("Entrou update")
if (errUpdate) {
res.end("Deu erro" + errUpdate);
} else {
res.end("Deu certo " + result);
}
});
});
});
}
And here is the mongoDB document structure:
{
"_id" : ObjectId("595bf19febbf3c14e481bc28"),
"id" : "2",
"Titulo" : "TItulo do negocio",
"ArrayTeste" : [
"dado1",
"dado2"
]
}
The "id" parameter is one created by me to easy the $elemMatch used in previous tests, so I don't have to search for the _id of the document.
When I run the code and insert stuff into the inputs, I am presented with this error:
(node:8712) UnhandledPromiseRejectionWarning: Unhandled promise
rejection (rejection id: 1): MongoError: document must be a valid
JavaScript object
and I have absolutely no idea of what is happening. the application simply freezes. I have searched through the posts and tried some stuff with $set and $addToSet, but the same error persists.
Any help is appreciated, thanks in advance!

To update a document you need two mandatory parameters:
criteria - to select documents to update
update - to modify selected documents
Here is the driver documentation: https://mongodb.github.io/node-mongodb-native/markdown-docs/insert.html#update.
The error says that collection.update expects an object (the second parameter) and it is receiving a function (your callback function).
To get your code working:
var select = {id: '2'}; // Here we are choosing the document
collection.update(select, query, function (errUpdate, result) {
if (errUpdate) {
res.end("Deu erro" + errUpdate);
} else {
res.end("Deu certo " + result);
}
});

Related

MeteorJS Infinite loop when using meteor call and meteor method

I have a sample code that goes like this:
Client Helper:
getUsername: function (userId) {
Meteor.call("getUsername", userId, function (err, result) {
if(!err) {
Session.set("setUsername", result);
else {
console.log(err);
}
});
return Session.get("setUsername");
}
Server
Meteor.methods({
"getUsername": function (userId) {
var x = Meteor.users.find({_id: userId}, {fields: {username:1}}).fetch()[0];
return x.username;
}
});
The result of this code is an infinite loop of username passing to the client. Is there a way to stop the loop and pass only the data that is needed on the client? I believe the reactivity is causing the data to loop infinitely and I am not sure how to stop it. I tried using "reactive":false on my query in the server but it does not work.
If you want to access username everywhere in client templates (so thats why you put it into session), I would not set it in template helper. I would set it on startup and get username from session in template helpers (without calling server method)
If you need username just in one template, so you want to return its value from your template helper, do not put it into session, just return it in your server method callback.
Based on your sample code, I assume, you have a set of posts and you are retrieving user name based on user id for each post. Then instead of doing it this way, you should use publish composite package to publish related users as well.
Meteor.publishComposite('getPosts', function (postIds) {
return [{
find: function() {
return Posts.find({ _id: { $in: postIds }});
// you can also do -> return Posts.find();
// or -> return Posts.find({ /* or what ever your selector is to get the posts you need*/ });
},
children: [{
find: function(post) {
return Meteor.users.find({
id: post.userId //or the correct field in your post document to get user id
}, {
fields: {
"profile": 1
}
});
}
}}
}]
});
This way your publication will take care of publishing related users along with posts. You don't need to use methods and call them each time.

Send a request with arrays in Node-soap (node.js)

I am communicating to a web service using nodejs and node-soap. But i just can't seem to get the syntax right for passing the parameters to the service.
The documentation says i need to send an array with the field uuid and its value.
Here is the Php code i got as an example from the web service owner
$uuid = "xxxx";
$param = array("uuid"=>new SoapVar($uuid,
XSD_STRING,
"string", "http://www.w3.org/2001/XMLSchema")
)
and here is the code i am using in my node server
function getSoapResponse()
{
var soap = require('soap');
var url = 'http://live.pagoagil.net/soapserver?wsdl';
var auth = [{'uuid': 'XXXXXXXXX'}];
soap.createClient(url, function(err, client) {
client.ListaBancosPSE(auth, function(err, result)
{
console.log(result);
console.log(err);
});
});
With this i get bad xml error
var auth = [{'uuid': 'XXXXXXXXX'}];
or
var auth = [["uuid",key1],XSD_STRING,"string","http://www.w3.org/2001/XMLSchema"];
and with this i get the response "the user id is empty" (the uuid)
var auth = {'uuid': 'XXXXXXXXX'};
Any suggestions?
Finally using the content in this answer and modifying the code in the soap-node module i was able to obtain the code i needed.
I needed something like this:
<auth xsi:type="ns2:Map">
<item>
<key xsi:type="xsd:string">uuid</key>
<value xsi:type="xsd:string">{XXXXXX}</value>
</item>
</auth>
so I used this for creating the parameters:
var arrayToSend=
{auth :
[
{ 'attributes' : {'xsi:type':"ns2:Map"},
'item':
[
{'key' :
{'attributes' :
{ 'xsi:type': 'xsd:string'},
$value: 'uuid'
}
},
{'value' :
{'attributes' :
{ 'xsi:type': 'xsd:string'},
$value: uuid
}
}
]
}
]
};
and sent it like this:
soap.createClient(url, myFunction);
function myFunction(err, client)
{
client.ListaBancosPSE(arrayToSend,function(err, result)
{
console.log('\n' + result);
});
}
Then the tricky part was modyfing the wsd.js so it didn't add a extra tag everytime i used and array. I went to line 1584 and changed the if for this:
if (Array.isArray(obj))
{
var arrayAttr = self.processAttributes(obj[0]),
correctOuterNamespace = parentNamespace || ns; //using the parent namespace if given
parts.push(['<', correctOuterNamespace, name, arrayAttr, xmlnsAttrib, '>'].join(''));
for (var i = 0, item; item = obj[i]; i++)
{
parts.push(self.objectToXML(item, name, namespace, xmlns, false, null, parameterTypeObject, ancXmlns));
}
parts.push(['</', correctOuterNamespace, name, '>'].join(''));
}
basically now it does not push the open and close tag in every iterarion but instead only before and after the whole cycle.
Also i needed to add the definitions for the xlmns of the message. Client.js:186
xml = "<soap:Envelope " +
"xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\" " +
'xmlns:xsd="http://www.w3.org/2001/XMLSchema"' +
'xmlns:ns2="http://xml.apache.org/xml-soap"' +
"xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" " +
Hopefully this could be of help for people using this library and being in this situation.
There is not much I can do for you but here are a few tips to get you started.
Use client.describe() to see how the service expects the arguments.
The service you are trying to reach has the following structure:
{ App_SoapService:
{ App_SoapPort:
{ Autorizar: [Object],
AutorizarAdvance: [Object],
AutorizarIac: [Object],
ListaBancosPSE: [Object],
AutorizarPSE: [Object],
AutorizarTuya: [Object],
AutorizarBotonCredibanco: [Object],
FinalizarPSE: [Object],
FinalizarTuya: [Object],
ConsultarReferencia: [Object] } } }
Taking a closer look to the specific method ListaBancosPSE it provides this info:
{input: { auth: 'soap-enc:Array' },
output: { return: 'soap-enc:Array' }}
I tried with this:
var soap = require('soap');
function getSoapResponse(url, auth) {
soap.createClient(url, function(err, client) {
console.log(client.describe());
console.log(client.describe().App_SoapService.App_SoapPort.ListaBancosPSE);
client.ListaBancosPSE(auth, function(err, result) {
console.log(JSON.stringify(result));
console.log(err);
});
});
}
getSoapResponse('http://live.pagoagil.net/soapserver?wsdl', {'soap-enc:Array' : {'uuid': 'XXXXXXXXX'}});
The response is the same "Negada, Error nombre de usuario vacio, No se pudo autenticar en pagoagil.net.".
The next steps for you would be to determine which is the message the service is expecting.
Could be something like:
<tns:ListaBancosPSE><uuid>XXXXXXXXX</uuid></tns:ListaBancosPSE>
Or
<tns:ListaBancosPSE><soap-enc:Array><uuid>XXXXXXXXX</uuid></soap-enc:Array></tns:ListaBancosPSE>
Once you know that, you just have to add a console.log in the node-soap package you installed, so go to where you have your node_modules installed and open the file
node_modules/soap/lib/client.js
Add a console.log at line 187, right after the message has been set and
console.log("Message! ", message);
This will show the message, that should give you enough information to figure out the format of the arguments.
Already a few years gone by, but I have another suggestion to solve this problem.
If you (like me) don't come along with all the namespace stuff (due to a lack of understanding), you can directly put serialized XML strings into a value like this:
var objToSend = {
someString: 'stringVal',
arrayContent: {
$xml: '<item>val1</item><item>val2</item>'
}
}

angular push adds operator that that results in MongoError

I am implementing the tutorial on the mean stack https://www.youtube.com/watch?v=AEE7DY2AYvI
I am adding a delete feature to remove items from the database on a button click
My client side controller has the following 2 functions to add to db and remove
$scope.createMeetup = function() {
var meetup = new Meetup();
meetup.name = $scope.meetupName;
meetup.$save(function (result) {
$scope.meetups.push(result);
$scope.meetupName = '';
});
}
$scope.deleteMeetup = function() {
item = $scope.meetups[0];
console.log("deleting meetup: " + item["name"]);
Meetup.delete(item);
scope.meetups.shift();
}
My server side has the following code
module.exports.create = function (req, res) {
var meetup = new Meetup(req.body);
meetup.save(function (err, result) {
res.json(result);
});
}
module.exports.remove = function(req, res) {
console.log("GOING TO REMOVE!!!");
console.log(req.query);
item = req.query;
Meetup.remove(item, function (err, results) {
console.log("done");
console.log(err);
console.log(results);
});
}
When I run my code and if I delete an already loaded item in the list, it is removed from Mongodb just fine. But if I add an item to the list and I do not refresh the page, it results in an error at my server that appears as
GOING TO REMOVE!!!
{ '$resolved': 'true',
__v: '0',
_id: '54ec04e70398fab504085178',
name: 'j' }
done
{ [MongoError: unknown top level operator: $resolved]
name: 'MongoError',
code: 2,
err: 'unknown top level operator: $resolved' }
null
I if I refresh the page, the it gets deleted fine. But if I added the entry, angular seems to be adding a new variable $resolved. Why is that happening?
Also another question, What is the proper way to call delete? I call it now but I am not able to put a callback. I want a callback which returns and then I shift the list of items. I tried adding a callback but the code never reaches it.
ie I tried the following
/*
Meetup.delete(item, function () {
console.log("In callback!!");
console.log(returnValue);
console.log(responseHeaders);
$scope.meetups.splice(item);
});
*/
/*Meetup.delete(item,
function (returnValue, responseHeaders) {
console.log("In callback!!");
console.log(returnValue);
console.log(responseHeaders);
$scope.meetups.splice(item);
},
function (httpResponse){
// error handling here
console.log("Need to handle errors");
});
*/
I am very new to node and am confused. Any help is very, very appreciated
Looks like it possible to call item.delete instead of Meetup.delete(item). You can call same methods on model instance. It prevent sending angular properties to server.
But better to make a rest API with delete method
DELETE /meetups/:id
and send just a _id
Meetup.remove({id: item._id});

Allow collection to update from client in Meteor

Just hit an insanely frustrating roadblock in prototyping. I need to update and increment values an array inside of a collection. To do this, I'm accessing the collection using the MongoDB syntax like so:
Players.update({_id: Session.get('p1_id'), 'opponents.$.id' : Session.get('p2_id')},
{$inc: {
'games_played' : 1
}}
);
When this runs I get an error saying: Uncaught Error: Not permitted. Untrusted code may only update documents by ID. [403]
Now, I searched the hell out of this and I know that it came down in an update and why they only allow update by id's. But my problem is that I can't seem to find a way around it. I tried forcing it by adding this to if (Meteor.isServer):
Players.allow({
insert: function(userId, doc, fields, modifier){
return true;
},
update: function(userId, doc, fields, modifier){
return true;
},
remove: function(userId, doc, fields, modifier){
return true;
}
});
Nothing seems to work, and all the examples I find talk about using a Meteor method (not really sure what that is) or are doing userId validation (I dont have any users and don't want to add them right now). I'm just prototyping/sketching and I'm not concerned about security. How can I proceed here?
Here's how you can make this into a method:
Meteor.methods({
incrementGames: function (player1Id, player2Id) {
check(player1Id, Meteor.Collection.ObjectID);
check(player2Id, Meteor.Collection.ObjectID);
Players.update({
_id: player1Id,
'opponents.$.id': player2Id
}, {
$inc: {
'games_played' : 1
}
}, function(error, affectedDocs) {
if (error) {
throw new Meteor.Error(500, error.message);
} else {
return "Update Successful";
}
});
}
});
And on your client:
Meteor.call("incrementGames", Session.get('p1_id'), Session.get('p2_id'), function(error, affectedDocs) {
if (error) {
console.log(error.message);
} else {
// Do whatever
}
});
You just got the update wrong. The first parameter of the update method should be the id. the second parameter is an object containing the modifiers.
Players.update(playerId, {$inc:{games_played:1}});
Optionally you can add a callback containing error as the first parameter and response as the second parameter.

node + mongo: updating a record requires a callback

So I'm listening for an event with socket.io, once that fires I'm trying to update a record to a new value.
socket.on('contentEdited', function (newContent) {
collection.update(
{ '_id' : ObjectId("5279262e74d92da751eb2b8e") },
{ $set: {
'content': newContent
}
}
), function (err, result) {
if (err) throw err;
console.log(result)
};
});
The syntax works in the shell, but throws the following error in node when the event fires:
Error: Cannot use a writeConcern without a provided callback
I tried adding an function at the end afterwards for basic error checking, but I'm not sure how to provide a callback in the way mongo expects.
Still kinda new to this, thanks
I think your problem is that the callback function needs to be inside the update function call instead of outside it. The format for the nodejs MongoDB driver can be found here: http://mongodb.github.io/node-mongodb-native/api-generated/collection.html#update
So it should look like this:
collection.update(
{ '_id' : ObjectId("5279262e74d92da751eb2b8e") },
{ $set: { 'content': newContent } },
function (err, result) {
if (err) throw err;
console.log(result);
})
Note that the parentheses has moved after the callback function.
You could also set the write concern to "unacknowledged" instead of "acknowledged."
The MongoDB concept of "Write Concerns" determines how certain you want to be that MongoDB successfully wrote to the DB. The lowest level of write concern, "Unacknowledged" just writes data to the server and doesn't wait to response. This used to be the default, but now the default is to wait for MongoDB to acknowledge the write.
You can learn more about write concerns here:
http://docs.mongodb.org/manual/core/write-concern/
To set the write concern to unacknowledged, add the option {w: 0}:
collection.update(
{ '_id' : ObjectId("5279262e74d92da751eb2b8e") },
{ $set: { 'content': newContent } },
{ w : 0 });
yes. maybe you have the wrong syntax. and this might make it even better
socket.on('contentEdited', function (newContent) {
collection.update(
{ '_id' : ObjectId("5279262e74d92da751eb2b8e") },
{ $set:
{ 'content': newContent }
},
{returnOriginal : false},
function (err, result) {
if (err) throw err;
console.log(result);
});
})

Categories