利用Hbase做二度关系人脉存储

碰到这样一个需求,需求方需要根据一个人的联系方式得到一度联系人的一度联系人,即二度关系人脉的存储,就是我们朋友的朋友,并且要求一下子把一二度联系人全部返回。这种关系就像QQ、支付宝好友中可能认识的人,抖音中关注他的人也关注了他。

由于关系人非常多,至少也得四五个亿,自然想到Hbase作为存储。并且是一下把一二度关系人全部返回,即我们每一个朋友的朋友都要返回,这就决定了我们不能单一关系存储,因为单一关系查询次数过多,这是我们所不能忍受的,所以要把一二度联系人存储作为Value放在在一起,这样根据传入人就可以直接返回其一二度关系人,无需多次查询。
 

一、 存储结构分析

其存储结构如下:

rowkey:手机号+姓名+时间戳+5位随机数  value:一度联系人手机号_姓名,二度联系人手机号_姓名

存储如下示例:

rowkey:17515081745小明155576871200012345 value:17615081745_小强,13515081745_小李
rowkey:17515081745小明155576871200015456 value:17615081745_小强,15515081745_小王
rowkey:17515081745小明155576871200010256 value:17715081745_张三,18515081745_小亮
rowkey:17515081745小明155576871200015892 value:17715081745_张三,13515081745_李四
rowkey:17515081745小明155576871200055892 value:17715081745_张三,15615081745_王五

rowkey:18515081745张无忌155576871200013457 value:15515081745_张三丰,12515081745_大师伯
rowkey:18515081745张无忌155576871200002555 value:15515081745_张三丰,12515081745_二师伯
rowkey:18515081745张无忌155576871200012345 value:15515081745_张三丰,14515081745_三师伯

rowkey:16615081745普京155576871200012345 value:15615081745_特朗普,15715081745_奥巴马

 二、存储关系分析

根据需求传入任何一个人的手机号码、姓名就可以返回其全部一度及对应二度联系人信息,如小明认识小强、小强认识小李

待存储关系人示例
要查询-零度一度联系人
小明小强
小强小李

存储时候得有以下排列组合:

需求查询关系示例
要查询-零度一度二度
小明小强小李
小李小强小明
小强小明小明的一度关系人
小强小李小李的一度关系人

 

然而存储的时候需要每次上传的关系组与数据库中关系组进行对比,看存储的关系组中在数据库中是否已经存在,若存在还需要把待上传的关系组与数据库存在的关系组的一度联系人进行排列组合。如我们待上传的一个关系组是 

小明小强
小明小李

此时,小明在已经存在数据库中存在其他关系组,如

小明张三李四
小明王五赵六
小强张飞赵七
小李张飞赵七

 这时候就需要查询小明是否数据库中存在,若存在待存入的小明一度关系人小 、小李还要和数据库中小明的一度关系人张三、王五组合。同时还要查询一度关系人小强、小李是在数据库中否存在,若存在,待存入的小强、小李一度关系人小明,还要和其已存在的一度关系人张飞组合。

新存储的需要如下关系组合 

待上传一度关系人互相组合
小强小明小李
小李小明小强
待上传一度关系人与已存在一度关系人组合
小强小明张三
张三小明小强
小李小明王五
王五小明小李
待上传零度关系人与已存一度关系的组合
小明小强张飞
张飞小强小明
小明小李张飞
张飞小李小明

 三、响应结构示例

例如,我们根据17515081745小明155576871200012345查询小明的一二度关系人,我们要把查询出的二度关系人按手机号_姓名分组合并后返回给客户端。部分代码如下:

	QueryOneLevelRespVo respVo = new QueryOneLevelRespVo();//响应实体
	respVo.setPhoneNumber(phoneNumber);//传入人号码
	respVo.setPhoneName(phoneName);//传入人姓名
    List<String> contactLists = hbaseUtil.scan(table, startRow,stopRow);
	List<KVContact> kvList = new ArrayList<>();
	//将转化为K,V实体,利用Stream进行分组合并
	for (String string : contactLists) {
		String[] oneRelationArr = string.split(",");
		KVContact kv = new KVContact();
		kv.setKey(oneRelationArr[0]);//一度联系人
		if (oneRelationArr.length==2) {
			kv.setValue(oneRelationArr[1]);//二度联系人
		}
		kvList.add(kv);
	}
	//利用Java8的Stream进行分组合并
	Map<String, List<String>> listMap = kvList.stream()
	        .collect(Collectors.groupingBy(KVContact::getKey,Collectors.mapping(KVContact::getValue, Collectors.toList())));
    //组装联系人
	List<RespConnections> respConnectionsList = new ArrayList<QueryOneLevelRespVo.RespConnections>();//一度联系人集合
	List<Contacts> contactList = null;
	RespConnections respConnection = null;
	for (Map.Entry<String, List<String>> m : listMap.entrySet()) {
		String[] oneRelationArr = m.getKey().split("_");//组装一度联系人
		respConnection = new RespConnections();
		respConnection.setPhone(oneRelationArr[0]);
		respConnection.setName(oneRelationArr[1]);
		//二度联系人集合
		contactList = new ArrayList<QueryOneLevelRespVo.RespConnections.Contacts>();
		Contacts contacts = null;
		for (String str : m.getValue()) {
			if (str!=null) {
				String[] twoRelationArr = str.split("_");//组装二度联系人
				contacts = new Contacts();
				contacts.setPhone(twoRelationArr[0]);
				contacts.setName(twoRelationArr[1]);
				contactList.add(contacts);
			}
		}
		respConnection.setContacts(contactList);
		
		respConnectionsList.add(respConnection);
	}
	respVo.setConnections(respConnectionsLis;

响应实体类QueryOneLevelRespVo

import java.io.Serializable;
import java.util.List;

public class QueryOneLevelRespVo implements Serializable {

    private static final long serialVersionUID = 1L;
    /**
     * 手机号
     */
    private String phoneNumber;
    /**
     * 手机号
     */
    private String phoneName;
    /**
     * 一度联系人集合
     */
    private List<RespConnections> connections;
    
	public String getPhoneNumber() {
		return phoneNumber;
	}
	public void setPhoneNumber(String phoneNumber) {
		this.phoneNumber = phoneNumber;
	}
	public String getPhoneName() {
		return phoneName;
	}
	public void setPhoneName(String phoneName) {
		this.phoneName = phoneName;
	}
	public List<RespConnections> getConnections() {
		return connections;
	}
	public void setConnections(List<RespConnections> connections) {
		this.connections = connections;
	}
	
	public static class RespConnections{
    	 /**
    	  * 一度联系人号码
    	  */
    	 private String phone;
    	 
    	 /**
    	   * 一度联系人姓名
    	   */
    	 private String name;
    	 /**
    	  *  二度联系人集合
    	  */
    	 private transient List<Contacts> contacts;
    	 
		public String getPhone() {
			return phone;
		}
		public void setPhone(String phone) {
			this.phone = phone;
		}
		public String getName() {
			return name;
		}
		public void setName(String name) {
			this.name = name;
		}
		public List<Contacts> getContacts() {
			return contacts;
		}
		public void setContacts(List<Contacts> contacts) {
			this.contacts = contacts;
		}

		public static class Contacts {
    	    	 /**
    	    	   * 二度联系人姓名
    	    	   */
    	    	private String name;
    	    	 /**
    	    	  * 二度联系人手机号
    	    	  */
    	    	private String phone;
    	    	 
				public String getName() {
					return name;
				}
				public void setName(String name) {
					this.name = name;
				}
				public String getPhone() {
					return phone;
				}

				public void setPhone(String phone) {
					this.phone = phone;
				}
    	 }
		
    }
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值