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.
Related
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.
I am making a to-do list using mongodb,node.js,express and EJS. The part where I am stuck is I am not being able to delete and item from the list and the database. The idea is that on clicking a checkbox, the item beside will be deleted. Here is a layout for the same,
[https://i.stack.imgur.com/83gMW.png][1]
I made the necessary changes in my EJS file to create a "/delete" route as given below :
<%for(var i=0;i<taskslist.length;i++){%>
<form action="/delete" method="POST">
<div class="item">
<input type="checkbox" name="checkitem" value="<%= taskslist[i]._id %> " onChange="this.form.submit()">
<p><%= taskslist[i].activity %> </p>
</div>
</form>
<%}%>
In my app.js folder, I also made the necessary changes by using findByIdAndRemove( ) method for mongoose :
app.post("/delete", function (req, res) {
const delitem = req.body.checkitem;
Item.findByIdAndRemove(delitem, function (err) {
if (err) {
console.log("Error");
} else {
console.log("Succesfully deleted" + delitem);
res.redirect("/");
}
});
console.log(delitem);
});
However, the item is not getting deleted from the database and an error is showing in terminal, even though the console.log(delitem) is working and returning the id of the item whose checkbox is selected.
I am also giving a screenshot of the output I am getting at the terminal:
[https://i.stack.imgur.com/hGEkn.png][1]
All the methods related to adding items and creating database are working. I don't understand where the error is. Please help me out with this.
I have an html/handlebars form set up with a Node/Express backend. the form offers options populated from a database. I am able to get the form to return a single user selected value and save it to my mongodb, but I really need the whole object.
{{#each proxyObj}}
<p>
<label>
<input type="radio" name="proxyTitle" value="{{title}}"/>
<span>{{title}}</span>
</label>
</p>
{{/each}}
and this is the express:
router.post("/proxies/:id", ensureAuthenticated, (req, res) => {
Project.findOne({
_id: req.params.id
}).then(project => {
const newProxy = {
proxyTitle: req.body.proxyTitle
// I need the other object values to go here, or to be able to retrieve them later
};
// Add to proxy array on the Project object in the collection
project.proxies.push(newProxy);
project.save().then(project => {
res.redirect(`/projects/stakeholders/${project.id}`);
});
});
});
Is it more sensible to try to load in the entire object as a value in the input field, or to return the id of the object, and look it up in the db? I need to display some of the returned object information on the same page, and also to use it later. Which is more efficient, and what is the best way to achieve it?
If I'm getting it right, the problem is that you're trying to put multiple inputs with the same name on one form in <input type="radio" name="proxyTitle" value="{{title}}"/>, which gives you something like
<input type="radio" name="proxyTitle" value="Title 1"/>
<input type="radio" name="proxyTitle" value="Title 2"/>
<input type="radio" name="proxyTitle" value="Title 3"/>
As explained here, the browsers will chew it, but the server-side handling may require some adjustments.
In your case, the easiest fix would be to add index to the names of parameters. So, your form would be looking like this:
{{#each proxyObj}}
<p>
<label>
<input type="radio" name="proxies[{{#key}}]" value="{{this}}"/>
<span>{{this}}</span>
</label>
</p>
{{/each}}
(note that if proxyObj is an array, you would have to use #index instead of #key; also, depending on the proxyObj fields' structure, you may have to use this.title as the values to display and whatnot).
As for your server-side handling, you'll have to loop through the proxies you receive and handle them one by one, e.g.
router.post("/proxies/:id", ensureAuthenticated, (req, res) => {
Project.findOne({
_id: req.params.id
}).then(project => {
project.proxies = []; // this is only in case you wanna remove the old ones first
const proxies = req.body.proxies;
for(let i = 0; i < proxies.length; i++) {
// Add to proxy array on the Project object in the collection
project.proxies.push({ proxyTitle: proxies[i].title });
}
project.save().then(project => {
res.redirect(`/projects/stakeholders/${project.id}`);
});
});
});
Hello I am working in Express Framework, I am using handlebars to render the data from my mysql table. While trying to render the data using below code,instead of rendering value it displaying [object object]. I posted my code below.
index.js:
var query = connection.query('SELECT * FROM requestor_auth WHERE question_id = ? AND answer = ? AND app_key = ? LIMIT 1', [data.qid, data.ansvalue, data.appid], function(err,rows)
{
if(err) {
console.log("Error Selecting : %s ",err );
res.redirect('/');
} else {
res.render('requestform',{page_title:"Edit Customers - Node.js",data:rows});
}
requestform.hbs:
<div class="addressto">
<h4>To,</h4>
<br>
<span style="font-size:18px;margin-left:10px;">The Collector Of</span>
<input type="text" value="{{data}}" class="line" class="text-line" style="margin-left:35px;"><br>
</div>
The value in the form input displaying as [object object]. I tried as data.key_value to render the data but it is not displaying the value.
Please give me a solution. Thank you.
Because the result of Mysql response is array so it should be:
var query = connection.query('SELECT * FROM requestor_auth WHERE question_id = ? AND answer = ? AND app_key = ? LIMIT 1', [data.qid, data.ansvalue, data.appid], function(err,rows) {
if(err) {
console.log("Error Selecting : %s ",err );
res.redirect('/');
} else {
res.render('requestform',{page_title:"Edit Customers - Node.js",data:rows[0]});
}
If there's a same error you should console.log() your result to check the value.
The rows argument in your callback function is by a select query always an array of objects. With handlebars you should be able to do the following:
<div class="addressto">
<h4>To,</h4>
<br>
<span style="font-size:18px;margin-left:10px;">The Collector Of</span>
<input type="text" value="{{data[0].answer}}" class="line text-line" style="margin-left:35px;">
<br>
</div>
Also multiple class names can be in one class attribute.
I'm using the Articles and Users implementation into Meanjs built-in app.
List of articles (View) with the button for every item:
<a data-ng-controller="MyArticlesController" data-ng-repeat="article in articles" class="list-group-item">
<button data-ng-click="addArt2me()" class="glyphicon glyphicon-plus addArt2me" ></button>
<h4 class="list-group-item-heading" data-ng-bind="article.title"></h4>
<p> {{article.content | limitTo:140}} </p>
</a>
And here the controller with the trigger function $scope.addArt2me():
'use strict';
angular.module('users').controller('MyArticlesController', ['$scope', 'Articles', 'Users', '$location',
function($scope, Articles, Users, $location) {
var myUser = new Users($scope.user);
$scope.addArt2me = function() {
var myArticle = new Articles($scope.article);
myUser.userArticles.push(myArticle._id);
myUser.$update(function(response) {
console.log("Actualize!! con : " + user.userArticles.length + "__" + response);
}, function(errorResponse) {
console.log("updatError: " + errorResponse);
$scope.error = errorResponse;
});
}
}
]);
In the user's model I've an array of articles._id userArticles.
The View renders a list of articles with a button that triggers the function addArt2me() in the controller, which pushes and updates the myArticle._id inside ´userArticles´.
It works successfully and saves the element into DB :)
console: Actualize!! con : 60__[object Object]
...but only the first time, the next time it triggers an error :(
PUT http://localhost:3000/users 400 (Bad Request)
updatError: [object Object]´
Do I need to deal with some kind of service to update the USERS module being in ARTICLES module?
Can't I just update the model of the user with Mongoose?
Why does it works well for the first saved article ?
Any guide is very appreciated. Thanks!
I just founded, after 2 day of looking for it.
Needed to declare the User Model, outside the subcontroller because it iterates for every article.
so move ´$scope.myUser = new Users($scope.user);´ into ´ArticlesController´ Did the job!
Thanks for your time!