<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Benoit Simard</title>
    <description>Benoit's Blog</description>
    <link>http://www.bsimard.com/</link>
    <atom:link href="http://www.bsimard.com/en/feed" rel="self" type="application/rss+xml"/>
    <pubDate>Sat, 20 Jul 2019 11:23:40 +0200</pubDate>
    <lastBuildDate>Sat, 20 Jul 2019 11:23:40 +0200</lastBuildDate>
    <generator>Jekyll v3.3.1</generator>

    
     
    
     
      <item>
        <title>Your computer is a graph !</title>
        <description>&lt;div id=&quot;toc&quot; class=&quot;toc&quot;&gt;
&lt;div id=&quot;toctitle&quot;&gt;Table of Contents&lt;/div&gt;
&lt;ul class=&quot;sectlevel1&quot;&gt;
&lt;li&gt;&lt;a href=&quot;#the-model&quot;&gt;The model&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#import-items&quot;&gt;Import items&lt;/a&gt;
&lt;ul class=&quot;sectlevel2&quot;&gt;
&lt;li&gt;&lt;a href=&quot;#computer-name&quot;&gt;Computer name&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#hardware&quot;&gt;Hardware&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#user-group&quot;&gt;User &amp;amp; Group&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#process-list&quot;&gt;Process list&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#list-of-installed-packages&quot;&gt;List of installed packages&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#network&quot;&gt;Network&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#conclusion&quot;&gt;Conclusion&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div id=&quot;preamble&quot;&gt;
&lt;div class=&quot;sectionbody&quot;&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;In this article I will show you how to import all the components of a Linux computer (package network connection, user, group, &amp;#8230;&amp;#8203;),
into a graph with the help of Neo4j.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;In an next article, we will play with this dataset and see what we value we can get.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;sect1&quot;&gt;
&lt;h2 id=&quot;the-model&quot;&gt;The model&lt;/h2&gt;
&lt;div class=&quot;sectionbody&quot;&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;This is the model I want to produce :&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;imageblock&quot;&gt;
&lt;div class=&quot;content&quot;&gt;
&lt;img src=&quot;/public/images/diag/diag-a43d29d5a7438ea5e0741d034529c6c1.png&quot; alt=&quot;diag a43d29d5a7438ea5e0741d034529c6c1&quot; width=&quot;1108&quot; height=&quot;766&quot;&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;Basicaly, I have a server that is composed of a list of:&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;ulist&quot;&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;installed packages with their dependencies&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;network interfaces with their connections&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;hardware (as tree)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;groups and users&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;process (as a tree)&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;From that model, we can create the indexes/constraints for the database :&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;listingblock&quot;&gt;
&lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;highlight&quot;&gt;&lt;code class=&quot;language-cypher&quot; data-lang=&quot;cypher&quot;&gt;CREATE CONSTRAINT ON (n:Server) ASSERT (n.fqdn) IS NODE KEY;
CREATE CONSTRAINT ON (n:Group) ASSERT (n.gid, n.fqdn) IS NODE KEY;
CREATE CONSTRAINT ON (n:User) ASSERT (n.uid, n.fqdn) IS NODE KEY;
CREATE CONSTRAINT ON (n:Process) ASSERT (n.pid, n.fqdn) IS NODE KEY;
CREATE CONSTRAINT ON (n:PackageSection) ASSERT (n.name) IS NODE KEY;
CREATE CONSTRAINT ON (n:Package) ASSERT (n.name) IS NODE KEY;
CREATE CONSTRAINT ON (n:PackageVersion) ASSERT (n.version, n.architecture,n.name ) IS NODE KEY;
CREATE CONSTRAINT ON (n:Hardware) ASSERT (n.id, n.fqdn ) IS NODE KEY;
CREATE CONSTRAINT ON (n:Vendor) ASSERT (n.name ) IS NODE KEY;
CREATE CONSTRAINT ON (n:Product) ASSERT (n.name, n.vendor ) IS NODE KEY;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;sect1&quot;&gt;
&lt;h2 id=&quot;import-items&quot;&gt;Import items&lt;/h2&gt;
&lt;div class=&quot;sectionbody&quot;&gt;
&lt;div class=&quot;sect2&quot;&gt;
&lt;h3 id=&quot;computer-name&quot;&gt;Computer name&lt;/h3&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;The first thing to retrieve is the &lt;a href=&quot;https://fr.wikipedia.org/wiki/Fully_qualified_domain_name&quot;&gt;FQDN&lt;/a&gt; of the computer.
For this I will use the command : &lt;code&gt;hostname --fqdn&lt;/code&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;listingblock&quot;&gt;
&lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;highlight&quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;$&amp;gt; hostname --fqdn
pythagore&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;This will help us to create the node &lt;code&gt;Server&lt;/code&gt; :&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;listingblock&quot;&gt;
&lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;highlight&quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;MERGE (:Server {fqdn:$fqdn});&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;sect2&quot;&gt;
&lt;h3 id=&quot;hardware&quot;&gt;Hardware&lt;/h3&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;To retrieve the entire composition of the server,
I will use the command &lt;code&gt;lshw&lt;/code&gt; that list all the hardware in a tree format.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;Moreover, the command can generate a JSON file if we pass the argument &lt;code&gt;-json&lt;/code&gt;:&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;listingblock&quot;&gt;
&lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;highlight&quot;&gt;&lt;code class=&quot;language-shell&quot; data-lang=&quot;shell&quot;&gt;FILE=&quot;$NEO4J_HOME/import/$1/hardware.json&quot;
  sudo lshw -json &amp;gt; $FILE&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;listingblock&quot;&gt;
&lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;highlight&quot;&gt;&lt;code class=&quot;language-json&quot; data-lang=&quot;json&quot;&gt;{
  &quot;id&quot; : &quot;pythagore&quot;,
  &quot;class&quot; : &quot;system&quot;,
  &quot;claimed&quot; : true,
  &quot;description&quot; : &quot;Computer&quot;,
  &quot;width&quot; : 64,
  &quot;capabilities&quot; : {
    &quot;vsyscall32&quot; : &quot;processus 32 bits&quot;
  },
  &quot;children&quot; : [
    {
      &quot;id&quot; : &quot;core&quot;,
      &quot;class&quot; : &quot;bus&quot;,
      &quot;claimed&quot; : true,
      &quot;description&quot; : &quot;Motherboard&quot;,
      &quot;physid&quot; : &quot;0&quot;,
      &quot;children&quot; : [
        {
          &quot;id&quot; : &quot;memory&quot;,
          &quot;class&quot; : &quot;memory&quot;,
          &quot;claimed&quot; : true,
          &quot;description&quot; : &quot;Mémoire système&quot;,
          &quot;physid&quot; : &quot;0&quot;,
          &quot;units&quot; : &quot;bytes&quot;,
          &quot;size&quot; : 16695070720
        }
        ...
      ]
    }
  ]
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;The import process of this file will be a little complexe,
because it&amp;#8217;s a &lt;strong&gt;JSON tree&lt;/strong&gt; with an undeterminated depth. So I need to create a &lt;strong&gt;recursive query&lt;/strong&gt; to create its structure in neo4j.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;For this I will use massively &lt;a href=&quot;https://neo4j-contrib.github.io/neo4j-apoc-procedures&quot;&gt;APOC&lt;/a&gt; for that !&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;For each item of the JSON, I want to create this : &lt;code&gt;(Vendor)-&amp;#8594;(Product)&amp;#8592;-(ProductInstance)&amp;#8592;-(ParentHardwareInstance)&lt;/code&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;If I translate that in Cypher in a custom procedure, it gives that :&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;listingblock&quot;&gt;
&lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;highlight&quot;&gt;&lt;code class=&quot;language-cypher&quot; data-lang=&quot;cypher&quot;&gt;CALL apoc.custom.asProcedure(
	'hardware',
  &quot;
  MATCH (p) WHERE id(p)=$parent

  MERGE (vendor:Vendor { name:coalesce($item.vendor, 'unknown')})
  MERGE (product:Product { name:coalesce($item.product, 'unknown'), vendor:coalesce($item.vendor, 'unknown')})
    ON CREATE SET product.description = $item.description
  MERGE (vendor)-[:HAS_PRODUCT]-&amp;gt;(product)

  MERGE (hardware:Hardware {id:$item.id + '-' + coalesce($item.physid, '') + '-' + coalesce($item.handle, '') , fqdn:$fqdn})
  SET hardware +=apoc.map.removeKeys($item,['id', 'children', 'capabilities', 'configuration', 'vendor', 'product'])
  MERGE (p)-[:HAS_HARDWARE]-&amp;gt;(hardware)
  MERGE (hardware)-[:TYPE_OF]-&amp;gt;(product)

  WITH  hardware
  UNWIND coalesce($item.children, []) AS newItem
    CALL custom.hardware($fqdn, id(hardware), newItem) YIELD row
    RETURN row
  &quot;,
  &quot;write&quot;,
  [[&quot;row&quot;,&quot;MAP&quot;]],
  [
    ['fqdn','STRING'],
    ['parent','LONG'],
    ['item','MAP']
  ]
);&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;As you see at the &lt;strong&gt;line 18&lt;/strong&gt;, I recall the procedure itself if there are some children.
Pretty cool no ?&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;admonitionblock note&quot;&gt;
&lt;table&gt;
&lt;tr&gt;
&lt;td class=&quot;icon&quot;&gt;
&lt;div class=&quot;title&quot;&gt;Note&lt;/div&gt;
&lt;/td&gt;
&lt;td class=&quot;content&quot;&gt;
The documentation for the cypher custom procedure feature is available  here : &lt;a href=&quot;https://neo4j-contrib.github.io/neo4j-apoc-procedures/#cypher-based-procedures-functions&quot; class=&quot;bare&quot;&gt;https://neo4j-contrib.github.io/neo4j-apoc-procedures/#cypher-based-procedures-functions&lt;/a&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;Then I just have to call the procedure with the data like that :&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;listingblock&quot;&gt;
&lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;highlight&quot;&gt;&lt;code class=&quot;language-cypher&quot; data-lang=&quot;cypher&quot;&gt;CALL apoc.load.json(&quot;file:///&quot; + $fqdn + &quot;/hardware.json&quot;) YIELD value WITH value
 MATCH (s:Server {fqdn:$fqdn})
 CALL custom.hardware($fqdn, id(s), value) YIELD row
 RETURN count(*);&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;And to finish, I put the &lt;code&gt;class&lt;/code&gt; attribut as a Label :&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;listingblock&quot;&gt;
&lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;highlight&quot;&gt;&lt;code class=&quot;language-cypher&quot; data-lang=&quot;cypher&quot;&gt;MATCH (n:Hardware)
CALL apoc.create.addLabels( id(n), [ n.class ] ) YIELD node
REMOVE node.class
RETURN count(*)&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;If you want to see the result in the browser, this is the query for just displaying the tree structure of hardwares :&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;listingblock&quot;&gt;
&lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;highlight&quot;&gt;&lt;code class=&quot;language-cypher&quot; data-lang=&quot;cypher&quot;&gt;MATCH p=(n:Server {fqdn:'pythagore.logisima'})-[:HAS_HARDWARE]-&amp;gt;(:Hardware)-[:HAS_HARDWARE*]-&amp;gt;(h:Hardware)
WHERE NOT (h)-[:HAS_HARDWARE]-&amp;gt;()
RETURN *&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;imageblock&quot;&gt;
&lt;div class=&quot;content&quot;&gt;
&lt;img src=&quot;/public/images/computer-as-a-graph/hardware-tree.svg&quot; alt=&quot;hardware tree&quot; width=&quot;70%&quot;&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;sect2&quot;&gt;
&lt;h3 id=&quot;user-group&quot;&gt;User &amp;amp; Group&lt;/h3&gt;
&lt;div class=&quot;sect3&quot;&gt;
&lt;h4 id=&quot;users&quot;&gt;Users&lt;/h4&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;Users are stored in the file &lt;code&gt;/etc/users&lt;/code&gt;, where each line represents a user.
For example : &lt;code&gt;bsimard:x:1000:1000:Benoit Simard,,,:/home/bsimard:/bin/bash&lt;/code&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;olist arabic&quot;&gt;
&lt;ol class=&quot;arabic&quot;&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Username:&lt;/strong&gt; The login of the user, &lt;code&gt;bsimard&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Password:&lt;/strong&gt;  Generally password is set to &lt;code&gt;x&lt;/code&gt;. It indicates that password are stored in the file &lt;code&gt;/etc/shadow&lt;/code&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;User ID (UID):&lt;/strong&gt;  The ID of the user.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Group ID (GID):&lt;/strong&gt;  The ID of the main group of the user&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;User ID Info:&lt;/strong&gt; Some information about the user seperated by a &lt;code&gt;,&lt;/code&gt;, like the fullname, phone, email&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Home directory:&lt;/strong&gt; The home directory of the user &lt;code&gt;/home/bsimard&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Command/shell:&lt;/strong&gt; The command for the shell, in my case &lt;code&gt;/bin/bash&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;This is the Cypher script to import this file in Neo4j :&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;listingblock&quot;&gt;
&lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;highlight&quot;&gt;&lt;code class=&quot;language-cypher&quot; data-lang=&quot;cypher&quot;&gt;LOAD CSV WITH HEADERS FROM &quot;file:///&quot; + $fqdn + &quot;/users.csv&quot; AS row FIELDTERMINATOR ':'
  MERGE (g:Group { gid:toInteger(row.gid), fqdn:$fqdn})
  MERGE(u:User {uid: toInteger(row.uid), fqdn:$fqdn})
  SET u.username = row.username,
      u.fullname = split(row.info, ',')[0],
      u.home_directory = row.home,
      u.shell = row.shell
  MERGE (g)-[:HAS_USER]-&amp;gt;(u);&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;sect3&quot;&gt;
&lt;h4 id=&quot;groups&quot;&gt;Groups&lt;/h4&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;Groups are stored in the file &lt;code&gt;/etc/groups&lt;/code&gt;, where each line represents a group.
For example : &lt;code&gt;adm:x:4:syslog,bsimard&lt;/code&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;olist arabic&quot;&gt;
&lt;ol class=&quot;arabic&quot;&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Group name:&lt;/strong&gt; Name of the group, in our example it&amp;#8217;s &lt;code&gt;adm&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Password:&lt;/strong&gt;  Generally password is not used, hence it is empty/blan, ie. &lt;code&gt;x&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Group ID (GID):&lt;/strong&gt; Each group must be assigned a group ID, , in our example it&amp;#8217;s &lt;code&gt;4&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Group List:&lt;/strong&gt; It is a list of user names of users who are members of the group, separated by a &lt;code&gt;,&lt;/code&gt;. In our example &lt;code&gt;syslog&lt;/code&gt; and &lt;code&gt;bsimard&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;This is the Cypher script to import this file in Neo4j :&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;listingblock&quot;&gt;
&lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;highlight&quot;&gt;&lt;code class=&quot;language-cypher&quot; data-lang=&quot;cypher&quot;&gt;LOAD CSV WITH HEADERS FROM &quot;file:///&quot; + $fqdn + &quot;/groups.csv&quot; AS row FIELDTERMINATOR ':'
  MATCH (s:Server {fqdn:$fqdn})
  MERGE (g:Group { gid:toInteger(row.gid), fqdn:$fqdn })
  MERGE (s)-[:HAS_GROUP]-&amp;gt;(g)
  SET g.name = row.name
  WITH row, g
  UNWIND split(row.users, &quot;,&quot;) AS username
    MATCH (u:User { username:username, fqdn:$fqdn})
    MERGE (g)-[:HAS_USER]-&amp;gt;(u);&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;sect3&quot;&gt;
&lt;h4 id=&quot;the-result&quot;&gt;The result&lt;/h4&gt;
&lt;div class=&quot;imageblock&quot;&gt;
&lt;div class=&quot;content&quot;&gt;
&lt;img src=&quot;/public/images/computer-as-a-graph/user-all.svg&quot; alt=&quot;user all&quot; width=&quot;70%&quot;&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;sect2&quot;&gt;
&lt;h3 id=&quot;process-list&quot;&gt;Process list&lt;/h3&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;To retrieve the list of all the running process, with their dependencies,
I will use the &lt;strong&gt;ps&lt;/strong&gt; command with those arguments :&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;ulist&quot;&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;e:&lt;/strong&gt; Select all processes&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;o:&lt;/strong&gt; To specify the format with&lt;/p&gt;
&lt;div class=&quot;ulist&quot;&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;pid:&lt;/strong&gt; The ID of the process&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;ppid:&lt;/strong&gt; The ID of the parent process&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;comm:&lt;/strong&gt; The command that has launched the process&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;ruser:&lt;/strong&gt; The username of the user that owns the process&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div class=&quot;listingblock&quot;&gt;
&lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;highlight&quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;$&amp;gt; ps -eo pid,ppid,comm,ruser

PID,PPID,%CPU,SIZE,COMMAND,RUSER
1,0,0.0,18964,systemd,root
2,0,0.0,0,kthreadd,root
4,2,0.0,0,kworker/0:0H,root
6,2,0.0,0,mm_percpu_wq,root&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;With the help of &lt;strong&gt;awk&lt;/strong&gt;, we can generate a CSV file :&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;listingblock&quot;&gt;
&lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;highlight&quot;&gt;&lt;code class=&quot;language-shell&quot; data-lang=&quot;shell&quot;&gt;FILE=&quot;$NEO4J_HOME/import/$1/processes.csv&quot;
  ps -eo pid,ppid,comm,ruser | awk '{print $1&quot;,&quot;$2&quot;,&quot;$3&quot;,&quot;$4}' &amp;gt; $FILE&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;And this is the cypher script to import it :&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;listingblock&quot;&gt;
&lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;highlight&quot;&gt;&lt;code class=&quot;language-cypher&quot; data-lang=&quot;cypher&quot;&gt;MATCH (s:Server {fqdn:$fqdn})
MERGE (p:Process {pid:0, command:'init', fqdn:$fqdn})
MERGE (s)-[:HAS_PROCESS]-&amp;gt;(p);

LOAD CSV WITH HEADERS FROM &quot;file:///&quot; + $fqdn + &quot;/processes.csv&quot; AS row
WITH row ORDER BY row.PPID, row.PID  ASC
  MATCH (u:User {username:row.RUSER, fqdn:$fqdn})
  MERGE (pp:Process {pid:toInteger(row.PPID), fqdn:$fqdn})
  MERGE (p:Process {pid:toInteger(row.PID), fqdn:$fqdn})
  SET p.command = split(row.COMMAND, '/')[0]
  MERGE (pp)-[:HAS_PROCESS]-&amp;gt;(p)
  MERGE (p)-[:OWNED_BY]-&amp;gt;(u);&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;When the import is finished, you should have something similar to this by excuting the following query :&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;listingblock&quot;&gt;
&lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;highlight&quot;&gt;&lt;code class=&quot;language-cypher&quot; data-lang=&quot;cypher&quot;&gt;MATCH path=(n:Server {fqdn:'pythagore.logisima'})-[:HAS_PROCESS]-&amp;gt;(:Process)-[:HAS_PROCESS*]-&amp;gt;(p:Process)
WHERE NOT (p)-[:HAS_PROCESS]-&amp;gt;()
RETURN *&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;imageblock&quot;&gt;
&lt;div class=&quot;content&quot;&gt;
&lt;img src=&quot;/public/images/computer-as-a-graph/process-tree.svg&quot; alt=&quot;process tree&quot; width=&quot;70%&quot;&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;We can also search all the processes of a user :&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;listingblock&quot;&gt;
&lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;highlight&quot;&gt;&lt;code class=&quot;language-cypher&quot; data-lang=&quot;cypher&quot;&gt;MATCH (n:Server {fqdn:'pythagore.logisima'})-[:HAS_GROUP|:HAS_USER*2..2]-&amp;gt;(u:User {name:'bsimard'})
WITH u
MATCH (u)&amp;lt;-[:OWNED_BY]-(p)
RETURN *&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;imageblock&quot;&gt;
&lt;div class=&quot;content&quot;&gt;
&lt;img src=&quot;/public/images/computer-as-a-graph/process-user.svg&quot; alt=&quot;process user&quot; width=&quot;40%&quot;&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;sect2&quot;&gt;
&lt;h3 id=&quot;list-of-installed-packages&quot;&gt;List of installed packages&lt;/h3&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;Because I have a debian system, I will use the &lt;strong&gt;dpkg&lt;/strong&gt; command, and produce a CSV from its output :&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;listingblock&quot;&gt;
&lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;highlight&quot;&gt;&lt;code class=&quot;language-shell&quot; data-lang=&quot;shell&quot;&gt;FILE=&quot;$NEO4J_HOME/import/$1/packages.csv&quot;
  dpkg-query -Wf '${Section}\t${Package}\t${Version}\t${Architecture}\t${Homepage}\t${Depends}\n' &amp;gt;&amp;gt; $FILE&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;I can import it with the following cypher script :&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;listingblock&quot;&gt;
&lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;highlight&quot;&gt;&lt;code class=&quot;language-cypher&quot; data-lang=&quot;cypher&quot;&gt;LOAD CSV WITH HEADERS FROM &quot;file:///&quot; + $fqdn + &quot;/packages.csv&quot; AS row FIELDTERMINATOR '\t'
  MATCH (s:Server {fqdn:$fqdn})

  MERGE (section:PackageSection {name:coalesce(row.section, 'None') })
  MERGE (package:Package {name:row.package})
  SET package.url = row.homepage
  MERGE (section)-[:HAS_PACKAGE]-&amp;gt;(package)

  MERGE (pv:PackageVersion { version:row.version, architecture:row.architecture, name:row.package})
  MERGE (package)-[:HAS_VERSION]-&amp;gt;(pv)

  MERGE (s)-[:HAS_PACKAGE]-&amp;gt;(pv)

  FOREACH( dep IN split(row.dependencies, ',') |
    MERGE (depNode:Package {name:split(dep, '(')[0] })
    MERGE (pv)-[r:HAS_DEPENDENCY]-&amp;gt;(depNode)
    SET r.constraint = replace(split(dep, '(' )[1], ')','')
  );&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;If you want to see the result, you can run the following query :&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;listingblock&quot;&gt;
&lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;highlight&quot;&gt;&lt;code class=&quot;language-cypher&quot; data-lang=&quot;cypher&quot;&gt;MATCH (n:Server {fqdn:'pythagore.logisima'})-[:HAS_PACKAGE]-&amp;gt;(:Package)-[:HAS_PACKAGE*]-&amp;gt;(p:Package)
WHERE NOT (p)-[:HAS_PACKAGE]-&amp;gt;()
RETURN *&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;imageblock&quot;&gt;
&lt;div class=&quot;content&quot;&gt;
&lt;img src=&quot;/public/images/computer-as-a-graph/packages.svg&quot; alt=&quot;packages&quot; width=&quot;70%&quot;&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;sect2&quot;&gt;
&lt;h3 id=&quot;network&quot;&gt;Network&lt;/h3&gt;
&lt;div class=&quot;sect3&quot;&gt;
&lt;h4 id=&quot;network-interfaces&quot;&gt;Network interfaces&lt;/h4&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;To get the list of the network interface, I will use the &lt;strong&gt;ip&lt;/strong&gt; command with the following arguments :&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;ulist&quot;&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;r:&lt;/strong&gt; See the routing table&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;n:&lt;/strong&gt; Display the IP adresses&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;w:&lt;/strong&gt; Don&amp;#8217;t truncate IP addresses&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div class=&quot;listingblock&quot;&gt;
&lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;highlight&quot;&gt;&lt;code class=&quot;language-shell&quot; data-lang=&quot;shell&quot;&gt;$&amp;gt;ip -o addr show

1: lo    inet 127.0.0.1/8 scope host lo\       valid_lft forever preferred_lft forever
1: lo    inet6 ::1/128 scope host \       valid_lft forever preferred_lft forever
2: wlp58s0    inet 10.0.1.11/24 brd 10.0.1.255 scope global dynamic wlp58s0\       valid_lft 2720sec preferred_lft 2720sec
2: wlp58s0    inet6 fe80::cc06:f22c:2e3b:6aaf/64 scope link \       valid_lft forever preferred_lft forever
3: br-8a43a73a9b0d    inet 172.24.0.1/16 brd 172.24.255.255 scope global br-8a43a73a9b0d\       valid_lft forever preferred_lft forever
4: br-f8fefbd33c18    inet 172.18.0.1/16 brd 172.18.255.255 scope global br-f8fefbd33c18\       valid_lft forever preferred_lft forever
5: br-0ba6b709cb23    inet 172.21.0.1/16 brd 172.21.255.255 scope global br-0ba6b709cb23\       valid_lft forever preferred_lft forever
6: docker0    inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0\       valid_lft forever preferred_lft forever
7: br-667de2307c35    inet 172.22.0.1/16 brd 172.22.255.255 scope global br-667de2307c35\       valid_lft forever preferred_lft forever
8: br-72683ea3a3d0    inet 172.19.0.1/16 brd 172.19.255.255 scope global br-72683ea3a3d0\       valid_lft forever preferred_lft forever&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;For my need, I need the IP addresses (v4 &amp;amp; v6) of the Interface and its name.
So by using awk, we can produce a CSV file :&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;listingblock&quot;&gt;
&lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;highlight&quot;&gt;&lt;code class=&quot;language-shell&quot; data-lang=&quot;shell&quot;&gt;FILE=&quot;$NEO4J_HOME/import/$1/network_interfaces.csv&quot;
  ip -o addr show  | awk '{print $2&quot;,&quot;$3&quot;,&quot;$4}' &amp;gt;&amp;gt; $FILE&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;Now I can load it in Neo4j :&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;listingblock&quot;&gt;
&lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;highlight&quot;&gt;&lt;code class=&quot;language-cypher&quot; data-lang=&quot;cypher&quot;&gt;LOAD CSV WITH HEADERS FROM &quot;file:///&quot; + $fqdn + &quot;/network_interfaces.csv&quot; AS row
WITH row,
  CASE  WHEN  row.type = 'inet' THEN split(row.ip, '/')[0] ELSE NULL END AS ipv4,
  CASE  WHEN  row.type = 'inet6' THEN split(row.ip, '/')[0] ELSE NULL END AS ipv6

  MATCH (s:Server {fqdn:$fqdn})

  MERGE (i:Interface { name: row.name, fqdn:$fqdn })
  SET i.ip = coalesce(ipv4, i.ip),
      i.ipv6 = coalesce(ipv6, i.ipv6)

  MERGE (s)-[:HAS_INTERFACE]-&amp;gt;(i);&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;It gives me the following graph  :&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;imageblock&quot;&gt;
&lt;div class=&quot;content&quot;&gt;
&lt;img src=&quot;/public/images/computer-as-a-graph/interfaces.svg&quot; alt=&quot;interfaces&quot; width=&quot;30%&quot;&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;sect3&quot;&gt;
&lt;h4 id=&quot;network-connections&quot;&gt;Network connections&lt;/h4&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;To see all the connections on our computer, I will use the &lt;strong&gt;netstat&lt;/strong&gt; command,
with the following arguments :&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;ulist&quot;&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;a:&lt;/strong&gt; Display every socket&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;u:&lt;/strong&gt; Filter on UDP sockets&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;t:&lt;/strong&gt; Filter on TCP sockets&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;p:&lt;/strong&gt; Display the process ID that use the socket&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;n:&lt;/strong&gt; Display the IP adresses&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;w:&lt;/strong&gt; Don&amp;#8217;t truncate IP addresses&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div class=&quot;admonitionblock note&quot;&gt;
&lt;table&gt;
&lt;tr&gt;
&lt;td class=&quot;icon&quot;&gt;
&lt;div class=&quot;title&quot;&gt;Note&lt;/div&gt;
&lt;/td&gt;
&lt;td class=&quot;content&quot;&gt;
you need to run this command as a root to see all the connections
&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div class=&quot;listingblock&quot;&gt;
&lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;highlight&quot;&gt;&lt;code class=&quot;language-shell&quot; data-lang=&quot;shell&quot;&gt;$&amp;gt; netstat -alpuetn

Connexions Internet actives (serveurs et établies)
Proto Recv-Q Send-Q Adresse locale          Adresse distante        Etat       User       Inode       PID/Program name
tcp        0      0 127.0.0.1:3306          0.0.0.0:*               LISTEN      124        28742       1317/mysqld
tcp        0      0 127.0.1.1:53            0.0.0.0:*               LISTEN      0          35483       3192/dnsmasq
tcp        0      0 127.0.0.1:631           0.0.0.0:*               LISTEN      0          4024761     5070/cupsd
tcp        0      0 127.0.0.1:5432          0.0.0.0:*               LISTEN      125        28000       1434/postgres
tcp        0      0 127.0.0.1:38630         127.0.0.1:7687          ESTABLISHED 1000       4856935     14182/firefox
tcp        0      0 10.0.1.11:39694         198.252.206.25:443      ESTABLISHED 1000       5159133     14182/firefox
tcp        0      0 127.0.0.1:39044         127.0.0.1:7687          ESTABLISHED 1000       4908177     14182/firefox
tcp        0      0 10.0.1.11:38388         52.222.163.46:443       ESTABLISHED 1000       5190592     14182/firefox
tcp        0      0 127.0.0.1:39034         127.0.0.1:7687          ESTABLISHED 1000       4903190     14182/firefox
tcp        0      0 10.0.1.11:49408         216.58.209.238:443      ESTABLISHED 1000       4994841     14182/firefox
tcp        0      0 10.0.1.11:42724         172.217.18.197:443      ESTABLISHED 1000       5163155     14182/firefox
tcp        0      0 10.0.1.11:38316         52.222.163.46:443       ESTABLISHED 1000       5157419     7587/libpepflashpla
tcp        0      0 127.0.0.1:38632         127.0.0.1:7687          ESTABLISHED 1000       4853218     14182/firefox
tcp        0      0 10.0.1.11:39698         198.252.206.25:443      ESTABLISHED 1000       5159174     14182/firefox
tcp        0      0 10.0.1.11:58296         10.0.0.1:445            ESTABLISHED 0          4628873     -
tcp        0      0 127.0.0.1:39046         127.0.0.1:7687          ESTABLISHED 1000       4914563     14182/firefox&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;Like above, I will create a CSV :&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;listingblock&quot;&gt;
&lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;highlight&quot;&gt;&lt;code class=&quot;language-shell&quot; data-lang=&quot;shell&quot;&gt;FILE=&quot;$NEO4J_HOME/import/$1/network_connections.csv&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;I will split in two parts (with sub-parts), the import of this file :&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;ulist&quot;&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;processes that listen on a port&lt;/p&gt;
&lt;div class=&quot;ulist&quot;&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;On IPv4&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;On IPv6&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;On all interfaces&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;processes that have an established connection&lt;/p&gt;
&lt;div class=&quot;ulist&quot;&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;With an IPv4&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;With an IPv6&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div class=&quot;sect4&quot;&gt;
&lt;h5 id=&quot;processes-that-listen&quot;&gt;Processes that listen&lt;/h5&gt;
&lt;div class=&quot;sect5&quot;&gt;
&lt;h6 id=&quot;on-ipv4&quot;&gt;On Ipv4&lt;/h6&gt;
&lt;div class=&quot;listingblock&quot;&gt;
&lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;highlight&quot;&gt;&lt;code class=&quot;language-cypher&quot; data-lang=&quot;cypher&quot;&gt;LOAD CSV WITH HEADERS FROM &quot;file:///&quot; + $fqdn + &quot;/network_connections.csv&quot; AS row
  WITH
    row,
    toInteger(last(split(row.local, ':'))) AS local_port,
    toInteger(split(row.pid,'/')[0]) AS pid
  WHERE
    row.state = 'LISTEN' AND
    size(split(row.local, '.')) &amp;gt; 1 AND // only IPv4
    (NOT row.local = '::' AND NOT row.local = '0.0.0.0') // Not for all interfaces
  WITH
    row,
    replace(row.local, ':'+ local_port, '') AS local_ip,
    local_port,
    pid,
    CASE WHEN pid IS NULL THEN [] ELSE [1] END AS ifPid

    MATCH (i:Interface { ip: local_ip, fqdn:$fqdn })
    MERGE (p:Port { number: local_port, ip:local_ip })
    MERGE (i)-[:USES_PORT]-&amp;gt;(p)
    SET p.ip = i.ip,
        p.ipv6 = i.ipv6

    FOREACH( x IN ifPid |
      MERGE (process:Process {pid:pid, fqdn:$fqdn})
      MERGE (process)-[:LISTEN_ON]-&amp;gt;(p)
    );&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;sect5&quot;&gt;
&lt;h6 id=&quot;on-ipv6&quot;&gt;On Ipv6&lt;/h6&gt;
&lt;div class=&quot;listingblock&quot;&gt;
&lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;highlight&quot;&gt;&lt;code class=&quot;language-cypher&quot; data-lang=&quot;cypher&quot;&gt;LOAD CSV WITH HEADERS FROM &quot;file:///&quot; + $fqdn + &quot;/network_connections.csv&quot; AS row
  WITH
    row,
    toInteger(last(split(row.local, ':'))) AS local_port,
    toInteger(split(row.pid,'/')[0]) AS pid
  WHERE
    row.state = 'LISTEN' AND
    size(split(row.local, '.')) = 0 AND // only IPv6
    (NOT row.local STARTS WITH '::' AND NOT row.local STARTS WITH '0.0.0.0') // Not for all interfaces
  WITH
    row,
    replace(row.local, ':'+ local_port, '') AS local_ip,
    local_port,
    pid,
    CASE WHEN pid IS NULL THEN [] ELSE [1] END AS ifPid

    MATCH (i:Interface { ipv6: local_ip, fqdn:$fqdn })
    MERGE (p:Port { number: local_port, ipv6:local_ip})
    MERGE (i)-[:USES_PORT]-&amp;gt;(p)
    SET p.ip = i.ip,
        p.ipv6 = i.ipv6

    FOREACH( x IN ifPid |
      MERGE (process:Process {pid:pid, fqdn:$fqdn})
      MERGE (process)-[:LISTEN_ON]-&amp;gt;(p)
    );&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;sect5&quot;&gt;
&lt;h6 id=&quot;on-all-interfaces&quot;&gt;On all interfaces&lt;/h6&gt;
&lt;div class=&quot;listingblock&quot;&gt;
&lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;highlight&quot;&gt;&lt;code class=&quot;language-cypher&quot; data-lang=&quot;cypher&quot;&gt;LOAD CSV WITH HEADERS FROM &quot;file:///&quot; + $fqdn + &quot;/network_connections.csv&quot; AS row
  WITH
    row,
    toInteger(last(split(row.local, ':'))) AS local_port,
    toInteger(split(row.pid,'/')[0]) AS pid
  WHERE
    row.state = 'LISTEN' AND
    ( row.local STARTS WITH '0.0.0.0') //  all interfaces
  WITH
    row,
    local_port,
    pid,
    CASE WHEN pid IS NULL THEN [] ELSE [1] END AS ifPid

    MATCH (i:Interface {fqdn:$fqdn })
    MERGE (p:Port { number: local_port, ip:i.ip})
    SET p.ipv6 = i.ipv6
    MERGE (i)-[:USES_PORT]-&amp;gt;(p)

    FOREACH( x IN ifPid |
      MERGE (process:Process {pid:pid, fqdn:$fqdn})
      MERGE (process)-[:LISTEN_ON]-&amp;gt;(p)
    );&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;listingblock&quot;&gt;
&lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;highlight&quot;&gt;&lt;code class=&quot;language-cypher&quot; data-lang=&quot;cypher&quot;&gt;LOAD CSV WITH HEADERS FROM &quot;file:///&quot; + $fqdn + &quot;/network_connections.csv&quot; AS row
  WITH
    row,
    toInteger(last(split(row.local, ':'))) AS local_port,
    toInteger(split(row.pid,'/')[0]) AS pid
  WHERE
    row.state = 'LISTEN' AND
    ( row.local STARTS WITH ':::*') //  all interfaces
  WITH
    row,
    local_port,
    pid,
    CASE WHEN pid IS NULL THEN [] ELSE [1] END AS ifPid

    MATCH (i:Interface {fqdn:$fqdn })
    MERGE (p:Port { number: local_port, ipv6:i.ipv6})
    SET p.ip = i.ip
    MERGE (i)-[:USES_PORT]-&amp;gt;(p)

    FOREACH( x IN ifPid |
      MERGE (process:Process {pid:pid, fqdn:$fqdn})
      MERGE (process)-[:LISTEN_ON]-&amp;gt;(p)
    );&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;sect5&quot;&gt;
&lt;h6 id=&quot;graph-example&quot;&gt;Graph example&lt;/h6&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;This is so cool, at the end we obtain the graph below just by running the following query :&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;listingblock&quot;&gt;
&lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;highlight&quot;&gt;&lt;code class=&quot;language-cypher&quot; data-lang=&quot;cypher&quot;&gt;MATCH path=(n:Server {fqdn:'pythagore.logisima'})-[:HAS_INTERFACE]-&amp;gt;(i:Interface)-[:USES_PORT]-&amp;gt;(:Port)&amp;lt;-[:LISTEN_ON]-(p:Process)
RETURN path&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;imageblock&quot;&gt;
&lt;div class=&quot;content&quot;&gt;
&lt;img src=&quot;/public/images/computer-as-a-graph/network-listen.svg&quot; alt=&quot;network listen&quot; width=&quot;70%&quot;&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;sect4&quot;&gt;
&lt;h5 id=&quot;processes-with-a-connection&quot;&gt;Processes with a connection&lt;/h5&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;This time, I import all the connections (incoming/outgoing) that computers have.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;sect5&quot;&gt;
&lt;h6 id=&quot;on-ipv4-2&quot;&gt;On Ipv4&lt;/h6&gt;
&lt;div class=&quot;listingblock&quot;&gt;
&lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;highlight&quot;&gt;&lt;code class=&quot;language-cypher&quot; data-lang=&quot;cypher&quot;&gt;LOAD CSV WITH HEADERS FROM &quot;file:///&quot; + $fqdn + &quot;/network_connections.csv&quot; AS row
  WITH
    row,
    toInteger(last(split(row.local, ':'))) AS local_port,
    toInteger(last(split(row.remote, ':'))) AS remote_port,
    toInteger(split(row.pid,'/')[0]) AS pid
  WHERE
    row.state = 'ESTABLISHED' AND
    size(split(row.local, '.')) &amp;gt; 1 // only ipv4
  WITH
    row,
    replace(row.local, ':'+ local_port, '') AS local_ip,
    local_port,
    replace(row.remote, ':'+ remote_port, '') AS remote_ip,
    remote_port,
    pid,
    CASE WHEN pid IS NULL THEN [] ELSE [1] END AS ifPid

    MATCH (s:Server {fqdn:$fqdn})

    MATCH (iLocal:Interface {ip: local_ip, fqdn:$fqdn })
    MERGE (iLocal)-[:USES_PORT]-&amp;gt;(pLocal:Port { number: local_port})
    SET pLocal.ip = iLocal.ip,
        pLocal.ipv6 = iLocal.ipv6

    MERGE (iLocal)-[:USES_PORT]-&amp;gt;(pLocal)

    MERGE (pRemote:Port { number: remote_port, ip:remote_ip })

    MERGE (con:Connection { fqdn:$fqdn, pid:coalesce(pid,'-'), local:row.local, remote:row.remote})
    MERGE (con)-[:SOURCE]-&amp;gt;(pLocal)
    MERGE (con)-[:TARGET]-&amp;gt;(pRemote)

    FOREACH( x IN ifPid |
      MERGE (process:Process {pid:pid, fqdn:$fqdn})
      MERGE (process)-[:HAS_CONNECTION]-&amp;gt;(con)
    );&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;sect5&quot;&gt;
&lt;h6 id=&quot;on-ipv6-2&quot;&gt;On Ipv6&lt;/h6&gt;
&lt;div class=&quot;listingblock&quot;&gt;
&lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;highlight&quot;&gt;&lt;code class=&quot;language-cypher&quot; data-lang=&quot;cypher&quot;&gt;LOAD CSV WITH HEADERS FROM &quot;file:///&quot; + $fqdn + &quot;/network_connections.csv&quot; AS row
  WITH
    row,
    toInteger(last(split(row.local, ':'))) AS local_port,
    toInteger(last(split(row.remote, ':'))) AS remote_port,
    toInteger(split(row.pid,'/')[0]) AS pid
  WHERE
    row.state = 'ESTABLISHED' AND
    size(split(row.local, '.')) = 0
  WITH
    row,
    replace(row.local, ':'+ local_port, '') AS local_ip,
    local_port,
    replace(row.remote, ':'+ remote_port, '') AS remote_ip,
    remote_port,
    pid,
    CASE WHEN pid IS NULL THEN [] ELSE [1] END AS ifPid

    MATCH (s:Server {fqdn:$fqdn})

    MATCH (iLocal:Interface {ipv6: local_ip, fqdn:$fqdn })
    MERGE (iLocal)-[:USES_PORT]-&amp;gt;(pLocal:Port { number: local_port})
    SET pLocal.ip = iLocal.ip,
        pLocal.ipv6 = iLocal.ipv6

    MERGE (pRemote:Port { number: remote_port, ip:remote_ip })

    MERGE (con:Connection { fqdn:$fqdn, pid:coalesce(pid,'-'), local:row.local, remote:row.remote})
    MERGE (con)-[:SOURCE]-&amp;gt;(pLocal)
    MERGE (con)-[:TARGET]-&amp;gt;(pRemote)

    FOREACH( x IN ifPid |
      MERGE (process:Process {pid:pid, fqdn:$fqdn})
      MERGE (process)-[:HAS_CONNECTION]-&amp;gt;(con)
    );&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;sect5&quot;&gt;
&lt;h6 id=&quot;graph-example-2&quot;&gt;Graph example&lt;/h6&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;And the result is :&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;listingblock&quot;&gt;
&lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;highlight&quot;&gt;&lt;code class=&quot;language-cypher&quot; data-lang=&quot;cypher&quot;&gt;MATCH path=(n:Server {fqdn:'pythagore.logisima'})-[:HAS_INTERFACE]-&amp;gt;(i:Interface)-[:USES_PORT]-&amp;gt;(:Port)&amp;lt;-[:SOURCE]-(:Connection)-[:TARGET]-&amp;gt;(p:Port)
OPTIONAL MATCH path2=(p)&amp;lt;-[:USES_PORT]-(:Interface)&amp;lt;-[:HAS_INTERFACE]-(:Server)
RETURN path, path2&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;imageblock&quot;&gt;
&lt;div class=&quot;content&quot;&gt;
&lt;img src=&quot;/public/images/computer-as-a-graph/network-connections.svg&quot; alt=&quot;network connections&quot; width=&quot;70%&quot;&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;sect1&quot;&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;
&lt;div class=&quot;sectionbody&quot;&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;It&amp;#8217;s really cool to see all the components of a computer as a graph,
and if we do that on every computer of a network (hey &lt;code&gt;nmap&lt;/code&gt;), we can have a complete vision of it, just with a discovery process.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;In a next article, I will show you what we can do with those data, stay tune !&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;</description>
        <pubDate>Sat, 29 Jun 2019 00:00:00 +0200</pubDate>
        <link>http://www.bsimard.com/2019/06/29/computer-as-a-graph.html</link>
        <guid isPermaLink="true">http://www.bsimard.com/2019/06/29/computer-as-a-graph.html</guid>
        
        
        <category>graph</category>
        
        <category>neo4j</category>
        
        <category> cmdb</category>
        
        <category> linux</category>
        
        <category> discovery</category>
        
        
        
      </item>
      
    
     
    
     
    
     
      <item>
        <title>How to use Kettle to import data in Neo4j</title>
        <description>&lt;div id=&quot;toc&quot; class=&quot;toc&quot;&gt;
&lt;div id=&quot;toctitle&quot;&gt;Table of Contents&lt;/div&gt;
&lt;ul class=&quot;sectlevel1&quot;&gt;
&lt;li&gt;&lt;a href=&quot;#kettle&quot;&gt;Kettle&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#neo4j-connector&quot;&gt;Neo4j connector&lt;/a&gt;
&lt;ul class=&quot;sectlevel2&quot;&gt;
&lt;li&gt;&lt;a href=&quot;#how-to-install-it&quot;&gt;How to install it&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#components&quot;&gt;Components&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#neo4j-cypher&quot;&gt;Neo4j Cypher&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#neo4j-output&quot;&gt;Neo4j Output&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#conclusion&quot;&gt;Conclusion&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div class=&quot;sect1&quot;&gt;
&lt;h2 id=&quot;kettle&quot;&gt;Kettle&lt;/h2&gt;
&lt;div class=&quot;sectionbody&quot;&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;Kettle (or Pentaho Data Integration) is a well known ETL tool.
It is mature tool, simple to learn due to its GUI and open-source.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;Moreover, Kettle has a lot of plugins for all various data editors, so can really do every thing you want with your data.
In this post, I will show you how to use the Neo4j components, but firstly, you need to have ketlle on your computer.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;To install kettle, it&amp;#8217;s pretty easy, you only need a JDK installed on your laptop (&amp;gt;= version 8), and then follow those steps :&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;ulist&quot;&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Download the last binaries at this location : &lt;a href=&quot;https://sourceforge.net/projects/pentaho/files/Data%20Integration/7.1/&quot; class=&quot;bare&quot;&gt;https://sourceforge.net/projects/pentaho/files/Data%20Integration/7.1/&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Extract it&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Run &lt;code&gt;spoon.sh&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div class=&quot;admonitionblock note&quot;&gt;
&lt;table&gt;
&lt;tr&gt;
&lt;td class=&quot;icon&quot;&gt;
&lt;div class=&quot;title&quot;&gt;Note&lt;/div&gt;
&lt;/td&gt;
&lt;td class=&quot;content&quot;&gt;
Spoon is the application name of the graphic interface on which you will design your process
&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;sect1&quot;&gt;
&lt;h2 id=&quot;neo4j-connector&quot;&gt;Neo4j connector&lt;/h2&gt;
&lt;div class=&quot;sectionbody&quot;&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;There are some Neo4j connectors on the Kettle marketplace, but I strongly recommend you to use this one : &lt;a href=&quot;https://github.com/knowbi/knowbi-pentaho-pdi-neo4j-output&quot; class=&quot;bare&quot;&gt;https://github.com/knowbi/knowbi-pentaho-pdi-neo4j-output&lt;/a&gt;
It is efficient, updated and maintained.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;It is based on the official Neo4j Java Driver and have all the options you want to import your data in Neo4j, even on a cluster environment.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;For now, this plugin is not on the marketplace but it will be.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;sect2&quot;&gt;
&lt;h3 id=&quot;how-to-install-it&quot;&gt;How to install it&lt;/h3&gt;
&lt;div class=&quot;ulist&quot;&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Go to this page &lt;a href=&quot;https://github.com/knowbi/knowbi-pentaho-pdi-neo4j-output/releases/&quot; class=&quot;bare&quot;&gt;https://github.com/knowbi/knowbi-pentaho-pdi-neo4j-output/releases/&lt;/a&gt;, and get the latest release.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Unzip it into the &lt;code&gt;plugins&lt;/code&gt; folder of your Kettle installation&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Restart spoon&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;sect2&quot;&gt;
&lt;h3 id=&quot;components&quot;&gt;Components&lt;/h3&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;This plugin comes with 2+1 components :&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;ulist&quot;&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Neo4j Cypher :&lt;/strong&gt; a very useful component where you can write a cypher query, and it  can be used as an input or output.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Neo4j Output :&lt;/strong&gt; allow you to dynamically create (or update) nodes or relationships. It will generate the cypher query for you.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Neo4j Graph Output:&lt;/strong&gt; By doing a graph mapping on the input fields, it will create/update the graph for you. But this component is in &lt;strong&gt;WIP&lt;/strong&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;sect2&quot;&gt;
&lt;h3 id=&quot;neo4j-cypher&quot;&gt;Neo4j Cypher&lt;/h3&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;This is (from my POV) the main component of this plugin (I&amp;#8217;m a Cypher expert, so I like to write my own query ^^).&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;imageblock&quot;&gt;
&lt;div class=&quot;content&quot;&gt;
&lt;img src=&quot;/public/images/neo4j-kettle/kettle-neo4j-cypher.png&quot; alt=&quot;kettle neo4j cypher&quot;&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;The first thing you need to do is to declare a &lt;strong&gt;Neo4j connection&lt;/strong&gt;.
If you have previously declare a Neo4j connection you can reuse it, otherwise you need to create a new one by fulfill this form :&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;imageblock&quot;&gt;
&lt;div class=&quot;content&quot;&gt;
&lt;img src=&quot;/public/images/neo4j-kettle/kettle-neo4j-connection.png&quot; alt=&quot;kettle neo4j connection&quot;&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;As you can see, the connection is using the &lt;strong&gt;Bolt&lt;/strong&gt; protocol, and you can also configure a cluster connection with a routing policy.
So it&amp;#8217;s really complete.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;The &lt;code&gt;batch size&lt;/code&gt; parameter allow you define the size of your transaction.
In my example, Every 500 rows the component will do a commit.
This is really important to control the transaction size during an import process, to have the best performances.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;Your Cypher query can be totally dynamic, ie, can be defined from an input field. To do it,  you just have to enable the &lt;code&gt;Get Cypher from input field?&lt;/code&gt; and then specify the &lt;code&gt;Cypher input field&lt;/code&gt;.
Otherwise, you have to write your query in the &lt;code&gt;Cypher&lt;/code&gt; field.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;Another good point is that the component allows you to use query parameters.
In my example, &lt;code&gt;$name&lt;/code&gt; and &lt;code&gt;$year&lt;/code&gt; come from the input fields.
As you can see, I have defined them into the &lt;code&gt;Parameters&lt;/code&gt; section, just below.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;And last point, If you want to use this component as an &lt;strong&gt;Input&lt;/strong&gt; one, you have to define the &lt;code&gt;Returns&lt;/code&gt; section with the name and type.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;For me, you have the hand on everything to create an efficient import process.
The only lacks are in the UI (because I&amp;#8217;m a lazy person ^^), where I would like to have :&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;ulist&quot;&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;a &lt;strong&gt;Get Fields&lt;/strong&gt; button on the &lt;code&gt;Parameters&lt;/code&gt; section, to fulfill it with all the input fields&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;a &lt;strong&gt;compute return values&lt;/strong&gt; button to automatically fulfill the &lt;code&gt;Return&lt;/code&gt; section.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;The &lt;a href=&quot;https://github.com/knowbi/knowbi-pentaho-pdi-neo4j-output/issues/22&quot;&gt;github issue&lt;/a&gt;  is already created, and &lt;a href=&quot;https://github.com/mattcasters&quot;&gt;Matt Caster&lt;/a&gt; (the father of Kettle and one of the dev. of this plugin) is on it.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;sect2&quot;&gt;
&lt;h3 id=&quot;neo4j-output&quot;&gt;Neo4j Output&lt;/h3&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;This component allow you to create nodes and relationships without writing a cypher query, it will do it for you.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;imageblock&quot;&gt;
&lt;div class=&quot;content&quot;&gt;
&lt;img src=&quot;/public/images/neo4j-kettle/kettle-neo4j-output-from.png&quot; alt=&quot;kettle neo4j output from&quot;&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;You can use it if you have Input fields define :&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;ulist&quot;&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;A node : you have to use only the &lt;code&gt;From Node&lt;/code&gt; tab&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Two nodes plus the relationship between them : you have to define the &lt;code&gt;From node&lt;/code&gt;, &lt;code&gt;To node&lt;/code&gt; and &lt;code&gt;Relationship&lt;/code&gt; tabulation.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;Same as before, you can define the &lt;code&gt;Batch size&lt;/code&gt;, and moreover you can also tell the component to create the Neo4j &lt;strong&gt;UNIQUE CONSTRAINTS&lt;/strong&gt; for you if you enable the &lt;code&gt;Create indexes?&lt;/code&gt; and specify the &lt;code&gt;Primary&lt;/code&gt; fields.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;This component has been designed to have the best performances for Neo4j.
For example if you create nodes with a batch size, the component will collect your input fields in an array and generate a query like that :&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;listingblock&quot;&gt;
&lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;highlight&quot;&gt;&lt;code class=&quot;language-cypher&quot; data-lang=&quot;cypher&quot;&gt;WITH $data
UNWIND $data as $object
  CREATE ....&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;So you will not have many queries, but only one that match your batch size.
This is really cool for performances.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;sect3&quot;&gt;
&lt;h4 id=&quot;example-for-nodes-and-relationship&quot;&gt;Example for nodes and relationship&lt;/h4&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;Imagine that you have the following input fields : &lt;code&gt;label, id, name, parent_id, type&lt;/code&gt;
What you want to do is to create :&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;ulist&quot;&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;a node with the label &lt;code&gt;label&lt;/code&gt; and with the properties &lt;code&gt;id&lt;/code&gt; and &lt;code&gt;name&lt;/code&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;a relationship between the current node and its &lt;code&gt;parent_id&lt;/code&gt; with the type &lt;code&gt;type&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;Then you need to fulfill the component by following those screenshots&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;imageblock&quot;&gt;
&lt;div class=&quot;content&quot;&gt;
&lt;img src=&quot;/public/images/neo4j-kettle/kettle-neo4j-output-from.png&quot; alt=&quot;From node tabulation&quot;&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;imageblock&quot;&gt;
&lt;div class=&quot;content&quot;&gt;
&lt;img src=&quot;/public/images/neo4j-kettle/kettle-neo4j-output-to.png&quot; alt=&quot;To node tabulation&quot;&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;imageblock&quot;&gt;
&lt;div class=&quot;content&quot;&gt;
&lt;img src=&quot;/public/images/neo4j-kettle/kettle-neo4j-output-rel.png&quot; alt=&quot;relationship tabulation&quot;&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;admonitionblock note&quot;&gt;
&lt;table&gt;
&lt;tr&gt;
&lt;td class=&quot;icon&quot;&gt;
&lt;div class=&quot;title&quot;&gt;Note&lt;/div&gt;
&lt;/td&gt;
&lt;td class=&quot;content&quot;&gt;
don&amp;#8217;t use the &lt;code&gt;CREATE&lt;/code&gt; mode for such a process, otherwise the &lt;code&gt;To&lt;/code&gt; node will be created each time.
&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;sect3&quot;&gt;
&lt;h4 id=&quot;tips&quot;&gt;Tips&lt;/h4&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;This component needs to have an input field for the label of nodes and for the relationship type.
If you don&amp;#8217;t have one because those value are static, you can use the &lt;code&gt;Add constants&lt;/code&gt; component that allows you to a constant field to the fields :&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;imageblock&quot;&gt;
&lt;div class=&quot;content&quot;&gt;
&lt;img src=&quot;/public/images/neo4j-kettle/kettle-constant.png&quot; alt=&quot;kettle constant&quot;&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;If you are using the &lt;code&gt;MERGE&lt;/code&gt; mode, sometimes it is useful to define a default value for the &lt;em&gt;property&lt;/em&gt; on which do the merge.
To do it, you can use the &lt;code&gt;Value Mapper&lt;/code&gt; component like this :&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;imageblock&quot;&gt;
&lt;div class=&quot;content&quot;&gt;
&lt;img src=&quot;/public/images/neo4j-kettle/kettle-map.png&quot; alt=&quot;kettle map&quot;&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;in this example, if the &lt;code&gt;parent_id&lt;/code&gt; is not set, I replace it with the value &lt;code&gt;0&lt;/code&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;sect1&quot;&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;
&lt;div class=&quot;sectionbody&quot;&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;This post is just an overview of Kettle, but as you can see its integration with Neo4j is really easy.
I recommend you to test it, and if you have questions, requests or issues, don&amp;#8217;t hesitate to create an issue on the github repository.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;Have fun, and boil your graph imports !&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;</description>
        <pubDate>Mon, 03 Sep 2018 00:00:00 +0200</pubDate>
        <link>http://www.bsimard.com/2018/09/03/kettle-neo4j.html</link>
        <guid isPermaLink="true">http://www.bsimard.com/2018/09/03/kettle-neo4j.html</guid>
        
        
        <category>Kettle</category>
        
        <category> neo4j</category>
        
        <category> ETL</category>
        
        
        
      </item>
      
    
     
    
     
      <item>
        <title>Monitoring Neo4j with influxdb TICK stack</title>
        <description>&lt;div id=&quot;toc&quot; class=&quot;toc&quot;&gt;
&lt;div id=&quot;toctitle&quot;&gt;Table of Contents&lt;/div&gt;
&lt;ul class=&quot;sectlevel1&quot;&gt;
&lt;li&gt;&lt;a href=&quot;#introduction&quot;&gt;Introduction&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#influxdb&quot;&gt;Influxdb&lt;/a&gt;
&lt;ul class=&quot;sectlevel2&quot;&gt;
&lt;li&gt;&lt;a href=&quot;#concept&quot;&gt;Concept&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#the-tick-stack&quot;&gt;The TICK stack&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#the-architecture&quot;&gt;The architecture&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#the-monitored-server&quot;&gt;The monitored server&lt;/a&gt;
&lt;ul class=&quot;sectlevel2&quot;&gt;
&lt;li&gt;&lt;a href=&quot;#neo4j&quot;&gt;Neo4j&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#telegraf&quot;&gt;Telegraf&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#the-monitoring-server&quot;&gt;The monitoring server&lt;/a&gt;
&lt;ul class=&quot;sectlevel2&quot;&gt;
&lt;li&gt;&lt;a href=&quot;#installation-2&quot;&gt;Installation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#influxdb-2&quot;&gt;InfluxDb&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#telegraf-2&quot;&gt;Telegraf&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#chronograf&quot;&gt;Chronograf&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#kapacitor&quot;&gt;Kapacitor&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#conclusion&quot;&gt;Conclusion&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div class=&quot;sect1&quot;&gt;
&lt;h2 id=&quot;introduction&quot;&gt;Introduction&lt;/h2&gt;
&lt;div class=&quot;sectionbody&quot;&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;When you push a service in production, it&amp;#8217;s really important to monitor its health status.
This will allow you to see if everything is OK,
to be alerted if something is going wrong but also in a case of a problem, to investigate.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;This rule should be applied for &lt;a href=&quot;http://neo4j.com/&quot;&gt;Neo4j&lt;/a&gt;, and you will see how to do it with the &lt;strong&gt;TICK&lt;/strong&gt; stack (from &lt;a href=&quot;http://influxdata.com/&quot;&gt;influxDB&lt;/a&gt;).&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;But before to explain how to do it for &lt;a href=&quot;http://neo4j.com/&quot;&gt;Neo4j&lt;/a&gt;, lets talk a little about Influxdb.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;sect1&quot;&gt;
&lt;h2 id=&quot;influxdb&quot;&gt;Influxdb&lt;/h2&gt;
&lt;div class=&quot;sectionbody&quot;&gt;
&lt;div class=&quot;sect2&quot;&gt;
&lt;h3 id=&quot;concept&quot;&gt;Concept&lt;/h3&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;&lt;a href=&quot;https://www.influxdata.com&quot;&gt;Influx DB&lt;/a&gt; is a &lt;a href=&quot;https://en.wikipedia.org/wiki/Time_series_database&quot;&gt;time series database&lt;/a&gt;.
It&amp;#8217;s made to store and query data in times, so it&amp;#8217;s the perfect tool to store metrics of a system.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;Influx has &lt;strong&gt;7 key concepts&lt;/strong&gt; : &lt;code&gt;time&lt;/code&gt;, &lt;code&gt;field&lt;/code&gt;, &lt;code&gt;tag&lt;/code&gt;, &lt;code&gt;measurement&lt;/code&gt;, &lt;code&gt;series&lt;/code&gt;, &lt;code&gt;retention policy&lt;/code&gt; and &lt;code&gt;continous queries&lt;/code&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;It&amp;#8217;s better to take an example to understand those concepts.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;Imagine that you have a captor of temperature and humidity in your living room and an other one in your bedroom.
Each time you will read one, you will receive the data at a point of time.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;Because it&amp;#8217;s the same kind of data, you will store them at the same location.
But you also want to be able to distinguish the data from the living room and the bedroom.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;At the end, you will have something like that :&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;listingblock&quot;&gt;
&lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;name: captor_temperature_humidity
----------------------------------
time                    temperature   humidity   room
2015-08-18T00:00:00Z    18.3          51.2       Living Room
2015-08-18T00:00:00Z    16.7          48.9       Bedroom
2015-08-18T00:01:00Z    18.5          51.1       Living Room
2015-08-18T00:01:02Z    16.9          49.0       Bedroom
...&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;Here we have a &lt;strong&gt;measurement name&lt;/strong&gt; called &lt;code&gt;captor_temperature_humidity&lt;/code&gt; (you can think about it like a table in SQL) and each line is &lt;strong&gt;measurement&lt;/strong&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;&lt;strong&gt;Measurement&lt;/strong&gt; is composed of : &lt;code&gt;time&lt;/code&gt; , &lt;code&gt;temperature&lt;/code&gt;, &lt;code&gt;humidity&lt;/code&gt; and &lt;code&gt;room&lt;/code&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;&lt;strong&gt;Temperature&lt;/strong&gt; and &lt;strong&gt;humidity&lt;/strong&gt; are measurement&amp;#8217;s &lt;strong&gt;fields&lt;/strong&gt;, and their values are the data you want to follow in time.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;On each measurement, I have added the &lt;strong&gt;room&lt;/strong&gt; to know where comes the measurement. It&amp;#8217;s a measurement&amp;#8217;s &lt;strong&gt;tag&lt;/strong&gt; (a metadata), and you can have multiple tags for a measurement.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;By the time, you will have a very huge number of measurement, and usually you don&amp;#8217;t want to save them forover.
It&amp;#8217;s where the concept of &lt;strong&gt;retention policy&lt;/strong&gt; takes place,
you can configure the database  to delete measurements older than X (X is hours, days, weeks, &amp;#8230;&amp;#8203;, years).&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;listingblock&quot;&gt;
&lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;highlight&quot;&gt;&lt;code class=&quot;language-sql&quot; data-lang=&quot;sql&quot;&gt;CREATE RETENTION POLICY one_week ON my_database DURATION 1w;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;admonitionblock note&quot;&gt;
&lt;table&gt;
&lt;tr&gt;
&lt;td class=&quot;icon&quot;&gt;
&lt;div class=&quot;title&quot;&gt;Note&lt;/div&gt;
&lt;/td&gt;
&lt;td class=&quot;content&quot;&gt;
The default &lt;strong&gt;retention policy&lt;/strong&gt; is to keep the data forever.
&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;Moreover, you can define some &lt;strong&gt;continious queries&lt;/strong&gt; to aggregate measurements over time. it will create a new (aggregated) measurement on the same database but with a new retention policy :&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;listingblock&quot;&gt;
&lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;highlight&quot;&gt;&lt;code class=&quot;language-sql&quot; data-lang=&quot;sql&quot;&gt;CREATE CONTINUOUS QUERY &quot;my_continious_query&quot; ON &quot;my_database&quot;
BEGIN
  SELECT mean(&quot;temperature&quot;) INTO &quot;my_database&quot;.&quot;one_week&quot;.&quot;average_temperature&quot; FROM &quot;captor_temperature_humidity&quot; GROUP BY time(1h)
END&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;So with the help of &lt;strong&gt;retention policies&lt;/strong&gt; and &lt;strong&gt;continious queries&lt;/strong&gt;, you can downsampling the data with somehting similar as that :&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;ulist&quot;&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;keep the fine data for 1 week&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;aggregate per minute for data between one week and one month&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;aggregate per 10 minutes for data older than a month&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;This give those retention policies :&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;listingblock&quot;&gt;
&lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;highlight&quot;&gt;&lt;code class=&quot;language-sql&quot; data-lang=&quot;sql&quot;&gt;CREATE RETENTION POLICY &quot;1_week&quot; ON &quot;my_database&quot; DURATION 7d REPLICATION 1 DEFAULT
CREATE RETENTION POLICY &quot;1_month&quot; ON &quot;my_database&quot; DURATION 30d REPLICATION 1
CREATE RETENTION POLICY &quot;forever&quot; ON &quot;my_database&quot; DURATION INF REPLICATION 1&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;And those continuous query :&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;listingblock&quot;&gt;
&lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;highlight&quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;CREATE CONTINUOUS QUERY &quot;cq_1min_1month&quot; ON &quot;my_database&quot;
BEGIN
  SELECT mean(*) INTO &quot;my_database&quot;.&quot;1_month&quot;.:MEASUREMENT FROM /.*/ GROUP BY time(60s),*
END;
CREATE CONTINUOUS QUERY &quot;cq_1min_1month&quot; ON &quot;my_database&quot;
BEGIN
  SELECT mean(*) INTO &quot;my_database&quot;.&quot;forever&quot;.:MEASUREMENT FROM /.*/ GROUP BY time(600s),*
END;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;sect2&quot;&gt;
&lt;h3 id=&quot;the-tick-stack&quot;&gt;The TICK stack&lt;/h3&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;&lt;strong&gt;TICK stack&lt;/strong&gt; is composed of :&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;ulist&quot;&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Telegraf :&lt;/strong&gt; an agent that collects metrics, and write them in an influx database.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Influx :&lt;/strong&gt; the time series database&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Chronograf :&lt;/strong&gt; a front-end applicatino that allow you to explore your data and to create dashboards.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Kapacitor :&lt;/strong&gt; an alerting system&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;It&amp;#8217;s a complete stack to collect, manage and exploit your time series data, so it&amp;#8217;s perfect for my goal.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;sect1&quot;&gt;
&lt;h2 id=&quot;the-architecture&quot;&gt;The architecture&lt;/h2&gt;
&lt;div class=&quot;sectionbody&quot;&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;Generally there is a server where all the metrics are a centralized, and where all the monitoring application are installed.
I will call it the &lt;strong&gt;Monitoring server&lt;/strong&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;This server will save all the metrics fron your system, and to do it there is two methods :&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;ulist&quot;&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;push&lt;/strong&gt; : metrics are directly pushed to the monitoring server by using an agent on each server that you want to monitor.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;pull&lt;/strong&gt; : the monitoring system will query all your servers to collect the metrics.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;Generally, the first solution is prefered, and it&amp;#8217;s the one that I will put in place.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;This is the architecture schema :&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;imageblock&quot;&gt;
&lt;div class=&quot;content&quot;&gt;
&lt;img src=&quot;/public/images/diag/diag-f2e5f75f5280dc00917b791b23c08a79.png&quot; alt=&quot;diag f2e5f75f5280dc00917b791b23c08a79&quot; width=&quot;440&quot; height=&quot;216&quot;&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;In red you have the monitoring server, and in grey the monitored server.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;admonitionblock note&quot;&gt;
&lt;table&gt;
&lt;tr&gt;
&lt;td class=&quot;icon&quot;&gt;
&lt;div class=&quot;title&quot;&gt;Note&lt;/div&gt;
&lt;/td&gt;
&lt;td class=&quot;content&quot;&gt;
With this kind of architecture, Neo4j send metrics locally, so it&amp;#8217;s very fast.
&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;sect1&quot;&gt;
&lt;h2 id=&quot;the-monitored-server&quot;&gt;The monitored server&lt;/h2&gt;
&lt;div class=&quot;sectionbody&quot;&gt;
&lt;div class=&quot;sect2&quot;&gt;
&lt;h3 id=&quot;neo4j&quot;&gt;Neo4j&lt;/h3&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;In its &lt;strong&gt;Enterprise Edition&lt;/strong&gt;, Neo4j has a &lt;a href=&quot;https://neo4j.com/docs/operations-manual/current/monitoring/metrics/&quot;&gt;monitoring system&lt;/a&gt;.
In fact there is four ways to monitor it :&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;ulist&quot;&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;JMX :&lt;/strong&gt; it&amp;#8217;s a standard java functionnality that allow you to retrive some metrics values.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Graphite connector :&lt;/strong&gt; you just have to configure your Graphana server, and Neo4j will send its metrics regulary.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Prometheus connector :&lt;/strong&gt; same as for Graphite but for Prometheus time series database.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;CSV file :&lt;/strong&gt;  Neo4j dumps all its metrics at a regular time interval&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;&lt;strong&gt;Telegraf&lt;/strong&gt; is compatible with the Graphite protocol, so I will use it.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;The configuration is really simple, just edit your &lt;code&gt;neo4j.conf&lt;/code&gt; file and put at the end those lines :&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;listingblock&quot;&gt;
&lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;highlight&quot;&gt;&lt;code class=&quot;language-properties&quot; data-lang=&quot;properties&quot;&gt;# Setting for enabling all supported metrics.
metrics.enabled=true
# Setting for enabling all Neo4j specific metrics.
metrics.neo4j.enabled=true
# Setting for exposing metrics about transactions; number of transactions started, committed, etc.
metrics.neo4j.tx.enabled=true
# Setting for exposing metrics about the Neo4j page cache; page faults, evictions, flushes and exceptions, etc.
metrics.neo4j.pagecache.enabled=true
# Setting for exposing metrics about approximately entities are in the database; nodes, relationships, properties, etc.
metrics.neo4j.counts.enabled=true
# Setting for exposing metrics about the network usage of the HA cluster component.
metrics.neo4j.network.enabled=true
# Enable the Graphite integration. Default is 'false'.
metrics.graphite.enabled=true
# The IP and port of the Graphite server on the format &amp;lt;hostname or IP address&amp;gt;:&amp;lt;port number&amp;gt;.
# The default port number for Graphite is 2003.
metrics.graphite.server=localhost:2003
# How often to send data. Default is 3 seconds.
metrics.graphite.interval=3s
# Prefix for Neo4j metrics on Graphite server.
metrics.prefix=MyHost&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;Like you see, I just :&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;ulist&quot;&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;enable the metrics feature and also each familly metric.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;enable the graphite integration, and configure its location, time interval and the prefix.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;You don&amp;#8217;t have to change anything, except the &lt;code&gt;metrics.prefix&lt;/code&gt;. This value will be used as the host identifier in metrics.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;sect2&quot;&gt;
&lt;h3 id=&quot;telegraf&quot;&gt;Telegraf&lt;/h3&gt;
&lt;div class=&quot;sect3&quot;&gt;
&lt;h4 id=&quot;installation&quot;&gt;Installation&lt;/h4&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;There are many ways to install Telegraf on your system, and you can check directly on &lt;a href=&quot;https://docs.influxdata.com/telegraf/v1.6/introduction/installation/#installation&quot;&gt;the documentation&lt;/a&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;My prefer OS is &lt;strong&gt;debian&lt;/strong&gt;, so I will show you how to do on it :&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;ulist&quot;&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;add the influxdb repository key&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;add the repository&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;perfom an update&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;install the package &lt;code&gt;telegraf&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div class=&quot;listingblock&quot;&gt;
&lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;highlight&quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;curl -sL https://repos.influxdata.com/influxdb.key | apt-key add -
echo &quot;deb https://repos.influxdata.com/debian jessie stable&quot; | tee -a /etc/apt/sources.list
sudo apt-get update
sudo apt-get install telegraf&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;sect3&quot;&gt;
&lt;h4 id=&quot;configuration&quot;&gt;Configuration&lt;/h4&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;All &lt;strong&gt;Telegraf&lt;/strong&gt;'s configuration is located in the file &lt;code&gt;/etc/telegraf/telegraf.conf&lt;/code&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;Firstly we need tell Telegraf to be able to act as a graphite server :&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;listingblock&quot;&gt;
&lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;highlight&quot;&gt;&lt;code class=&quot;language-property&quot; data-lang=&quot;property&quot;&gt;[[inputs.socket_listener]]
  service_address = &quot;tcp://:2003&quot;
  separator = &quot;.&quot;
  data_format = &quot;graphite&quot;
  templates = [
    &quot;*.neo4j.*.* host.measurement.measurement.field* name=neo4j,vlan=testing&quot;,
    &quot;*.vm.*.* host.measurement.measurement.field* name=neo4j,vlan=testing&quot;
  ]&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;I think it&amp;#8217;s easy to understand, except for the &lt;code&gt;templates&lt;/code&gt; part.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;In fact, in graphite all metrics follow a schema like this one &lt;code&gt;MyHost.neo4j.bolt.messages_done 10&lt;/code&gt;.
So we need to tell &lt;strong&gt;Telegraf&lt;/strong&gt; how to parse it to find the field measurement, the value,  and tags.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;The part &lt;code&gt;*.neo4j.*.*&lt;/code&gt; is a filter. If a line match this pattern, then it will parsed with &lt;code&gt;host.measurement.measurement.field*&lt;/code&gt;.
With the example &lt;code&gt;MyHost.neo4j.bolt.messages_done 10&lt;/code&gt;, we will have :&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;ulist&quot;&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Tags: &lt;code&gt;MyHost&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Measurement: &lt;code&gt;neo4j.bolt&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Field: &lt;code&gt;messages_done&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;value: &lt;code&gt;10&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;Moreover, at the end of each template you can see this &lt;code&gt;name=neo4j,vlan=testing&lt;/code&gt;.
It&amp;#8217;s a list of static tags that will be added to each metric.
This can be really useful if you want to monitor multiple Neo4j server (like a cluster).&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;Ok, now we have the metrics, but we need to push them to our centralized &lt;strong&gt;influx&lt;/strong&gt; database.
For this, you need to configure &lt;strong&gt;Telegraf&lt;/strong&gt; like this :&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;listingblock&quot;&gt;
&lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;highlight&quot;&gt;&lt;code class=&quot;language-property&quot; data-lang=&quot;property&quot;&gt;[[outputs.influxdb]]
  ## The full HTTP or UDP URL for your InfluxDB instance.
  urls = [&quot;http://10.0.0.12:8086&quot;]

  ## The target database for metrics; will be created as needed.
  database = &quot;telegraf&quot;

  ## Name of existing retention policy to write to.  Empty string writes to
  ## the default retention policy.
  retention_policy = &quot;&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;You just have to change the &lt;code&gt;urls&lt;/code&gt; property with yours (in my case &lt;code&gt;&lt;a href=&quot;http://10.0.0.12:8086&quot; class=&quot;bare&quot;&gt;http://10.0.0.12:8086&lt;/a&gt;&lt;/code&gt;), and optionnally the database name (by default it&amp;#8217;s &lt;code&gt;telegraf&lt;/code&gt;) and the rentention policy you want.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;admonitionblock note&quot;&gt;
&lt;table&gt;
&lt;tr&gt;
&lt;td class=&quot;icon&quot;&gt;
&lt;div class=&quot;title&quot;&gt;Note&lt;/div&gt;
&lt;/td&gt;
&lt;td class=&quot;content&quot;&gt;
In the general section of the configuration, you can configure the batch size if you want.
&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;sect1&quot;&gt;
&lt;h2 id=&quot;the-monitoring-server&quot;&gt;The monitoring server&lt;/h2&gt;
&lt;div class=&quot;sectionbody&quot;&gt;
&lt;div class=&quot;sect2&quot;&gt;
&lt;h3 id=&quot;installation-2&quot;&gt;Installation&lt;/h3&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;On this server we will install : InfluxDb, Chronograf, Kapacitor and Telegraf (to monitor the monitoring system ^^)&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;I will follow the same process as explained on the installation of &lt;strong&gt;Telegraf&lt;/strong&gt; : via &lt;strong&gt;apt&lt;/strong&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;listingblock&quot;&gt;
&lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;highlight&quot;&gt;&lt;code class=&quot;language-shell&quot; data-lang=&quot;shell&quot;&gt;curl -sL https://repos.influxdata.com/influxdb.key | apt-key add -
echo &quot;deb https://repos.influxdata.com/debian jessie stable&quot; | tee -a /etc/apt/sources.list
sudo apt-get update
sudo apt-get install telegraf influxdb chronograf kapacitor&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;sect2&quot;&gt;
&lt;h3 id=&quot;influxdb-2&quot;&gt;InfluxDb&lt;/h3&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;I change nothing in the default configuration of &lt;strong&gt;Influxdb&lt;/strong&gt;,
The only thing I will do it&amp;#8217;s to create a database &lt;code&gt;telegraf&lt;/code&gt; with a custom retention policy that keep the data for 3 months.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;For this I will use the &lt;strong&gt;Influxdb&lt;/strong&gt; CLI,  &lt;code&gt;sudo influx&lt;/code&gt;, and typing those commands :&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;listingblock&quot;&gt;
&lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;highlight&quot;&gt;&lt;code class=&quot;language-SQL&quot; data-lang=&quot;SQL&quot;&gt;CREATE DATABASE telegraf
USE telegraf
CREATE RETENTION POLICY &quot;3_month&quot; ON &quot;monitoring&quot; DURATION 90d REPLICATION 1&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;admonitionblock note&quot;&gt;
&lt;table&gt;
&lt;tr&gt;
&lt;td class=&quot;icon&quot;&gt;
&lt;div class=&quot;title&quot;&gt;Note&lt;/div&gt;
&lt;/td&gt;
&lt;td class=&quot;content&quot;&gt;
If you want to check, you can type &lt;code&gt;SHOW RETENTION POLICIES&lt;/code&gt; to display all RPs.
&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;sect2&quot;&gt;
&lt;h3 id=&quot;telegraf-2&quot;&gt;Telegraf&lt;/h3&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;I have installed &lt;strong&gt;Telegraf&lt;/strong&gt; just to monitor the monitoring server (CPU, network, disk, &amp;#8230;&amp;#8203;).
You just have to configure it to send all the data to the &lt;code&gt;telegraf&lt;/code&gt; (the default value).&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;listingblock&quot;&gt;
&lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;highlight&quot;&gt;&lt;code class=&quot;language-property&quot; data-lang=&quot;property&quot;&gt;[[outputs.influxdb]]
  ## The full HTTP or UDP URL for your InfluxDB instance.
  # default is localhost with the standard port of influx
  # urls = [&quot;http://10.0.0.12:8086&quot;]

  ## The target database for metrics; will be created as needed.
  database = &quot;telegraf&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;sect2&quot;&gt;
&lt;h3 id=&quot;chronograf&quot;&gt;Chronograf&lt;/h3&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;By default, Chronograf is listening on the port &lt;code&gt;8888&lt;/code&gt;.
So open your browser at &lt;a href=&quot;http://MONITORING_SERVER_IP:8888/&quot; class=&quot;bare&quot;&gt;http://MONITORING_SERVER_IP:8888/&lt;/a&gt; (change &lt;code&gt;MONITORING_SERVER_IP&lt;/code&gt; with the corresponding IP).&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;You can take a look at the &lt;strong&gt;Host List&lt;/strong&gt;, you should see a list with two items : names of the monitored and monitoring server.
Click on one, and should see something like this :&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;imageblock&quot;&gt;
&lt;div class=&quot;content&quot;&gt;
&lt;img src=&quot;/public/images/monitor-neo4j-influx/chronograf.png&quot; alt=&quot;chronograf&quot;&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;Now you can create a new dashboard for Neo4j with the following widgets :&lt;/p&gt;
&lt;/div&gt;
&lt;table class=&quot;tableblock frame-all grid-all spread&quot;&gt;
&lt;colgroup&gt;
&lt;col style=&quot;width: 16.6666%;&quot;&gt;
&lt;col style=&quot;width: 16.6666%;&quot;&gt;
&lt;col style=&quot;width: 66.6668%;&quot;&gt;
&lt;/colgroup&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th class=&quot;tableblock halign-left valign-top&quot;&gt;Name&lt;/th&gt;
&lt;th class=&quot;tableblock halign-left valign-top&quot;&gt;Type&lt;/th&gt;
&lt;th class=&quot;tableblock halign-left valign-top&quot;&gt;Query&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;Thread Jetty&lt;/p&gt;&lt;/td&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;Line Graph&lt;/p&gt;&lt;/td&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;div&gt;&lt;div class=&quot;listingblock&quot;&gt;
&lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;highlight&quot;&gt;&lt;code class=&quot;language-sql&quot; data-lang=&quot;sql&quot;&gt;SELECT mean(&quot;threads.jetty.all&quot;) AS &quot;mean_threads.jetty.all&quot;,
       mean(&quot;threads.jetty.idle&quot;) AS &quot;mean_threads.jetty.idle&quot;
FROM &quot;telegraf&quot;.&quot;autogen&quot;.&quot;neo4j.server&quot;
WHERE time &amp;gt; :dashboardTime:
GROUP BY time(:interval:) FILL(null)&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;JVM memory&lt;/p&gt;&lt;/td&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;Stacked Graph&lt;/p&gt;&lt;/td&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;div&gt;&lt;div class=&quot;listingblock&quot;&gt;
&lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;highlight&quot;&gt;&lt;code class=&quot;language-sql&quot; data-lang=&quot;sql&quot;&gt;SELECT pool.g1_survivor_space/1000000,
       pool.metaspace/1000000,
       pool.g1_eden_space/1000000,
       pool.g1_old_gen/1000000
FROM &quot;telegraf&quot;.&quot;autogen&quot;.&quot;vm.memory&quot;
WHERE time &amp;gt; :dashboardTime:&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;JVM GC Time&lt;/p&gt;&lt;/td&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;Line Graph&lt;/p&gt;&lt;/td&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;div&gt;&lt;div class=&quot;listingblock&quot;&gt;
&lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;highlight&quot;&gt;&lt;code class=&quot;language-sql&quot; data-lang=&quot;sql&quot;&gt;SELECT DIFFERENCE(&quot;time.g1_young_generation&quot;) AS &quot;mean_time.g1_young_generation&quot;,
       DIFFERENCE(&quot;time.g1_old_generation&quot;) AS &quot;mean_time.g1_old_generation&quot;
FROM &quot;telegraf&quot;.&quot;autogen&quot;.&quot;vm.gc&quot;
WHERE time &amp;gt; :dashboardTime:&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;Transactions&lt;/p&gt;&lt;/td&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;Line Graph&lt;/p&gt;&lt;/td&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;div&gt;&lt;div class=&quot;listingblock&quot;&gt;
&lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;highlight&quot;&gt;&lt;code class=&quot;language-sql&quot; data-lang=&quot;sql&quot;&gt;SELECT DIFFERENCE(last(&quot;started&quot;)) AS &quot;mean_started&quot;
FROM &quot;telegraf&quot;.&quot;autogen&quot;.&quot;neo4j.transaction&quot;
WHERE time &amp;gt; :dashboardTime:
GROUP BY time(:interval:) FILL(linear)&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;Page cache&lt;/p&gt;&lt;/td&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;Line Graph&lt;/p&gt;&lt;/td&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;div&gt;&lt;div class=&quot;listingblock&quot;&gt;
&lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;highlight&quot;&gt;&lt;code class=&quot;language-sql&quot; data-lang=&quot;sql&quot;&gt;SELECT mean(&quot;hits&quot;) AS &quot;mean_hits&quot;,
       mean(&quot;page_faults&quot;) AS &quot;mean_page_faults&quot;,
       mean(&quot;flushes&quot;) AS &quot;mean_flushes&quot;,
       mean(&quot;evictions&quot;) AS &quot;mean_evictions&quot;,
       mean(&quot;eviction_exceptions&quot;) AS &quot;mean_eviction_exceptions&quot;
FROM &quot;telegraf&quot;.&quot;autogen&quot;.&quot;neo4j.page_cache&quot;
WHERE time &amp;gt; :dashboardTime:
GROUP BY time(:interval:) FILL(null)&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;JVM Threads&lt;/p&gt;&lt;/td&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;Line Graph&lt;/p&gt;&lt;/td&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;div&gt;&lt;div class=&quot;listingblock&quot;&gt;
&lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;highlight&quot;&gt;&lt;code class=&quot;language-sql&quot; data-lang=&quot;sql&quot;&gt;SELECT mean(&quot;total&quot;) AS &quot;mean_total&quot;
FROM &quot;telegraf&quot;.&quot;autogen&quot;.&quot;vm.thread&quot;
WHERE time &amp;gt; :dashboardTime:
GROUP BY time(:interval:) FILL(null)&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;Number of Nodes&lt;/p&gt;&lt;/td&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;Line Graph + Single stat&lt;/p&gt;&lt;/td&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;div&gt;&lt;div class=&quot;listingblock&quot;&gt;
&lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;highlight&quot;&gt;&lt;code class=&quot;language-sql&quot; data-lang=&quot;sql&quot;&gt;SELECT max(&quot;node&quot;) AS &quot;max_node&quot;
FROM &quot;telegraf&quot;.&quot;autogen&quot;.&quot;neo4j.ids_in_use&quot;
WHERE time &amp;gt; :dashboardTime:
GROUP BY time(:interval:) FILL(none)&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;Number of relationships&lt;/p&gt;&lt;/td&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;Line Graph + Single stat&lt;/p&gt;&lt;/td&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;div&gt;&lt;div class=&quot;listingblock&quot;&gt;
&lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;highlight&quot;&gt;&lt;code class=&quot;language-sql&quot; data-lang=&quot;sql&quot;&gt;SELECT last(&quot;relationship&quot;) AS &quot;last_relationship&quot;
FROM &quot;telegraf&quot;.&quot;autogen&quot;.&quot;neo4j.ids_in_use&quot;
WHERE time &amp;gt; :dashboardTime:
GROUP BY time(:interval:) FILL(none)&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;Number of Properties&lt;/p&gt;&lt;/td&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;Line Graph + Single stat&lt;/p&gt;&lt;/td&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;div&gt;&lt;div class=&quot;listingblock&quot;&gt;
&lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;highlight&quot;&gt;&lt;code class=&quot;language-sql&quot; data-lang=&quot;sql&quot;&gt;SELECT last(&quot;property&quot;) AS &quot;last_property&quot;
FROM &quot;telegraf&quot;.&quot;autogen&quot;.&quot;neo4j.ids_in_use&quot;
WHERE time &amp;gt; :dashboardTime:
GROUP BY time(:interval:) FILL(none)&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;Number of Relationship Types&lt;/p&gt;&lt;/td&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;Line Graph + Single stat&lt;/p&gt;&lt;/td&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;div&gt;&lt;div class=&quot;listingblock&quot;&gt;
&lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;highlight&quot;&gt;&lt;code class=&quot;language-sql&quot; data-lang=&quot;sql&quot;&gt;SELECT last(&quot;relationship_type&quot;) AS &quot;last_relationship_type&quot;
FROM &quot;telegraf&quot;.&quot;autogen&quot;.&quot;neo4j.ids_in_use&quot;
WHERE time &amp;gt; :dashboardTime:
GROUP BY time(:interval:) FILL(none)&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;Opened Transactions&lt;/p&gt;&lt;/td&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;Line Graph + Single stat&lt;/p&gt;&lt;/td&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;div&gt;&lt;div class=&quot;listingblock&quot;&gt;
&lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;highlight&quot;&gt;&lt;code class=&quot;language-sql&quot; data-lang=&quot;sql&quot;&gt;SELECT started  - committed - rollbacks
FROM &quot;telegraf&quot;.&quot;autogen&quot;.&quot;neo4j.transaction&quot;
WHERE time &amp;gt; :dashboardTime:&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;And the result :&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;imageblock&quot;&gt;
&lt;div class=&quot;content&quot;&gt;
&lt;img src=&quot;/public/images/monitor-neo4j-influx/neo-dashboard.png&quot; alt=&quot;neo dashboard&quot;&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;sect2&quot;&gt;
&lt;h3 id=&quot;kapacitor&quot;&gt;Kapacitor&lt;/h3&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;Kapacitor is the alerting systen of the stack.
You can create some rules for Kapacitor directly in Chronograf :&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;imageblock&quot;&gt;
&lt;div class=&quot;content&quot;&gt;
&lt;img src=&quot;/public/images/monitor-neo4j-influx/kapacitor.png&quot; alt=&quot;kapacitor&quot;&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;This alert sends a message on slack as soon as there less than 20% of free space on my disk :)&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;sect1&quot;&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;
&lt;div class=&quot;sectionbody&quot;&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;You see it&amp;#8217;s really easy to monitor your infrastructure and Neo4j servers with the &lt;strong&gt;TICK&lt;/strong&gt; stack.
But there are some lacks :&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;ulist&quot;&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Telegraf doesn&amp;#8217;t have a JMX plugin&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;It&amp;#8217;s not possible to make a generic continious query that downsample the data with the same field name (you will have an aggregation prefix).
This is really annoying when you want to have only one query to make your dashboard (and not one per retention). See this &lt;a href=&quot;https://github.com/influxdata/influxdb/issues/7332&quot;&gt;link for more details&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;I would like to make the same kind of article but this time with Prometheus and Grafana.
So if you are interested, please leave a comment, it will motivate me to write it :)&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;</description>
        <pubDate>Tue, 22 May 2018 00:00:00 +0200</pubDate>
        <link>http://www.bsimard.com/2018/05/22/monitoring-neo4j-influxdb.html</link>
        <guid isPermaLink="true">http://www.bsimard.com/2018/05/22/monitoring-neo4j-influxdb.html</guid>
        
        
        <category>monitoring</category>
        
        <category> neo4j</category>
        
        <category> influx</category>
        
        <category> tick</category>
        
        
        
      </item>
      
    
     
    
     
      <item>
        <title>How to use Sigmajs to display your graph ?</title>
        <description>&lt;div id=&quot;toc&quot; class=&quot;toc&quot;&gt;
&lt;div id=&quot;toctitle&quot;&gt;Table of Contents&lt;/div&gt;
&lt;ul class=&quot;sectlevel1&quot;&gt;
&lt;li&gt;&lt;a href=&quot;#introduction&quot;&gt;Introduction&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#installation&quot;&gt;Installation&lt;/a&gt;
&lt;ul class=&quot;sectlevel2&quot;&gt;
&lt;li&gt;&lt;a href=&quot;#how-to&quot;&gt;How-to&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#how-to-choose-between-svg-canvas-and-webgl&quot;&gt;How to choose between SVG, Canvas and Webgl ?&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#the-sigmajs-graph-structure&quot;&gt;The SigmaJS graph structure&lt;/a&gt;
&lt;ul class=&quot;sectlevel2&quot;&gt;
&lt;li&gt;&lt;a href=&quot;#definition&quot;&gt;Definition&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#example&quot;&gt;Example&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#loading-graph-in-sigma&quot;&gt;Loading graph in sigma&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#live-example&quot;&gt;Live example&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#graph-layout-algorithm&quot;&gt;Graph layout algorithm&lt;/a&gt;
&lt;ul class=&quot;sectlevel2&quot;&gt;
&lt;li&gt;&lt;a href=&quot;#definition-2&quot;&gt;Definition&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#force-atlas2&quot;&gt;Force-Atlas2&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#live-example-2&quot;&gt;Live example&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#plugins&quot;&gt;Plugins&lt;/a&gt;
&lt;ul class=&quot;sectlevel2&quot;&gt;
&lt;li&gt;&lt;a href=&quot;#edge-labels&quot;&gt;Edge labels&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#edge-paralleledges&quot;&gt;Edge parallelEdges&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#relative-size&quot;&gt;Relative size&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#sigma-and-neo4j&quot;&gt;Sigma and Neo4j&lt;/a&gt;
&lt;ul class=&quot;sectlevel2&quot;&gt;
&lt;li&gt;&lt;a href=&quot;#create-a-graph-structure-from-a-neo4j-query&quot;&gt;Create a graph structure from a Neo4j query&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#neo4j-sigma-neosig&quot;&gt;Neo4j + Sigma = NeoSig&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div class=&quot;sect1&quot;&gt;
&lt;h2 id=&quot;introduction&quot;&gt;Introduction&lt;/h2&gt;
&lt;div class=&quot;sectionbody&quot;&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;In this article, I will show how to display and customize a graph in a browser with the help of &lt;a href=&quot;http://sigmajs.org&quot;&gt;SigmaJS&lt;/a&gt;,
a JS library dedicated to graph drawing delivered under the &lt;a href=&quot;https://opensource.org/licenses/MIT&quot;&gt;MIT licence&lt;/a&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;Initially, SigmaJS has been developed for &lt;a href=&quot;https://gephi.org/&quot;&gt;Gephi&lt;/a&gt; to export a graph on the web.
Today, &lt;a href=&quot;http://sigmajs.org&quot;&gt;sigmajs&lt;/a&gt; is a stable, full features and highly configurable graph library.
Moreover, the small cherry on the cake is its compatibility with touch screens.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;sect1&quot;&gt;
&lt;h2 id=&quot;installation&quot;&gt;Installation&lt;/h2&gt;
&lt;div class=&quot;sectionbody&quot;&gt;
&lt;div class=&quot;sect2&quot;&gt;
&lt;h3 id=&quot;how-to&quot;&gt;How-to&lt;/h3&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;There are four steps to follow :&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;&lt;strong&gt;Step 1 : Install sigmajs binaries&lt;/strong&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;&lt;a href=&quot;http://sigmajs.org&quot;&gt;sigmajs&lt;/a&gt; can be found on :&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;ulist&quot;&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://www.npmjs.com/package/sigma&quot;&gt;NPM&lt;/a&gt; : you just have to type : &lt;code&gt;npm sigma --save-dev&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/jacomyal/sigma.js/releases&quot;&gt;Github&lt;/a&gt; : download the &lt;code&gt;build.zip&lt;/code&gt; from a release like &lt;a href=&quot;https://github.com/jacomyal/sigma.js/releases/download/v1.2.0/build.zip&quot;&gt;this&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;&lt;strong&gt;Step 2 : Import the JavaScript library in your web page&lt;/strong&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;The minimal script you have to load is : &lt;code&gt;&amp;lt;script src=&quot;./build/sigma.min.js&quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;/code&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;Personally, I prefer to import all the source files, it helps to lighten the library by removing the unused source files (ex: I don&amp;#8217;t need SVG &amp;amp; Webgl renderers if I use canvas).&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;&lt;strong&gt;Step 3 :  Create an HTML container&lt;/strong&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;You have to create an HTML element, for example a &lt;code&gt;div&lt;/code&gt; with an &lt;code&gt;id&lt;/code&gt; like this : &lt;code&gt;&amp;lt;div id='sigma-container'&amp;gt;&amp;lt;/div&amp;gt;&lt;/code&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;Moreover, you also need to give it an  &lt;code&gt;height&lt;/code&gt; and &lt;code&gt;width&lt;/code&gt;, otherwise it will have a size of 0 pixel.
This is easily done with some CSS : &lt;code&gt;#sigma-container { width:100%, height:100%}&lt;/code&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;&lt;strong&gt;Step 4: Initialize sigmajs&lt;/strong&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;To initialize Sigmajs, you need to call the function &lt;code&gt;sigma&lt;/code&gt; with those arguments :&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;ulist&quot;&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;A &lt;strong&gt;render&lt;/strong&gt; : it contains the HTML element  that sigmajs will use to display the graph, plus the type of renderer (SVG, canvas or WebGL)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;sigmajs&amp;#8217;s &lt;strong&gt;settings&lt;/strong&gt; : this object will override the default settings, so you can pass an empty object for now. The complete settings list is available &lt;a href=&quot;https://github.com/jacomyal/sigma.js/wiki/Settings&quot;&gt;here&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;At the end, you should have something like that :&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;listingblock&quot;&gt;
&lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;highlight&quot;&gt;&lt;code class=&quot;language-xml&quot; data-lang=&quot;xml&quot;&gt;&amp;lt;html&amp;gt;
  &amp;lt;head&amp;gt;
    &amp;lt;title&amp;gt;SigmaJS example&amp;lt;/title&amp;gt;
    &amp;lt;script src=&quot;./build/sigma.min.js&quot;&amp;gt;&amp;lt;/script&amp;gt;
    &amp;lt;style&amp;gt;
      html { height:100%;}
      body {height: 100%;}
      #sigma-container { width:100%; height:100%; background-color:#E1E1E1}
    &amp;lt;/style&amp;gt;
  &amp;lt;/head&amp;gt;
  &amp;lt;body&amp;gt;
    &amp;lt;div id='sigma-container'&amp;gt;&amp;lt;/div&amp;gt;
    &amp;lt;script&amp;gt;
      // Initialize sigma:
      var s = new sigma(
        {
           renderer: {
             container: document.getElementById('sigma-container'),
             type: 'canvas'
           },
           settings: {}
         }
       );
    &amp;lt;/script&amp;gt;
  &amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;sect2&quot;&gt;
&lt;h3 id=&quot;how-to-choose-between-svg-canvas-and-webgl&quot;&gt;How to choose between SVG, Canvas and Webgl ?&lt;/h3&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;&lt;a href=&quot;http://sigmajs.org&quot;&gt;SigmaJS&lt;/a&gt; can used one this technology to display your graph : &lt;strong&gt;svg&lt;/strong&gt;, &lt;strong&gt;canvas&lt;/strong&gt; &amp;amp; &lt;strong&gt;webgl&lt;/strong&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;&lt;strong&gt;SVG&lt;/strong&gt; is a markup language, directly interpreted by your browser,
so it&amp;#8217;s easy to customize nodes &amp;amp; edges style, you just have to add some CSS rules.
But when you apply a layout algorithm on the graph, you continuously change the DOM of the SVG,
and if this DOM is heavy, the result is slow and can freeze your browser.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;&lt;strong&gt;Canvas&lt;/strong&gt; is an HTML5 element compatible with all modern browsers.
It defines an area on your web page where you can draw some pixels by using a JavaScript API.
The JavaScript code is directly interpreted by your browser and it decides what and when to render.
Due to this, Canvas scales more than the SVG, but if you want to change the color of a shape, you need a way to specify it in the code.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;On the other hand, there is &lt;strong&gt;WebGL&lt;/strong&gt;, compatible with almost all newest browsers (even if the API is not stable).
As in Canvas, you need to write some JavaScript code for rendering your visualisation,
but this time, its computation is delegated to &lt;strong&gt;OpenGL&lt;/strong&gt; so to your &lt;strong&gt;GPU&lt;/strong&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;To resume :&lt;/p&gt;
&lt;/div&gt;
&lt;table class=&quot;tableblock frame-all grid-all spread&quot;&gt;
&lt;colgroup&gt;
&lt;col style=&quot;width: 25%;&quot;&gt;
&lt;col style=&quot;width: 25%;&quot;&gt;
&lt;col style=&quot;width: 25%;&quot;&gt;
&lt;col style=&quot;width: 25%;&quot;&gt;
&lt;/colgroup&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;/th&gt;
&lt;th class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;strong&gt;SVG&lt;/strong&gt;&lt;/th&gt;
&lt;th class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;strong&gt;Canvas&lt;/strong&gt;&lt;/th&gt;
&lt;th class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;strong&gt;WebGL&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;&lt;strong&gt;Browser compatibility&lt;/strong&gt;&lt;/p&gt;&lt;/td&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;ALL (even old ones)&lt;/p&gt;&lt;/td&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;ALL&lt;/p&gt;&lt;/td&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;Modern browsers&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;&lt;strong&gt;Render phases&lt;/strong&gt;&lt;/p&gt;&lt;/td&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;SVG creation &amp;gt; Browser parsing &amp;gt; Browser rendering&lt;/p&gt;&lt;/td&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;JS code &amp;gt; Browser rendering&lt;/p&gt;&lt;/td&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;JS Code &amp;gt; OpenGL rendering&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;&lt;strong&gt;Performance&lt;/strong&gt;&lt;br&gt;
to display a large graph (more than 1000 elements)&lt;/p&gt;&lt;/td&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;Slow&lt;/p&gt;&lt;/td&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;Normal&lt;/p&gt;&lt;/td&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;Performant&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;The choice will depend on your needs, and also of the &lt;a href=&quot;http://sigmajs.org&quot;&gt;SigmaJS&lt;/a&gt; plugins you want to use (plugins are not compatible with every format).&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;I suggest you to choose &lt;strong&gt;Canvas&lt;/strong&gt; or &lt;strong&gt;Webgl&lt;/strong&gt; if you want to really display a big graph.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;sect1&quot;&gt;
&lt;h2 id=&quot;the-sigmajs-graph-structure&quot;&gt;The SigmaJS graph structure&lt;/h2&gt;
&lt;div class=&quot;sectionbody&quot;&gt;
&lt;div class=&quot;sect2&quot;&gt;
&lt;h3 id=&quot;definition&quot;&gt;Definition&lt;/h3&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;Now that sigma is initialized, we need to give it a graph.
It structure is simple : an object with an array of nodes and an array of edges.&lt;/p&gt;
&lt;/div&gt;
&lt;table class=&quot;tableblock frame-all grid-all spread&quot;&gt;
&lt;caption class=&quot;title&quot;&gt;Table 1. Node definition&lt;/caption&gt;
&lt;colgroup&gt;
&lt;col style=&quot;width: 14.2857%;&quot;&gt;
&lt;col style=&quot;width: 14.2857%;&quot;&gt;
&lt;col style=&quot;width: 28.5714%;&quot;&gt;
&lt;col style=&quot;width: 42.8572%;&quot;&gt;
&lt;/colgroup&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th class=&quot;tableblock halign-left valign-top&quot;&gt;Field&lt;/th&gt;
&lt;th class=&quot;tableblock halign-left valign-top&quot;&gt;Type&lt;/th&gt;
&lt;th class=&quot;tableblock halign-left valign-top&quot;&gt;Required&lt;/th&gt;
&lt;th class=&quot;tableblock halign-left valign-top&quot;&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;&lt;code&gt;id&lt;/code&gt;&lt;/p&gt;&lt;/td&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;Any&lt;/p&gt;&lt;/td&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;Yes&lt;/p&gt;&lt;/td&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;Node identifier, must be unique across all the node&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;&lt;code&gt;label&lt;/code&gt;&lt;/p&gt;&lt;/td&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;String&lt;/p&gt;&lt;/td&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;No&lt;/p&gt;&lt;/td&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;&lt;code&gt;x&lt;/code&gt; &amp;amp; &lt;code&gt;y&lt;/code&gt;&lt;/p&gt;&lt;/td&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;Float&lt;/p&gt;&lt;/td&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;Yes&lt;/p&gt;&lt;/td&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;Position of the node in 2D. Can be initialize with &lt;code&gt;Math.random()&lt;/code&gt;.&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;&lt;code&gt;size&lt;/code&gt;&lt;/p&gt;&lt;/td&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;Float&lt;/p&gt;&lt;/td&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;No  &lt;em&gt;(0)&lt;/em&gt;&lt;/p&gt;&lt;/td&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;Size of the node that is use to render it.&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;&lt;code&gt;color&lt;/code&gt;&lt;/p&gt;&lt;/td&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;Color RGB&lt;/p&gt;&lt;/td&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;No &lt;em&gt;(&lt;code&gt;defaultNodeColor&lt;/code&gt;)&lt;/em&gt;&lt;/p&gt;&lt;/td&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;Color used to display the node.&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;table class=&quot;tableblock frame-all grid-all spread&quot;&gt;
&lt;caption class=&quot;title&quot;&gt;Table 2. Edge definition&lt;/caption&gt;
&lt;colgroup&gt;
&lt;col style=&quot;width: 14.2857%;&quot;&gt;
&lt;col style=&quot;width: 14.2857%;&quot;&gt;
&lt;col style=&quot;width: 28.5714%;&quot;&gt;
&lt;col style=&quot;width: 42.8572%;&quot;&gt;
&lt;/colgroup&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th class=&quot;tableblock halign-left valign-top&quot;&gt;Field&lt;/th&gt;
&lt;th class=&quot;tableblock halign-left valign-top&quot;&gt;Type&lt;/th&gt;
&lt;th class=&quot;tableblock halign-left valign-top&quot;&gt;Required&lt;/th&gt;
&lt;th class=&quot;tableblock halign-left valign-top&quot;&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;&lt;code&gt;id&lt;/code&gt;&lt;/p&gt;&lt;/td&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;Any&lt;/p&gt;&lt;/td&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;Yes&lt;/p&gt;&lt;/td&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;Edge identifier, must be unique across all the edge.&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;&lt;code&gt;source&lt;/code&gt;&lt;/p&gt;&lt;/td&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;Any&lt;/p&gt;&lt;/td&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;Yes&lt;/p&gt;&lt;/td&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;Starting node&amp;#8217;s identifier of the edge.&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;&lt;code&gt;target&lt;/code&gt;&lt;/p&gt;&lt;/td&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;Any&lt;/p&gt;&lt;/td&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;Yes&lt;/p&gt;&lt;/td&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;Ending node&amp;#8217;s identifier of the edge.&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;&lt;code&gt;type&lt;/code&gt;&lt;/p&gt;&lt;/td&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;&lt;code&gt;line&lt;/code&gt;,&lt;br&gt;
&lt;code&gt;curve&lt;/code&gt;,&lt;br&gt;
&lt;code&gt;arrow&lt;/code&gt; or&lt;br&gt;
&lt;code&gt;curvedArrow&lt;/code&gt;&lt;/p&gt;&lt;/td&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;No &lt;em&gt;(line)&lt;/em&gt;&lt;/p&gt;&lt;/td&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;Edge type for its render.&lt;br&gt;
The choice will depend on if you want to display an oriented graph. If so, you need an arrow type.
And if you want to display a high number of edges, it&amp;#8217;s faster to use a line type (ie. line or arrow).&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;&lt;code&gt;size&lt;/code&gt;&lt;/p&gt;&lt;/td&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;Float&lt;/p&gt;&lt;/td&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;No &lt;em&gt;(&lt;code&gt;minEdgeSize&lt;/code&gt;)&lt;/em&gt;&lt;/p&gt;&lt;/td&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;Size of the edge that is use to render it.&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;&lt;code&gt;color&lt;/code&gt;&lt;/p&gt;&lt;/td&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;Color RGB&lt;/p&gt;&lt;/td&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;No &lt;em&gt;(&lt;code&gt;defaultEdgeColor&lt;/code&gt;)&lt;/em&gt;&lt;/p&gt;&lt;/td&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;p class=&quot;tableblock&quot;&gt;Color used to display the edge.&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div class=&quot;sect2&quot;&gt;
&lt;h3 id=&quot;example&quot;&gt;Example&lt;/h3&gt;
&lt;div class=&quot;listingblock&quot;&gt;
&lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;highlight&quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;var graph = {
  nodes: [
    { id: &quot;n0&quot;, label: &quot;A node&quot;, x: 0, y: 0, size: 3, color: '#008cc2' },
    { id: &quot;n1&quot;, label: &quot;Another node&quot;, x: 3, y: 1, size: 2, color: '#008cc2' },
    { id: &quot;n2&quot;, label: &quot;And a last one&quot;, x: 1, y: 3, size: 1, color: '#E57821' }
  ],
  edges: [
    { id: &quot;e0&quot;, source: &quot;n0&quot;, target: &quot;n1&quot;, color: '#282c34', type:'line', size:0.5 },
    { id: &quot;e1&quot;, source: &quot;n1&quot;, target: &quot;n2&quot;, color: '#282c34', type:'curve', size:1},
    { id: &quot;e2&quot;, source: &quot;n2&quot;, target: &quot;n0&quot;, color: '#FF0000', type:'line', size:2}
  ]
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;sect2&quot;&gt;
&lt;h3 id=&quot;loading-graph-in-sigma&quot;&gt;Loading graph in sigma&lt;/h3&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;Sigma has a complete API to manage its graph data. I let you see the &lt;a href=&quot;https://github.com/jacomyal/sigma.js/wiki/Graph-API&quot;&gt;API documentation&lt;/a&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;To load a graph in sigma, you just have to call the method &lt;code&gt;read&lt;/code&gt; on the sigma graph instance : &lt;code&gt;s.graph.read(graph)&lt;/code&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;Once it&amp;#8217;s done, we need to tell sigma to draw the graph by calling its &lt;code&gt;refresh&lt;/code&gt; function : &lt;code&gt;s.refresh()&lt;/code&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;sect2&quot;&gt;
&lt;h3 id=&quot;live-example&quot;&gt;Live example&lt;/h3&gt;
&lt;script async src=&quot;https://jsfiddle.net/sim51/gxum1dq1/embed/result,js/&quot;&gt;&lt;/script&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;sect1&quot;&gt;
&lt;h2 id=&quot;graph-layout-algorithm&quot;&gt;Graph layout algorithm&lt;/h2&gt;
&lt;div class=&quot;sectionbody&quot;&gt;
&lt;div class=&quot;sect2&quot;&gt;
&lt;h3 id=&quot;definition-2&quot;&gt;Definition&lt;/h3&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;What is hard in displaying a graph is to rapidly display it in such a way
that we can see all nodes and their edges without overlaps (in fact less as possible).
To do it, we need an algorithm that computes the position of each nodes, and the most known for that are the &lt;strong&gt;force-directed&lt;/strong&gt; algorithms.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;The principle is simple, you need to consider two forces :&lt;/p&gt;
&lt;/div&gt;
&lt;table class=&quot;tableblock frame-all grid-all spread&quot;&gt;
&lt;colgroup&gt;
&lt;col style=&quot;width: 50%;&quot;&gt;
&lt;col style=&quot;width: 50%;&quot;&gt;
&lt;/colgroup&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th class=&quot;tableblock halign-left valign-top&quot;&gt;Repulsive&lt;/th&gt;
&lt;th class=&quot;tableblock halign-left valign-top&quot;&gt;Attractive&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;div&gt;&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;Nodes repulse each others. You can consider nodes like particle with the same electric charge.&lt;/p&gt;
&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;div&gt;&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;Two nodes with an edge, attract themselves. You can consider an edge as a spring&lt;/p&gt;
&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;div&gt;&lt;div class=&quot;imageblock&quot;&gt;
&lt;div class=&quot;content&quot;&gt;
&lt;img src=&quot;/public/images/sigmajs/repulsion.png&quot; alt=&quot;repulsion&quot;&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;
&lt;td class=&quot;tableblock halign-left valign-top&quot;&gt;&lt;div&gt;&lt;div class=&quot;imageblock&quot;&gt;
&lt;div class=&quot;content&quot;&gt;
&lt;img src=&quot;/public/images/sigmajs/attraction.png&quot; alt=&quot;attraction&quot;&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;Then you run an algorithm that compute on each iteration, the sum of the applied forces on each node, and move them in consequence.
After a number of iteration, you will see that graph is in a stable state.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;sect2&quot;&gt;
&lt;h3 id=&quot;force-atlas2&quot;&gt;Force-Atlas2&lt;/h3&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;&lt;a href=&quot;http://sigmajs.org&quot;&gt;SigmaJS&lt;/a&gt; include (as a plugin) a forced-directed algorithm called &lt;strong&gt;Force-Atlas2&lt;/strong&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;To use it, you need to :&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;ulist&quot;&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Step 1 : import the plugin files&lt;/strong&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div class=&quot;listingblock&quot;&gt;
&lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;highlight&quot;&gt;&lt;code class=&quot;language-xml&quot; data-lang=&quot;xml&quot;&gt;&amp;lt;script src=&quot;./build/plugins/sigma.layout.forceAtlas2/supervisor.js&quot;&amp;gt;&amp;lt;/script&amp;gt;
&amp;lt;script src=&quot;./build/plugins/sigma.layout.forceAtlas2/worker.js&quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;ulist&quot;&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Step 2 : Run it&lt;/strong&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;Now that the plugin is loaded, we can directly call it on the sigma instance : &lt;code&gt;s.startForceAtlas2()&lt;/code&gt;;
This creates a &lt;a href=&quot;https://developer.mozilla.org/fr/docs/Utilisation_des_web_workers&quot;&gt;web worker&lt;/a&gt; where all the algorithm iterations will be calculated.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;ulist&quot;&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Step 3 (al) : Stop it&lt;/strong&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;The algorithm won&amp;#8217;t stop by itself, so I recommend you to stop it after a predefined duration (10 seconds in my example) : &lt;code&gt;window.setTimeout(function() {s.killForceAtlas2()}, 10000);&lt;/code&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;sect2&quot;&gt;
&lt;h3 id=&quot;live-example-2&quot;&gt;Live example&lt;/h3&gt;
&lt;script async src=&quot;https://jsfiddle.net/sim51/xck9a7yf/embed/result,js/&quot;&gt;&lt;/script&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;sect1&quot;&gt;
&lt;h2 id=&quot;plugins&quot;&gt;Plugins&lt;/h2&gt;
&lt;div class=&quot;sectionbody&quot;&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;&lt;a href=&quot;http://sigmajs.org&quot;&gt;SigmaJS&lt;/a&gt; has a lot of plugins, you can see the list &lt;a href=&quot;https://github.com/jacomyal/sigma.js/tree/master/plugins&quot;&gt;here&lt;/a&gt;.
I will not show you all of them, so I have done a list of my most used plugins.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;sect2&quot;&gt;
&lt;h3 id=&quot;edge-labels&quot;&gt;Edge labels&lt;/h3&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;This plugin allows you to add a label on each edge. I mainly use it to display the Neo4j&amp;#8217;s relationship type.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;To use it :&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;ulist&quot;&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Import the needed script (in my case &lt;code&gt;settings.js&lt;/code&gt;, &lt;code&gt;sigma.canvas.edges.labels.def.js&lt;/code&gt; &amp;amp; &lt;code&gt;sigma.canvas.edges.labels.curvedArrow.js&lt;/code&gt;)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Add a &lt;code&gt;label&lt;/code&gt; property on yours edges&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;script async src=&quot;https://jsfiddle.net/sim51/uzmxvg3u/embed/result,js/&quot;&gt;&lt;/script&gt;
&lt;/div&gt;
&lt;div class=&quot;sect2&quot;&gt;
&lt;h3 id=&quot;edge-paralleledges&quot;&gt;Edge parallelEdges&lt;/h3&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;If you want parallel edges (ie. to have multiple relationship between two nodes), it&amp;#8217;s the plugin you must have.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;To use it :&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;ulist&quot;&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Import the needed script :&lt;/p&gt;
&lt;div class=&quot;ulist&quot;&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;utils.js&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;sigma.canvas.edges.curvedArrow.js&lt;/code&gt;  &amp;amp; &lt;code&gt;sigma.canvas.edgehovers.curvedArrow.js&lt;/code&gt; if you have a directed graph.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;sigma.canvas.edgehovers.curve.js&lt;/code&gt; &amp;amp; &lt;code&gt;sigma.canvas.edges.curve.js&lt;/code&gt;  if you want the an undirected graph.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;sigma.canvas.edges.labels.curve.js&lt;/code&gt;  if you have enabled label on edges&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Add a property &lt;code&gt;count&lt;/code&gt; that represent the index of the edge in the set of parallel edges. Inversely proportional to the amplitude of the vertex of the edge curve.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;script async src=&quot;https://jsfiddle.net/sim51/5vL3e72e/embed/result,js/&quot;&gt;&lt;/script&gt;
&lt;/div&gt;
&lt;div class=&quot;sect2&quot;&gt;
&lt;h3 id=&quot;relative-size&quot;&gt;Relative size&lt;/h3&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;This plugin is really useful when you want to see which node is most connected.
The size of the node depends of its degree, ie. its number of in-going &amp;amp; outgoing edges.&lt;/p&gt;
&lt;/div&gt;
&lt;script async src=&quot;https://jsfiddle.net/sim51/y7kwmc3g/embed/result,js/&quot;&gt;&lt;/script&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;sect1&quot;&gt;
&lt;h2 id=&quot;sigma-and-neo4j&quot;&gt;Sigma and Neo4j&lt;/h2&gt;
&lt;div class=&quot;sectionbody&quot;&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;Now that you know how to use Sigma, the next step is to build a graph visualisation from Neo4j.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;To do this there is two points :&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;ulist&quot;&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;How to query Neo4j in your browser ?&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;From a query result, how to build a sigma graph structure&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;I will not explain the first point, Michael has already done this part in this &lt;a href=&quot;https://medium.com/neo4j/hands-on-graph-data-visualization-bd1f055a492d&quot;&gt;excellent post&lt;/a&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;So let see the second one !&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;sect2&quot;&gt;
&lt;h3 id=&quot;create-a-graph-structure-from-a-neo4j-query&quot;&gt;Create a graph structure from a Neo4j query&lt;/h3&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;Result of a query is a collection of tuple, ie. composed of &lt;strong&gt;rows&lt;/strong&gt; where each row has some &lt;strong&gt;columns&lt;/strong&gt;.
Moreover, each &lt;em&gt;cell&lt;/em&gt; is typed, and to display a graph we only want &lt;code&gt;node&lt;/code&gt;, &lt;code&gt;relationship&lt;/code&gt; and &lt;code&gt;path&lt;/code&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;To create our data structure, we need to iterate over rows, then over columns and finnaly check the type.
If it&amp;#8217;s a node or relationship, we can add it to our sigma graph structure (if it&amp;#8217;s not already present).&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;And what about &lt;code&gt;path&lt;/code&gt;  ? A &lt;code&gt;path&lt;/code&gt; in Neo4j driver types, is an array of &lt;code&gt;segment&lt;/code&gt; where each segmet is composed of :&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;ulist&quot;&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;a starting node&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;a relationship&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;an ending node&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;So if we have a path, we need also to iterate over it to add starting &amp;amp; ending node, plus the relationship.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;But wait, a Neo4j node (resp. relationship) is not a Sigma node (resp. relationship), so we also need to convert them.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;If you code it, finally you should have something like this :&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;listingblock&quot;&gt;
&lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;highlight&quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;let graph = { nodes:[], edges:[]} ;
this.driver.session().run(&quot;MATCH (n)-[r]-&amp;gt;(m) RETURN n,r,m LIMIT $limit&quot;, {limit:50}).then(
  (result) =&amp;gt; {
    // for each rows
    result.records.forEach( record =&amp;gt; {
      // for each column
      record.forEach( ( value, key ) =&amp;gt; {
        // if it's a node
        if ( value &amp;amp;&amp;amp; value.hasOwnProperty( 'labels' ) ) {
          graph.nodes.push(convertionToSigmaNode(value));
        }
        // if it's an edge
        if ( value &amp;amp;&amp;amp; value.hasOwnProperty( 'type' ) ) {
          graph.edges.push(convertionToSigmaEdge(value));
        }
        // if it's a path
        if ( value &amp;amp;&amp;amp; value.hasOwnProperty( 'segments' ) ) {
          value.segments.forEach( ( seg ) =&amp;gt; {
            // add starting &amp;amp; ending nodes + relationship
            graph.nodes.push(convertionToSigmaNode(seg.start));
            graph.nodes.push(convertionToSigmaNode(seg.end));
            graph.edges.push(convertionToSigmaEdge(seg.rel));
          });
        }
      });
    })
  })&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;And here we go, you have everything to display your graph from a Cypher query with SigmaJS !&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;It&amp;#8217;s a little borring, no ? All this code just to display a graph&amp;#8230;&amp;#8203;
And what if I tell you that I have already made the work for you  ?&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;sect2&quot;&gt;
&lt;h3 id=&quot;neo4j-sigma-neosig&quot;&gt;Neo4j + Sigma = NeoSig&lt;/h3&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;To avoid you the complexity of doing all the above work, I have created a library for that : &lt;strong&gt;NeoSig&lt;/strong&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;To use it, you need to import the library + the Neo4j driver :&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;listingblock&quot;&gt;
&lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;highlight&quot;&gt;&lt;code class=&quot;language-xml&quot; data-lang=&quot;xml&quot;&gt;&amp;lt;script src=&quot;https://cdn.jsdelivr.net/npm/neo4j-driver@1.6.0&quot;&amp;gt;&amp;lt;/script&amp;gt;
&amp;lt;script src=&quot;https://cdn.jsdelivr.net/npm/neosig@1.2.2/docs/neosig-1.2.2.js&quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;admonitionblock note&quot;&gt;
&lt;table&gt;
&lt;tr&gt;
&lt;td class=&quot;icon&quot;&gt;
&lt;div class=&quot;title&quot;&gt;Note&lt;/div&gt;
&lt;/td&gt;
&lt;td class=&quot;content&quot;&gt;
The library embed SigmaJS (with some customs code) but not the Neo4j driver.
&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;Then you can call the function &lt;code&gt;Neo4jGraph(neo4jConfig, neo4jStyle, query, queryParams)&lt;/code&gt;, it returns a &lt;em&gt;promise&lt;/em&gt; with the sigma graph object.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;listingblock&quot;&gt;
&lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;highlight&quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;Neo4jGraph(neo4jConfig, neo4jStyle, 'MATCH (n)-[r]-&amp;gt;(m) RETURN n,r,m LIMIT $limit', {limit:20}).then( function(graph) {
  s.graph.read(graph);
  // enable drag'n'drop
  sigma.plugins.dragNodes(s, s.renderers[0]);
  // start layout
  s.startForceAtlas2();
  setTimeout(() =&amp;gt; { s.stopForceAtlas2() }, Math.log(graph.nodes.length*graph.edges.length)*1000);
});&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;Where &lt;code&gt;neo4jConfig&lt;/code&gt; is an object with :&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;listingblock&quot;&gt;
&lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;highlight&quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;const neoConfig = {
  url:'bolt://localhost:7867',
  user: 'neo4j',
  password: 'letmein',
  driver : {
    // all the driver configuration (optional)
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;And &lt;code&gt;neo4jStyle&lt;/code&gt; is :&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;listingblock&quot;&gt;
&lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;highlight&quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;const neoStyle = {
  labels: { // Map of label
    Person : {
      label: 'name', // The node's property to display as label
      color: '#654321', // Color of the node
      size: 10, // Size of the node
      icon: { // icon object
        name: 'f007', // Fontawesome unicode
        color: '#FFF', // Color of the font
        scale: 1.0 // Scale ratio
      }
    },
    Movie : {
      label: 'title',
      color: '#123456',
      size: 10,
      icon: {
        name: 'f008',
        color: '#FFF',
        scale: 1.0
      }
    }
  },
  edges: { // Map of edges
    ACTED_IN: { // Name of the relationship type
      label: 'roles',
      //color: '#202020',
      // size: 2
    }
  }
};&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;admonitionblock note&quot;&gt;
&lt;table&gt;
&lt;tr&gt;
&lt;td class=&quot;icon&quot;&gt;
&lt;div class=&quot;title&quot;&gt;Note&lt;/div&gt;
&lt;/td&gt;
&lt;td class=&quot;content&quot;&gt;
By default, a node is black, with a size of 5, and the label is its Neo4j&amp;#8217;ID ; a relationship is black with a size of 1, and the label is its Neo4j type.
&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;And the final result is :&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;imageblock&quot;&gt;
&lt;div class=&quot;content&quot;&gt;
&lt;img src=&quot;/public/images/sigmajs/neosig.png&quot; alt=&quot;neosig&quot;&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;You can see the code here :&lt;/p&gt;
&lt;/div&gt;
&lt;script src=&quot;https://jsfiddle.net/sim51/ep6g95st/embed/js/&quot;&gt;&lt;/script&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;Now have fun !&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;&lt;em&gt;A big thanks to &lt;a href=&quot;https://github.com/jacomyal&quot;&gt;Jacomyal&lt;/a&gt;, &lt;a href=&quot;https://github.com/yomguithereal&quot;&gt;Yomguithereal&lt;/a&gt; and  &lt;a href=&quot;https://github.com/astik&quot;&gt;Astik&lt;/a&gt; for the help !&lt;/em&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;</description>
        <pubDate>Wed, 25 Apr 2018 00:00:00 +0200</pubDate>
        <link>http://www.bsimard.com/2018/04/25/graph-viz-with-sigmajs.html</link>
        <guid isPermaLink="true">http://www.bsimard.com/2018/04/25/graph-viz-with-sigmajs.html</guid>
        
        
        <category>sigma</category>
        
        <category> graph</category>
        
        <category> visualisation</category>
        
        <category> dataviz</category>
        
        
        
      </item>
      
    
     
    
  </channel>
</rss>
