Elasticsearch pagination with MongoDB and ExpressJS - javascript

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.

Related

Complex Mongoose findOneAndUpdate query not working looking for specific advice

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>"
}
]
}

How to filter an array of subdocuments by two fields in each subdocument

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 });

How to retrieve both caller-name and carrier from Twilio in node.js?

Im having problems working with the twilio-api for node.
i wrote this code:
let typeArray = ['caller-name','carrier'];
this.client.phoneNumbers(phoneNumberToCheck).get({
type: typeArray
}, (error, number) => {
// working on the number data results
// ...
});
The problem is that i dont get ANY of them(carrier/caller-name) - although passing array to argument 'type' is the way to do it in other languages(php,c#..) but it doesnt work on node.js, instead i get this:
// -> get
{
"caller_name":null,
"country_code":"US",
"phone_number":"+123456789",
"national_format":"(248) 123-456",
"carrier":null,
"add_ons":null,
"url":"https://lookups.twilio.com/v1/PhoneNumbers/+123456789",
"callerName":null,
"countryCode":"US",
"phoneNumber":"+123456789",
"nationalFormat":"(248) 123-456",
"addOns":null
}
note: if i send each one separately (only carrier or only caller-name) - i get the partial information for each.
how can i get both in one request in node.js?
Twilio developer evangelist here.
You should be calling the Lookups API in Node this way:
client.lookups.phoneNumbers.get(phoneNumber)
.fetch({
type: ['carrier', 'caller-name']
},
function(err, result) {
// do something
}
)
The docs are a little lacking in Node.js on the Lookups documentation and I will raise that with the team.

Trying to simplify access to private posts with Express

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.

Mongoose: Adding an element to array

I'm using Drywall to create a website.
I'm trying to add a dashboard element to the accounts section of the admin site. The dashboard element is to store an array of dashboards (strings) that the user has access to.
I've managed to successfully add the "dashboards" into the schema and store data in it.
Here's the problem:
I need to be able to add elements to the array. The way the code stands currently replaces the contents of dashboards in the database.
I know I can use $addToSet, but I'm not sure how I'd do that since the fieldsToSet variable is sent to the findByIdAndUpdate() method as a single object.
Here's the snippet of my code:
workflow.on('patchAccount', function() {
var fieldsToSet = {
name: {
first: req.body.first,
middle: req.body.middle,
last: req.body.last,
full: req.body.first +' '+ req.body.last
},
company: req.body.company,
phone: req.body.phone,
zip: req.body.zip,
search: [
req.body.dashboards,
req.body.first,
req.body.middle,
req.body.last,
req.body.company,
req.body.phone,
req.body.zip,
]
};
req.app.db.models.Account.findByIdAndUpdate(req.params.id, fieldsToSet, function(err, account) {
if (err) {
return workflow.emit('exception', err);
}
workflow.outcome.account = account;
return workflow.emit('response');
});
});
Here's a link to the original file: (lines 184-203)
Thanks!
fieldsToSet is a bad name (at least misleading in this case), the parameter is actually update which can take $actions like $addToSet
I don't think you want to set (only) the search field with dashboards. I'm guessing that field is used to index users for a search. So you'll probably wind up doing something like this:
fieldsToSet = {
....all the regular stuff,
$addToSet: {dashboard: req.body.dashboardToAdd}
//I'm not sure that you can add multiple values at once
}
Since this is setting all of the values each time I'm not sure you actually will want to add single dashboard items. Instead you might want to get the full set of dashboards the user has and set the whole array again anyway (what if they removed one?)
fieldsToSet = {
....all the regular stuff,
dashboards: req.body.dashboards
//In this case you'd want to make sure dashboards is an appropriate array
}

Categories

Resources