在之前的准备工作中,我们知道用户之间存在朋友(friend)关系,而且还通过用户互相评论过的商店计算过他们之间的相似度。那我们可不可以基于它们两者给用户推荐商店呢?
很显然是可以的。通过朋友给用户推荐商店在生活中是一种很常见的需求(如哪一家美食店朋友去过),因为朋友之间多多少少有一些共同兴趣之处,朋友去过的的商店该用户可能也会喜欢。一个用户可能有很多朋友,而朋友也可能去过很多家商店,他们对商店有评价高的,也有评价低的,简单将朋友去过的商店推荐给用户显然是不行的,因为这有可能给用户推荐了一个大家评价都比较差的商店,导致不好的用户体验。而且给用户推荐的商店数量应该是有限的,推荐全部商店存在上一句说到的问题;另外用户的精力是有限的,他/她只会关注最好的几个,这跟生活中的“人们只会记住第一名”有意思相似之处;最后,用户不喜欢做选择题,想一想我们平常为了找一家好的美食店翻尽美食 APP 商店的情形就清楚了。综合来讲,我们应该按朋友对商店的平均评价进行排序(Ranking),然后只给用户推荐平均评价最高的的几家商店(Top N)。
除了基于朋友关系进行推荐,还可以基于用户相似度进行推荐。两个用户相似度高说明他们兴趣相似的可能性大,给一个用户推荐另一个用户去过的商店也是很合理的。
基于朋友关系和用户相似度推荐都涉及到对社交关系进行建模。而对于此种关系建模,图形数据库有着天然的优势,因为图形数据库本身就是建立节点(Node)、关系(Relationship)及属性(Property)这 3 个概念之上,非常擅长处理关系数据;我们最终选择的 Neo4J 这款比较闻名稳定的图形数据库更是把关系当做第一等公民(first class citizen)。例如,我们建立在 Neo4J 上的关系模型如下:
用户之间存在好友关系(FRIEND)、相似度关系(SIMILARITY),以及用户对商店存在评价关系(REVIEW)。当要查询某个用户的好友、相似的用户或评价过的商店时都是比较方便的,而且在这查询过程中还可以做一些聚合的操作(如求和)。
Neo4J 相关概念
- Node:节点
- Relationship:关系,用于连接节点
- Property:属性,节点和关系都可以有属性
- Label:标签,用于标识节点或关系
- Cypher:Neo4J 提供的一个类似于 SQL 的查询工具,详细可参考其官方文档
导入数据
关于导入 CSV 文件,可以参考 Neo4J 官方详细的说明和示例。
唯一性约束
用户和商店的 db_id (从 MySQL 导出)都是唯一的,为了防止在数据导入过程中存在数据重复隐患,我们为这两者的 db_id 加上唯一性约束。除了这个好处,Neo4J 在为某一个字段添加唯一性约束时会为该字段自动添加索引,这能够提高查询效率,还能够加快朋友、相似度、评论数据集的导入(因为涉及到对用户和商店 db_id 的查询)。
添加唯一性约束的命令如下:
1 | -- 下边的 USER/BUSINESS 是标签(Label);user/business 是节点。表示"分别"具有 USER/BUSINESS 标签的 user/business 节点的 db_id 都是唯一的 |
执行完成后,我们可以在 Neo4J 提供的 Web 界面中查询操作是否成功:
查询已添加的约束
查询已添加的索引
Neo4J 导入数据
Neo4J 导入数据之前需要先设置一下导入目录,具体可参考官方说明。
导入的数据文件是在之前的准备工作中处理后保存下来的。下边是导入数据的 Cypher 语句:
1 | -- 用户 |
可视化
我们可以在 Web 界面查询得到上边导入的三种关系(以 db_id 为 1 的用户为例):
朋友关系
:双向(Neo4J 只能表示单向关系,这里的单向在实际意义上是双向的)1
MATCH (u1:USER {db_id: 1})-[f:FRIEND]-(u2:USER) RETURN u1, f, u2 limit 10;
相似度关系
:双向1
MATCH (u1:USER {db_id: 1})-[s:SIMILARITY]-(u2:USER) RETURN u1, s, u2 limit 4;
评论关系
:单向1
MATCH (u1:USER {db_id: 1})-[r:REVIEW]-(b:BUSINESS) RETURN u1, r, b limit 8;
推荐
基于朋友关系推荐
基于朋友关系的 Cypher 语句如下(以 db_id 为 65 的用户为例):
1 | MATCH (u1:USER)-[r:REVIEW]->(biz:BUSINESS), (u1)-[f:FRIEND]-(u2:USER {db_id: 65}) WHERE NOT ((u2)-[:REVIEW]->(biz)) -- 过滤出用户 u2(如 db_id 为 1 的用户)没评论过但朋友评论过的商店 |
得到的结果如下:
从以上结果,按推荐值从大到小,就可以给用户推荐商店了。
基于相似度关系推荐
基于相似度关系的 Cypher 语句如下(以 db_id 为 6 的用户为例):
1 | MATCH (u1:USER)-[r:REVIEW]->(biz:BUSINESS), (u1)-[s:SIMILARITY]-(u2:USER {db_id: 6}) |
得到的结果如下:
从以上结果,按推荐值从大到小,就可以给用户推荐商店了。