Сегодня я хочу познакомить вас с VivaGraphJS — библиотекой JavaScript Graph Graphing, созданной Андреем Кащей из Ясива . Он поддерживает рендеринг графиков с использованием форматов WebGL, SVG или CSS и в настоящее время поддерживает принудительную компоновку. Библиотека предоставляет API, который отслеживает изменения графиков и отражает изменения на поверхности рендеринга, что делает его фантастическим для исследования графиков.
Сегодня мы будем интегрировать его с Neo4j и Alchemy API .
Alchemy API предоставляет набор инструментов обработки естественного языка, и мы будем использовать их возможности Entity Extraction .
Мы собираемся взглянуть на новости мира, извлечь упомянутые в них сущности, соединить их все вместе в Neo4j и визуализировать их. Чтобы получать новости, мы будем использовать Feedzilla .
Задание sidekiq будет запускаться каждые шесть часов, чтобы собрать последние 100 новостных статей:
module Job class GetNews include Sidekiq::Worker sidekiq_options :retry => false def perform Job::GetNews.perform_in(6.hours) feed = HTTPClient.get("http://api.feedzilla.com/v1/categories/19/articles.json?order=date&count=100&title_only=1") parsed_feed = Oj.load(feed.body) parsed_feed["articles"].each do |article| Job::GetArticle.perform_async(article["url"]) end end end end
Из каждой статьи мы получим историю, передадим ее в Alchemy API и попросим найти сущности в статье .
@entities = Oj.load(HTTPClient.post_content("http://access.alchemyapi.com/calls/url/URLGetRankedNamedEntities", {:url => article_url, :apikey => ENV['ALCHEMY_API'], : outputMode => "json"}), {:'Accept-encoding' => "gzip"})["entities"]
Мы создадим узел для каждой статьи и свяжем каждую сущность, найденную с помощью отношения «MENTIONED»:
commands = [] @batch_result.each do |b| commands << [:create_relationship, "MENTIONED", @article_node, b["body"]["self"].split("/").last] end
Некоторые из этих сущностей имеют интересные описания, поэтому мы запросим dbpedia для получения дополнительной информации.
Переходя к нашему клиентскому приложению , мы предоставим окно автозаполнения, которое будет запрашивать индекс сущностей нашего графика:
get '/search' do content_type :json neo = Neography::Rest.new cypher = "START me=node:entities({query}) RETURN ID(me), me.text ORDER BY me.text LIMIT 15" neo.execute_query(cypher, {:query => "text:*#{params[:term]}* OR uri:*#{params[:term]}*" })["data"].map{|x| { label: x[1], value: x[0]}}.to_json end
Мы также предоставим конечную точку для извлечения узла и всех узлов, подключенных к нему, и возврата объекта JSON с этими данными:
get '/edges/:id' do content_type :json neo = Neography::Rest.new cypher = "START me=node(#{params[:id]}) MATCH me -- related RETURN ID(me), me.text, me.description, me.type, ID(related), related.text, related.description, related.type" connections = neo.execute_query(cypher)["data"] connections.collect{|n| {"source" => n[0], "source_data" => {:label => n[1], :description => n[2], :type => n[3] }, "target" => n[4], "target_data" => {:label => n[5], :description => n[6], :type => n[7]}} }.to_json end
Наш javascript будет содержать обращения к Vivagraph:
var graph = Viva.Graph.graph(); var layout = Viva.Graph.Layout.forceDirected(graph, { springLength:100, springCoeff:0.0001, dragCoeff:0.02, gravity:-1 });
Наши узлы будут отображать как текст, найденный в объекте, так и изображение для представления типа объекта.
var ui = Viva.Graph.svg('g'), svgText = Viva.Graph.svg('text').attr('y', '-4px').text(node.data.label), img = Viva.Graph.svg('image') .attr('width', 32) .attr('height', 32) .link('/img/' + node.data.type + '.png'); ui.append(svgText); ui.append(img);
Мы добавим событие mouseover, чтобы обновить боковую панель с описанием нашего узла:
$(ui).hover(function() { // mouse over highlightRelatedNodes(node.id, true); $('#explanation').html(node.data.description); }, function() { // mouse out highlightRelatedNodes(node.id, false); });
… И загрузить узлы, связанные с этой сущностью, по клику:
$(ui).click(function() { console.log("click", node); if (!node || !node.position) return; renderer.rerender(); loadData(graph,node.id); } );
Как обычно, код доступен на github , и вы можете увидеть, как он работает на Heroku .
Посмотрите еще несколько демонстраций Vivagraph.js:
- Amazon Visualization — показывает связанные продукты на Amazon.com, использует SVG в качестве вывода графика
- Визуализация YouTube — показывает похожие видео с YouTube. На основе SVG.
- Визуализация Facebook — визуализация дружбы на Facebook. На основе WebGL.
- Graph Viewer — визуализация коллекции разреженных матриц Университета Флориды. На основе WebGL.
- Вконтакте Визуализация — визуализация дружбы крупнейшей социальной сети в России vk.com . На основе WebGL.