@yanglfyangl
2018-07-19T03:11:24.000000Z
字数 4341
阅读 683
在这里的关系引擎的重点还是关系存储引擎,并不是关系查询引擎
- 前期用Redis开发,功能调试
- 上线前转成Pika+Redis模式
- 存储基本关系
- 关系列举(尽量不要有上限)
- 基本关系查找
- 共同关系查找
- WIR关系查找
- 支持GEO查找
- 支持一定的排行榜能力
目前不支持的
- 事务
- 超过2跳的查询(这部分最好用图来做)
基于关系的特点(可失败,由用户补偿),所以第一期暂时不支持事务。
/*
为了让关系的命名更加合理,命名最好符合如下模式
"node名" +"关系" + ”node名“
*/
class NodeType:{
public String User = "User"
public String Circle = "Circle"
public String Crowd = "Crowd"
}
class RelationEnum:{
public String BeFriend = "ToBeFriends",
public String UserJoinToCircle = "UserJoinToCircle"
public String CrowdJoinToCircle = "CrowdJoinToCircle"
}
//如果是双向,则建立两个set,将关系全存下来。
//如果是单向,则只存一方关系即可。
class Direction:{
public int Single = 0;
public int Both = 1;
}
/*
我称为Who In Reation搜索,比如:
解释一下什么是Who In Relation搜索
1. "Who in Relation"并不是传统意义上的搜索,他还是通过key来找value的一种方式。
2. 主要支持的都是通过K-V的交并集等操作能做到的结果。
下面个例子:
想搜索我在某个圈子中的好友,可以这样做
{
whoID = 人的ID;
whoRelation = RelationEnum.ToBeFriends
inID = 圈子ID;
inRelation = RelationEnum.UserJoinToCircle;
}
实现方式是:
取出两个Set来,做交集操作。
*/
class WIRCriteria:{
String whoID;
RelationEnum whoRelation;
String inID;
RelationEnum inRelation;
}
class RelationEngine:{
/*
更新节点的信息。
1. 如果不存在,则创建;
2. 如果存在,则更新;
*/
public bool updateNodeBasicInfo(String type,String nodeID, Map<String, String> info);
/*
删除节点
为了达到性能尽可能高,删除节点时只做
1. 删除节点基本信息;
2. 删除本节点对应的关系;
*/
public bool deleteNode(String nodeID);
/*
建立关系
结果为在KV库中增加
1. Key为:
A. 主动部分(比如加入群,是人主动)
"rel_"+relation+"_ID_"+fromID+""
意思是:由这个fromID来drive了这样一个relation
B. 包含/被动部分(比如加入群,是群)
"rel_"+relation+"_ID_"+toID+""
意思是:某个toID中,包含了某个relation中加入了新值。
2. value格式为:Set
*/
public Boolean joinRelation(String fromID, String toID, RelationEnum relation, Direction direction);
/*
退出关系
*/
public Boolean quiteRelation(String fromID, String toID, RelationEnum relation, RelationDirection direction);
/*
获取关系列表
*/
public List<String> getRelationList(String fromID, String toID, RelationEnum relation, String startID, int count);
/*
获取两两共同关系,例如:共同好友,共同圈子,共同社群。。。
*/
public List<String> getCommonRelations(String id1, String id2, RelationEnum relation);
/*
获取WIR关系
*/
public List<String> searchWIR(WIRCriteria cirteria);
}
/*
稍复杂些的查询逻辑用GEOCriteria
主要业务逻辑是
1. 通过BaseQuery和Relation各做一次搜索
2. count最大值为1000
3. 得到结果后做交集
4. 交集结果小于10时,但结果超过1000时,则再做一次搜索。
*/
class GEOCriteria:{
BaseQuery:{
groupName
lat
lon
radio
}
Relation:{
String whoID;
RelationEnum relation;
}
}
/*
更新某个节点的 GPS相关信息
如果不设置分组,则是放到大组中。(比如默认查找人员,或圈子等,不需要设置分组名)
*/
public bool updateNodeGeoInfo(String groupName, String nodeID, String lon, String lat);
/*
获取某个组中按中心点和半径的”Node"列表
*/
public List<String> searchByLocation(String groupName, String lon, String lat, double radio, int count)
/*
按结果进行搜索
*/
public List<String> searchByCriteria(GEOCriteria cirteria)
/*
更新某个节点的 "Scroe"(分数) 相关信息
*/
public bool updateNodeScroeInfo(String GroupName, String nodeID, score);
其它的与GeoHandler类似。。。
//生成或从mysql获取用户ID
RelationEngine engine = RelationEngine.getInstance();
engine.updateNodeBasicInfo(NodeType.User, usrID, userInfoDTO);
//生成或从库中获取圈子ID
RelationEngine engine = RelationEngine.getInstance();
engine.updateNodeBasicInfo(NodeType.Circle, cirID, userInfoDTO);
RelationEngine engine = RelationEngine.getInstance();
engine.joinRelation(usrID, circleID, RelationEnum.UserJoinToCircle, Direction.Both)
RelationEngine engine = RelationEngine.getInstance();
engine.GetGeoHandler().updateNodeGeoInfo(userID, ..., "UserLocation");
//查找当前GPS坐标周围的人
List<String> ret = engine.GetGeoHandler().searchByLocation("UserLocation", 100);
//查找当前GPS坐标周围的我的好友
Criteria criteria = new Criteria()
criteria.setBaseQuery();
criteria.setRelationQuery();
List<String> ret = engine.GetGeoHandler().searchByCriteria(criteria);
RelationEngine engine = RelationEngine.getInstance();
engine.GetRankHandler().updateNodeRankInfo(userID, ..., "UserLocation");
//获取某个排行榜的用户列表
List<String> ret = engine.GetRankHandler().getListByRank("UserLocation", 100);
//获取某个排行榜中某个ID所在的位置
int ret = engine.GetRankHandler().getListByRank("UserLocation", myID);
//获取我好友在某个排行榜中的排行
Criteria criteria = new Criteria()
criteria.setBaseQuery();
criteria.setRelationQuery();
//获取排行榜ID
String rankID = engine.GetGeoHandler().queryRank(criteria);
//获取某个排行榜中某个ID所在的位置,-1表示未上榜;-2表示排行榜已过期不存在了。
int ret = engine.GetRankHandler().getListByRank(rankID, myID);
有很多类似这样的场景,比如
- 获取我的好友列表,但不是整体的好友列表,而是某个时刻之后的好友列表。
- 不光是好友列表,还有变化,比如说是删除了好友,好友信息更新了等等操作。
- 还需要支持分页
如果不能支持,则对于移动端缓存数据,几乎就变成了不可能,特别是想支持很大数据量的情况。
所以,关系引擎需要支持类似的操作记录。
方法:
- 每个releation都建立一个###_log 的zset, scroe为当前的时间戳。
- 写入格式为:[ID_ACTION, 时间戳]
- 对于所有的add, delete操作,写入入对列。
- 对于update操作。分两种
- 全局性的node update更新,放到一个全局性的update更新 log中。(全局)
- 某个特殊关系的,直接写入关系对应的当中去。(局部)
需要按某个时间戳获取时
- 将全局与局部的进行并集。
- 找出前端时间戳之后的数据。
- 将数据进行去重。
- 进行后续操作。