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
Related
I am trying to remove an item from $firebaseArray (boxes).
The remove funcion:
function remove(boxJson) {
return boxes.$remove(boxJson);
}
It works, however it is immediately added back:
This is the method that brings the array:
function getBoxes(screenIndex) {
var boxesRef = screens
.child("s-" + screenIndex)
.child("boxes");
return $firebaseArray(boxesRef);
}
I thought perhaps I'm holding multiple references to the firebaseArray and when one deletes, the other adds, but then I thought firebase should handle it, no?
Anyway I'm lost on this, any idea?
UPDATE
When I hack it and delete twice (with a timeout) it seems to work:
function removeForce(screenIndex, boxId) {
setTimeout(function () {
API.removeBox(screenIndex, boxId);
}, 1000);
return API.removeBox(screenIndex, boxId);
}
and the API.removeBox:
function removeBox(screenIndex, boxId) {
var boxRef = screens
.child("s-" + screenIndex)
.child("boxes")
.child(boxId);
return boxRef.remove();
}
When you remove something from firebase it is asynchronous. Per the docs the proper way to remove an item is from firebase, using AngularFire is:
var obj = $firebaseObject(ref);
obj.$remove().then(function(ref) {
// data has been deleted locally and in the database
}, function(error) {
console.log("Error:", error);
});
$remove() ... Removes the entire object locally and from the database. This method returns a promise that will be fulfilled when the data has been removed from the server. The promise will be resolved with a Firebase reference for the exterminated record.
Link to docs: https://www.firebase.com/docs/web/libraries/angular/api.html#angularfire-firebaseobject-remove
The most likely cause is that you have a security rules that disallows the deletion.
When you call boxes.$remove Firebase immediately fires the child_removed event locally, to ensure the UI is updated quickly. It then sends the command to the Firebase servers to check it and update the database.
On the server there is a security rule that disallows this deletion. The servers send a "it failed" response back to the client, which then raises a child_added event to fix the UI.
Appearantly I was saving the items again after deleting them. Clearly my mistake:
function removeSelected(boxes) {
var selectedBoxes = Selector.getSelectedBoxes(boxes);
angular.forEach(selectedBoxes, function (box) {
BoxManager.remove(box);
});
Selector.clearSelection(boxes, true);
}
In the clearSelection method I was updating a field on the boxes and saved them again.
Besides the obvious mistake this is a lesson for me on how to work with Firebase. If some part of the system keeps a copy of your deleted item, saving it won't produce a bug but revive the deleted item.
For those, who have the similar issue, but didn't solve it yet.
There are two methods for listening events: .on() and .once(). In my case that was the cause of a problem.
I was working on a migration procedure, that should run once
writeRef
.orderByChild('text_hash')
.equalTo(addItem.text_hash)
.on('value', val => { // <--
if (!val.exists()) {
writeRef.push(addItem)
}
});
So the problem was exactly because of .on method. It fires each time after a data manipulation from FB's console.
Changing to .once solved that.
I have a alerts collection in my mongodb..
I wish to get the attributes of the object present in my alerts model, i have written the following code....
Alert.find({},function(err,alerts){
if(err)
console.log(err);
console.log(alerts);
alerts.forEach(function(result){
console.log(result.name);
console.log(result._id);
});
});
In first console, i am getting all the objects of alerts model,
but in second console it is 'Undefined' even result.name is present in the model..
But the _id is generated by mongodb itself and im able to get that id..
From 1st console:
{ __v: 0,
_id: 55128d3a1b35c366eb974285,
brand: 'SanDisk',
name: 'SanDisk Cruzer Blade 8 GB Pen Drives Black (Combo of 2)',
source:
{ name: 'Snapdeal',
productId: '141951',
inStock: true}
}
From 2nd console:
Undefined
From 3rd console
55128d3a1b35c366eb974285
Cannot able to get the problem, what is the correct way of doing this..
Thanks in advance
There is no problem here but you have to understand that MongoDB find() method returns a cursor to the documents that match the query criteria. So what you see in the console.log(alerts) is actually the cursor returned. When the find() method “returns documents,” the method is actually returning a cursor to the documents. So what you see in the first and third console outputs are cursor objects and properties.
You need to add a toArray() method to the resulting cursor from the Alert.find() operation, e.g.
Alert.find({}).toArray(function(err, alerts){
if(err)
console.log(err);
console.log(alerts);
alerts.forEach(function (result) {
console.log(result.name);
console.log(result._id);
});
});
The toArray() method returns an array that contains all the documents from a cursor. The method iterates completely the cursor, loading all the documents into RAM and exhausting the cursor.
I was using mongoose..
and the solution for the above is applying lean() function
i.e
Alert.find({}).lean().exec(function(err,alerts){
if(err)
console.log(err);
console.log(alerts);
alerts.forEach(function(result){
console.log(result.name);
console.log(result._id);
});
});
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.
I am just learning meteor (well development in general) and I am trying to insert into a collection using a simple for loop on server side
getFriendsData: function() {
for (i=0; i<10; i+=1){
console.log (i)
Friendlist.insert({
name: "test"+i,
"userid": Meteor.user()._id
})
}
For some reason this only gives me one result (the first one test0). The log is showing that the entire loop is being run but there is only one insert. Could someone explain what is happening here? Thanks in advance
Your code seems ok, how do you assert that it's not doing what it's supposed to ?
Here is a rewrite of your code that is doing fine when tested on my machine :
lib/collections/friendlist.js :
Friendlist=new Meteor.Collection("friendlist");
server/server.js :
Meteor.methods({
getFriendsData:function(){
if(!this.userId){
throw new Meteor.Error(500,"Must be signed in to get friends data !");
}
for(var i=0;i<10;i++){
console.log(i);
Friendlist.insert({
name:"test"+i,
userId:this.userId
});
}
Friendlist.find().forEach(function(name){
console.log(friend.name);
});
}
});
Meteor.startup(function(){
// for testing purpose : always reset data on startup
Friendlist.remove({});
});
Then you Meteor.call("getFriendsData"); in your browser console and in your server console you see the 10 documents of Friendlist being printed as they should.
If you didn't remove the autopublish package, you can type Friendlist.find().fetch() in your browser console to check that your 10 documents have been correctly inserted.
If it still doesn't work, show more of your code that might have side effects.
I've been trying to do Meteor's leaderboard example, and I'm stuck at the second exercise, resetting the scores. So far, the furthest I've got is this:
// On server startup, create some players if the database is empty.
if (Meteor.isServer) {
Meteor.startup(function () {
if (Players.find().count() === 0) {
var names = ["Ada Lovelace",
"Grace Hopper",
"Marie Curie",
"Carl Friedrich Gauss",
"Nikola Tesla",
"Claude Shannon"];
for (var i = 0; i < names.length; i++)
Players.insert({name: names[i]}, {score: Math.floor(Random.fraction()*10)*5});
}
});
Meteor.methods({
whymanwhy: function(){
Players.update({},{score: Math.floor(Random.fraction()*10)*5});
},
}
)};
And then to use the whymanwhy method I have a section like this in if(Meteor.isClient)
Template.leaderboard.events({
'click input#resetscore': function(){Meteor.call("whymanwhy"); }
});
The problem with this is that {} is supposed to select all the documents in MongoDB collection, but instead it creates a new blank scientist with a random score. Why? {} is supposed to select everything. I tried "_id" : { $exists : true }, but it's a kludge, I think. Plus it behaved the same as {}.
Is there a more elegant way to do this? The meteor webpage says:
Make a button that resets everyone's score to a random number. (There
is already code to do this in the server startup code. Can you factor
some of this code out and have it run on both the client and the
server?)
Well, to run this on the client first, instead of using a method to the server and having the results pushed back to the client, I would need to explicitly specify the _ids of each document in the collection, otherwise I will run into the "Error: Not permitted. Untrusted code may only update documents by ID. [403]". But how can I get that? Or should I just make it easy and use collection.allow()? Or is that the only way?
I think you are missing two things:
you need to pass the option, {multi: true}, to update or it will only ever change one record.
if you only want to change some fields of a document you need to use $set. Otherwise update assumes you are providing the complete new document you want and replaces the original.
So I think the correct function is:
Players.update({},{$set: {score: Math.floor(Random.fraction()*10)*5}}, {multi:true});
The documentation on this is pretty thorough.