使用 GitHub Actions 和 Neo4j Aura 无成本抓取数据。在处理数据时,一项常见任务是定期从某个外部来源获取数据,并将其导入数据库以供进一步分析或作为我们应用程序的一部分。设置服务器来处理这个问题可能既费时又容易出错。我最近遇到了一个使用GitHub Actions和Neo4j Aura的工作流程,这让这变得轻而易举,而且 GitHub Actions 和 Neo4j Aura 可以免费设置和永久运行 - 非常适合辅助项目!
在这篇文章中,我们将了解如何设置此工作流程以从Lobster新闻聚合器中抓取数据并使用 GitHub Actions 导入Neo4j Aura Free实例。
1.数据源-Lobsters
Lobsters是一个社交新闻聚合器。用户在讨论中发布文章链接,社区对其进行投票和评论。通过算法确定提交的排名,将最新和值得注意的文章浮动到首页。用户必须由现有用户邀请,才能提交文章链接和评论,并且用户的邀请图是公开的,有助于保持文明讨论和避免虚假投票。
我们想要构建一个应用程序来帮助使用图形可视化探索,使用 Lobster 的数据非常合适
Lobsters 提供了两个 JSON 数据接口来获取有关最新和“最热门”提交的数据。每个都有类似的格式,看起来像这样。
现在我们知道我们的 JSON 数据是什么样子了,我们可以考虑如何开始将这些数据作为图表导入 Neo4j Aura。
2.Neo4j Aura
Neo4j Aura是 Neo4j 的托管云服务,让我们只需单击几下即可在云中运行 Neo4j 集群,并处理诸如备份、升级和管理之类的事情,因此我们不必担心。最重要的是,对于我们来说,Neo4j Aura 有一个免费层,它允许我们构建这个项目,甚至不需要使用任何个人信息。
登录 Neo4j Aura并创建一个 Aura Free 实例
我们将立即获得一个生成的密码,我们需要保存该密码才能访问我们的 Neo4j Aura 实例。
一旦我们的 Neo4j Aura 实例准备就绪,我们将在仪表板中看到连接字符串。打开 Neo4j 浏览器开始处理我们的数据。
由于我们使用的是 JSON 数据,我们可以利用APOC 标准库apoc.load.json中的过程来使用 Cypher 导入 JSON 文件。首先,让我们把这个文件拉进来,以确保我们可以解析它。这个 Cypher 语句将解析最新的 Lobster 提交并返回我们可以在 Cypher 中使用的对象数组:
CALL apoc.load.json("https://lobste.rs/newest.json") YIELD value
RETURN value
如果我们在 Neo4j 浏览器中运行它,我们将看到返回的已解析对象数组。我们实际上还没有在数据库中创建任何数据——我们只是解析 JSON 文件并返回结果。
我们将使用 Cypher 来定义我们想要从这些数据创建的图形结构,首先需要考虑一下我们希望如何将这些数据建模为图形。图形建模的步骤是:
- 识别我们数据中的实体——这些成为节点。
- 确定这些实体是如何连接的——这些连接变成了关系。
- 识别描述实体或连接的数据片段 - 这些成为属性。
Arrows.app是绘制和存储这些图形模型的绝佳工具。这是我们的Lobsters数据图形模型在箭头中的样子:
现在我们可以根据这个模型编写 Cypher 来导入我们的数据。我们将用于apoc.load.json解析 JSON 文件,然后用于UNWIND迭代此对象数组。然后,我们将使用MERGECypher 子句将用户、文章和标签添加到数据库中。该MERGE语句允许我们避免在图中创建重复项(MERGE仅创建图中尚不存在的模式)。
因此,除非数据发生变化,否则我们不会对数据库进行任何更改。
CALL apoc.load.json("https://lobste.rs/newest.json") YIELD value
UNWIND value AS article
MERGE (s:User {username: article.submitter_user.username})
ON CREATE SET s.about = article.submitter_user.about,
s.created = DateTime(article.submitter_user.created_at),
s.karma = article.submitter_user.karma,
s.avatar_url = "https://lobsete.rs" + article.submitter_user.avatar_url
MERGE (i:User {username: article.submitter_user.invited_by_user})
MERGE (i)<-[:INVITED_BY]-(s)
MERGE (a:Article {short_id: article.short_id})
SET a.url = article.url,
a.score = article.score,
a.created = DateTime(article.created_at),
a.title = article.title,
a.comments = article.comments_url
MERGE (s)-[:SUBMITTED]->(a)
WITH article, a
UNWIND article.tags AS tag
MERGE (t:Tag {name: tag})
MERGE (a)-[:HAS_TAG]->(t)
为了验证我们是否正确导入了这些数据,我们可以在 Neo4j 浏览器中对其进行可视化
随着新文章的提交和投票,Lobsters 的数据将不断变化,但我们希望持续导入它。这时候就需要使用 GitHub Actions 来做到这一点!
3.Github Actions 和 flat data
GitHub Actions允许我们定义由 GitHub 事件(如提交、拉取请求等)触发的工作流程。我们可以编写自定义代码以在触发 Action 时运行,或者从 GitHub Action Marketplace 中社区成员发布的现有 Actions 中进行选择。虽然通常用于 CI/CD 工作流,但我们还可以安排操作以指定的时间间隔运行。
GitHub 团队最近发布了Flat Data 项目,旨在简化数据和 ETL 工作流程。Flat Data 包括 Flat Data GitHub Action、用于创建 Flat Data 工作流的 VSCode 扩展以及用于查看数据的 Web 应用程序。Flat Data Action 允许我们安排 GitHub 操作以通过 URL 或 SQL 语句定期获取数据,将数据检查到 git,并运行后处理步骤,例如,将数据插入数据库。
要创建一个新的 GitHub 操作,我们将添加一个新文件.github/workflows/lobsters.yml。在这个 YAML 文件中,我们将
- 为新操作命名。
- 标识我们要触发此操作的事件。我们希望它在此 YAML 文件更新时运行,并安排它每 60 分钟运行一次。
- 然后我们定义我们想要在 Action 中运行的步骤:
actions/checkout@v2首先,我们将使用操作检查 repo- 然后我们将使用
githubocto/flat@v2Action 来获取我们的 Lobster JSON 文件并将其签入到我们的 repo 中newest.json
name: Lobsters Data Import
on:
push:
paths:
- .github/workflows/lobsters.yml
workflow_dispatch:
schedule:
- cron: '*/60 * * * *'
jobs:
scheduled:
runs-on: ubuntu-latest
steps:
- name: Check out repo
uses: actions/checkout@v2
- name: Fetch newest
uses: githubocto/flat@v2
with:
http_url: https://lobste.rs/newest.json
downloaded_filename: newest.json
我们的 GitHub Action 将在提交新文件后立即运行,几秒钟后我们将看到一个新的提交,newest.json并使用来自 Lobster 的数据签入文件。
我们已经获取了最新的Lobsters文章提交并将它们导入我们的存储库,并安排了一个 GitHub 操作来每小时刷新这些数据。现在我们准备更新我们的 GitHub Action 以将数据导入 Neo4j。
首先,我们需要将数据库的登陆信息添加到我们的 GitHub 存储库,以便我们的 Action 可以连接到我们的 Neo4j Aura 实例,并且我们不必公开我们的连接凭据。在 GitHub 中选择“Settings”选项卡,然后导航到“Secrets”部分并选择“New repository secret”按钮。
我们将使用我们之前创建的 Neo4j Aura 实例特定的连接凭证创建三个秘密NEO4J_USER、NEO4J_PASSWORD和。NEO4J_URI
我们现在可以在 YAML 文件中引用这些秘密值,我们在其中定义 GitHub Action 以连接到我们的 Neo4j Aura 实例。
4.Flat Data GitHub Action
Flat Data GitHub Action 包括对后处理步骤的支持,允许我们在获取数据后运行 JavaScript 或 Python 脚本。我们可以编写一个简单的脚本来使用 Neo4j 语言驱动程序连接到我们的 Neo4j Aura 实例并运行 Cypher 导入语句,将 JSON 数据作为 Cypher 参数传递,但是直接使用Flat Graph GitHub Action 可以做到这一点。
Flat Graph 旨在与 Flat Data GitHub Action 一起使用,并允许我们声明我们想要运行的 Cypher 导入语句和我们的 Neo4j Aura 连接凭据作为我们 GitHub Action 中的另一个步骤。Flat Graph 将加载指定的 JSON 文件并将其作为 Cypher 参数传递,$value因此我们的 Cypher 导入语句只需要引用此 Cypher 参数即可处理在操作的上一步中由 Flat Data 获取的数据。
我们将使用我们在上面编写的 Cypher 语句 using apoc.load.json,但对其进行调整以使用此约定。我们还将引用我们定义为 GitHub 机密的 Neo4j Aura 连接凭据。让我们更新我们的lobsters.yml文件
name: Lobsters Data Import
on:
push:
paths:
- .github/workflows/lobsters.yml
workflow_dispatch:
schedule:
- cron: '*/60 * * * *'
jobs:
scheduled:
runs-on: ubuntu-latest
steps:
- name: Check out repo
uses: actions/checkout@v2
- name: Fetch newest
uses: githubocto/flat@v2
with:
http_url: https://lobste.rs/newest.json
downloaded_filename: newest.json
- name: Neo4j import
uses: johnymontana/flat-graph@v1.2
with:
neo4j-user: ${{secrets.NEO4J_USER}}
neo4j-password: ${{secrets.NEO4J_PASSWORD}}
neo4j-uri: ${{secrets.NEO4J_URI}}
filename: newest.json
cypher-query: >
UNWIND $value AS article
MERGE (s:User {username: article.submitter_user.username})
ON CREATE SET s.about = article.submitter_user.about,
s.created = DateTime(article.submitter_user.created_at),
s.karma = article.submitter_user.karma,
s.avatar_url = "https://lobsete.rs" + article.submitter_user.avatar_url
MERGE (i:User {username: article.submitter_user.invited_by_user})
MERGE (i)<-[:INVITED_BY]-(s)
MERGE (a:Article {short_id: article.short_id})
SET a.url = article.url,
a.score = article.score,
a.created = DateTime(article.created_at),
a.title = article.title,
a.comments = article.comments_url
MERGE (s)-[:SUBMITTED]->(a)
WITH article, a
UNWIND article.tags AS tag
MERGE (t:Tag {name: tag})
MERGE (a)-[:HAS_TAG]->(t)
一旦我们提交更改,我们的 Action 将运行 - 将最新的 Lobster 文章数据加载到我们的 Neo4j Aura 实例中。此操作将每小时运行一次,使用最新的 Lobster 数据更新我们的数据库。
我们现在设置了一个无服务器数据抓取工作流程,以获取提交给 Lobster 的所有新文章并使用 GitHub Actions 导入 Neo4j Aura。接下来,我们将看看数据可视化选项,我们开始探索这个数据集并构建一个 Web 应用程序来帮助我们找到相关信息。