Clean Code that Works.

http://java.dzone.com/articles/modelling-data-neo4j-0

이번에도 마음대로 번역!@

내용을 요약하면 노드을의 관계에 대해서 검증(확실하게 아는)된 것이면 노드에 속성을 부여(관계+1속성)을 사용하는 것 보다, 여러 타입의 관계를 사용 하는것이 read 속도에서 압도적으로 빠르다.

지금 하고있는 생활 코딩 소스를 보면 아래와 같은 관계로 표현하는것이 관계를 여러개 타입으로 사용하는 것이다.
관계에 대한 것을 아래처럼 type으로 정의할 수도 있고, 관계 클래스를 만들어서 정의(클래스에 속성값 사용 가능, 관계 + 속성) 할 수 있다.

@RelatedTo(type = "FRIENDS", direction = Direction.OUTGOING)
private Set<leaguesummoner> friends;
@RelatedTo(type = "LIKES", direction = Direction.OUTGOING)
private Set<leaguesummoner> likes;
@RelatedTo(type = "INTEREST", direction = Direction.OUTGOING)
private Set<leaguesummoner> interest;
@RelatedTo(type = "TROLL", direction = Direction.OUTGOING)
private Set<leaguesummoner> trolls;

아래부터 번역 시작.
Java로 작성된 샘플 코드는 Neo4J Java API를 사용한 것이고, Cypher(사이퍼)로 작성된 것은 Neo4j에서 제공하는 Cypher Query Language로 된 것이다.(SQL 처럼)

=======================================================================================

'초보자를 위한 Neo4j 모델링' 시리즈의 이전 포스트에서 양방향성 관계에 대해 살펴보았다. 이 포스트에서는 다른 타입의 여러 관계와 관계에 속성을 사용하는 방법에 대해 비교해 보도록 하겠다.

검증을 위한 속성(Properties as Qualifiers)

Neo4j에서 영화에 대한 평점을 매기는 모델링에 대해 이야기해보자. 사람들은 1~5까지 평점을 매길수 있고 이것을 모델링 하는 처음으로 생각한 방법은 'RATED' 라는 관계를 생성하고 1~5사이의 값을 가지고 있는 rating이라는 속성을 만드는 것이다.

Java 와 Cypher을 사용하여 이 모델에 대한 쿼리를 만드는것은 간단하다. 펄프 픽션에 대한 긍정적인 평가들을 구할려면 아래와 같이 rating이 3이상인 것을 구하면 된다.

1.for (Relationship r : pulpFiction.getRelationships(INCOMING, RATED)) {
2.if ((int) r.getProperty("rating") > 3) {
3.Node fan = r.getStartNode(); //do something with it
4.}
5.}

동일하게 사이퍼에서는 아래와 같이 하면 된다.

1.START   pulpFiction=node({id})
2.MATCH   (pulpFiction)<-[r:RATED]-(fan)
3.WHERE   r.rating > 3
4.RETURN  fan


다양한 관계들

우리는 가능한 관계들에 대해서 다 알고 있기 때문에 각각 평점에 대해 다른 관계를 설정하는 방법을 생각할 수도 있다.  
예를 들어 1~5까지의 평점에 따라  LOVED, LIKED, NEUTRAL, DISLIKED, HATED의 관계를 만들 수 있다. 
이 관계를 그래프로 표현하면 아래와 같다.

이전에 사용했던 쿼리들은 동일한 결과를 표시하기 위해 약간 수정을 해야 한다. 펄프 픽션의 팬이 누구인지 자바로 표현하면 아래와 같다.

1.for (Relationship r : pulpFiction.getRelationships(INCOMING, LIKED, LOVED)) {
2.Node fan = r.getStartNode(); //do something with it
3.}

그리고 사이퍼로 표현하면

1.for (Relationship r : pulpFiction.getRelationships(INCOMING, LIKED, LOVED)) {
2.Node fan = r.getStartNode(); //do something with it
3.}


비교 하기

위 두가지 방법에 대한 쿼리 문법은 크게 다르지 않다. 
예를 들어 보면 10가지 다른  관계가 있고 이중에서 7가지를 조회하는 쿼리를 만들 경우 첫번째 방식이 더 효과적이라고 주장할 것이다. 
이 방식은 우리가 찾길 원하는 관계 타입에 대해서 전부 입력해서 조회하지 않아도 된다.

하지만 우리는 성능에 관점을 두고 두가지 방법으로 탐색을 해보도록 하자. 
첫 번째 실험은 두 가지 방법사이의 쓰기 처리량의 차이가 있는지 알 수 있도록 되어있다.
노드 사이의 1000개의 관계를 생성 하고 이걸 생성하는데 걸리는 시간을 측정했다. 
결과는 아래와 같다.

둘 사이의 명확한 쓰기량 차이는 보이지 않지만 탐색을 위한 경우에는 다르게 나온다.

두 번째 실험은, 100개의 노드와 5000개의 무작위 관계를 가지 쿼리를 수행했다.(무슨 말인지 이해가 안되서 아래 원문을 참조 하세요)
In the second experiment, we executed all the queries shown earlier 100 times on a graph with 100 nodes and 5,000 randomly qualified, uniformly distributed relationships (which makes the degree of each node 100, on average).

실험은 아래 3개지 다른 세팅을 가지고 수행하였다.

1. 캐쉬 없이 데이터를 디스크에서 읽는다.
2. 데이터에 대한 로우레벨 캐시만 적용되어 있고, 하이레벨 캐쉬는 중지
3. 데이터에 대한 하이레벨 캐시 적용.

아래 두가지 그림은 각각 Cypher와 Java에서 수행했을때의 결과를 보여준다.

다양한 관계를 생성하는 방법이 항상 단일관계-1개 속성의 방법보다 더 뛰어난 성능을 보여주고 가끔 8배 이상의 성능을 보여준다. 
여기에는 Neo4j에서 디스크와 메모리에서의 데이터를 구성하는 방법의 기술적인 이유가 있다. 하지만 이 내용을 여기선 다루지 않고, 다음 글에서 이야기 할예정이다.

Single-hop 탐색 측정을 수행한것이 중요한데 이 결과가 8배 빠른데 관계의 깊이가 2레벨이 되면 64배 빠를수 있고 3레벨이 되면 512배 빠를 수 있다.

결론

가능하면 다양한 관계를 설정하는 방식이 단일관계-1개 속성의 방식보다 월등한 성능을 보여준다. 이 방식은 두번째 방식 보다 항상 최소한 2배 이상 빠르다.

만약 하이레벨 캐쉬를 사용하고 native Java API를 사용하면 다양한 관계를 설정하는 방법이 두번째 방식보다 8배 이상 빠르다.