Skip to content

Instantly share code, notes, and snippets.

@Podbrushkin
Created September 24, 2024 14:47
Show Gist options
  • Save Podbrushkin/e88d89030b04dd75029a555535c15329 to your computer and use it in GitHub Desktop.
Save Podbrushkin/e88d89030b04dd75029a555535c15329 to your computer and use it in GitHub Desktop.
Neo4j Tutor

Install Neo4j from ZIP

$url = 'http://dist.neo4j.org/neo4j-community-5.22.0-windows.zip'
$name = ($url -split '/')[-1]

# Download:
Invoke-RestMethod $url -OutFile "~\Downloads\$name"

# Extract:
Expand-Archive "~\Downloads\$name" "~\AppData\Local\Programs"

# Add to PATH, i.e.:
$env:Path += "C:\Users\user\AppData\Local\Programs\neo4j-community-5.22.0\bin;"

# Set initial password:
neo4j-admin dbms set-initial-password qwertyuiop

# ? Set-ExecutionPolicy RemoteSigned
# Done. Now if you'll start this server, you can connect to it with neo4j/qwertyuiop

Cypher simple

CREATE (m1:Movie {id: 1, title: 'The Matrix', release_year: 1999}),
       (m2:Movie {id: 2, title: "The Devil's Advocate", release_year: 1997}),
       (m3:Movie {id: 3, title: 'The Godfather', release_year: 1972}),
       (m4:Movie {id: 4, title: 'John Wick', release_year: 2014});
UNWIND [
  {
    id: 1,
    name: "Keanu Reeves",
    birthYear: 1964
  },
  {
    id: 2,
    name: "Al Pacino",
    birthYear: 1940
  },
  {
    id: 3,
    name: "Hugo Weaving",
    birthYear: 1960
  }
] AS node 
CREATE (n:Person) SET n = node;
UNWIND [
  [1, 1],
  [3, 1],
  [1, 2],
  [2, 2],
  [2, 3],
  [1, 4]
] AS pair
MATCH (p:Person {id: pair[0]}), (m:Movie {id: pair[1]})
CREATE (p)-[:ACTED_IN]->(m);
MATCH (p:Person)-[:ACTED_IN]->(m:Movie)
RETURN p.name, COLLECT(m.title) AS movies

Cypher-shell

MATCH (p:Person)-[:ACTED_IN]->(m:Movie)
RETURN p.name, COLLECT(m.title)[..4] AS movies
LIMIT 10
Get-Clipboard | cypher-shell -u neo4j -p qwertyuiop
Get-Clipboard | cypher-shell -u neo4j -p qwertyuiop --format verbose

Use params

@'
:params {blabla: 'Al.*'}
MATCH (n:Person) WHERE n.name =~ $blabla RETURN id(n),n.name;
'@ | cypher-shell.bat -u neo4j -p qwertyuiop

@'
MATCH (n:Person) WHERE n.name =~ $blabla RETURN id(n),n.name;
'@ | cypher-shell.bat -u neo4j -p qwertyuiop -P "{blabla: 'Al.*'}"
'RETURN $blabla' | cypher-shell.bat -u neo4j -p qwertyuiop -P "{blabla: time()}"

Fix encoding

$env:JAVA_TOOL_OPTIONS = '-Dstdout.encoding=UTF-8'
[Console]::InputEncoding = [console]::OutputEncoding = [System.Text.UTF8Encoding]::new()
'RETURN "ЖУЖЕЛИЦА ⨌   🤡" AS x;' | cypher-shell.bat -u neo4j -p qwertyuiop

HTTP Connector

function Invoke-CypherNeo4j ($cypher, $params) {
	if ($null -eq $cred) {$Global:cred = Get-Credential}
	$bodyJson = $bodyJson = @{ statements=@( @{statement=$cypher} ) } | ConvertTo-Json -d 99
	if ($params) {
		$bodyJson = @{ statements=@( @{statement=$cypher; parameters=$params}) } | ConvertTo-Json -d 99
	}
	$resp = Invoke-RestMethod http://localhost:7474/db/neo4j/tx/commit -Method Post -ContentType 'application/json' -Body $bodyJson -Credential $cred -AllowUnencryptedAuthentication

	if ($resp.errors.Count -ne 0) {return $resp.errors}
	else {
		$resp.results.data | % {
			$obj = [ordered]@{}
			for ($i = 0; $i -lt $_.row.count; $i++) {
			 $key = ([array]($resp.results.columns)).get($i)
			 $value = $_.row[$i]
			 $obj[$key] = $value
			}
			[pscustomobject]$obj
			}
	}
}
{
  "statements": [
    {
      "statement": "MATCH (p:Person)-[:ACTED_IN]->(m:Movie)\r\nWHERE p.name STARTS WITH 'H'\r\nRETURN p.name, COLLECT(m.title) AS movies"
    }
  ]
}
{
  "statements": [
    {
      "statement": "UNWIND $coolguys as guy CREATE (p:Person) SET p = guy RETURN p",
      "parameters": {
        "coolguys": [
          {
            "name": "Vasya",
            "age": 69
          },
          {
            "name": "Petya",
            "age": 33
          },
          {
            "name": "Kolya",
            "age": 55
          }
        ]
      }
    }
  ]
}
{
  "statements": [
    {
      "statement": "RETURN 2/0"
    },
    {
      "statement": "MATCH (n) DETACH DELETE n"
    }
  ]
}
{
  "statements": [
    {
      "statement": "RETURN 100/$x",
      "parameters": {
	      "x": 5
      }
    },
    {
      "statement": "MATCH (n) WHERE n.name STARTS WITH $suffix AND n.born < $num RETURN n",
      "parameters": {
	      "suffix": "A",
	      "num": 1961
      }
    }
  ]
}

Send request

curl -X POST -H 'Content-type: application/json' http://neo4j:qwertyuiop@localhost:7474/db/neo4j/tx/commit -d $bodyJson
$cred = Get-Credential
Invoke-WebRequest http://localhost:7474/db/neo4j/tx/commit -Method Post -ContentType 'application/json' -Body $bodyJson -Credential $cred -AllowUnencryptedAuthentication | % Content

HTTP Connector import csv,json

function Invoke-Sparql ($sparql) {
$sparql = $sparql -join "`r`n"
$wdurl = 'https://query.wikidata.org/sparql'
#curl --silent -X POST -H 'Content-type: application/sparql-query' -H 'Accept: text/csv' -d $sparql $wdurl | ConvertFrom-Csv
# or
 Invoke-RestMethod -Uri $wdUrl -Method Post -Body @{query=$sparql} -Headers @{"Accept" = "text/csv"} | ConvertFrom-Csv
}
# create people csv
$sparql = @'
SELECT ?el ?elLabel (SAMPLE(YEAR(?birthdate)) AS ?byear) (SAMPLE(YEAR(?deathdate)) AS ?dyear) ?links
WHERE {
  wd:Q130734 wdt:P40* ?el .
  OPTIONAL {
    ?el p:P569 ?pb .
    ?pb psv:P569      [
      wikibase:timeValue         ?birthdate;
      wikibase:timePrecision ?precisionb ;
    ];
        FILTER ( xsd:integer(?precisionb) >= 9 )
  }
  OPTIONAL {
    ?el p:P570 ?pd .
    ?pd psv:P570      [
      wikibase:timeValue         ?deathdate;
      wikibase:timePrecision ?precisiond ;
    ];
        FILTER ( xsd:integer(?precisiond) >= 9 )
  }
  OPTIONAL {?el wikibase:sitelinks ?links } 
  SERVICE wikibase:label { bd:serviceParam wikibase:language "ru,[AUTO_LANGUAGE],en". }
} GROUP BY ?el ?elLabel ?links
'@
Invoke-Sparql $sparql | ConvertTo-Csv > delmePpl.csv

# create haschild rels csv
$sparql = @'
# Ancestry table
SELECT DISTINCT ?parent ?child WHERE {
  wd:Q130734 wdt:P40* ?parent .
  ?parent wdt:P40 ?child .
  MINUS {
    ?otherParent wdt:P40 ?child .
    FILTER (?otherParent != ?parent)
  }
}
'@
Invoke-Sparql $sparql | ConvertTo-Json > delmeRels.json

#create places csv
$sparql = @'
SELECT DISTINCT ?el ?place ?point ?lat ?long ?placeLabel WHERE {
  { wd:Q130734 wdt:P40* ?el .}
  UNION {
    wd:Q130734 wdt:P40* ?el .
  }
  ?el wdt:P19 ?place .
  
  ?place wdt:P625 ?point ;
         p:P625 ?coordinate.
  
  ?coordinate psv:P625 [
    wikibase:geoLatitude ?lat;
    wikibase:geoLongitude ?long
  ] .
  SERVICE wikibase:label { bd:serviceParam wikibase:language "ru,en". }
}
'@
Invoke-Sparql $sparql | ConvertTo-Csv > delmePlaces.csv
#import people from csv
$2dArray = Import-Csv delmePpl.csv | % {,($_.psobject.properties.value)}
$cypher = @'
UNWIND $rows as row
CREATE (p:Person {qid: row[0], name: row[1], birth: toInteger(row[2]), death: toInteger(row[3]), links: toInteger(row[4])});
'@
Invoke-CypherNeo4j $cypher -params @{rows = $2dArray}

#import rels from json
$rels = cat .\delmeRels.json | ConvertFrom-Json
$cypher = @'
UNWIND $objs as obj
MATCH (p1:Person {qid: obj.parent}), (p2:Person {qid: obj.child})
CREATE (p1)-[:HAS_CHILD]->(p2);
'@
Invoke-CypherNeo4j $cypher -params @{objs = $rels}

#import places from csv
$places = Import-Csv delmePlaces.csv
$cypher = @'
UNWIND $objs AS obj
MATCH (n:Person {qid: obj.el})
MERGE (x:Place { 
	qid: obj.place, 
	name: obj.placeLabel, 
	location: point({longitude: toFloat(obj.long), latitude: toFloat(obj.lat)}) 
	})
CREATE  (n)-[:BORN_IN]->(x);
'@
Invoke-CypherNeo4j $cypher -params @{objs = $places}

Query

MATCH (n:Person) -[r:BORN_IN]-> (pl:Place) 
RETURN pl.name,count(n),collect(n);
MATCH p = shortestPath(
  (p1:Person {name: 'Николай I'})-[:HAS_CHILD*]-(p2:Person {qid: 'http://www.wikidata.org/entity/Q43274'})
)
UNWIND nodes(p) AS node
MATCH (node)-[r:HAS_CHILD]->(child)
RETURN node, COLLECT(child) AS children, r
MATCH p = shortestPath(
  (p1:Person {name: 'Николай I'})-[:HAS_CHILD*]-(p2:Person {name: 'Николай II'})
)
UNWIND nodes(p) AS node
MATCH (node)-[:HAS_CHILD]->(child)
RETURN node, COLLECT(child) AS children
MATCH p = shortestPath(
  (p1:Person {name: 'Николай I'})-[rels:HAS_CHILD*]-(p2:Person {name: 'Николай II'})
)
UNWIND rels AS rel
WITH startNode(rel) AS from, endNode(rel) AS to
MERGE (from) -[:HAS_CHILD_TMP]-> (to);

LOAD CSV With headers, from Wikidata

SELECT ?el ?elLabel (SAMPLE(YEAR(?birthdate)) AS ?byear) (SAMPLE(YEAR(?deathdate)) AS ?dyear) ?links
WHERE {
  wd:Q130734 wdt:P40* ?el .
  OPTIONAL {
    ?el p:P569 ?pb .
    ?pb psv:P569      [
      wikibase:timeValue         ?birthdate;
      wikibase:timePrecision ?precisionb ;
    ];
        FILTER ( xsd:integer(?precisionb) >= 9 )
  }
  OPTIONAL {
    ?el p:P570 ?pd .
    ?pd psv:P570      [
      wikibase:timeValue         ?deathdate;
      wikibase:timePrecision ?precisiond ;
    ];
        FILTER ( xsd:integer(?precisiond) >= 9 )
  }
  OPTIONAL {?el wikibase:sitelinks ?links } 
  SERVICE wikibase:label { bd:serviceParam wikibase:language "ru,[AUTO_LANGUAGE],en". }
} GROUP BY ?el ?elLabel ?links
$sparql = gcb -Raw
curl -X POST -H 'Content-Type: application/sparql-query' -H 'Accept: text/csv' --data $sparql https://query.wikidata.org/sparql -o delme.csv

where.exe neo4j
@'
LOAD CSV WITH HEADERS FROM 'file:///delme.csv' AS node
CREATE (p:Person) SET p = node;
'@ | cypher-shell.bat -u neo4j -p qwertyuiop

Export Cypher, APOC Core

$neo4jHome = where.exe neo4j.bat | gi | % Directory | % Parent | % FullName
cd $neo4jHome
copy .\labs\apoc* .\plugins\
'apoc.export.file.enabled=true' > .\conf\apoc.conf
neo4j console
CALL apoc.export.cypher.all(null,{stream:true});

CALL apoc.export.cypher.all(null,{stream:true}YIELD cypherStatements RETURN cypherStatements;

CALL apoc.export.cypher.all('delmeAll.cypher');

NeoDash

# Install node.js:
# https://nodejs.org/en/download/prebuilt-binaries
$url = 'https://nodejs.org/dist/v20.16.0/node-v20.16.0-win-x64.zip'
$name = ($url -split '/')[-1]
irm $url -OutFile "~\Downloads\$name"

Expand-Archive ~/downloads/$name $env:LOCALAPPDATA/Programs
$env:PATH += "C:\Users\user\AppData\Local\Programs\node-v20.16.0-win-x64\;"
corepack enable

Install Neodash

cd $env:LOCALAPPDATA/Programs
git clone --depth 1 https://github.com/neo4j-labs/neodash
cd neodash
yarn install

# run:
yarn run dev

Ancestry

MATCH (p:Person)
WHERE p.birth IS NOT NULL AND p.death IS NOT NULL
WITH p.birth / 10 * 10 as decadeOfBirth, avg(p.death - p.birth) as yearsLived, COUNT(p) AS peopleBorn
RETURN decadeOfBirth, yearsLived, peopleBorn
ORDER BY decadeOfBirth
MATCH p = (n:Person) -[:HAS_CHILD]-> ()
WHERE n.name = $neodash_person_name
RETURN p;

Install Neo4j Browser

irm 'https://repo1.maven.org/maven2/org/neo4j/client/neo4j-browser/5.21.0/neo4j-browser-5.21.0.jar' -OutFile C:\Users\user\Downloads\neo4j-browser-5.21.0.jar

Expand-Archive C:\Users\user\Downloads\neo4j-browser-5.21.0.jar '.\neo4j-browser-5.21.0\'

python -m http.server --directory .\browser\ 7474
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment