as the title says, I want to update a value in a map inside an array in a document, Im using Mongoose. This is for a Discord bot, basically adding exp to a char every time user does a command, and it works, but it doesn't look right and I've been told it doesn't work sometimes:
This is how the maps inside the array look like:
click
// This gets the user document
let user = bot.userinventories.findOne({ userID: message.author.id })
// This searches the specific map I need by name
let check = await user.get('characters').filter(char => char.name === user.get('leveling'));
// This modifies the map in only 1 value
check[0].exp++;
// This removes the entire map and saves it again
await bot.userinventories.updateOne({ userID: message.author.id },
{ $pull:
{
characters: { name: check[0].name }
}
})
await bot.userinventories.updateOne({ userID: message.author.id },
{ $push:
{
characters: check[0]
}
})
As you can see, the part that doesn't seem right is having to entirely remove the map before saving it again, this also breaks any order by date. The big problem is that users have told me they've lost characters to this.
Another thing that sometimes fails is the 'let check' line, even tho it finds a map by the exact name (because of my code) it fails randomly like 1/20 times.
I found this which works, even tho it stills moves the object to the last place of the map, but it seems cleaner than what I had:
await bot.userinventories.updateOne(
{ userID: message.author.id, "characters.name": check[0].name },
{ $set: { "characters.$.exp" : check[0].exp } }
)
Edit: for it to not move the edited object to last place, I had to make a new variable or not use a variable for '"characters.$.exp" : ' for some reason
I have a mongoose Model, I try to remove all documents from a collection but after I run the code, the collection remains unmodified. Please see the code below:
return ProcessedStats.remove({}).then(function (d) {
console.log(d);
return ProcessedStats.find({}).count().then(function(c) {
console.log(c);
});
}).then(function() {
db.disconnect();
});
I call remove with an empty object, so to remove all documents, when I call find, I still have the count to 16000 documents.
What could be wrong?
Weird: if I do
find({})
and prin out data.length it equals 0, but if I do
.find({}).count()
it will equal 16000. What is wrong with the behaviour? What am I missing?
I think it was a caching issue, after I restarted the server, the count was ok
I can't figure out how to delete a Parse.Role in cloud code. The guide says they are subclasses of Parse.Object and have all the same methods. But I get the following error why I try to delete one:
Object [object Object] has no method 'destroy'
Parse.Cloud.afterDelete("Project", function(request) {
Parse.Cloud.useMasterKey();
var query = new Parse.Query(Parse.Role);
query.equalTo("name", "hasSound_" + request.object.id);
query.find().then(function(role) {
if(typeof role === 'undefined') {
console.log("role is undefined, cannot delete");
}
else {
role.destroy();
}
}).then(function(success) {
console.log("role deleted: hasSound_" + request.object.id);
}, function(error) {
console.log("error deleting role");
});
});
I had some console.log statements verifying the "name" is correct, so the query should be finding the right object. I'm not sure what else to try.
Also, my roles are named after the ids of Projects. So each Project has a role called "hasSound_[id of project]". When I delete a project, I no longer need the role associated with it. I mean I could leave the unused projects in the database, but that seems wasteful.
find() is fulfilled with an array of matching objects. Since your roles are unique, you can safely change find() to be first(). Or you can keep using find(), but treat the result as an array.
I have the following query:
Section.find({"clause": sub.section}, function(err, docs){
var detail = parseInt(sub.detail);
docs[0].subClauses[0].additionalDetails[detail-1].percentile = sub.percentile;
docs[0].markModified("subClauses");
docs[0].save(function(err, doc){
if(err) { return callback(err) };
callback();
})
})
When I log the doc inside the .save function it appears to have saved successfully. However on querying the db elsewhere the save has not happened. The object its trying to save to is 3 deep like this:
[
{
"clause": "1",
"subClauses:
[
{
"clause":"1.1",
"additionalDetails:
[
{
"detail": "1",
"content": "whatever"
}
]
}
]
}
]
With several items inside each array.
Am I not able to save data to the nested array object using Mongo?
If I understand what you are trying to do, then I think what you are wanting to use is the Mongo Position Operator ($). This is used to index nested arrays in a document. In your current code you are only referencing the first object in the array. Using the position operator you will be able to reference other positions based of some other data, such as a query.
Here is the documentation for the operator: http://docs.mongodb.org/manual/reference/operator/update/positional/
And here is another Stack Overflow response which may help a bit also:
MongoDB $ operator
I'm trying to set up and update some capped collections in MongoDB using Node.js (using the native MongoDB driver).
My goal is to, upon running app.js, insert documents into a capped collection, and also to update existing documents in a capped collection. Both of these are running on setInterval(), so every few seconds.
My questions:
I want to create a collection if the collection does not already exist, but if it does I want to insert a document into it instead. What is the correct way to check this?
With capped collections, should I even be explicitly creating them first before inserting anything into them? Normally I believe you can just insert things into a collection without explicitly creating them first, but in this case I need to ensure they are capped. Once the capped collection exists I know how to insert new documents into it, the problem is I need some way to handle the app being used for the first time (on a new server) where the collection doesn't already exist, and I want to do this creation using node and not having to jump into the mongo cli.
The trick here is that the collection needs to be capped, so I can do something like: db.createCollection("collectionName", { capped : true, size : 100000, max : 5000 } ). That will create the capped collection for me, but every time I call it it will call createCollection() instead of updating or inserting - if I call createCollection(), once the collection already exists, will it completely overwrite the existing collection?
An alternative is to turn a collection into a capped one with: db.runCommand({"convertToCapped": "collectionName", size: 100000, max : 5000 });. The problem with this is that node doesn't see runCommand() as a valid function and it errors. Is there something else that I'm meant to be calling to get this to work? It works in the mongo cli but not within node
What type of query do you use to find the first document in a collection? Again, within the mongo cli I can use db.collections.find() with some query, but within node it states that find() is not a valid function
How would I use collection.update() to add some new fields to an existing document? Lets say the document is some simple object like {key1: "value", key2: "value"} but I have an object that contains {key3: "value"}. Key 3 does not exist in the current document, how would I add that to what currently exists? This is somewhat related to #4 above in that I'm not sure what to pass in as the query parameter given that find() doesn't seem to play well with node.
Regarding your questions 1 - 4 about capped collections and creating them automatically, there are several ways to do this. On the one hand, you could run a script to initialise your database so that it has the capped collections available to your client when you run it for the first time. On the other hand, you could have a check to see if there are any documents in the given collection before inserting a document. If there are, you just insert your document and if there aren't, you create the capped collection and then insert the document as a callback to that function. It would work something like this:
var host = "localhost",
port = 27017,
dbName = "so";
var MongoClient = require('mongodb').MongoClient, Server = require('mongodb').Server;
var mongoclient = new MongoClient(new Server(host, port));
var db = mongoclient.db(dbName);
db.open(function(err, db) {
if(err) throw err;
// Capped collection.
var capped = db.collection('capped');
// Document to be inserted.
var document = { "foo": 1, "bar": 1 }
capped.find().count(function(err, count) {
if(err) throw err;
if (count === 0) {
console.log("Creating collection...");
db.createCollection("capped",
{ "capped": true,
"size": 100000,
"max": 5000 },
function(err, collection) {
if(err) throw err;
// Insert a document here.
console.log("Inserting document...");
collection.insert(document, function(err, result) {
if (err) throw err;
});
});
} else {
// Insert your document here without creating collection.
console.log("Inserting document without creating collection...");
capped.insert(document, function(err, result) {
if (err) throw err;
});
}
});
});
Regarding question 5, you can use findOne() to find a document in the collection, though this is not necessarily the first or last. If you want to guarantee the first or last, you can run a find() with a sort() and limit() of 1. Sorting by _id ascending should give you the first document. More information here.
// Sort 1 for ascending, -1 for descending.
capped.find().sort([["_id", 1]]).limit(1).nextObject(function(err, item) {
console.log(item);
});
Finally for question 6, you just use the $set operator with the update() method. More information here.
capped.update({ "foo": 1 }, { "$set": { "bar": 2 } }, {}, function(err, result) {
console.log(result);
});
Note that you can only update documents in place for capped collections, so you cannot do the insert of the extra field you mention. There are other restrictions enumerated here that you might want to be aware of.
[EDIT: Add updating nested fields in last document.]
If you want to update a nested field in the first or last document (use 1 or -1 in the sort, respectively), you can fetch the document, extract the _id, then perform an atomic update on that document. Something like this:
capped.find().sort([["_id", -1]]).limit(1).nextObject(function(err, item) {
if(err) throw err;
capped.update({ "_id": item._id },
{ "$set": { "timeCollected": 15, "publicIP.ip" : "127.0.0.1" }},
function(err, result) {
if(err) throw err;
console.log(result);
});
});
Note that even when updating a field that exists in a document in a capped collection, you need to ensure that the new value fits in the space allocated for the document. So, for example, updating a string value from "1" to "127.0.0.1" will not necessarily work.