I'm using autosubscribe to get a list of 50 latest chat documents in minimongo. As more messages are posted the older messages are removed from minimongo by autosubscribe. How can I get autosubscribe to not remove certain messages that I mark as active?
I know that I can just manually separately subscribe to a list of "active" messages but that seems unnecessarily laborious. Thanks.
Edit: the active marking is client side only, each user gets to choose the messages that he cares about, it's something ephemeral. The user's marking a the message as the one he's replying so, so it shouldn't be suddenly removed.
You need to sort on the time (_id captures the order it was inserted hence time) as well as with status, both in descending order.
Server code:
Meteor.publish("messages", function () {
return Messages.find({}, {sort: {active: -1, _id:-1}, limit: 50});
});
In the publish function, sort on status.
Meteor.publish("messages", function () {
return Messages.find({}, {sort: {status: 1}, limit: 50});
});
Unless your implementation is limited to a single user being able to mark a line active, then the marking of the chat-line document needs to use the active users id.
This sadly leads to the need for separate subscription even if it 'seems unnecessarily laborious'
Another 'laborious way' would be to make a local client-only collection copy of the selected active messages.
Per client, maintain a session variable containing an array of marked doc IDs: Session.set('markedMessages', matchedDocs)
Within your publish statement, use a $in statement that will match doc id's within the session array, combine this an $or statement to leverage your existing query, limit/slice.
Meteor.publish("markedMessages", function () {
Messages.find({$or: [{ your_existing_query_goes_here },
{_id: { $in: Session.get('markedMessages')}} ] }).fetch()
})
;
Note, within your handlebars template, compare the message id against your markedMessages Session to identify if the message was marked by the user.
Related
I am new to JS and I am utilizing the MEAN stack to create a place where students can add classes to their user profile. I already have a session store in my database, so "req.user" will return the currently logged in user information and specifically "req.user.id" will return the currently logged in user's id. Also, I have figured out how to search a course in my database from my application. Ultimately, my goal is that when the user makes the post request to search in the database, I also want those "values" to be pushed into the classes "key". I have provided two options, both of which do not add the respective strings to the database. Thank you for any help!
Portion of Search.JS Option #1
router.post('/', ensureAuthenticated, function (req,res,next) {
var query = {course: req.body.coursename};
db.collection('courses').find(query).toArray()
.then(db.collection('DefaultUser').update({_id: req.user.id}, {$push: {classes: req.body.coursename}}));
res.render('search', {title: 'search'})
});
Portion of Search.JS Option #2
router.post('/', ensureAuthenticated, function(req,res,next) {
var query = {course: req.body.coursename};
db.collection('courses').find(query).toArray((err, results) => {
if (err) {
throw err;
} else {
db.collection('DefaultUser').updateOne({_id: '5c17161d3347e79410ff29ba'}, {
$push: {
classes: req.body.coursename
}
})
console.log(results)
res.render('search', {courses: results, title: 'search'})
}
})
});
Some tips may help:
req.body will hold nothing if you forget to use app.use(express.urlencoded()) to parse the posted data.
You may use ObjectId('<string>') (in option #2) for finding and updating queries, not just the string, because mongodb stores _id as ObjectId type by default.
You may use $addToSet instead of $push modifier to add a unique element to an array, or you may get one student subscribed with two more same class, if he repeatedly submit the form.
In your code, you find in the courses collection first and then update, but since you did nothing with the find result, it is not necessary (empty result does not throw any error). Checking the data is valid is good practice, if you would like to do so, in both options, you need to use findOne instead of find to make sure the course is existed, and .then(course => {/* check if the course is null and then throws an error */} ).
I don't have the full code of your project so I can only guess there may be the problems listed above, wish you good luck!
I'm working on Meteor, trying to find some values from Mongodb collection.
here is the code:
var sameLogins = Users.findOne({login: 'a'});
console.log(sameLogins);
But it's returning and "undefined".
But record exists in collection:
So, can anybody tell what I'm missing?
Also, in mongo console - everything is working fine:
I was looking in Publish/Subsribe stuff, but i'm using autopublish module yet.
Thank you!
I will leave the answer for this issue for new users having the same problem.
If you're using autopublish package then you should be aware that it's publishing the result of .find() for every collection.
But, Meteor.users.find(), be default, will return only _id and profile fields, so documents in your Meteor.users client collection will have these two fields only.
The most easy workaround for this would be to create your own publication (allUsers, for example) and in it to return those fields you need:
Server:
Meteor.publish('allUsers', () => {
// check for Meteor.userId() is omitted, put it here, if needed
return Meteor.users.find({}, { fields: { ... } });
});
Don't forget to subscribe to it:
Client:
Meteor.subscribe('allUsers');
Update for Meteor:
Right now you are storing a cursor in your variable sameLogins. In order to retrieve the results you want, you must actually execute this query by either calling fetch(). What is returned from findOne without fetch is essentially an object that you could use to iterate over and find mongoDB documents - (called a collection cursor). The cursor is not your result itself.
Calling fetch would like something like:
Users.findOne({login: 'a'}).fetch()
I'm having a big deal - the meteor app I've been developing the last weeks is finally online. But, for an update, I need to add a field to my users profile.
I thought that walling a methods with the following code would work :
updateUsrs_ResetHelps: function(){
if(Meteor.users.update({}, {
$set: {
'profile.helps': []
}
}))
console.log("All users profile updated : helps reset");
else
throw new Meteor.Error(500, 'Error 500: updateUsrs_ResetHelps',
'the update couldn\'t be performed');
}
The problem is that my users have the classic Meteor.accounts document, whith emails, _id, services, profile, etc... but, in the profile, they don't have a .helps fields. I need to create it.
For the future users, I've modified the accounts creation function to add this fields when they sign up, but for the 200 users I already got signed up, I do really need a solution.
EDIT : Might it be because of the selector in the update ? Is a simple {} selector valid to update all the users / documents of the collection at once ?
From the Mongo documentation (http://docs.mongodb.org/manual/reference/method/db.collection.update/):
By default, the update() method updates a single document. Set the
Multi Parameter to update all documents that match the query criteria.
If you've already taken care of adding the field for new users and you just need to fix the old ones, why not just do it one time directly in the database?
Run meteor to start your application, then meteor mongo to connect to the database. Then run an update on records where the field doesn't already exist. Something like:
db.users.update({"profile.helps": {"$exists": false}}, {"$set": {"profile.helps": []}}, {multi:true})
The Mongo documentation specifies the multi parameter as:
Optional. If set to true, updates multiple documents that meet the
query criteria. If set to false, updates one document. The default
value is false.
I have a couple of ideas for stopping duplicate handling of messages from Amazon's SQS queues. The app will also have a MongoDB server, which I think can be an effective part of either strategy:
Store queue items in Mongo, with a 'status' field - default to Pending. Then use SQS to queue the ID of the new message. One of the worker processes will get the ID, then do a findAndModify on the actual item in Mongo to set the status to Processing, unless it's already being processed, when it will flag that up.
Store queue items in the queue. Workers pick up items from the queue, then attempt to do an insert into Mongo with the item ID and some other info. If the item already existed, don't do the insert or continue, since it's a dupe.
The problems and questions I have:
Solution 1 seems counter-intuitive: why use SQS at all? I think it's because polling SQS is more correct than a whole load of worker processes polling Mongo for work.
Solution 2 I don't know how to implement. Is there an atomic find-and-insert-if-doesn't-exist? A simple get-or-insert-but-tell-me-which-occurred operation would do the trick.
Will any of these work in a large scale scenario, and/or is there a proven method that I haven't grasped?
....Humm, just wrote the question above, then had a thought for a get-or-insert-but-tell-me-which-occurred operation (in JS psuedocode):
var thingy = getrandomnumber();
findAndModify({
new: false,
upsert: true,
query: { $eq: { id: item_id } },
update: { thingy: thingy },
fields: { thingy: 1 }
});
If the item exists (and this is a conflict), then since new is false, the old document will be returned.
If the item didn't exist, new is false, so an empty document {} would be returned.
So either we got {}, indicating it resulted in an insert, or an actual document, indicating it was a get, and that ID already exists... all atomic. The thingy is in there because I don't know if MongoDB actually needs data there, I guess it would? If I used $inc on a duplicates field instead, would that work with an upsert? Then we could get stats on dupes later.
Is that right, maybe that would work?
I'm not able to use the node server debugger so I'm posting here to see if I can get a nudge in the right direction.
I am trying to allow multiple users to edit documents created by any of the users within their specific company. My code is below. Any help would be appreciated.
(Server)
ComponentsCollection.allow({
// Passing in the user object (has profile object {company: "1234"}
// Passing in document (has companyId field that is equal to "1234"
update: function(userObject, components) {
return ownsDocument(userObject, components);
}
});
(Server)
// check to ensure user editing document created/owned by the company
ownsDocument = function(userObject, doc) {
return userObject.profile.company === doc.companyId;
}
The error I'm getting is: Exception while invoking method '/components/update' TypeError: Cannot read property 'company' of undefined
I'm trying to be as secure as possible, though am doing some checks before presenting any data to the user, so I'm not sure if this additional check is necessary. Any advice on security for allowing multiple users to edit documents created by the company would be awesome. Thanks in advance. -Chris
Update (solution):
// check that the userId specified owns the documents
ownsDocument = function(userId, doc) {
// Gets the user form the userId being passed in
var userObject = Meteor.users.findOne(userId);
// Checking if the user is associated with the company that created the document being modified
// Returns true/false respectively
return doc.companyId === userObject.profile.companyId;
}
Looking at the docs, it looks like the first argument to the allow/deny functions is a user ID, not a user document. So you'll have to do Meteor.users.findOne(userId) to get to the document first.
Do keep in mind that users can write to their own profile subdocument, so if you don't disable that, users will be able to change their own company, allowing them to edit any post. You should move company outside of profile.
(If you can't use a proper debugger, old-fashioned console.log still works. Adding console.log(userObject) to ownsDocument probably would have revealed the solution.)