Make jquery datatables load faster Nodejs backend - node.js

I am using Jquery datatables to display data to the user.
So when a user makes a request to the page in my Nodejs server:
router.get("/",middleware.isLoggedIn,async function (req, res) {
try{
let result = await rows.query(`
Select col1,col2,col3,col4,col5 from mytable where condition`)
res.render("Page.ejs",{data:result.recordset})
}
catch(err){
console.log(err)
}
})
Now, using this data:
let tblAssignedJobs = $('#tblAssigned').DataTable({
"pageLength": 25,
"oLanguage": {
"sEmptyTable":"There are no jobs assigned to you at this moment"
}
});
function populateData(assignedData) {
let frmEditJobString = ""
let rows = ""
tblAssigned.clear().draw()
for(let item of assignedData){
frmEditJobString =
`<form action="/editJobs" method="POST">
<input style="display:none" name="jobNumber" value="${item.RoeseJobNumber}">
<button class="btn btn-primary">Edit Job</button></form>`
rows = rows + `<tr><td>${item.Number}</td>
<td>${item.Type}</td>
<td>${item.Name}</td>
<td>${item.CustomerName}</td>
<td>${item.Description}</td>
<td>${item.Number}</td>
<td>${item.Address}</td>
<td>${frmEditString}</tr>`
}
tblAssigned.rows.add($(rows)).draw();
//Remove from memory
frmEditJobString = ""
}
Now, to load 15,858 rows it is taking about 5 seconds which is not that bad but I feel like it would be much better if I could just load the first page and load other pages in the background since I am showing 25 records per page.
Or, is there a way to make requests per page, instead of loading the entire data. If someone has used Nodejs with bootstrap datatable what kind of solution did you implement?

Related

Need to call an api for each key stroke in react, but the response can have thousands of objects

I am using react and axios for frontend, and nextjs with prisma for backend. I have in the database 4000 exercices that contain fitness exercices. I want to create a function where by each key stroke, the api will look for the relevant exercice. I finished creating it, but i have some issues:
The main problem is that the response is delayed from the first keystrokes, because the payload response is tooo large. I created a scrollable UL element to render the elements, because I want to get also the Gif images. So the elements, if the API will find those, will be rendered on the screen.
If I add to each element an on click event, to select the exercice's Id, I get an error "too many re-rendering on the screen".
How can I optimise the function, and how can I solve the error of too many re-render on the screen? Nextjs tells me that it will create an infinite loop....
The frontend looks like this:
const [open, setOpen] = useState(false);
const [keyWord, setKeyWord] = useState('');
const [array, setArray] = useState([]);
const [exerciceId, setExerciceId] = useState('');
// Add exercice
const hadnleAddExercie = async event => {
event.preventDefault();
console.log('exercice added');
}
// Look for exercices
const searchExercices = async event => {
event.preventDefault();
setKeyWord(event.target.value);
const arrayExercices = await getExercicesByKeyWords(keyWord);
setArray(arrayExercices);
console.log(arrayExercices);
}
<div className='flex mt-3 flex-col'>
<input onChange={searchExercices} required placeholder='Search by word...' className='border border-slate-400 p-1 rounded-md flex-1 max-w-sm my-2'/>
<ul className='border border-slate-400 p-1 rounded-md max-w-sm my-2 max-h-52 overflow-scroll'>
{
array.length > 1 && array.map(exercice => (
<li key={exercice.id} className='flex flex-wrap p-2 bg-slate-200 m-2 items-center rounded-md'>
<span><Image className='rounded-xl mr-2' priority width={40} height={40} src={exercice.gifUrl} alt={exercice.name}/></span>
<span>{ exercice.name }</span>
</li>
))
}
</ul>
</div>
The backend Uses prisma and I use the OR clause to look for a word in different rows:
export default async function handler(req, res) {
try {
const param = req.query.slug[0];
console.log(param);
// Get exercices where the two rows contains a single parametter
const exercices = await prisma.exercices.findMany({
where: {
OR: [
{
name: {
contains: param
}
},
{
target: {
contains: param
}
},
{
equipment: {
contains: param
}
}
]
}
});
res.status(200).send(exercices);
}
catch (error) {
console.log(error);
res.status(500).send(error);
}
}
An example can be this:
Only for finding an exercice I used 500mb...
Here are a few ways I can think of to optimize this:
Use pagination and fetch more results as user scrolls down or actually separate it by using pages. You can read more on how to implement pagination in Prisma here.
Add debounce to your search term so it doesn't actually fire on every single keystroke, you could use something like useDebounce.
Use React.memo to prevent the list from being re-rendered every time some state changes, only re-render it when the actual list changes.

How insert result of a Netlify function into the HTML web page whose form called the function (example: calculator)

Background:
I'm coming from the server-side world of Rails, and trying to figure out the Netlify static html + serverless functions approach to doing a few extremely basic landing page web apps which need a serverless function to insert data into an HTML page.
I'm trying to start with the simplest possible case of an HTML page with a form and a serverless function that returns a result back to the page. (e.g., no static site generators).
I have not found any Netlify tutorials that show how a HTML page can have a form that posts to a function which then returns the result of that function back into the same web page.
The simplest sample app I can think of is a page asks a question, the user POSTs their answer to a serverless function, and the same HTML page is updated with the result of the function... a trivial case being to display "your answer was X" above the form. (It is immaterial to me whether the actual page is rewritten again with the result string included, or the result string is dynamically inserted by somehow poking the string to the div, so long as the result string originates in a serverless function; integrating serverless functions results with HTML pages is what I'm trying to learn.)
In the code below a simple HTML page below displays a form, the form POSTs an answer to a javascript function check_answer.js, and the javascript function erases the current page and displays the string "Your answer was XXXX".
That was simple to do, and lots of tutorials show how to have a function accept a form post then return a result string to the browser (overwriting the prior page).
My question:
How can the serverless function insert the result string back into the original HTML page (at the div id="answer") instead of outputting the result to a blank page?
Current code:
# index.html
<html>
<head>
<title>A test form</title>
</head>
<body>
<div id="answer">
</div>
<p>How much is 1 + 3?</p>
<p>form using POST:</p>
<form method="post" name="calc 2" action="/.netlify/functions/check_answer" id="calcform2" data-netlify="true" >
<p>
<label for="my_answer">Answer:</label>
<input type="text" name="my_answer" id="my_answer">
<label for="my_comment">Comment:</label>
<input type="text" name="my_comment" id="my_comment">
</p>
<p>
<input type="submit">
</p>
</form>
</body>
</html>
# functions/check_answer.js
exports.handler = async event => {
console.log(event.queryStringParameters);
console.log(event);
console.log(event.body);
if (event.httpMethod == 'POST')
{
console.log('is POST');
var params = parseQuery(event.body);
console.log(params);
var answer_string = params['my_answer'];
}
else
{
console.log('is GET');
var answer_string = event.queryStringParameters.my_answer || 'empty'
};
return {
statusCode: 200,
body: `Your answer was ${answer_string}`,
}
}
// handle parsing form query aaaa=bbbbb&cccc=dddd into hash object
// from https://stackoverflow.com/a/13419367/597992
function parseQuery(queryString) {
var query = {};
var pairs = (queryString[0] === '?' ? queryString.substr(1) : queryString).split('&');
for (var i = 0; i < pairs.length; i++) {
var pair = pairs[i].split('=');
query[decodeURIComponent(pair[0])] = decodeURIComponent(pair[1] || '');
}
return query;
}

I want to show images in my "index" with some delay

The images are named as "0.png", "1.png"...
at the moment I can only see the fourth picture.
How can I make them all show with 0.5 seconds delay?
const rootComponent = {
template: `<div style="text-align:center">
<QR/>
</div>`,
};
const QRComponent = {
created: function() {
for(let x=1;x<5;x++)
{
this.img = "685287/"+x+".png";
}
},
template: `<div class="QR"> <img v-bind:src='img'></div>`
};
const app = Vue.createApp(rootComponent);
app.component('QR', QRComponent);
const vm = app.mount("#app");
Your for loop is renaming the same property 4 times before returning. By the time the template is evaluated, this.img is only assigned to the most recent value in the loop. You need to do one of two things:
get rid of the loop and create 4 QR components that each set this.img only once
change this.img to an array and make your template iterate over the image array.
I'm not familiar with vue, but to get the 500ms delay you could probably set the values of this.img in a window.setTimeout()

Neo4j NodeJS - Create Optional 1 to Many Relationships for Existing Nodes

I'm using NodeJS 10.16.2, Neo4j 3.5.6, neo4j-driver v1, Express, EJS
My goal is to create a relationship of either a 1:1 or 1:Many for objects that previously exist in a Neo4j datastore. Example: Create (b:Beer)-[:BREWED_WITH]->(h:Hop). This relationship is created for either 1 beer and 1 hop, or 1 beer and multiple selected hops (the code below will explain more). At this point in time, the 1 Beer to 1 Hop works no problem, but the 1 Beer to Many Hops does not work.
Here are all the necessary code parts...
server.js
This code gets the data...
app.get('/', async (req, res) => {
try {
//paring this Beer example down from its usual
const beerResult = await session.run(`MATCH (b:Beer) RETURN b`);
const beerArr = beerResult.records.map(({_fields}) => {
return {name:_fields[0]};
});
const hopResult = await session.run('MATCH(h:Hop) RETURN h Order By h.name ASC');
const hopArr = hopResult.records.map(({_fields}) => {
const {identity, properties} = _fields[0];
return {id: identity.low, name: properties.name};
});
res.render('index', {
beer: beerArr,
hop:hopArr
});
} catch(e) {
console.log("Something went wrong", e)
}
});
index.ejs
This code displays the data in a form...
<h2>Beer->Hop</h2>
<form method="post" action="/beerhop/add">
<label>Beer Name</label><br>
<select class="form-control" type="text" name="beername">
<% beer.forEach(function(beer){ %>
<option><%= beer.name %></option>
<% }) %>
</select><br>
<label>Hop Name</label><br>
<select class="form-control" type="text" name="hopname" multiple>
<% hop.forEach(function(hop){ %>
<option><%= hop.name %></option>
<% }) %>
</select><br>
<input class="button" type="submit" value="Submit">
</form>
server.js
This code contains the query that posts to the Neo4j database...
app.post('/beerhop/add',async (req, res) => {
const {beername, hopname} = req.body;
try {
const result = await session.run(`Match (b:Beer {name: $beernameParam})
Match (h:Hop {name: $hopnameParam})
Create (h)<-[r:BREWED_WITH]-(b)
Return h.name,b.name`,
{hopnameParam:hopname, beernameParam:beername});
if (result) {
res.redirect('/');
console.log(hopname);
session.close()
}
} catch (e) {
console.log("Something went wrong", e)
};
});
I realize there are many different ways to Create relationships for existing nodes in Neo4j. The above query is the drop dead easiest.
What Works
If I select 1 Beer and 1 Hop from the form and Submit, the relationship is created in Neo4j.
What Doesn't Work
If I select 1 Beer and 2+ Hops from the form and Submit, the relationships are not created in Neo4j.
Please note that running this neo4j query
Match (b:Beer {name: 'Disco Wolf'})
Match (h:Hop)
Where h.name IN ['Citra', 'Strata', 'Idaho7']
Create (b)-[:BREWED_WITH]->(h)
Return b,h
directly in the Neo4j Browser works to make the multiple relationships, BUT it does not work from my app.
I have also tried a FOREACH statement with my Neo4j query,
Match (b:Beer {name: $beernameParam}), (h:Hop {name: $hopnameParam})
With b as beer, collect(h) AS hops
FOREACH (hop IN hops | Create (beer)-[:BREWED_WITH]->(hop))
Return beer,hops
it also does not work in my app (but is a viable query in Neo Browser).
In both cases,console.log(hopname); will show either the 1 hop as a single name: Galaxy, or the multiple hops in an array:
[ 'Citra', 'Strata', 'Idaho7' ].
Any idea how I can get the 1 Beer to Many Hops relationships created?
Many Thanks,
r
You can make sure that you always pass a list of hop names to the query:
...
const result = await session.run(`MATCH (b:Beer), (h:Hop)
WHERE b.name = $beername AND h.name IN $hopnames
CREATE (h)<-[r:BREWED_WITH]-(b)
RETURN h.name, b.name`,
{hopnames: Array.isArray(hopname) ? hopname : [hopname], beername: beername});
...
By the way, you may want to use MERGE instead of CREATE to avoid creating duplicate relationships.

How to display an image with <img> from Mongoose using React front-end

Ultimate goal: have the user upload pictures (less than 16mb so no need to worry about Grid FS), have that picture stored in my database which is Mongodb through Mongoose, and display the picture on the screen using the attribute.
To upload files I use Multer and add it to the database as follows:
newItem.picture.data = Buffer(fs.readFileSync(req.file.path), 'base64');
newItem.picture.contentType = 'image/png';
And it seems to be successfully added to the mongodb. Looks something like this:
how the image appears on mongodb
I'm able to send a get request from my front-end and, when I console.log it, this is what I'm getting: Data after being retreived from database. The question now is, how can I add it to an attribute and show the image on the screen. Thanks!
Edit: question has been marked as too broad by the moderators. Fair enough, I wasn't too sure how to approach it. Since I was able to solve it, this is what my front-end looks like.
componentDidMount() {
const PATH = "http://localhost:8080/apii/items/getitems";
axios.get(PATH)
.then(res => {
let picture64Bit = res.data[0].data.data
picture64Bit = new Buffer(x, 'binary').toString('base64');
this.setState({picture: picture64Bit})
})
.catch(err => console.log(err))
}
The key here is that, 1) res.data[0].data.data is equal to that random list of numbers. I take that convert it back to base64, so it appears exactly as it did in the first picture above from mongodb. Then, displaying it inline in an img attribute is very easy:
<img src = {`data:image/png;base64,${this.state.picture}`} />
There are a couple libraries you could use, but I will arbitrarily select Axios for a demonstration. It sounds good if the images are already in Mongo DB.
Your objective is to get photos from the server to the client, so you need a function to get them on demand. You could also investigate fetch or request.
Axios: https://www.npmjs.com/package/axios
In React, try something like this
async getPhotos() {
const res = await Axios.get('/photos')
console.log('RESPONSE', res)
const photos = res.data
console.log('IMAGES', photos)
this.setState({ photos })
}
Here is a more complete example
import React, { Component } from 'react'
import Axios from 'axios'
class List extends Component {
constructor(props) { // super props allows props to be available
super(props) // inside the constructor
this.state = {
photos : [], // Initialize empty list to assert existence as Array type
// and because we will retrieve a list of jpegs
error: '', // Initialize empty error display
}
}
componentDidMount() {
this.getPhotos() // Do network calls in componentDidMount
}
async getPhotos() {
try {
const res = await Axios.get('/photos')
console.log('RESPONSE', res)
const photos = res.data
console.log('IMAGES', photos)
this.setState({ photos, error: '' })
} catch (e) {
this.setState({ error: `BRUTAL FAILURE: ${e}` })
}
}
render() {
if (error.length) {
return (
<div>{this.state.error}</div>
)
}
if (!photos.length) {
return (
<div>No photos yet</div>
)
}
// Assuming shape { id: 0, caption: 'Cats again', src: 'http://www.com/win.jpg' }
// Make sure to include key prop when using map (for state management)
return (
<ul>
{this.state.photos.map(photo => (
<li key={photo.id} style={{ position: 'relative' }}>
<span>{photo.caption}</span>
<img src={photo.src}
<div
className="overlay"
style={{
position: 'absolute'
width: '100%',
height: '100%',
}}
/>
</li>
))}
</ul>
)
}
}
Citation: In React.js should I make my initial network request in componentWillMount or componentDidMount?
If you want to fetch one more photo after, you should try to think immutably and replace the this.state.photos Array with a duplicate of itself plus the new image pushed onto the end of the array. We will use the spread operator for this to do a shallow copy on the existing photos Array. This will allow React to diff against the two states and efficiently update for the new entry.
const res = await Axios.get('/photo?id=1337')
const photo = res.data
this.setState({
photos: [...photos, photo]
})
Note: the secret trick is to avoid ever doing this.state.photos.push(photo). You must place an illegal sign on setting state like that.
In React, try to consider a way you can get an Object or Array. Once you have it in your mind, throw it into a Component's state. As you progress into Redux, you will end up storing items sometimes in the Redux store. That is too complex and unnecessary to describe now. The photos would be available perhaps as this.props.photos via the Redux Connect Function.
For most other times, a Component's state field is an excellent place to store anything of interest to a Component.
You can imagine it like a holder at the top of the Component.

Resources