Show HN: 我们构建了一个针对 RedisGraph/FalkorDB 的类型安全 Python ORM

2作者: hello-tmst14 天前
我们厌倦了编写原始的 Cypher 查询——转义引号、零代码补全、重构噩梦——所以我们构建了 GraphORM:一个使用纯 Python 对象的、针对 RedisGraph/FalkorDB 的类型安全的 Python ORM。 它能做什么 不再需要脆弱的 Cypher 查询: ```cypher query = """ MATCH (a:User {user_id: 1})-[r1:FRIEND]->(b:User)-[r2:FRIEND]->(c:User) WHERE c.user_id <> 1 AND b.active = true WITH b, count(r2) as friend_count WHERE friend_count > 5 RETURN c, friend_count ORDER BY friend_count DESC LIMIT 10 """ ``` 你可以编写类型安全的 Python 代码: ```python stmt = select().match( (UserA, FRIEND.alias("r1"), UserB), (UserB, FRIEND.alias("r2"), UserC) ).where( (UserA.user_id == 1) & (UserC.user_id != 1) & (UserB.active == True) ).with_( UserB, count(FRIEND.alias("r2")).label("friend_count") ).where( count(FRIEND.alias("r2")) > 5 ).returns( UserC, count(FRIEND.alias("r2")).label("friend_count") ).orderby( count(FRIEND.alias("r2")).desc() ).limit(10) ``` 主要特性: • 使用 Python 类型提示的类型安全模式 • 流畅的查询构建器 (select().match().where().returns()) • 自动批量处理 (flush(batch_size=1000)) • 原子事务 (with graph.transaction(): ...) • 零字符串转义——O'Connor 和 "The Builder" 都能正常工作 目标受众 • AI/LLM 代理开发者:将长期记忆存储为图 (User → Message → ToolCall) • 网络爬虫工程师:用 12 行代码插入 1 万个页面 + 链接,而 Cypher 需要 80 行 • 社交网络构建者:使用 indegree()/outdegree() 查询“朋友的朋友” • 数据工程师:跟踪血缘关系 (Dataset → Transform → Output) • 刚接触图的 Python 开发者:避免 Cypher 学习曲线 数据插入:真正的变革 原始 Cypher 噩梦: ``` queries = [ """CREATE (:User {email: "alice@example.com", name: "Alice O\'Connor"})""", """CREATE (:User {email: "bob@example.com", name: "Bob \"The Builder\""})""" ] for q in queries: graph.query(q) # 没有事务安全! ``` GraphORM 的幸福: ```python alice = User(email="alice@example.com", name="Alice O'Connor") bob = User(email="bob@example.com", name='Bob "The Builder"') graph.add_node(alice) graph.add_edge(Follows(alice, bob, since=1704067200)) graph.flush() # 一次网络调用,原子事务 ``` 30 秒内试用 ```bash pip install graphorm ``` ```python from graphorm import Node, Edge, Graph class User(Node): __primary_key__ = ["email"] email: str name: str class Follows(Edge): since: int graph = Graph("social", host="localhost", port=6379) graph.create() alice = User(email="alice@example.com", name="Alice") bob = User(email="bob@example.com", name="Bob") graph.add_node(alice) graph.add_edge(Follows(alice, bob, since=1704067200)) graph.flush() ``` GitHub: [https://github.com/hello-tmst/graphorm](https://github.com/hello-tmst/graphorm) 我们很乐意收到诚实的反馈: • 这是否解决了您真正的痛点? • 生产环境使用还缺少什么? • 有任何 API 设计建议吗?
查看原文
We were tired of writing raw Cypher — escaping quotes, zero autocomplete, refactoring nightmares — so we built GraphORM: a type-safe Python ORM for RedisGraph&#x2F;FalkorDB using pure Python objects.<p>What it does Instead of fragile Cypher:<p><pre><code> query = &quot;&quot;&quot; MATCH (a:User {user_id: 1})-[r1:FRIEND]-&gt;(b:User)-[r2:FRIEND]-&gt;(c:User) WHERE c.user_id &lt;&gt; 1 AND b.active = true WITH b, count(r2) as friend_count WHERE friend_count &gt; 5 RETURN c, friend_count ORDER BY friend_count DESC LIMIT 10 &quot;&quot;&quot; </code></pre> You write type-safe Python:<p><pre><code> stmt = select().match( (UserA, FRIEND.alias(&quot;r1&quot;), UserB), (UserB, FRIEND.alias(&quot;r2&quot;), UserC) ).where( (UserA.user_id == 1) &amp; (UserC.user_id != 1) &amp; (UserB.active == True) ).with_( UserB, count(FRIEND.alias(&quot;r2&quot;)).label(&quot;friend_count&quot;) ).where( count(FRIEND.alias(&quot;r2&quot;)) &gt; 5 ).returns( UserC, count(FRIEND.alias(&quot;r2&quot;)).label(&quot;friend_count&quot;) ).orderby( count(FRIEND.alias(&quot;r2&quot;)).desc() ).limit(10) </code></pre> Key features: • Type-safe schema with Python type hints • Fluent query builder (select().match().where().returns()) • Automatic batching (flush(batch_size=1000)) • Atomic transactions (with graph.transaction(): ...) • Zero string escaping — O&#x27;Connor and &quot;The Builder&quot; just work<p>Target audience • AI&#x2F;LLM agent devs: store long-term memory as graphs (User → Message → ToolCall) • Web crawler engineers: insert 10k pages + links in 12 lines vs 80 lines of Cypher • Social network builders: query &quot;friends of friends&quot; with indegree()&#x2F;outdegree() • Data engineers: track lineage (Dataset → Transform → Output) • Python devs new to graphs: avoid Cypher learning curve<p>Data insertion: the real game-changer<p>Raw Cypher nightmare: queries = [ &quot;&quot;&quot;CREATE (:User {email: &quot;alice@example.com&quot;, name: &quot;Alice O\\&#x27;Connor&quot;})&quot;&quot;&quot;, &quot;&quot;&quot;CREATE (:User {email: &quot;bob@example.com&quot;, name: &quot;Bob \\&quot;The Builder\\&quot;&quot;})&quot;&quot;&quot; ] for q in queries: graph.query(q) # No transaction safety!<p>GraphORM bliss: alice = User(email=&quot;alice@example.com&quot;, name=&quot;Alice O&#x27;Connor&quot;) bob = User(email=&quot;bob@example.com&quot;, name=&#x27;Bob &quot;The Builder&quot;&#x27;) graph.add_node(alice) graph.add_edge(Follows(alice, bob, since=1704067200)) graph.flush() # One network call, atomic transaction<p>Try it in 30 seconds pip install graphorm<p><pre><code> from graphorm import Node, Edge, Graph class User(Node): __primary_key__ = [&quot;email&quot;] email: str name: str class Follows(Edge): since: int graph = Graph(&quot;social&quot;, host=&quot;localhost&quot;, port=6379) graph.create() alice = User(email=&quot;alice@example.com&quot;, name=&quot;Alice&quot;) bob = User(email=&quot;bob@example.com&quot;, name=&quot;Bob&quot;) graph.add_node(alice) graph.add_edge(Follows(alice, bob, since=1704067200)) graph.flush() </code></pre> GitHub: <a href="https:&#x2F;&#x2F;github.com&#x2F;hello-tmst&#x2F;graphorm" rel="nofollow">https:&#x2F;&#x2F;github.com&#x2F;hello-tmst&#x2F;graphorm</a><p>We&#x27;d love honest feedback: • Does this solve a real pain point for you? • What&#x27;s missing for production use? • Any API design suggestions?