Blog

Seien Sie nicht ein einsames Dokument

Aktualisiert Oktober 21, 2025
7 Minuten

Seien Sie nicht ein einsames Dokument": Das ist ein berühmtes Zitat von Emil Eifrem. Letzte Woche auf der Graphconnect hat er es noch einmal wiederholt, zusammen mit dem Auftrag, über die Konferenz zu twittern. Das hat mich dazu inspiriert, Twitter nach den Schlüsselwörtern "neo4j" und "graphconnect" zu durchsuchen und in Neo4j einzugeben. Verbinden sich die Menschen wirklich?

In meinem ersten Setup habe ich versucht, Tweets in Echtzeit mit Logstash abzurufen, den Stream in Kafka zu veröffentlichen und einen Spark Streaming Job laufen zu lassen, der jeden Tweet verarbeitet und in Neo4j einfügt.

Sie können die folgende Logstash-Konfiguration verwenden, um genau das zu tun.

Eingabe {
  twitter {
  consumer_key =>  "foo"
  consumer_secret =>  "bar"
  oauth_token =>  "baz"
  oauth_token_secret =>  "qux"
  Schlüsselwörter =>  ["graphconnect", "neo4j", "GraphConnect"]
  }
}
Ausgabe {
  kafka {
  codec =>  einfach {
  Format =>  "%{message}"
  }
  topic_id =>  "Tweets"
  }
}

Der nächste Schritt ist der Spark Streaming-Auftrag. Ich hatte ein altes Testprojekt, das genau das tut. Einige Code-Beispiele finden Sie unter: https://github.com/rweverwijk/twitter-to-neo4j

Ein schwacher Laptop-Akku zwang mich, dieses kleine Experiment abzubrechen, aber es ging mir nicht aus dem Kopf.

Später zu Hause suchte ich nach einer neuen Lösung, um alle Tweets mit den ausgewählten Schlüsselwörtern zu sammeln. Ich habe das folgende einfache Python-Skript erstellt, um nach Tweets zu suchen und das JSON in einer Datei zu speichern:

importieren tweepy
importieren Zeit
importieren json

ckey = 'foo'
csecret = 'bar'
atoken = 'baz'
asecret = 'qux'

OAUTH_KEYS = {'verbraucher_schlüssel': ckey, 'verbraucher_geheimnis':csecret,
 access_token_key':atoken, access_token_secret'.:asecret}
auth = tweepy.OAuthHandler(OAUTH_KEYS['consumer_key'], OAUTH_KEYS['verbraucher_geheimnis'])
api = tweepy.API(auth)

def limit_handled(Cursor):
 während True:
  Versuchen Sie:
   Ertrag Cursor.nächste()
  außer tweepy.TweepError als e:
   drucken(e.fehler_msg)
   Zeit.schlafen(15 * 60)

def Suche(Stichwort):
 # Extrahieren Sie die ersten "xxx" Tweets zum Thema "schnelles Auto".
 mit öffnen Sie('tweets_friday.json', 'a') als die_Datei:
  für tweet in limit_handled(tweepy.Cursor(api.Suche, q=Stichwort, seit='2017-05-09').Artikel()):
   die_Datei.Schreiben Sie(json.dumps(tweet._json) + 'n')

Jetzt kann der eigentliche Spaß beginnen: Das Laden der Tweets in Neo4j. Die ausgewählte Struktur ist sehr einfach. Da ich mich besonders für Menschen interessiere, die sich verbinden, werde ich nach Twitter-Nutzern und den Erwähnungen in Tweets suchen. Darüber hinaus möchte ich zwischen dem ursprünglichen Verfasser eines Tweets und den Retweetern unterscheiden. Daraus ergibt sich die folgende Struktur:

Neo4j Schema

Die Eingabedaten liegen im JSON-Format vor. Ich verwende Python, um diese Daten zu lesen, die Felder zu extrahieren, die ich speichern möchte, und sie in Neo4j zu speichern. Der folgende Codeschnipsel tut genau das:

importieren json
von neo4j.v1 importieren GraphDatabase
importieren Zeit

def speichern_tweet(tx, tweet):
 neo4j_params = {"user_id": tweet['Benutzer']['id'],
     "user_name": tweet['Benutzer']['Name'],
     "tweet_id": tweet['id'],
     "tweet_text": tweet['Text'],
     "tweet_time": Zeit.strftime('%Y-%m-%d  %H:%M:%S', Zeit.strptime(tweet['erstellt_am'],'%a %b  %d  %H:%M:%S +0000 %Y')),
     "Erwähnungen": tweet['Entitäten']['user_mentions']
       }
 tx.laufen("""
  MERGE (u:Benutzer {uid: $user_id})
  on create set u.name = $user_name
  MERGE (t:Tweet {uid: $tweet_id})
  on create set t.text = $tweet_text, t.time = $tweet_time
  MERGE (u)-[:TWEETS]->(t)
  WITH t, $mentions als Erwähnungen
  unwind erwähnt als Erwähnung
  MERGE (u:User {uid: mention.id}) on create set u.name = mention.name
  MERGE (t)-[:MENTIONS]->(u)
  """, neo4j_params)

def verarbeiten_datei(datei_name):
 mit GraphDatabase.Treiber("bolt://localhost:7687", auth=("neo4j", "Test")) als Treiber:
  mit öffnen Sie(datei_name, 'r') als die_Datei:
   mit Treiber.Sitzung() als Sitzung:
    mit Sitzung.begin_transaction() als tx:
     für Zeile in die_Datei:
      tweet = json.lädt(Zeile)
      speichern_tweet(tx, tweet)

      wenn 'retweeted_status' in tweet:
       speichern_tweet(tx, tweet['retweeted_status'])
       retweet_data = {
        'tweet_id': tweet['id'],
        "retweet_id": tweet['retweeted_status']['id']
       }

       tx.laufen("""
  MATCH (t:Tweet {uid: $tweet_id})
  MATCH (r:Tweet {uid: $retweet_id})
  MERGE (t)-[:RE_TWEETS]->(r)
  """, retweet_data)

verarbeiten_datei(tweets_friday2.json'.)

Schauen wir mal, was wir jetzt in Neo4j finden können.

Lassen Sie uns zunächst einen kurzen Blick auf die Beziehungen innerhalb von MENTIONS werfen:

MATCH p=()-[r:MENTIONS]->() RETURN p LIMIT 50
Neo4j Übersicht

Das sieht schon ganz nett aus!

Lassen Sie uns herausfinden, welcher Benutzer am häufigsten erwähnt wird:

MATCH (t:Tweet)-[r:MENTIONS]->(erwähnt:Benutzer)
RETURN mentioned.name, count(r) as numberOfMentions
order by anzahlderBemerkungen desc
Grenze 10

führt zu:

BenutzernumberOfMentions
Neo4j1014
GraphConnect613
Emil Eifrem236
Jim Webber150
ICIJ149
Rik Van Bruggen99
GraphAware88
LARUS86
Philip Rathle86
CluedIn69

Wenn wir die Konten der Organisationen ausschließen, sind die stärksten Einflussnehmer in der Grafik Emil, Jim und Rik. Sie waren ganz sicher keine einsamen Dokumente.

Lassen Sie uns weiterforschen und herausfinden, wer die Tweets mit den Erwähnungen schreibt:

MATCH (u:Benutzer)-[:TWEETS]->(t:Tweet)
RETURN u.name as user, count(t) as numberOfTweets
order by Anzahl der Tweets desc
Grenze 10
BenutzernumberOfTweets
GraphConnect433
Hakaishin Hokutosei379
Neo4j244
Yuxing Sun113
Christophe Willemsen109
Neo Fragen85
Bence Arato42
Cedric Fauvet41
Nigel Klein38
Mark Wood36

GraphConnect und Neo4j scheinen ziemlich offensichtlich zu sein, aber Hakaishin Hokutosei kenne ich nicht und 379 scheint eine Menge Tweets zu sein. Worüber tweetet dieser Benutzer?

MATCH (u:Benutzer)-[:TWEETS]->(t:Tweet)
wo u.name = "Hakaishin Hokutosei"
RETURN t.text
Grenze 100
t.text
RT @BenceArato: Wichtige @neo4j Meilensteine von Version 3.0 bis zu aktuellen und zukünftigen Plänen #GraphConnect https://t.co/J99pXpSzV5
RT @GraphConnect: .@jimwebber: #Neo4j macht keine verrückten JOINs oder Sets - es jagt einfach Zeigern hinterher#GraphConnect
RT @GraphConnect: .@jimwebber: Weil #Neo4j eine native #Grafikdatenbank ist und wir den gesamten Stack besitzen, können wir jede Clustering-Anforderung...
RT @GraphConnect: .@jimwebber: #Neo4j 3.1 führte Sicherheit und Causal Clusteringn#GraphConnect
RT @GraphConnect: .@jimwebber: Causal Clustering, eingeführt in #Neo4j 3.1, kann jetzt mehrere Rechenzentren umfassen#GraphConnect
RT @GraphConnect: .@jimwebber: #Neo4j 3.2 Treiber sind sich auch der Causal Clustersn#GraphConnect
RT @GraphConnect: .@jimwebber: #Neo4j 3.2 kann jetzt #Kerberos verwenden, vor allem für diejenigen unter Ihnen in #FinServ, die es verwenden müssen#GraphC...
RT @matethurzo: Closing keynote of #graphconnect @jimwebber is always fun to watch #graph #graphdb #conferenceday #neo4j #devlife https://...
RT @mfalcier: Verfolgen Sie @neo4j #graphconnect Dr. @jimwebber 's Vortrag vom Sofa aus? Fantastisch! https://t.co/U0bCXGEd0D
RT @GraphConnect: .@jimwebber: Letztes Jahr in London wurde mit #Neo4j 3.0 die obere Speichergrenze ganz abgeschafft#GraphConnect

Moment mal, jeder Tweet beginnt mit "RT". Retweetet er nur, oder haben wir auch selbst geschriebene Tweets?

Schauen wir mal:

MATCH (u:Benutzer)-[:TWEETS]->(t:Tweet)
wo u.name = "Hakaishin Hokutosei"
und nicht (t)-[:RE_TWEETS]->()
RETURN count(t)
zählen(t)
0

Wir müssen also Tweets von Retweets trennen, um zwischen Originalautoren und Retweetern unterscheiden zu können:

MATCH (u)-[r1:TWEETS]->(t)
wo nicht (t)-[:RE_TWEETS]->()
optionale Übereinstimmung (u)-[:TWEETS]->(rt)-[r2:RE_TWEETS]->()
RETURN u.name, count(distinct r2) as numberOfReTweets, count(distinct r1) as numberOfTweets
order by Anzahl der Tweets desc
u.NamenumberOfReTweetsnumberOfTweets
GraphConnect238195
Neo4j65179
Yuxing Sun0113
Neo Fragen085
Mark Wood432
Bence Arato1824
Carina Birt323
Marlon Samuels020
Tägliche technische Fragen016
Andres L. Martinez115
Neo4j Frankreich1115
Louis Dubruel015
Nigel Klein2315
Adam Hill515
Rik Van Bruggen115

Was sind die beliebtesten Tweets?

MATCH  (rt)-[r2:RE_TWEETS]->(t)[:TWEETS]-(u)
RETURN u.name AS user, t.text, count(rt) AS numberOfRetweets
ORDER BY numberOfRetweets DESC
Benutzert.textnumberOfRetweets
Mar CabraArbeiten Sie 6 Monate lang von DC, Paris oder Madrid aus mit @ICIJorg zusammen und machen Sie komplexe Daten und Diagramme dank @neo4js... https://t.co/Z0rR3Rt7zV20
ICIJSind Sie daran interessiert, mit Daten Geschichten zu finden? Möchten Sie am nächsten Projekt von ICIJ mitarbeiten? Bewerben Sie sich für das Connected Data Fellowship https://t.co/LUdsjWKwRJ 18
William LyonDemokratisierung von Daten bei @AirbnbEng mit Dataportal, einem neuen Tool zur Skalierung der Datensuche und -entdeckung, angetrieben von @neo4j nnhttps://t.co/e12fHuA26M18
Pat PattersonVisualisierung & Analyse von Salesforce-Daten mit #StreamSets Data Collector & @Neo4j https://t.co/DunEFtAPyO Vielen Dank für gr... https://t.co/pXwBISQtme18
ICIJAufregende Ankündigung: Wir stellen jetzt einen Neo4j Connected Data Fellow ein! Mehr Informationen und wie Sie sich bewerben können hier: https://t.co/knjHKgyQiz #GraphConnect 17
Dr. GP PulipakaAnkündigung von Neo4j im Microsoft Azure Marketplace (Teil I). #BigData #DataScience #Neo4J #Azure #Analytics... https://t.co/1XEqQHgedu 16
Kursion#GraphConnect Neo4j 3.2 steht heute zum Download bereit https://t.co/QZu3XvAjts15

Die beliebtesten Tweets drehen sich also um ICIJ und dessen Connected Data Fellowship oder die neue Neo4j-Version.

Und nicht zuletzt: Welches sind die Tweets, die die meisten Retweets erhalten haben und zu Gewinnern des Preises für das am wenigsten "einsame Dokument" erklärt werden können (wenn das eine echte Auszeichnung wäre):

MATCH  (rt)-[r2:RE_TWEETS]->(t)[:TWEETS]-(u)
RETURN u.name as user, count(rt) as numberOfRetweets
order by numberOfRetweets desc
Rik van Bruggen

Oder wie mein lieber Freund Rik sagen würde. "Vielleicht bin ich das einsamste Dokument und das ist der Grund, warum ich so viel über Neo4j twittere, denn ich habe keine Hobbys. ;) "

Wir stellen ein

Contact

Let’s discuss how we can support your journey.