Использование фреймворков визуализации графиков с базой данных графов Neo4j

Оказалось, что контента по визуализации графиков гораздо больше, чем умещается в одной статье, поэтому мы превратим это в серию неопределенной длины.

Надеемся, вам понравится. Сообщите нам об этом в комментариях.

Введение: запрос данных из Neo4j

База данных графа хранит свои данные в виде узлов и отношений, каждый из которых имеет тип и атрибуты. Это упрощает представление связанной, реальной информации, чтобы находить новые идеи в данных.

Для визуализации хранимых данных мы часто используем фреймворки визуализации данных Javascript.

На этот раз мы будем рисовать не диаграммы или карты, а графики, иначе говоря. сети. Вы можете легко сделать и первое, используя агрегирование или геоинформацию, взятую из графика, но это в другой раз.

Чтобы запросить базу данных Neo4j, мы используем Cypher - язык запросов графических графов, который представляет шаблоны как Ascii Art.

Где мы…

MATCH (a:Node)-[con:CONNECTED_TO]->(another:Node)
RETURN a, con, another

Если вы еще не установили Neo4j, вы можете сделать это быстро с Neo4j-Desktop. После установки просто создайте проект и график, запустите движок и откройте браузер Neo4j (который сам по себе является приложением React). В командной строке вверху введите :play movies graph и щелкните на втором слайде сценарий создания, запустите его один раз, и у вас должен быть базовый набор данных для игры.

Вы также можете просто запустить Neo4j Sandbox, в котором доступно множество различных наборов данных, например Twitter, Paradise Papers, рекомендации по фильмам и многое другое. Для пустой базы данных просто используйте Пустую песочницу, а затем создайте базу данных фильмов, как указано выше. Обратите внимание на свой пароль, IP-адрес сервера и порт для болта (не HTTP!).

Итак, самый простой и быстрый запрос:

MATCH (first)-->(second) 
RETURN id(first) AS source, id(second) AS target

Этот запрос может извлечь миллион отношений из графика за одну секунду, что намного больше, чем может представить большинство систем визуализации.

Он извлекает только структуру графика, что полезно при уменьшении масштаба.

Для получения дополнительных сведений мы можем захотеть узнать метки узлов и типы отношений, чтобы мы могли соответствующим образом раскрасить их.

MATCH (first)-[r]->(second)
RETURN { id: id(first), label:head(labels(first)) } AS source,
       { type:type(r) } as rel,
       { id: id(second), label:head(labels(second)) } AS target

Если мы хотим, мы можем вернуть более подробную информацию, например заголовок, размеры, кластеры, веса:

MATCH (first)-[r]->(second)
RETURN { id: id(first), label:head(labels(first)), 
         caption:first.name, size:first.pagerank, 
         cluster:first.community } AS source,
       { type:type(r), size:r.weight } as rel,
       { id: id(second), label:head(labels(second)),
         caption:second.name, size:second.pagerank, 
         cluster:second.community } AS target

Эти запросы возвращают постепенно больше данных, поэтому, возможно, более разумно извлекать эту дополнительную информацию только в том случае, если пользователь увеличивает масштаб.

Затем мы можем предоставить список уже загруженных, видимых идентификаторов (с первым запросом) в качестве фильтра:

MATCH (first)-[r]->(second)
WHERE id(first) IN $ids AND id(second) IN $ids
RETURN 
    { id:id(first),label:head(labels(first)),caption: first.name, 
        size:first.pagerank, cluster:first.community } AS source,
    { type:type(r), size:r.weight } as rel,
    { id:id(second),label:head(labels(second)),caption:second.name,
       size:second.pagerank, cluster:second.community } AS target

Вы можете предварительно установить параметры в Neo4j-Browser с помощью :param ids:[1,2,3]

Чтобы получить полную подробную информацию об узле (со всеми возможными свойствами), мы можем загрузить эти данные, когда пользователь наводит указатель мыши на узел или щелкает по нему:

MATCH (node)
WHERE id(node) = $id
RETURN { id: id(node), label:head(labels(node)), 
         caption:node.name, size:node.pagerank, 
         cluster:node.community,
         outDegree: size((node)-->()), inDegree: size((node)<--()), 
         .* } AS details

Загрузка данных с помощью драйвера Javascript

Запросы данных с помощью Neo4j Javascript Driver очень производительны и довольно просты, я упомяну единственную странность, когда мы дошли до этого.

Вы можете добавить драйвер через npm или через CDN (rawgit) в свой проект.

Начнем с запроса оболочки узла:

npm install neo4j-driver
node

модуль импорта, создайте драйвер

const neo4j = require('neo4j-driver');
# for local installation
const driver = neo4j.driver('bolt://localhost', 
                            neo4j.auth.basic('user', 'password'));
# for sandbox
const driver = neo4j.driver('bolt://serverip:boltport', 
                            neo4j.auth.basic('user', 'password'));

Если соединение не установлено, убедитесь, что

  1. ваш сервер работает,
  2. адрес (особенно в удаленном случае) и
  3. аутентификация.

создать сеанс

const session = driver.session({database:"dbName"});

получить один узел

var query = "MATCH (n) RETURN n LIMIT 1";

var _ = session.run(query).then(console.log);

структура данных результатов

{ records:
   [ Record {
       keys: [Array],
       length: 1,
       _fields: [Array],
       _fieldLookup: [Object] } ],
  summary:
   ResultSummary {
     statement: { text: 'MATCH (n) RETURN n LIMIT 1', 
                  parameters: {} },
     statementType: 'r',
     counters: StatementStatistics { _stats: [Object] },
     updateStatistics: StatementStatistics { _stats: [Object] },
     plan: false,
     profile: false,
     notifications: [],
     server: ServerInfo { address: 'localhost:7687', 
                          version: 'Neo4j/3.3.4' },
     resultConsumedAfter: Integer { low: 0, high: 0 },
     resultAvailableAfter: Integer { low: 4, high: 0 } } }

Мы видим атрибут records, каждый из которых имеет keys, get(key) метод и частные атрибуты _fields и _fieldLookup. Также есть summary с resultConsumedAfter/resultAvailableAfter таймингом, statement, counters, plan, notifications, serverInfo.

возвращать ключи и значения для каждой записи

var _ = session.run(query).then( 
          result => result.records.forEach(
              r => console.log(r.keys, r.keys.map(k => r.get(k)))));

Мы видим этот неожиданный тип Integer, который является 'числовым' типом драйвера Neo4j, из-за неспособности Javascripts представлять целые числа ›2 ^ 53. Вы можете вызвать value.toNumber() на нем, более подробная информация в документации / файле readme neo4j-javascript -Водитель

превратить отдельную запись в объект

var query = 
 `MATCH (n:Movie) 
  RETURN id(n) as id, labels(n) as labels, n.title, n.released  
  LIMIT 1`;
var _ = session.run(query).then(result => console.log(result.records[0].toObject()) );
{ id: Integer { low: 0, high: 0 },
  labels: [ 'Movie' ],
  'n.title': 'The Matrix',
  'n.released': Integer { low: 1999, high: 0 } }

тест производительности

Небольшой тест производительности на моей машине с использованием кросс-продукта, который мы ограничиваем 1 млн результатов с 3 значениями каждое.

var query = 
       'MATCH (n),(m),(o) RETURN id(n), id(m),id(o) LIMIT 1000000';
var start = new Date();
var _ = session.run(query).then(result => console.log(result.records.length,new Date()-start,"ms"));
> 1000000 1970 ms

Он показывает, что для получения этого 1 миллиона строк требуется чуть меньше 2 секунд. Если бы мы обрабатывали эти данные в потоковом режиме и не создавали большой массив, это было бы намного быстрее (менее 1 секунды).

var query = `MATCH (n),(m),(o) 
             RETURN id(n), id(m),id(o) LIMIT 1000000`;
var count = 0;
var start = new Date();
var _ = session.run(query).subscribe({onNext: r => count++, onCompleted : () => console.log(count,new Date()-start,"ms")});
> 1000000 912 ms

закрыть сеанс и драйвер

session.close();
driver.close();

Хорошо, теперь мы должны знать, как запрашивать Neo4j.

Код на веб-странице

В браузере наш код в основном будет выглядеть так. Весь дополнительный код будет настраивать или конфигурировать фреймворк визуализации.

Если вы подключаетесь к действующей базе данных с веб-страницы, обязательно настройте учетную запись только для чтения. В качестве альтернативы вы можете попросить пользователя ввести логин и пароль в форме. В следующих версиях Neo4j будут добавлены возможности дополнительных ограничений на данные.

<script src="https://unpkg.com/neo4j-driver"></script>
<script>
  // create driver & session
  const driver = neo4j.driver("bolt://localhost", 
                   neo4j.auth.basic("user", "password"));
  const session = driver.session({database:"neo4j"});
  const start = new Date()
  // run query
  session
    .run('MATCH (n)-->(m) RETURN id(n) as source, id(m) as target LIMIT $limit', {limit: neo4j.int(200)})
    .then(function (result) {
      // turn records into list of link-objects (can have different shapes depending on framework)
      // note that we turn the node-ids into javascript ints
      const links = result.records.map(r => 
          { return {source:r.get('source').toNumber(), 
                    target:r.get('target').toNumber()}});
      // close session as soon as we have the data
      session.close();
      // log results & timing
      console.log(links.length+" links loaded in "
                  +(new Date()-start)+" ms.")
      // gather node-ids from both sides
      const ids = new Set();
      links.forEach(l => {ids.add(l.source);ids.add(l.target);});
      // create node-array
      const nodes = Array.from(ids).map(id => {return {id:id}})
      // create "graph" representation
      const graphData = { nodes: nodes, links: links};
      // pass graph data to visualization framework 
      // (here 3d-force-graph)
      const elem = document.getElementById('3d-graph');
      ForceGraph3D()(elem).graphData(graphData);
    })
    .catch(function (error) {
      console.log(error);
    });
</script>

Фреймворки для визуализации графиков Javascript

Большинство фреймворков графической визуализации имеют своего рода «графовый» API, который принимает ряд узлов и ряд ссылок или отношений, а затем отображает их с помощью SVG, Canvas или WebGL. Некоторые из них принимают в качестве входных данных простые объекты, у других есть API для добавления узлов и связей.

Большинство фреймворков предлагают несколько вариантов стилей, взаимодействия, алгоритмов построения графиков и т. Д. Когда мы обсудим каждый по очереди, мы укажем детали и где найти дополнительную информацию.

Вот фреймворки с открытым исходным кодом, которые мы рассмотрим в этой серии:

Существует ряд других коммерческих фреймворков и инструментов, которые мы также попытаемся представить в этой серии с помощью каждого поставщика.

  • yWorks yФайлы
  • Linkurio.us OGMA
  • Keylines
  • Перспективы Тома Сойера
  • Графика
  • Графилеон

Большинство из них имеют встроенную поддержку Neo4j.

В следующем посте на следующей неделе Уильям Лайон представит neovis.js - оболочку Neo4j, которую он создал для vis.js.

Если у вас есть вопросы по визуализации графиков, обязательно присоединяйтесь к Neo4j-Users Slack и задавайте их в канале # help-viz.

Следите за обновлениями