diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a56a7ef --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +node_modules + diff --git a/index.html b/index.html index c6dfe36..247df03 100644 --- a/index.html +++ b/index.html @@ -14,14 +14,14 @@ const session = driver.session({database:"gameofthrones"}); const start = new Date() session - .run('MATCH (n)-->(m) RETURN id(n) as source, id(m) as target LIMIT $limit', {limit: neo4j.int(5000)}) + .run('MATCH (n)-[:INTERACTS1]->(m) RETURN id(n) as source, id(m) as target LIMIT $limit', {limit: neo4j.int(5000)}) .then(function (result) { const links = result.records.map(r => { return {source:r.get('source').toNumber(), target:r.get('target').toNumber()}}); session.close(); console.log(links.length+" links loaded in "+(new Date()-start)+" ms.") const ids = new Set() links.forEach(l => {ids.add(l.source);ids.add(l.target);}); - const gData = { nodes: Array.from(ids).map(id => {return {id:id}}), links: links} + const gData = { nodes: Array.from(ids).map(id => {return {id}}), links: links} const Graph = ForceGraph3D()(document.getElementById('3d-graph')).graphData(gData); }) .catch(function (error) { diff --git a/particles.html b/particles.html index 30807b6..c231fc8 100644 --- a/particles.html +++ b/particles.html @@ -15,7 +15,7 @@ const session = driver.session({database:"gameofthrones"}); const start = new Date() session - .run('MATCH (n)-[r]->(m) RETURN { id: id(n), label:head(labels(n)), community:n.louvain, caption:n.name, size:n.pagerank } as source, { id: id(m), label:head(labels(m)), community:n.louvain, caption:m.name, size:m.pagerank } as target, {weight:r.weight, type:type(r), community:case when n.community < m.community then n.community else m.community end} as rel LIMIT $limit', {limit: neo4j.int(5000)}) + .run('MATCH (n)-[r:INTERACTS1]->(m) RETURN { id: id(n), label:head(labels(n)), community:n.louvain, caption:n.name, size:n.pagerank } as source, { id: id(m), label:head(labels(m)), community:n.louvain, caption:m.name, size:m.pagerank } as target, {weight:r.weight, type:type(r), community:case when n.community < m.community then n.community else m.community end} as rel LIMIT $limit', {limit: neo4j.int(5000)}) .then(function (result) { const nodes = {} const links = result.records.map(r => { diff --git a/react-graph-viz/.gitignore b/react-graph-viz/.gitignore new file mode 100644 index 0000000..4d29575 --- /dev/null +++ b/react-graph-viz/.gitignore @@ -0,0 +1,23 @@ +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +# dependencies +/node_modules +/.pnp +.pnp.js + +# testing +/coverage + +# production +/build + +# misc +.DS_Store +.env.local +.env.development.local +.env.test.local +.env.production.local + +npm-debug.log* +yarn-debug.log* +yarn-error.log* diff --git a/react-graph-viz/README.md b/react-graph-viz/README.md new file mode 100644 index 0000000..c931a9c --- /dev/null +++ b/react-graph-viz/README.md @@ -0,0 +1,27 @@ +# React Graph Viz +This project shows how to use [2d-force-graph react components](https://github.com/vasturiano/react-force-graph) with a [Neo4j Database](https://neo4j.com/developer). + +A Cypher query from a textarea is used to query the database. +The results are then rendered as graph visualization. + +This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app). + +## Available Scripts + +In the project directory, you can run: + +### `yarn start` + +Runs the app in the development mode.
+Open [http://localhost:3000](http://localhost:3000) to view it in the browser. + +The page will reload if you make edits.
+You will also see any lint errors in the console. + +### `yarn build` + +Builds the app for production to the `build` folder.
+It correctly bundles React in production mode and optimizes the build for the best performance. +Your app is ready to be deployed! + +See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information. \ No newline at end of file diff --git a/react-graph-viz/package.json b/react-graph-viz/package.json new file mode 100644 index 0000000..6e82e65 --- /dev/null +++ b/react-graph-viz/package.json @@ -0,0 +1,36 @@ +{ + "name": "graph-viz", + "version": "0.1.0", + "private": true, + "dependencies": { + "@testing-library/jest-dom": "^4.2.4", + "@testing-library/react": "^9.3.2", + "@testing-library/user-event": "^7.1.2", + "neo4j-driver": "^4.0.2", + "react": "^16.13.1", + "react-dom": "^16.13.1", + "react-force-graph-2d": "^1.16.3", + "react-scripts": "3.4.1" + }, + "scripts": { + "start": "react-scripts start", + "build": "react-scripts build", + "test": "react-scripts test", + "eject": "react-scripts eject" + }, + "eslintConfig": { + "extends": "react-app" + }, + "browserslist": { + "production": [ + ">0.2%", + "not dead", + "not op_mini all" + ], + "development": [ + "last 1 chrome version", + "last 1 firefox version", + "last 1 safari version" + ] + } +} diff --git a/react-graph-viz/public/favicon.ico b/react-graph-viz/public/favicon.ico new file mode 100644 index 0000000..bcd5dfd Binary files /dev/null and b/react-graph-viz/public/favicon.ico differ diff --git a/react-graph-viz/public/index.html b/react-graph-viz/public/index.html new file mode 100644 index 0000000..aa069f2 --- /dev/null +++ b/react-graph-viz/public/index.html @@ -0,0 +1,43 @@ + + + + + + + + + + + + + React App + + + +
+ + + diff --git a/react-graph-viz/public/logo192.png b/react-graph-viz/public/logo192.png new file mode 100644 index 0000000..fc44b0a Binary files /dev/null and b/react-graph-viz/public/logo192.png differ diff --git a/react-graph-viz/public/logo512.png b/react-graph-viz/public/logo512.png new file mode 100644 index 0000000..a4e47a6 Binary files /dev/null and b/react-graph-viz/public/logo512.png differ diff --git a/react-graph-viz/public/manifest.json b/react-graph-viz/public/manifest.json new file mode 100644 index 0000000..080d6c7 --- /dev/null +++ b/react-graph-viz/public/manifest.json @@ -0,0 +1,25 @@ +{ + "short_name": "React App", + "name": "Create React App Sample", + "icons": [ + { + "src": "favicon.ico", + "sizes": "64x64 32x32 24x24 16x16", + "type": "image/x-icon" + }, + { + "src": "logo192.png", + "type": "image/png", + "sizes": "192x192" + }, + { + "src": "logo512.png", + "type": "image/png", + "sizes": "512x512" + } + ], + "start_url": ".", + "display": "standalone", + "theme_color": "#000000", + "background_color": "#ffffff" +} diff --git a/react-graph-viz/public/robots.txt b/react-graph-viz/public/robots.txt new file mode 100644 index 0000000..e9e57dc --- /dev/null +++ b/react-graph-viz/public/robots.txt @@ -0,0 +1,3 @@ +# https://www.robotstxt.org/robotstxt.html +User-agent: * +Disallow: diff --git a/react-graph-viz/src/App.css b/react-graph-viz/src/App.css new file mode 100644 index 0000000..74b5e05 --- /dev/null +++ b/react-graph-viz/src/App.css @@ -0,0 +1,38 @@ +.App { + text-align: center; +} + +.App-logo { + height: 40vmin; + pointer-events: none; +} + +@media (prefers-reduced-motion: no-preference) { + .App-logo { + animation: App-logo-spin infinite 20s linear; + } +} + +.App-header { + background-color: #282c34; + min-height: 100vh; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + font-size: calc(10px + 2vmin); + color: white; +} + +.App-link { + color: #61dafb; +} + +@keyframes App-logo-spin { + from { + transform: rotate(0deg); + } + to { + transform: rotate(360deg); + } +} diff --git a/react-graph-viz/src/App.js b/react-graph-viz/src/App.js new file mode 100644 index 0000000..9a3d9f0 --- /dev/null +++ b/react-graph-viz/src/App.js @@ -0,0 +1,13 @@ +import React from 'react'; +import './App.css'; +import CypherViz from './CypherViz'; + +function App({driver}) { + return ( +
+ +
+ ); +} + +export default App; diff --git a/react-graph-viz/src/App.test.js b/react-graph-viz/src/App.test.js new file mode 100644 index 0000000..4db7ebc --- /dev/null +++ b/react-graph-viz/src/App.test.js @@ -0,0 +1,9 @@ +import React from 'react'; +import { render } from '@testing-library/react'; +import App from './App'; + +test('renders learn react link', () => { + const { getByText } = render(); + const linkElement = getByText(/learn react/i); + expect(linkElement).toBeInTheDocument(); +}); diff --git a/react-graph-viz/src/CypherViz.js b/react-graph-viz/src/CypherViz.js new file mode 100644 index 0000000..d3e391f --- /dev/null +++ b/react-graph-viz/src/CypherViz.js @@ -0,0 +1,52 @@ +import React from 'react'; +import './App.css'; +import ForceGraph2D from 'react-force-graph-2d'; + +// Usage: + +class CypherViz extends React.Component { + constructor({driver}) { + super(); + this.driver = driver; + this.state = { + query: ` + MATCH (n:Character)-[:INTERACTS1]->(m:Character) + RETURN n.name as source, m.name as target + `, + data : {nodes:[{name:"Joe"},{name:"Jane"}],links:[{source:"Joe",target:"Jane"}]} } + } + + handleChange = (event) => { + this.setState({query:event.target.value}) + } + loadData = async () => { + let session = await this.driver.session({database:"gameofthrones"}); + let res = await session.run(this.state.query); + session.close(); + console.log(res); + let nodes = new Set(); + let links = res.records.map(r => { + let source = r.get("source"); + let target = r.get("target"); + nodes.add(source); + nodes.add(target); + return {source, target}}); + nodes = Array.from(nodes).map(name => {return {name}}); + this.setState({ data : {nodes, links}}); + } + + render() { + return ( +
+