logo
咨询企业版

特性讲解技术分享

Demo|图数据库如何让社交软件精准推荐「你可能认识的人」?

你有没有过这样的经历?刚下载一个社交 App,它就给你推荐了多年未见的老同学,或是隔壁部门可能有过一面之缘的同事。你心里不禁犯嘀咕:“它怎么知道的?”

其实,这背后很可能是图数据库在默默工作。它把整个社交平台制造成一张巨大的关系网,通过分析这张网里错综复杂的连接,来做出更懂你的推荐。

今天,NebulaGraph 就通过一个简单的 Demo,带你一窥这位“关系大师”是怎么工作的。

Demo 链接 https://www.nebula-graph.com.cn/demo 欢迎在 NebulaGraph 官网体验多场景 Demo,亲自动手验证图数据库的能力~

一、图模型设计

💡在实际使用中,可对 Schema 进行相应修改与管理,为便于在线体验,本 Demo 暂不支持。

在正式推理开始前,我们得先知道上文提到的关系网是怎么搭起来的。

你可以把这张网想象成三个简单的部分:

  • 人(节点):代表每个用户。
  • 关系(边):“好友”、“关注”、“点赞”、“评论”这些让每个用户之间,产生关系的互动行为。
  • 内容/社群/Tag(节点):比如一篇“旅游攻略”,或者一个“篮球爱好者群”。

💡在真实的业务里, 社群通常可以作为用户的一个属性来存储。但在我们这个演示里,特意将其单独建模成了一个节点。这是为了让你更直观地看清“用户 – 社群 – 内容”这条完整链条,感受图数据库在多跳关系查询上的优势。实际生产环境里,完全可以根据业务需求灵活调整模型。

图数据库要做的,就是把它们之间千丝万缕的联系,全部存进去,并且让这些关系可以被随时查询和分析。

节点类型包括下表:

节点类型 标签 核心属性
用户(自然人) Person person_id (唯一标识), name (昵称/姓名), city (所在城市), register_time (注册时间)
社区/圈子 Community community_id (唯一标识), name (社区名称), topic (主题分类), created_time (创建时间)
内容/帖子 Content content_id (唯一标识), title (标题), category (内容分类), publish_time (发布时间)
用户兴趣标签 UserTag tag_name (标签名,如“AI”、“旅行”)

边类型包括下表:

边类型 起始节点 → 目标节点 说明
Friend PersonPerson 好友关系(双向社交)
FollowKOL PersonPerson 关注 KOL/大V(单向社交)
MemberOf PersonCommunity 加入社区/群组
Forward PersonContent 转发/分享内容
Like PersonContent 点赞/喜欢内容
Comment PersonContent 评论内容
HasInterest PersonUserTag 用户兴趣标签
Cites ContentContent 内容引用/关联

了解完图数据库要做什么后,我们首先要在图数据库中,把基本规则创建起来。

首先,创建图类型与图空间,并执行以下语句定义节点、边结构及属性:

CREATE GRAPH TYPE IF NOT EXISTS `social_recommendation` AS {
NODE TYPE `Person` (LABEL `Person`{`person_id` STRING NOT NULL, `name` STRING DEFAULT NULL, `city` STRING DEFAULT NULL, `register_time` ZONED DATETIME DEFAULT NULL, PRIMARY KEY (`person_id`)}),
NODE TYPE `Community` (LABEL `Community`{`community_id` STRING NOT NULL, `name` STRING DEFAULT NULL, `topic` STRING DEFAULT NULL, `created_time` ZONED DATETIME DEFAULT NULL, PRIMARY KEY (`community_id`)}),
NODE TYPE `Content` (LABEL `Content`{`content_id` STRING NOT NULL, `title` STRING DEFAULT NULL, `category` STRING DEFAULT NULL, `publish_time` ZONED DATETIME DEFAULT NULL, PRIMARY KEY (`content_id`)}),
NODE TYPE `UserTag` (LABEL `UserTag`{`tag_name` STRING NOT NULL, PRIMARY KEY (`tag_name`)}),
EDGE TYPE `Friend` (`Person`)-[LABEL `Friend`{`since_time` ZONED DATETIME NOT NULL, `closeness` DOUBLE DEFAULT NULL, MULTIEDGE KEY(`since_time`)}]->(`Person`),
EDGE TYPE `MemberOf` (`Person`)-[LABEL `MemberOf`{`join_time` ZONED DATETIME NOT NULL, `role_name` STRING DEFAULT NULL, MULTIEDGE KEY(`join_time`)}]->(`Community`),
EDGE TYPE `Forward` (`Person`)-[LABEL `Forward`{`forward_time` ZONED DATETIME NOT NULL, `action_weight` DOUBLE DEFAULT NULL, MULTIEDGE KEY(`forward_time`)}]->(`Content`),
EDGE TYPE `Like` (`Person`)-[LABEL `Like`{`like_time` ZONED DATETIME NOT NULL, `score` DOUBLE DEFAULT NULL, MULTIEDGE KEY(`like_time`)}]->(`Content`),
EDGE TYPE `Comment` (`Person`)-[LABEL `Comment`{`comment_time` ZONED DATETIME NOT NULL, `content_text` STRING DEFAULT NULL, MULTIEDGE KEY(`comment_time`)}]->(`Content`),
EDGE TYPE `FollowKOL` (`Person`)-[LABEL `FollowKOL`{`follow_time` ZONED DATETIME NOT NULL, `interaction_level` DOUBLE DEFAULT NULL, MULTIEDGE KEY(`follow_time`)}]->(`Person`),
EDGE TYPE `HasInterest` (`Person`)-[LABEL `HasInterest`{`weight` DOUBLE DEFAULT NULL}]->(`UserTag`),
EDGE TYPE `Cites` (`Content`)-[LABEL `Cites`{`cite_time` ZONED DATETIME DEFAULT NULL}]->(`Content`)
};
CREATE GRAPH `social_recommendation` :: `social_recommendation`;

二、数据导入

💡本 Demo 仅预置 30 名用户及数千条社交关系,实际导入时,数据量可达千亿级甚至更多。

定义完“规则”,我们执行以下语句,导入数据。

-- 导入用户节点
TABLE `person_data` { `person_id`, `name`, `city`, `register_time` } =
("p001", "Alice", "Beijing", "2024-01-05T09:00:00 +0800"), ("p002", "Bob", "Beijing", "2024-01-06T10:30:00 +0800"), ("p003", "Charlie", "Beijing", "2024-01-07T11:00:00 +0800"), ("p004", "David", "Beijing", "2024-01-08T14:20:00 +0800"), ("p005", "Evan", "Beijing", "2024-01-09T15:00:00 +0800"), ("p006", "Fiona", "Beijing", "2024-01-10T16:45:00 +0800"), ("p007", "George", "Beijing", "2024-01-11T09:15:00 +0800"), ("p008", "Hannah", "Beijing", "2024-01-12T10:30:00 +0800"), ("p009", "Ivan", "Beijing", "2024-01-13T11:45:00 +0800"), ("p010", "Julia", "Beijing", "2024-01-14T13:00:00 +0800"),
("p011", "Kevin", "Shanghai", "2024-01-15T09:00:00 +0800"), ("p012", "Linda", "Shanghai", "2024-01-16T10:30:00 +0800"), ("p013", "Mike", "Shanghai", "2024-01-17T11:00:00 +0800"), ("p014", "Nancy", "Shanghai", "2024-01-18T14:20:00 +0800"), ("p015", "Oscar", "Shanghai", "2024-01-19T15:00:00 +0800"), ("p016", "Paula", "Shanghai", "2024-01-20T16:45:00 +0800"), ("p017", "Quinn", "Shanghai", "2024-01-21T09:15:00 +0800"), ("p018", "Rachel", "Shanghai", "2024-01-22T10:30:00 +0800"), ("p019", "Steve", "Shanghai", "2024-01-23T11:45:00 +0800"), ("p020", "Tina", "Shanghai", "2024-01-24T13:00:00 +0800"),
("p021", "Uma", "Hangzhou", "2024-01-25T09:00:00 +0800"), ("p022", "Victor", "Hangzhou", "2024-01-26T10:30:00 +0800"), ("p023", "Wendy", "Hangzhou", "2024-01-27T11:00:00 +0800"), ("p024", "Xavier", "Hangzhou", "2024-01-28T14:20:00 +0800"), ("p025", "Yvonne", "Hangzhou", "2024-01-29T15:00:00 +0800"), ("p026", "Zack", "Hangzhou", "2024-01-30T16:45:00 +0800"), ("p027", "Amy", "Hangzhou", "2024-01-31T09:15:00 +0800"), ("p028", "Brian", "Hangzhou", "2024-02-01T10:30:00 +0800"), ("p029", "Cathy", "Hangzhou", "2024-02-02T11:45:00 +0800"), ("p030", "Derek", "Hangzhou", "2024-02-03T13:00:00 +0800")
USE `social_recommendation`
FOR re IN `person_data`
INSERT (@`Person` {`person_id`: re.`person_id`, `name`: re.`name`, `city`: re.`city`, `register_time`: CAST(re.`register_time` AS ZONED DATETIME)})
-- 导入社群节点
TABLE `community_data` { `community_id`, `name`, `topic`, `created_time` } =
("c001", "AI与大模型研究", "Technology", "2023-12-01T08:00:00 +0800"), ("c002", "电商运营交流", "Business", "2023-12-05T09:00:00 +0800"), ("c003", "硬核数码科技", "Hardware", "2023-12-10T10:00:00 +0800"), ("c004", "城市慢生活", "Lifestyle", "2023-12-15T11:00:00 +0800")
USE `social_recommendation`
FOR re IN `community_data`
INSERT (@`Community` {`community_id`: re.`community_id`, `name`: re.`name`, `topic`: re.`topic`, `created_time`: CAST(re.`created_time` AS ZONED DATETIME)})
-- 导入内容节点
TABLE `content_data` { `content_id`, `title`, `category`, `publish_time` } =
("ct001", "图数据库在AI时代的机遇", "Tech", "2024-03-01T08:00:00 +0800"), ("ct002", "2024电商新风向标", "Business", "2024-03-02T09:00:00 +0800"), ("ct003", "深度解析PageRank算法", "Tech", "2024-03-03T10:00:00 +0800"), ("ct004", "我的极简桌面搭配", "Life", "2024-03-04T11:00:00 +0800"), ("ct005", "机械键盘轴体科普", "Hardware", "2024-03-05T12:00:00 +0800"),
("ct006", "LLM微调实战指南", "Tech", "2024-03-06T13:00:00 +0800"), ("ct007", "直播带货的底层逻辑", "Business", "2024-03-07T14:00:00 +0800"), ("ct008", "周末探店:魔都咖啡", "Life", "2024-03-08T15:00:00 +0800"), ("ct009", "显卡选购避坑指南", "Hardware", "2024-03-09T16:00:00 +0800"), ("ct010", "如何高效管理时间", "Life", "2024-03-10T17:00:00 +0800")
USE `social_recommendation`
FOR re IN `content_data`
INSERT (@`Content` {`content_id`: re.`content_id`, `title`: re.`title`, `category`: re.`category`, `publish_time`: CAST(re.`publish_time` AS ZONED DATETIME)})

数据导入完成后,社交数据在图数据库里就变成了这样一张图(Graph)。

三、关键查询示例

在 NebulaGraph Demo 的 console 界面中,输入图数据库查询语句,点击 run 按钮,系统就会执行一段查询指令,并把结果以可视化关系图的形式呈现出来。

比如每个用户是一个小圆点,关系是连接圆点的线条。你可以拖拽、缩放这张图,直观地看到人与人之间的连接路径,让推荐结果看得见、摸得着。

在这里,我们用四类查询语句和对应的可视化页面,进一步让你了解图数据库是如何让社交软件,精准推荐「你可能认识的人」。

💡在 Demo 中,已经完成了 schema 设计和数据导入,大家可以在 console 页面,任意探索社交场景的不同语句、玩法。

1. 查找某人的 1-3 度好友

目的:全面掌握目标用户的社交网络范围,发现潜在的兴趣传播路径和社群影响力范围。

USE `social_recommendation`
MATCH p=(src@`Person` {`person_id`: "p001"})-[:`Friend`]-{1,3}(friend)
RETURN p

基于图的解释:该图展示了从用户 p001 出发,经过 1 到 3 跳好友关系能到达的所有用户节点及路径。

2. 熟人好友推荐

目的:基于“朋友的朋友是朋友”的社交理论,通过共同好友数量为用户推荐可能认识的人。

USE `social_recommendation`
MATCH (src@`Person` {`person_id`: "p001"})-[:`Friend`]-(mutual_friend)-[:`Friend`]-(candidate)
WHERE candidate.`person_id` <> "p001" // 排除自己
AND NOT EXISTS((src)-[:`Friend`]-(candidate)) // 排除已经是好友的人
RETURN candidate.`person_id` AS recommended_friend,
candidate.`name` AS name,
count(mutual_friend) AS mutual_friends_count
ORDER BY mutual_friends_count DESC
LIMIT 10

基于表格的解释:为用户 p001 推荐共同好友最多的前 10 位潜在朋友。mutual_friends_count越高,推荐的理由越充分,用户接受推荐的概率越大。

3. 兴趣社群推荐 (基于朋友圈子)

目的:通过分析用户好友的社群加入行为,推测用户可能也感兴趣的社群,实现社群推荐。

USE `social_recommendation`
MATCH (src@`Person` {`person_id`: "p001"})-[:`Friend`]-(friend)-[:`MemberOf`]-(community)
WHERE NOT EXISTS((src)-[:`MemberOf`]-(community))
RETURN community.`name` AS community_name,
community.`topic` AS topic,
count(friend) AS friends_joined_count
ORDER BY friends_joined_count DESC
LIMIT 5

基于表格的解释:为用户 p001 推荐其好友们最常加入的 5 个社群。friends_joined_count越高,说明该社群在当前用户的社交圈子中越流行,推荐价值越高。

4. 基于相似行为的内容推荐

目的:找到与目标用户有相同转发行为的相似用户,然后将这些相似用户点赞过的其他内容推荐给目标用户,实现协同过滤推荐。

USE `social_recommendation`
MATCH (src@`Person` {`person_id`: "p002"})-[:`Forward`]->(common_content)<-[:`Forward`]-(similar_user)-[:`Like`]->(recommended_content)
WHERE NOT EXISTS((src)-[:`Like`]->(recommended_content))
AND NOT EXISTS((src)-[:`Forward`]->(recommended_content))
RETURN recommended_content.`title` AS content_title,
similar_user.`name` AS liked_by_user,
count(common_content) AS similarity_score
ORDER BY similarity_score DESC
LIMIT 10

基于表格的解释:为用户 p002 推荐由与其转发行为最相似的 10 位用户所喜欢的内容。similarity_score代表推荐内容与用户兴趣的相似度权重,liked_by_user字段提供了推荐理由。

四、总结

综上所述,图数据库的核心理念其实并不复杂,其实就是将“人与人”、“人与内容”之间的关系作为一等公民进行存储与计算。然而,正是这一思路的转变,为推荐系统带来了质的变化。

回顾前文所述的所有查询示例,可以清晰地看到,借助 NebulaGraph 构建的社交兴趣关联网络,推荐系统在以下四个维度上实现了显著提升:

1. 发现多层社交路径:突破传统推荐系统局限于个体历史行为的视野,图数据库可沿好友、关注等链路穿透至三度、四度关系,挖掘深层社交联系与影响力传导路径。

2. 提供可量化指标:基于共同好友数、相似行为频次等结构化数据,为每条推荐提供可量化的信任度依据,如“你们有8位共同好友”,让推荐结果有据可循。

3. 统一查询模式:熟人推荐、社群推荐、协同过滤等多样化策略,均可通过图数据库的统一查询语言实现,无需为每种逻辑单独搭建技术栈,显著降低开发复杂度。

4. 天然路径回溯:图数据库可清晰展示推荐结果的完整产生链路(如“A的好友B点赞了C,故推荐C给A”),为策略优化与用户沟通提供透明化支撑。

总而言之,图数据库以一种更贴近人类认知直觉的方式,将世界视作一张相互连接的网络,正是这一视角的转换,使社交推荐从“猜你喜欢”进化为“基于关系的精准匹配”。如果您的产品正面临社交推荐的效率瓶颈,不妨从绘制一张关系图谱开始探索新的可能。

💡本 Demo 展示了基础查询,仅作为简单场景下的 NebulaGraph 使用参考。在实际生产环境中,还可结合 PageRank、标签传播、连通分量等图算法,实现更智能的社区发现与兴趣扩散。

如果你觉得 NebulaGraph 能帮到你,或者你只是单纯支持开源精神,可以在 GitHub 上为 NebulaGraph 点个 Star!

每一个 Star 都是对我们的支持和鼓励✨

GitHub:https://github.com/vesoft-inc/nebula

官网:https://www.nebula-graph.com.cn/

论坛:https://discuss.nebula-graph.com.cn/