我为 Neo4j 构建了一个免费 Cypher 查询格式化器——原因与方法

发布: (2026年3月11日 GMT+8 02:37)
5 分钟阅读
原文: Dev.to

Source: Dev.to

Cover image for I Built a Free Cypher Query Formatter for Neo4j — Here's Why and How

如果你已经使用 Neo4j 有一段时间了,你可能已经看到过一大段未格式化的 Cypher,类似下面这样:

MATCH (p:Person {name: 'Alice'})-[:FRIENDS_WITH]->(friend:Person)-[:WORKS_AT]->(c:Company) WHERE friend.age > 25 AND c.name STARTS WITH 'Neo' WITH p, friend, c, count(*) AS cnt ORDER BY cnt DESC LIMIT 10 OPTIONAL MATCH (friend)-[:LIVES_IN]->(city:City) WHERE city.population > 100000 RETURN p.name AS person, collect(DISTINCT friend.name) AS friends, c.name AS company, city.name AS city ORDER BY person ASC

并不容易阅读。我一直在从日志、Slack 消息以及 Stack Overflow 的答案中复制粘贴这种查询——手动重新格式化它们耗费了大量时间。所以我构建了一个工具来解决这个问题。

我构建的

Cypher Query Formatter — 一个免费、基于浏览器的工具,能够即时对凌乱的 Cypher 查询进行格式化,提供正确的缩进、换行和语法高亮。

上面的查询格式化后变为:

MATCH (p:Person {name: 'Alice'})-[:FRIENDS_WITH]->(friend:Person)-[:WORKS_AT]->(c:Company)
WHERE friend.age > 25
    AND c.name STARTS WITH 'Neo'
WITH p, friend, c, count(*) AS cnt
ORDER BY cnt DESC
LIMIT 10
OPTIONAL MATCH (friend)-[:LIVES_IN]->(city:City)
WHERE city.population > 100000
RETURN p.name AS person,
    collect(DISTINCT friend.name) AS friends,
    c.name AS company,
    city.name AS city
ORDER BY person ASC

好多了。

Features

  • Instant formatting — paste your query, click Format (or hit Ctrl+Enter).
  • Syntax highlighting — keywords, functions, labels, properties, strings and parameters all highlighted in distinct colors.
  • Configurable indent — 2 spaces, 4 spaces, or tabs.
  • Keyword case conversion — UPPERCASE, lowercase, or Capitalize.
  • Dark and light theme.
  • Copy to clipboard or download as .cypher file.
  • 100 % client‑side — your queries are never sent to any server.

Source:

工作原理 — 技术细节

Tokenizer(分词器)

核心是一个手写的分词器,它逐字符遍历查询并输出带类型的 token(clausesub_clausefunctionstringnumbercommentparameteroperatorbracket 等)。

难点在于正确处理 多词子句。Cypher 有诸如 OPTIONAL MATCHON CREATE SETORDER BYSTARTS WITHIS NOT NULL 等结构。若采用朴素的逐词方式,会把 STARTS WITH 错误地拆分为操作符 STARTS 和主要子句 WITH

解决方案是 在检查主要子句之前先匹配多词子句,使用最长匹配优先的已排序列表:

const MULTI_WORD_SUB_CLAUSES_SORTED = [
    'IS NOT NULL',
    'STARTS WITH',
    'ENDS WITH',
    'WITH HEADERS',
    'IS NULL'
];

先检查这些项后,STARTS WITH 能被正确识别为子句操作符,而不会被误匹配为主要子句 WITH

Formatter(格式化器)

在完成分词和分类后,格式化器重新构建查询字符串,具体规则如下:

  • 在每个主要子句(MATCHWHERERETURN 等)前插入换行。
  • WHERE 块内部的 ANDORXOR 前插入缩进和换行。
  • RETURNWITHSET 子句的逗号后插入缩进和换行。
  • 在操作符和括号周围保持适当的空格。

Syntax Highlighter(语法高亮)

高亮器对已格式化的输出再次运行相同的分词器,并用带有 CSS 类的 <span> 包裹每种 token。上下文很重要——冒号 : 之后的标识符被视为标签(红色),点号 . 之后的标识符被视为属性(蓝色),否则视为变量(粉色)。

我学到的

  • 从头编写分词器出乎意料地有趣。 边缘情况才是关键——字符串中的转义引号、反引号转义的标识符、块注释、以 $ 开头的参数。
  • 多词关键字匹配需要仔细排序。 这在我刚开始时绊倒了我,导致格式化结果非常错误,直到我把顺序弄对为止。
  • Cloudflare Pages 是托管此类静态工具的绝佳免费平台。 零配置、从 Git 即时部署、免费额度慷慨。

Try It

👉 cypher-formatter.pages.dev

它是免费且向所有人开放的,并且可以在任何现代浏览器中使用。如果您遇到格式化不正确的查询,请在评论中告诉我。Cypher 中的边缘情况非常多,我确信我遗漏了一些。

使用原生 JavaScript 构建。没有框架、没有依赖、没有服务器。

0 浏览
Back to Blog

相关文章

阅读更多 »

Show HN:现代 React 入职导览库

!react-tourlight https://github.com/btahir/react-tourlight/raw/main/assets/logo.svg https://github.com/btahir/react-tourlight/blob/main/assets/logo.svg 美丽的...