neo4j前端开源代码
以下文章来自Neo4j系列,该系列首次出现在maxdemarzi.com (由Neo倡导者Max De Marzi 撰写的博客)上。
开源软件的好处之一是,如果您想更改某件事的完成方式,则可以 。
在新技术 ,我们有一个小团队“现场工程师”谁不真正在产品上 ,而是与产品的工作。 我们帮助客户解决各种问题,回答问题,提出建议以及我们为使人们的项目成功所需要做的一切。 不久前,我有一张遍历遍历时间比他们希望的更长的支持票。
考虑一下社交网络,您可能想做的一件事就是告诉用户他们朋友网络的规模。 但是为什么要停在那里? 他们的朋友朋友甚至朋友网络的朋友怎么样? 与关系数据库相比,这是图形数据库擅长的问题类型。 让我们看看他们在做什么:
@GET
@Path("/network/{id}")
public Response getNetworkSize(@PathParam("id") Long id, @Context GraphDatabaseService db) throws IOException {
Node user = db.getNodeById(id);
final Evaluator depth = Evaluators.toDepth(4);
final TraversalDescription traversalDescription = new TraversalDescriptionImpl().
breadthFirst().evaluator(depth).relationships(FRIENDS, Direction.OUTGOING);
final Integer[] sizes = new Integer[] { 0, 0, 0, 0, 0 };
for (final org.neo4j.graphdb.Path path : traversalDescription.traverse(user))
sizes[path.length()]++;
}
return Response.ok().entity(objectMapper.writeValueAsString(sizes)).build();
}
您正在查看的是一个非托管扩展 ,该扩展使用一个节点id参数并将该节点用作遍历的起点。 遍历将首先沿FRIENDS关系遍历广度,直至深度为4跳,并且在路径的每一步将递增size数组内的计数器。 这里不是很明显,但是Neo4j遍历中的默认“唯一性”是“ 全局唯一性” ,因此,如果我们已经看过节点,则不会两次访问该节点。
为了测试这一点,我创建了一个具有一百万个节点的图形,并在它们之间随机创建了1,450万个唯一关系。 我可以通过REST查询它,它看起来像这样:
http> get /example/service/network/1
==> 200 OK
==> [1,20,230,3446,48444]
那有多快?
我转向久经考验的真正的加特林工具,并为它添加20秒钟的节点ID列表,并重复测试几次:
import com.excilys.ebi.gatling.core.Predef._
import com.excilys.ebi.gatling.http.Predef._
import akka.util.duration._
import bootstrap._
class NetworkSize extends Simulation {
val httpConf = httpConfig
.baseURL("http://localhost:7474")
.acceptHeader("application/json")
val testfile = csv("test-data.txt").circular
val scn = scenario("Network Size")
.during(20) {
feed(testfile)
.exec(
http("Test Network Size")
.get("/example/service/network/${id}")
.check(status.is(200)))
.pause(0 milliseconds, 1 milliseconds)
}
setUp(
scn.users(8).protocolConfig(httpConf)
)
}
…和最好的结果:
每秒大约60个请求,平均延迟为135ms,最大延迟为640ms。 不错,但是可以更快吗? 我做了一些挖掘工作,以了解幕后情况,并发现了一些有关如何维护节点的全局唯一性的有趣信息。
class GloballyUnique extends AbstractUniquenessFilter
{
private final Set<Long> visited = new HashSet<Long>();
GloballyUnique( PrimitiveTypeFetcher type )
{
super( type );
}
public boolean check( TraversalBranch branch )
{
return visited.add( type.getId( branch ) );
}
...
在代码中,我们有一组Longs,可以帮助我们判断是否已经看到一个节点。 这是有道理的,可以工作,但是如果我们采用不同的方式会怎样。 除了存储Longs之外,我们还可以使用BitSet来存储true或false,无论是否看到节点ID。 但是遇到一个小问题……标准的Java BitSet被限制为2,147,483,647(一个int),我们可以拥有数百亿个节点。 但是不要担心, OpenBitSet可以解救。 Lucene的这个小类最多可以处理64 * 2 ** 32-1位,我将给您一点时间做数学……这是274,877,906,943位。 完善。
所以我从github的Neo4j存储库中拉出1.9分支并进行了编辑 :
class GloballyUnique extends AbstractUniquenessFilter
{
private final OpenBitSet visited = new OpenBitSet();
GloballyUnique( PrimitiveTypeFetcher type )
{
super( type );
}
public boolean check( TraversalBranch branch )
{
if ( visited.get( type.getId( branch ) ) ) {
return false;
} else {
visited.set( type.getId( branch ) );
return true;
}
}
...
我还向LICENSES.txt和NOTICES.txt文件添加了“ Lucene Core”,并修改了pom.xml文件:
<properties>
...
<lucene.version>3.6.2</lucene.version>
</properties>
...
<dependency>
<groupId>org.apache.lucene</groupId>
<artifactId>lucene-core</artifactId>
<version>${lucene.version}</version>
</dependency>
然后在/ neo4j / community / kernel目录中运行:
mvn clean install -DminimalBuild -Dlicense.skip=true
并将target / neo4j-kernel-1.9.5.jar复制到我的neo4j / lib目录中,以覆盖现有内核并重新运行测试:
现在,我们每秒可处理近100个请求,平均时间为81毫秒,最大为420毫秒,几乎是我们以前的性能数字的两倍。 几行代码还不错。 因此,请看一下Neo4j的内幕,如果发现不喜欢的东西,请对其进行更改……并向我们发送您的拉取请求!
顺便说一句……如果需要,您可以走得更快,但这并不漂亮。 我称它为OpenBitSet Dance:
@GET
@Path("/network/{id}")
public Response getNetworkSize(@PathParam("id") Long id, @Context GraphDatabaseService db) throws IOException {
Node user = db.getNodeById(id);
final Long[] sizes = new Long[] { 1L, 0L, 0L, 0L, 0L };
OpenBitSet[] seen = new OpenBitSet[]{new OpenBitSet(),new OpenBitSet(),new OpenBitSet(),new OpenBitSet(),new OpenBitSet() };
seen[0].set(user.getId());
for(Relationship level1 : user.getRelationships(Direction.OUTGOING, FRIENDS )){
Node friend = level1.getEndNode();
seen[1].set(friend.getId());
}
for (int i = seen[1].nextSetBit(0); i >= 0; i = seen[1].nextSetBit(i+1)) {
Node friend = db.getNodeById((long)i);
for(Relationship level2 : friend.getRelationships(Direction.OUTGOING, FRIENDS )){
Node fof = level2.getEndNode();
seen[2].set(fof.getId());
}
}
for (int i = seen[2].nextSetBit(0); i >= 0; i = seen[2].nextSetBit(i+1)) {
Node friend = db.getNodeById((long)i);
for(Relationship level2 : friend.getRelationships(Direction.OUTGOING, FRIENDS )){
Node fof = level2.getEndNode();
seen[3].set(fof.getId());
}
}
for (int i = seen[3].nextSetBit(0); i >= 0; i = seen[3].nextSetBit(i+1)) {
Node friend = db.getNodeById((long)i);
for(Relationship level2 : friend.getRelationships(Direction.OUTGOING, FRIENDS )){
Node fof = level2.getEndNode();
seen[4].set(fof.getId());
}
}
seen[1].andNot(seen[0]);
seen[2].andNot(seen[1]);
seen[2].andNot(seen[0]);
seen[3].andNot(seen[2]);
seen[3].andNot(seen[1]);
seen[3].andNot(seen[0]);
seen[4].andNot(seen[3]);
seen[4].andNot(seen[2]);
seen[4].andNot(seen[1]);
seen[4].andNot(seen[0]);
sizes[0] = seen[0].cardinality();
sizes[1] = seen[1].cardinality();
sizes[2] = seen[2].cardinality();
sizes[3] = seen[3].cardinality();
sizes[4] = seen[4].cardinality();
return Response.ok().entity(objectMapper.writeValueAsString(sizes)).build();
}
如果您找到进一步提高速度的方法,请告诉我!
-
通过Shutterstock 打开标志照片
翻译自: https://jaxenter.com/neo4j-and-the-power-of-open-source-108097.html
neo4j前端开源代码