I'm learning Node.js with MongoDB and Express and it is going quite well.
I have my user registration working fine and every user can create posts.
Now I'm trying something more complicated, I'd like user to create private posts and only user who created it and other allowed users can see the post.
I did something and it seems to work but I think it can be done in some better way.
What I have now to get the post at this address www.mywebsite.com/post-title is this:
Post
.findOne({ permalink: req.params.permalink })
.exec(function(err, post) {
if (!post) {
req.flash('errors', { msg: 'Post not found' });
return res.redirect('/');
} else {
if (post._creator == req.user.id) {
res.render('post/home', {
title: post.name,
post: post
});
} else {
req.flash('errors', { msg: 'You are not allowed to see this post' });
res.redirect('/');
}
}
});
It works fine but if I wish to add few more options to this post and create another link like: www.mywebsite.com/post-title/tags to get that page I have to repeat the whole code posted above...
I wish to find a way to match easily the owner of the post or allowed people and a way to get the post through the permalink without useing fineOne for every get...
Is this possible?
Does it make sense?
Thanks
I might have helped you to "simplify" your question and just explain what you wanted to do. But those who take the time to read all of it would eventually see that you basically want to
" List all posts including private and allowed posts for the current user.. "
Which is basically the simplified version of the question.
So all you basically need are some fields on your "Post" document that allow the access control:
{
"title": "this is the title",
"permalink": "some/sort/of/slug",
"body": "post body here",
"creator": "Bill",
"_private": true,
"_allowed": ["Ted","Fred"]
}
So basically you are not going to care about the "_allowed" list where "private" is false, but you do want to care where this is true. So you want this logic in the query rather than evaluating it per document retrieved:
Post.find(
{
"$or": [
{ "_private": false },
{
"_private": true,
"$or": [
{ "creator": req.user.id },
{ "_allowed": req.user.id }
]
}
}
},
function(err,docs) {
So essentially your logic is based of a nested $or operation which either allows the public posts to display or otherwise where the post is private then only the "creator" $or the "_allowed" users will receive this in any query.
The logic applies to whether you are retrieving a list of posts for paging results or whether recalling an individual post for a single in depth display.
Related
I'm trying to implement the Optimistic UI for the like functionality but I can't handle it because of the dedicated query I must re-fetch to update the articles.
Let me explain it a bit better via code examples:
Here is the article document I have.
I want to mutate the liked property and I have a mutation to do it:
mutation LikeArticle($status: Boolean!, $articleId: String!) {
likeArticle(status: $status, articleId: $articleId)
}
to fetch the Article document I have this query:
query GetArticle($id: String!) {
getArticle(id: $id) {
id
createdAt
liked
likes
author {
id
username
}
topics {
id
name
}
}
}
In my UI I just render the Article document based on the data getQuery provides. Also, I have the Like button and when the user presses on it the likeArticle mutation executes and I must refetch the getArticle query to update the UI.
And here is the issue, I just want to add the optimistic UI to not to wait for the long server response however it doesn't work at all. Probably because of the re-fetching getArticle query but I'm not sure
const [
likeArticle,
{
data: likeArticleData,
loading: likeArticleLoading,
error: likeArticleError,
},
] = useMutation(LIKE_ARTICLE, {
refetchQueries: [
{
query: GET_ARTICLE_QUERY,
variables: { id: article.id },
},
],
optimisticResponse: {
getArticle: {
id: article.id,
__typename: "Article",
liked: !article.liked,
},
},
});
Example of executing likeArticle mutation
likeArticle({
variables: {
status: !article.liked,
articleId: article.id,
},
});
I'll appreciate any help/information
Using client and optimisticResponse options might help in this case. Here is the Official Docs. It's often possible to predict the most likely result of a mutation before the GraphQL server actually returns it. Apollo Client can use this likely result to update your UI optimistically, making the app feel more responsive to the user. using client is way ahead but it won't work in case of request failed and you want the reversal of the last optimistic action take manually. I would give priority to optimisticResponse over client.
I have a somewhat complex Mongoose query I want to make and while I can find the different parts around the internet (including here) I can't seem to find exactly what I need and piecing the existing info together has not worked so I am looking for specific how to do this.
I want to findOneAndUpdate to add data to blockSection array in my document.
Block.findOneAndUpdate({
section: {$elemMatch: {section_code: sectionID}}
},
{"$push": {blockSection.section: blocSecData}}, // blocSecData is defined elsewhere and is an object
(err, result) => {
if(err) {
return res.json({success: false, err})
}
... // more code here to processstuff
})
Rough example of a sample Block with only one item per array
{
"section_code":"<abc>",
"blockSection": [
{
section: [{"text":"hello world"}], // add the data here
"_id":"<foobar>",
"blockSecID":"<a uuid>"
}
]
}
I am attempting to add a help request system which allows the requestor to make only one request for help on each topic from an expert. If the expert lists multiple topics which they can help, I want to limit each requestor to one help request per topic per expert.
I am using node.js and mongoose.js with a self-hosted mongodb instance
I have tried using the $and operator to find the ._id of the expert as long as they don't already have an existing request from the same requestor on the same topic. It works for one update but after the experts document has a subdocument inserted with either the topic_id or the requestor_id the filter is applied and no expert is returned.
// Schema
ExpertSchema = new mongoose.Schema({
expert_id: String,
helpRequests: [
requestor_id: String,
topic_id: String
]
});
//query
const query = {
$and:[
{expert_id: req.body.expert_id},
{'helpRequests.requestor_id': {$ne: req.body.requestor_id}},
{'helpRequests.topic_id': {$ne: req.body.topic_id}}
]
};
// desired update
const update = {
$push: {
helpRequests: {
requestor_id: req.body.requestor_id,
topic_id: req.body.topic_id
}
}
Expert.findOneAndUpdate(query, update, {new: true}, (err, expert) =>{
// handle return or error...
});
The reason you are not getting any expert is condition inside your query.
Results always returned based on the condition of your query if your condition inside query get satisfied you will get your result as simple as that.
Your query
{'helpRequests.requestor_id': {$ne: req.body.requestor_id}},
{'helpRequests.topic_id': {$ne: req.body.topic_id}}
you will get your expert only if requestor_id and topic_id is not exists inside helpRequests array. thats you are querying for.
Solution
As per you schema if helpRequests contains only requestor_id and topic_id then you can achieve what you desire by below query.
Expert.findOneAndUpdate(
{
expert_id: req.body.expert_id,
}, {
$addToSet: {
helpRequests: {
requestor_id: req.body.requestor_id,
topic_id: req.body.topic_id
}
}
}, { returnNewDocument: true });
I have a meeting document like this:
{
"name":"Meeting Name",
"uuid":"NYoc2aL6",
"participants":[
{
"id":"JLKGZnfFkGvX9DHgz",
"status":"joined",
"name":"Guest 03"
},
{
// newly invited user, user hasn't logged in with invite url yet
"id":"",
"status":"invited",
"name":"email#email.com"
}
]
}
and I need to synchronize the 'name' field with the name in the Users collection. Is there an automatic way to do this, like at the database level, or am I stuck with manually updating in every place that the name is changed?
This is a pretty common pattern in Meteor. You can use the matb33:collection-hooks package to "hook" the collection update to synchronize the shared value(s). This should be done server-side of course so you don't have to worry about some of the related documents not being available to you.
Example:
Meteor.users.after.update((userId, doc, fieldNames, modifier, options)=>{
if ( fieldNames.indexOf('profile.name') > -1 ){ // the name was changed
Meetings.update({ 'participants.id': doc._id },
{ $set: { 'participants.name': doc.profile.name }},
{ multi: true });
}
});
There is no "automatic" way to do this that I know of in Mongo or Meteor. However, why not take the common fields out of your document and just link the ID? This is known as "Database Normalization", which is a process by which you remove redundant data from your tables (collections in Mongo) to prevent these sorts of problems.
This could be done 'automatically' by observing changes on the users db:
var usersCursor = Meteor.users.find();
usersCursor.observeChanges({"changed":function(id, fields){
if(fields.profile.name){
... do whatever needs to be done ...
}
}});
I tried to search online for a solution but to be honest I still didn't found anything that help me to achieve this.
It is the first time I use ElasticSearch and I'm also pretty new with Node and MongoDB.
So, I followed a kind of tutorial and implemented Mongooastic from NPM to let ElasticSearch work with Node.
It seems to work fine even if on a total of 12 users indexed, if I type in the search "user" in the search list view I can find 12 records, it show 10 in a for each and the first one has missing values...
But the main problem for me is the pagination... or a sort of... it will be also nice to implement infinite scroll on it but I don't really know how to handle it.
So, the controller that handle it is the following at the moment:
exports.searchUsers = function(req, res) {
User.search({
query_string: {
query: req.query.q
}
},
{ hydrate: true },
function(err, results) {
if (err) res.send(err);
res.render('search-results', {
results: results,
users: results.hits.hits
});
});
};
I don't really know where to put size and from in here... and after understanding this I also would like to know how to implement, if possible, a sort of infinite scroll... And also how to handle link for the pagination... i.e.: prev, 1, 2, 3, 4, ..., next
The view has a simple input text for the search, after pressing submit it open a new page with the list of hits, so it should be nothing complex...
I hope you may help. Thanks
By default elasticsearch returns the 10 first results: you will have to set the size parameter if you want more.
Also, in order to add the size and from parameters, you need to write your query like so:
User.search({
query: {
query_string: {
query: req.query.q
}
},
size: 30,
from: 30
},
{hydrate: true},
function (err, results) {
if (err) res.send(err);
res.render('search-results', {
results: results,
users: results.hits.hits
});
});
(see here: https://github.com/taterbase/mongoosastic/issues/123 )
This will give you user n°30 up to user n°60 (more info here: http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/search-request-from-size.html). You will have to play with your frontend to get the values you want for your size and from parameters.