Mongodb: add a date field/column to documents which do not have a date field - mongodb

I have a collection called posts, initially they only had "text" and "author" field. Later I added a "date" field for later posts.
How I can add a date field for initial posts, for those who does not have date/created-at field.
I guess I need to use following two commands, but cant figure out how to make the connection (i.e. loop)?
db.posts.find({"date":{"$exists":false}}
ObjectId(_id).getTimestamp()

db.posts
.find({"date":{"$exists":false}) // looking for records without `date` field
.forEach(function (record) { // iterating through each record
record.date = null; // if you want to define current date use `new Date()`
db.posts.save(record); // saving updated record
});

I guess there is a forEach() function that you could use. forEach
db.posts.find({"date":{"$exists":false}).forEach(<function - do something>)

Related

MongoDb: Insert Document in collection only if collection has no newer document since point in time

I want to depict the following use case using MongoDb:
I want to read from a collection and memorize that particular point in time.
When writing the next time to that collection, I want to not be able to write a new document, if another document has been added to that collection in between.
Using a timestamp property on the documents would be ok.
Is this possible?
One trick is use findAndModify
Assume at the time of reading, your most recent timestamp on a document is oldTimestamp:
db.collection.findAndModify({
query: {timestamp: {$gt: oldTimestamp}},
new: true, // Return modified / inserted document
upsert: true, // Update if match found, insert otherwise
update: {
$setOnInsert: {..your document...}
}
})
This will not insert your document if another document is inserted between your read and write operation.
However, this won't let you know that the document is inserted or not directly.
You should compare returned document with your proposed document to find that out.
In case using nodejs driver, the correct pattern should be:
collection.findAndModify(criteria[, sort[, update[, options]]], callback)
According to the example, our query should be:
db.collection('test').findAndModify(
{timestamp: {$gt: oldTimestamp}}, // query, timestamp is a property of your document, often set as the created time
[['timestamp','desc']], // sort order
{$setOnInsert: {..your document..}}, // replacement, replaces only the field "hi"
{
new: true,
upsert: true
}, // options
function(err, object) {
if (err){
console.warn(err.message); // returns error if no matching object found
}else{
console.dir(object);
}
});
});
This can be achieved, using a timestamp property in every document. You can take a look at the Mongoose Pre Save path validation hook . Using this hook, you can write something like this.
YourSchema.path('timestamp').validate(function(value, done) {
this.model(YourSchemaModelName).count({ timestamp: {$gt : value} }, function(err, count) {
if (err) {
return done(err);
}
// if count exists and not zero hence document is found with greater timestamp value
done(!count);
});
}, 'Greater timestamp already exists');
Sounds like you'll need to do some sort of optimistic locking at the collection level. I understand you are writing new documents but never updating existing ones in this collection?
You could add an index on the timestamp field, and your application would need to track the latest version of this value. Then, before attempting a new write you could lookup the latest value from the collection with a query like
db.collection.find({}, {timestamp: 1, _id:0}).sort({timestamp:-1}).limit(1)
which would project just the maximum timestamp value using a covered query which is pretty efficient.
From that point on, it's up to your application logic to handle the 'conflict'.

How to find and return a specific field from a Mongo collection?

Although I think it is a general question, I could not find a solution that matches my needs.
I have 2 Mongo collections. The 'users' collection and the second one 'dbInfos'.
Now, I have a template called 'Infos' and want the already existing fields in the Mongo collections to be presented to the user in input fields in case there is data in the collection. When no data is provided in the database yet, it should be empty.
So here is my code, which works fine until I want to capture the fields from the second collection.
Template.Infos.onRendered(function() {
$('#txtName').val(Meteor.user().profile.name);
$('#txtEmail').val(Meteor.user().emails[0].address);
});
These 2 work great.
But I don´t know how to query the infos from the collection 'dbInfos', which is not the 'users' collection. Obviously Meteor.user().country does not work, because it is not in the 'users' collection. Maybe a find({}) query? However, I don´t know how to write it.
$('#txtCountry').val( ***query function***);
Regarding the structure of 'dbInfos': Every object has an _id which is equal to the userId plus more fields like country, city etc...
{
"_id": "12345",
"country": "countryX",
"city": "cityY"
}
Additionally, how can I guarantee that nothing is presented, when the field in the collection is empty? Or is this automatic, because it will just return an empty field?
Edit
I now tried this:
dbInfos.find({},{'country': 1, '_id': 0})
I think this is the correct syntax to retrieve the country field and suppress the output of the _id field. But I only get [object Object] as a return.
you're missing the idea of a foreign key. each item in a collection needs a unique key, assigned by mongo (usually). so the key of your country info being the same as the userId is not correct, but you're close. instead, you can reference the userId like this:
{
"_id": "abc123",
"userId": "12345",
"country": "countryX",
"city": "cityY"
}
here, "abc123" is unique to that collection and assigned by mongo, and "12345" is the _id of some record in Meteor.users.
so you can find it like this (this would be on the client, and you would have already subscribed to DBInfos collection):
let userId = Meteor.userId();
let matchingInfos = DBInfos.find({userId: userId});
the first userId is the name of the field in the collection, the second is the local variable that came from the logged in user.
update:
ok, i think i see where you're getting tripped it. there's a difference between find() and findOne().
find() returns a cursor, and that might be where you're getting your [object object]. findOne() returns an actual object.
for both, the first argument is a filter, and the second argument is an options field. e.g.
let cursor = DBInfos.find({
userId: Meteor.userId()
},
{
fields: {
country: 1
}
});
this is going to:
find all records that belong to the logged in user
make only the country and _id fields available
make that data available in the form of a cursor
the cursor allows you to iterate over the results, but it is not a JSON object of your results. a cursor is handy if you want to use "{{#each}}" in the HTML, for example.
if you simply change the find() to a findOne():
let result = DBInfos.findOne({ /** and the rest **/
... now you actually have a JSON result object.
you can also do a combination of find/fetch, which works like a findOne():
let result = DBInfos.find({
userId: Meteor.userId()
},
{
fields: {
country: 1
}
}).fetch();
with that result, you can now get country:
let country = result.country;
btw, you don't need to use the options to get country. i've been assuming all this code is on the client (might be a bad assumption). so this will work to get the country as well:
let result = DBInfos.findOne({userId: Meteor.userId()});
let country = result.country;
what's going on here? it's just like above, but the result JSON might have more fields in it than just country and _id. (it depends on what was published).
i'll typically use the options field when doing a find() on the server, to limit what's being published to the client. on the client, if you just need to grab the country field, you don't really need to specify the options in that way.
in that options, you can also do things like sort the results. that can be handy on the client when you're going to iterate on a cursor and you want the results displayed in a certain order.
does all that make sense? is that what was tripping you up?

Mongodb find() don't work

Why dosen't db.find work? The console.log gets undefined...
var course = (db.courses.find({ _id: mongo.helper.toObjectID(param.course)}));
console.log(course.body)
The way you are trying use Selects documents in a collection and returns a cursor to the selected documents., so you can't use the way you are trying to use it.
You need to use a callback() to get the records matching the query.
The below code will give result in an array format :-
db.courses.findOne({ _id: mongo.helper.toObjectID(param.course)}).toArray(function(err, result)
{
console.log(result[0]); // will give you the matched record.
})

Mongo - exclude entries in one collection from another find()

I have a local collection that logs when a user has viewed an entry. It stores the ID of the entry and the time it was viewed:
viewedDate = new Date();
notifications.insert({
'viewed': this.data._id,
'viewedDate': viewedDate
});
I want to exclude any of the 'viewed' ids in this collection from another find() (I basically want to count how many entries haven't been viewed)
How can I use the results of a notifications.find() to exclude results from another find()? If I assign the notifications.find() to a variable, it returns all kinds of stuff as an object.
edit
OK, so if I use fetch() I can restrict what comes back - can I do something with this in a find()?
myNotes = notifications.find({}, {fields: {'viewedDate' :0, _id:0}}).fetch();
This returns
[
Object
viewed: "HqYcCma3qKseHALyv"
__proto__: Object
]
Thanks to some invaluable help from garilla_ in the Meteor IRC, I got it working, solution as follows:
myViewedOffers = notifications.find({},{fields: {'viewed':1}}).fetch();
myViewedArray = myViewedOffers.map(function(viewed){return viewed.viewed});
offerCount = Offers.find({_id: {$nin: myViewedArray}}).count();

duplicate mongo record in same collection

In mongo I have a collections with records. These record are very complex. Now I would like to duplicate one of them.
I can easily select the one
mongo> var row = db.barfoo.find({"name":"bar"});
Now I actually don't know what to do. I don't know what is in row because I cannot find a way to print its content. How can I change specific properties and finally insert this modified row again
mongo> db.barfoo.insert(row);
thnx
You must change value _id - generate new:
var row = db.barfoo.findOne({"name":"bar"});
row._id = ObjectId();
db.barfoo.insert(row);
Good Luck!
I am going to assume that you're working directly inside the mongo shell.
Once you have your document (not a row :P ), you'd modify the properties in the same way you would a normal JavaScript object:
var doc = db.barfoo.findOne( { "name": "bar" } );
doc.name = "Mr Bar";
Note that the find() command returns a cursor, so if you're looking to extract a single document, you should use the findOne() function. This function returns a single document.
If you are interested in duplicating numerous documents, you can use the find() function and iterate over the cursor to retrieve each document:
db.barfoo.find( { "name": "bar" } ).forEach( function( doc ){
doc.name = "Mr Bar";
}
After you change the relevant properties, you can use the insert/save methods to persist the data back to mongo. Don't forget to change/delete the _id attribute so that you'll actually create a new document.
As a side note, in order to view the contents of an object in the mongo shell, you can use the print() function. If you want a more visually appealing output, you could use printjson().