一、原始互动图
原始图谱结构包含Twitter账号和帖子,关系类型有点赞|转发|评论等,生成的图谱如下所示:
二、生成的虚拟图
生成虚拟图之后,隐藏了全部的帖子节点,只展示出人与人之间的关系:
三、可视化数据格式样例
{"totalNodeSize":3,"totalRelationSize":2,"consume":"Total consume 0s,average consume 0s/request","failed":0,"totalQuery":1,"message":true,"results":[{"data":[{"graph":{"relationships":[{"startNode":161,"id":40122,"type":"发帖","endNode":34650,"properties":{"relationType":"发帖","pub_time_mills":1573569457000,"update_time_mills":1582283028366}},{"startNode":497,"id":40123,"type":"转发","endNode":34650,"properties":{"pub_time_mills":1573575391000,"update_time_mills":1582283028366}}],"nodes":[{"id":161,"properties":{"_unique_uuid":"0cd911cea1f97c6ef610721ac81adc61","name":"Denise Ho (HOCC)","nameNodeSpace":"Denise Ho (HOCC)","update_time_mills":1582283028366},"labels":["TwitterID"]},{"id":34650,"properties":{"_unique_uuid":"e6c41e85e33ee38d826fcf2c0ba2d8b9","nameNodeSpace":"转推了","name":"转推了","update_time_mills":1582283028366},"labels":["Twitter发帖"]},{"id":497,"properties":{"_unique_uuid":"6fa25ec0b043445c144e44e33c57121d","loadRecommendEnginePathSize":6,"nameNodeSpace":"Wenli9187","name":"Wenli9187","update_time_mills":1582283148428},"labels":["TwitterID"]}],"properties":[]}}],"columns":["p"]}],"successful":1}
四、相关类
实现思路:遍历图谱,对于账号之间存在帖子的两层关系线进行合并,将账号直接连接到另一个账号,实现收缩图谱的目的。生成的虚拟图不持久化到到图库,是即时分析的结果。
- VirtualGraphManager虚拟图IDS管理对象
/**
* @author YanchaoMa [email protected]
* @PACKAGE_NAME: casia.isi.zdr.graph.services.graph.virtual
* @Description: TODO(虚拟图IDS管理对象)
* @date 2019/5/20 16:18
*/
public class VirtualGraphManager {
// 从RESULT中获取NODES - KEY
public final static String nodesFieldName = "nodes";
// 从RESULT中获取RELATIONSHIPS - KEY
public final static String relationshipsFieldName = "relationships";
/**
* 虚拟图ID都为负值
**/
/**
* 自增关系ID变量 - 互动网络虚拟关系线ID都为负值
**/
private static long relationshipId;
/**
* 关系线虚拟ID管理LIST
**/
private final static CopyOnWriteArrayList<VirtualRelaId> virtualRelaIdManager = new CopyOnWriteArrayList<>();
private static void clear(int virtualRelaIdSize) {
if (virtualRelaIdManager.size() >= virtualRelaIdSize) {
virtualRelaIdManager.clear();
}
}
private static void add(VirtualRelaId virtualRelaId) {
if (!virtualRelaIdManager.contains(virtualRelaId)) {
virtualRelaIdManager.add(virtualRelaId);
}
}
/**
* @param targetId:开始节点ID
* @param endId:结束节点ID
* @return
* @Description: TODO(从虚拟关系ID列表中获取关系ID)
*/
private static long getVirtualRelaId(long targetId, long endId, String virRelaName) {
return virtualRelaIdManager.parallelStream()
.filter(v -> v.pathEquals(targetId, endId, virRelaName))
.findAny().orElse(new VirtualRelaId()).getRelationshipId();
}
/**
* @param
* @param targetId
* @param endId
* @return
* @Description: TODO(关系ID生成 - 获取关系ID)
*/
// 并发操作可能导致生成相同的ID,加锁操作
public synchronized static long relaIdProducer(long targetId, long endId, String virRelaName) {
Optional optional = Optional.ofNullable(getVirtualRelaId(targetId, endId, virRelaName));
if (optional.isPresent() && (long) optional.get() != 0) {
return (long) optional.get();
} else {
clear(1000);
add(new VirtualRelaId(targetId, endId, -++relationshipId, virRelaName));
return -relationshipId;
}
}
/**
* @param targetId:开始节点ID
* @param endId:结束节点ID
* @param virRelaName:虚拟关系名称
* @param properties:除关系名之外的其它关系属性
* @return
* @Description: TODO(关系对象生成)
*/
public static JSONObject virRelas(long targetId, String virRelaName, long endId, JSONObject properties) {
if (targetId != endId) {
JSONObject pro = new JSONObject();
pro.put("name", virRelaName);
if (properties != null) {
pro.putAll(properties);
}
JSONObject relationship = new JSONObject();
relationship.put("startNode", targetId);
relationship.put("id", relaIdProducer(targetId, endId, virRelaName));
relationship.put("type", virRelaName);
relationship.put("endNode", endId);
relationship.put("properties", pro);
return relationship;
}
return null;
}
}
- VirtualRelaId虚拟图关系ID对象-如何排重关系
/**
* @author YanchaoMa [email protected]
* @PACKAGE_NAME: casia.isi.zdr.graph.services.graph.interactive
* @Description: TODO(虚拟图关系ID对象)
* @date 2019/5/16 11:37
*/
public class VirtualRelaId {
private long startNodeId;
private long endNodeId;
private String virRelaName;
private long relationshipId;
public VirtualRelaId() {
}
public VirtualRelaId(long startNodeId, long endNodeId, long relationshipId, String virRelaName) {
this.startNodeId = startNodeId;
this.endNodeId = endNodeId;
this.relationshipId = relationshipId;
this.virRelaName = virRelaName;
}
@Override
public String toString() {
return "VirtualRelaId{" +
"startNodeId=" + startNodeId +
", endNodeId=" + endNodeId +
", virRelaName=" + virRelaName +
", relationshipId=" + relationshipId +
'}';
}
/**
* @param
* @return
* @Description: TODO(( 开始节点)->(结束节点) (结束节点)->(开始节点) 关系名一样时使用相同的关系ID )
*/
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
VirtualRelaId that = (VirtualRelaId) o;
return ((this.startNodeId == that.startNodeId && this.endNodeId == that.endNodeId) ||
(this.startNodeId == that.endNodeId && this.endNodeId == that.startNodeId)) &&
(this.virRelaName.equals(that.virRelaName));
}
@Override
public int hashCode() {
return Objects.hash(startNodeId, endNodeId, virRelaName);
}
/**
* @param
* @return
* @Description: TODO(判断是否是同一虚拟条路径)
*/
public boolean pathEquals(long startNodeId, long endNodeId, String virRelaName) {
if (((this.startNodeId == startNodeId && this.endNodeId == endNodeId) ||
(this.startNodeId == endNodeId && this.endNodeId == startNodeId)) &&
(this.virRelaName.equals(virRelaName))) {
return true;
}
return false;
}
public long getStartNodeId() {
return startNodeId;
}
public void setStartNodeId(long startNodeId) {
this.startNodeId = startNodeId;
}
public long getEndNodeId() {
return endNodeId;
}
public void setEndNodeId(long endNodeId) {
this.endNodeId = endNodeId;
}
public long getRelationshipId() {
return relationshipId;
}
public void setRelationshipId(long relationshipId) {
this.relationshipId = relationshipId;
}
public String getVirRelaName() {
return virRelaName;
}
public void setVirRelaName(String virRelaName) {
this.virRelaName = virRelaName;
}
}