MDB(엑세스) 나 엑셀(XLS) 을 자바로 읽기위해서 , 먼저 JDBC-ODBC 를 알아보았는데

제어판에 32비트 , 64비트 ODBC 설정하는 부분에서  엑셀 , 엑세스에 대한 드라이버가 32비트만 설정가능한듯..

이것저것 찾아봤는데,  결국 나의 64비트 윈도우즈 8 에서는 답이 없는거 같아서 또 찾아보다가 발견한것.

생각해보니 한 7년전쯤에 , MFC 로 MDB 읽어서 사용하는 어플을 만들었다가, 32비트 OS 에서만 되서 망했던 

기억이 떠오르기도 하고... 


Apache POI (http://poi.apache.org/)


이런게 있더군요. 역시나 MS 세상에서  없을리가...한글 블로그도 엄청 많고..ㅎㅎ 

추천 블로그 : http://yanggoony.tistory.com/3


하지만 먼가 쿼리처럼 검색해서 데이터를 가져오고싶은데 그건 안되는것같네요. 그래서 일단 메모리로 올려서

검색기능을 직접만들어서 사용함.


P.S  

파이썬으로 MDB 읽어왔더니 (따로 ODBC 제어판에서 설정같은거 안하고) 바로됨 +.+  왜 자바로는 안됬을까? 

아마 자바가 64비트 버전이고 파이썬은 32비트 버전으로 윈도우에 깔아놔서 그런게 아닌가 싶습니다.

자바 32비트 버전깔아서 확인해보면 되는데.. 귀찮아서 ;;

import pypyodbc 
             
conn = pypyodbc.win_connect_mdb('D:\\Share\\EC_content\\ec_qst_20151104.mdb')

#connection_string = 'Driver={Microsoft Access Driver (*.mdb)};DBQ=D:\\database.mdb'

#connection = pypyodbc.connect(connection_string)

cur = conn.cursor()

SQL = 'select Question from EC_QST where seq > 3000 and seq < 3010'


cur.execute(SQL)

print "column name"


for d in cur.description :
    print d[0]



print "data---"

for row in cur.fetchall() :
    for field in row :
            print (field, ' ')
    
    print (' ')

cur.close()
conn.close()


https://code.google.com/p/pypyodbc/


다음 내용은 정익사의 화일구조의 마지막에 나온 소스 (부록3. 디스크 기반 해시) 를 기반으로 설명된다.



                                                             (그림 - 1)


그림에서 주요 포인트는 :  디스크 , HashPage , HashRecord  이다. 


1. 초기화 


    -  디스크에 저장하기위해 파일을 만든다.(creat 함수, fopen 함수이용) 

    -  페이지 자체의 크기를 4096byte 로 설정한다.   (실제 디스크의 블럭크기와 일치시키면 좋을듯) 

    -  페이지의 갯수를 3000개로 설정한다. (자신이 만들 어플리케이션의 용도에 맞추면 좋고~) 

    -  HashPage  객체하나로  1->2999 번  (1개는 헤더용)  값을 바꾸어서 , 그 내용을 디스크상에 그대로 쓴다.

    -  아직 HashPage 안에 recArray 는 아직 아무 값이 없겠지요.

    -  결국 HashPage 는 아래처럼 초기화 된다. (pageNo 만 의미있는 수치를 갖음) 

           HashPage->pageNo = 1~2999

           HashPage->numOfRecords = 0;

     HashPage->prePageNo = -1;

     HashPage->nextPageNo = -1;

           HashPage->recArray = NULL;


                

2. 삽입 


 - 10,  'helloworld'  를 삽입한다고 치자.  (key 가 10 ,  value 가 "helloworld) 


1. 먼저  임의의 HashPage 객체를 만든다.

2. 10을 해쉬함수를 통해서 목표지점을 얻는다. ( key % 3000 => 10 ) 목표지점은 10 페이지. 

3. 디스크상의 10 번째 페이지를 검색해서  (검색방법은 아래 3.검색에서 살펴보자) 

     3-1. 동일한 키가 존재하면 FALSE 혹은 NULL 을 리턴한다.  (동일키 삽입 불가 정책) 

     3-2. 동일한 키가 없으면 4번으로 

4. 10번 페이지를 디스크상에서 읽어서 HashPage 객체에 저장. 

5. 해당 페이지(10번 페이지) 에 여유공간이 있는지 살펴본다.

     5-1. 여유 공간이 있으면 6 번으로 간다.

     5-2. 여유 공간이 없으면 다음 페이지 (11번 페이지) 로 넘어가서 여유공간을 확인한다.

     5-3. 모든 페이지에 여유 공간이 없으면 새로운 페이지를 만든다.  (3000개 -> 3001개의 페이지가 되겠지 )

6. HashPage 객체에 (10, "helloworld") 를 추가해서

7. 위의 객체를  디스크상의 해당 페이지 위치에 쓴다.



3. 검색 


- 위에서 말한  디스크상의 10번째 페이지를 검색해서 key 10 번이 있는지 확인해보자

1.  해당 10번 페이지를 디스크에서 읽어옴 

2.  recArray 에서 key 가 10번이 있는지 확인한다. 

     2-1.  있으면  멈추고  리턴 해줌.  ( 동일 키가 있다고 알려줌)  

3. 해당 페이지 (10 번 페이지) 에 키가 없으면 , 다음 페이지를 검색한다.( 페이지 끝까지 검색한다. 좀 비효율적) 

4. 모든 페이지에서 key 10 번을 못찾으면 FALSE 를 리턴해준다. 



정리를 해보자.


- 디스크상에 파일을 통해서 고정크기를 할당해두고, 고정크기만큼 나누어준다.

- 고정크기를 페이지라고하고 , 해당 페이지에는 같은 값으로 해슁된 키를 우선 넣어둔다.

- 한곳의 페이지에 키들이 몰리면, 나머지 페이지들의 공간에 낭비가 생기기때문에,

  페이지에 공간이 없으면 다음 페이지에 키를 넣어둔다. (다음페이지도 없으면 다다음) 

- 덕분에, 검색할때는 모든 페이지를 검색해야하는 단점이 있다.



코드 몇 조각  (전체소스는 책 참고. 인터넷에 소스는 없는듯)


struct hashRecord{

Key key;

Value value;

} ;


struct hashPage{

int pageNo;

int numOfRecords;

int prevPageNo;

int nextPageNo;

HashRecord * recArray;

};


struct BufferManger{

File * fp;

int pageSize;

int maxPageNo;

int lastFreePageNo;

};


BOOL readPage(int pageNo, BYTE* buffer){

int ret;

fseek(bufferManger->fp, pageNo * bufferManager->pageSize, SEEK_SET);

ret = (int) fread(buffer, sizeof(BYTE), bufferManger->pageSize, bufferManager->fp);

if(ret>0) return TRUE;

else return FALSE;

}


https://www.knime.org/files/knime_bigdata_energy_timeseries_whitepaper.pdf   요약 




요약


"빅데이터" 컨셉을 둘러싼 주요 토픽중에 하나는 거대한 시간기반 데이터 또는 검침(telemetry) 데이터의 활용이다.의미있는 분석에 사용하고자 더욱 더 상세한 데이터를  얻는게 저비용의  획득 및 저장 디바이스의 출현과 함께 가능해지고있다. 

데이터에 대한 매우 높은 상세 해상도는 주로 시간을 말하는데, (역주: 이전에 전력회사는 과금을 위해서  가정당 한달에 하나정도의 데이터만 필요했다면, 이제는 수요조절등을 위해서 1분당 혹은 1초당 데이터가 필요해질수있다.)   

요즘,  시간 스트리밍 데이터는 기반 시스템에 대해 더 잘 알기위해 또는 높은 정확도로 미래 사건을 예측하기위해  거의 모든 디바이스로부터 기록될수있는 환경이 조성되고있다.

이 작업은 아일랜드 스마트 에너지 모의운용에서 나온 - 각각의 미터 IDs 와 함께 시간당 모니터링된 6000 가구와 회사의 전력사용을 통한 -  데이터에 촛점을 맞추고있다. 

이 프로젝트의 주요 목적은 2가지가있다:


- 고객 맞춤 계약을 정의하기위한 능력 키우기

- 미래에 전력부족 및 전력과잉으로부터 전기 회사를 방어하기위한 전기사용 예측 


6000 의 커스터마이징된 계약 제공 정의는 사실 현실적이진 않다. (실제는 훨씬 많다) 목적은 고객 맞춤 계약을 정의하기위한,  공통적인 전기사용 행동양식 을 가진 작은 그룹을 정의하게 되는것이다. 그러므로 이 프로젝트의 첫번째 단계는 - 데이터를 수집하고, 정제하고 변환한 후에 -   전기 사용 행동에 관한 각각의 미터 ID 를 나타내기 위한 몇몇개의 측정치를  정의하는 것 이었다.

그런 측정치들은  오리지널 6000 미터 IDs  를  전기사용에 관해  비슷한 행동양식을 가진 미터 IDs 를 포함한 최대치 30 클러스터로 그룹 짓기 위해 사용되었다. 그 클러스터의 전기사용수치에 대한 평균 타임시리즈는 클러스터 타임시리즈 프로토타입으로서 채택되었다.

두번째 목적이 염두되는 한도에서 -  즉 전기소비에 대한 예측같은 -  몇몇 옵션들이 활용된다.
그 예측은 전체 에너지 사용량을 염두할수있다. 그러나 정확치 않을수도있고 기저 사용 패턴이 너무 일반적일수도있다. 


스케일의 다른 쪽 끝부분은 미래 에너지 사용은 각각의 미터 ID 에 대해 예측될수있다. 이것은 문제를 오버해석할수도 있는데 마지막 결과의 매우 어려운 해석에 도달하기위해  과도한 양의  계산을 하기 때문이다.

절충안으로서, 우리는 오직 시간상 어떤 포인트에서 전체 전기사용의 부분적 수치로서 클러스터의 프로토타입 타임시리즈에 촛점을 맞추었다. 자동회귀(auto-regressive) 모델은 각각 그것의 과거에 기반하여 미래의  타임시리즈를 예측하는것으로 채택되었다. 이 프로젝트는 또한 "빅데이터" 기회를 부가적으로 제공했다.  6000 meter IDs 에서 일년이 넘는 동안 매 30분 각격의 샘플을 -이것에 적합하며 파워풀한 머쉰상에서도 이 데이터의 프로세싱은 꽤 오래 걸린다 - 의미있는  많은 데이터를 생산했다. 

고전적인 접근을 빅데이터 접근과 비교하면,  분석의 첫번째 부분은 KNIME 를 활용한 빅데이터 엔진을 사용하여 돌렸다.

이 논문에서 기술된 분석은 대중적으로 활용할만한 데이터 와 거대한 양의 데이터 변형 ,타임시리즈 클러스터, 타임시리즈분석적용, 그리고 예측분석 및 비지니스 판단을 위해  오픈소스 KNIME (나임) 플랫폼을 사용한다.

빅데이터 컴포넌트들이 오픈소스는 아니고  trial 로 활용할수있다. 모든 예제는 www.knime.com 웹사이트에서 다운로드할수있다.


장면 세팅 : 시간과 빅데이터

일반적으로 검침 기반 데이터는 타임스템프 요소를 가지고, 그것은 예측을 위한 타임시리즈 분석에 사용된다.
제조업,화학,생명과학,운수,자동차,에너지나 보안등에 빅데이터가 활용될수있다.이 논문은 에너지 산업을 위한 스마트에너지에 촛점을 맞추며 그리고 공공활용데이터 와 오픈소스데이터 분석 플랫폼인 KNIME 기반이다.

에너지 산업은 현재 변화를 맞이하고있다.

복잡한 네트워크, 경쟁자들의 등장 및  규제 증가 , 소비가격 및 그린/안전한 에너지등 에너지 산업은 옛날처럼 단순하게 운용될수없어지고있다.  하나의 긍정적인 트랜드는 스마트 미터의 등장이다. 스마트 미터는 에너지 회사가 소비자의 에너지 사용을 관리하고 이해시키는데 도움을 주는 방법이다. 또한 에너지 회사가 더 나은 에너지 수요조절능력을 갖게 될수있는 만들수도있다.KNIME 은 데이터 분석,  전체 분석 프로세스를 위한 사용자친화 그래픽 워크벤치,데이터접근, 데이터이동,초기투자,강력한 예측 분석,가시화,리포팅툴등을 제공하는 오픈소스 플랫폼이다. 

KNIME 은 데스크탑에 다운로드해서 공짜로 사용할수있다. KNIME 제품은 추가적인 기능들 예를들어 공유저장소,인증,원격실행,스케쥴링,SOA 모음, 웹유저 인터페이스등을 포함한다. 강력한 빅데이터 익스텐션은 하둡처럼 분산 프레임워크로 활용할수있다. KNIME 은 60개국 3000 조직에서 사용된다. 

이 프로젝트를 개발하는 워크플로우는 KNIME에 기반하며, 원본 거대한 양의 에너지데이터를 변형하고, 타임시리즈를 클러스터하며 타임시리즈 분석 테크닉을 적용한다. 그리고 예측 분석, 센서블 빅데이터 프로세싱, 비지니스 판단등을 데이터로부터 그려냅니다.



분석 접근 오버뷰  (역주:  가구당 전체 전력 사용을 분석함.  전자제품별 분석은 아님)

 

1. 각각의 meter ID  별로, 타임시리즈 데이터를 임포트해서 정제하고, 모읍니다. ( 하루당 /  시간당 ) 

2. 각 meter ID 의 전기사용에 대한 행동양식을 계산하고 정의합니다. (K-Menas) 

3. 모든 meterID 를 행동양식에 기반하여 클러스터링합니다. 6000가구를 30개의 클러스터로 축소합니다. (K-Means)

4. 타임 시리즈 예측으로 옮깁니다. 각각의 클러스터는 예측 모델을 만듭니다. (과거기반으로 미래를 예측하기위해)

5. 예측 모델들을 평가합니다. 모든 값에 대한 예측 오류 나 에너지 피크등에  의해....  (AR - *  / NN - * ) 

6. 빅데이터 플랫폼을 사용하여 스텝 1,2 의  행동측정계산을 다시 구현하고 실행합니다. 



33.8. Traversal


The Matrix

이것은 우리가 순회할  첫번째 그래프 모습이다.


Figure 33.2. Matrix node space view

examples-matrix.png


친구들  / 친구들의 친구들


private Traverser getFriends(
        final Node person )
{
    TraversalDescription td = graphDb.traversalDescription()
            .breadthFirst()
            .relationships( RelTypes.KNOWS, Direction.OUTGOING )
            .evaluator( Evaluators.excludeStartPosition() );
    return td.traverse( person );
}


 실제 순회를 돌아보고 결과를 찍어보자.

int numberOfFriends = 0;
String output = neoNode.getProperty( "name" ) + "'s friends:\n";
Traverser friendsTraverser = getFriends( neoNode );
for ( Path friendPath : friendsTraverser )
{
    output += "At depth " + friendPath.length() + " => "
              + friendPath.endNode()
                      .getProperty( "name" ) + "\n";
    numberOfFriends++;
}
output += "Number of friends found: " + numberOfFriends + "\n";


결과:

Thomas Anderson's friends:
At depth 1 => Morpheus
At depth 1 => Trinity
At depth 2 => Cypher
At depth 3 => Agent Smith
Number of friends found: 4


누가 매트릭스를 코드했나?


private Traverser findHackers( final Node startNode )
{
    TraversalDescription td = graphDb.traversalDescription()
            .breadthFirst()
            .relationships( RelTypes.CODED_BY, Direction.OUTGOING )
            .relationships( RelTypes.KNOWS, Direction.OUTGOING )
            .evaluator(
                    Evaluators.includeWhereLastRelationshipTypeIs( RelTypes.CODED_BY ) );
    return td.traverse( startNode );
}


:결과 

String output = "Hackers:\n";
int numberOfHackers = 0;
Traverser traverser = findHackers( getNeoNode() );
for ( Path hackerPath : traverser )
{
    output += "At depth " + hackerPath.length() + " => "
              + hackerPath.endNode()
                      .getProperty( "name" ) + "\n";
    numberOfHackers++;
}
output += "Number of hackers found: " + numberOfHackers + "\n";


이제 우리는 알게됬다.

Hackers:
At depth 4 => The Architect
Number of hackers found: 1



순서에 따라서  경로를 걸어보자


토이그래프를 만들자

Node A = db.createNode();
Node B = db.createNode();
Node C = db.createNode();
Node D = db.createNode();

A.createRelationshipTo( C, REL2 );
C.createRelationshipTo( D, REL3 );
A.createRelationshipTo( B, REL1 );
B.createRelationshipTo( C, REL2 );

example-ordered-path.svg


Now, the order of relationships (REL1 → REL2 → REL3) is stored in an ArrayList. Upon traversal, the Evaluator can check against it to ensure that only paths are included and returned that have the predefined order of relationships:


어떻게 경로를 걸어갈지 정의하자.

final ArrayList<RelationshipType> orderedPathContext = new ArrayList<RelationshipType>();
orderedPathContext.add( REL1 );
orderedPathContext.add( withName( "REL2" ) );
orderedPathContext.add( withName( "REL3" ) );
TraversalDescription td = db.traversalDescription()
        .evaluator( new Evaluator()
        {
            @Override
            public Evaluation evaluate( final Path path )
            {
                if ( path.length() == 0 )
                {
                    return Evaluation.EXCLUDE_AND_CONTINUE;
                }
                RelationshipType expectedType = orderedPathContext.get( path.length() - 1 );
                boolean isExpectedType = path.lastRelationship()
                        .isType( expectedType );
                boolean included = path.length() == orderedPathContext.size() && isExpectedType;
                boolean continued = path.length() < orderedPathContext.size() && isExpectedType;
                return Evaluation.of( included, continued );
            }
        } )
        .uniqueness( Uniqueness.NODE_PATH );

Note that we set the uniqueness to Uniqueness.NODE_PATH as we want to be able to revisit the same node dureing the traversal, but not the same path.


순회를 하고 결과를 찍어보자

Traverser traverser = td.traverse( A );
PathPrinter pathPrinter = new PathPrinter( "name" );
for ( Path path : traverser )
{
    output += Paths.pathToString( path, pathPrinter );
}


결과:

(A)--[REL1]-->(B)--[REL2]-->(C)--[REL3]-->(D)


여기서 우리는 패스 아웃풋을 표현하기위해 커스텀 클래스를 사용했다. 

static class PathPrinter implements Paths.PathDescriptor<Path>
{
    private final String nodePropertyKey;

    public PathPrinter( String nodePropertyKey )
    {
        this.nodePropertyKey = nodePropertyKey;
    }

    @Override
    public String nodeRepresentation( Path path, Node node )
    {
        return "(" + node.getProperty( nodePropertyKey, "" ) + ")";
    }

    @Override
    public String relationshipRepresentation( Path path, Node from, Relationship relationship )
    {
        String prefix = "--", suffix = "--";
        if ( from.equals( relationship.getEndNode() ) )
        {
            prefix = "<--";
        }
        else
        {
            suffix = "-->";
        }
        return prefix + "[" + relationship.getType().name() + "]" + suffix;
    }
}


Uniqueness of Paths in traversals


This example is demonstrating the use of node uniqueness. Below an imaginary domain graph with Principals that own pets that are descendant to other pets.

Figure 33.3. Descendants Example Graph

Descendants-Example-Graph-Uniqueness-of-Paths-in-traversals.svg

In order to return all descendants of Pet0 which have the relation owns to Principal1 (Pet1 and Pet3), the Uniqueness of the traversal needs to be set to NODE_PATH rather than the default NODE_GLOBAL so that nodes can be traversed more that once, and paths that have different nodes but can have some nodes in common (like the start and end node) can be returned.

final Node target = data.get().get( "Principal1" );
TraversalDescription td = db.traversalDescription()
        .uniqueness( Uniqueness.NODE_PATH )
        .evaluator( new Evaluator()
{
    @Override
    public Evaluation evaluate( Path path )
    {
        boolean endNodeIsTarget = path.endNode().equals( target );
        return Evaluation.of( endNodeIsTarget, !endNodeIsTarget );
    }
} );

Traverser results = td.traverse( start );

This will return the following paths:

(2)--[descendant,2]-->(3)<--[owns,5]--(4)
(2)--[descendant,0]-->(0)<--[owns,3]--(4)

In the default path.toString() implementation, (1)--[knows,2]-->(4) denotes a node with ID=1 having a relationship with ID 2 or type knows to a node with ID-4.

Let’s create a new TraversalDescription from the old one, having NODE_GLOBAL uniqueness to see the difference.

[Tip]Tip

The TraversalDescription object is immutable, so we have to use the new instance returned with the new uniqueness setting.

TraversalDescription nodeGlobalTd = td.uniqueness( Uniqueness.NODE_GLOBAL );
results = nodeGlobalTd.traverse( start );

Now only one path is returned:

(2)--[descendant,2]-->(3)<--[owns,5]--(4)


Social network

Social networks (know as social graphs out on the web) are natural to model with a graph. This example shows a very simple social model that connects friends and keeps track of status updates.


Simple social model

Figure 33.4. Social network data model

socnet-model.svg

The data model for a social network is pretty simple: Persons with names and StatusUpdates with timestamped text. These entities are then connected by specific relationships.

  • Person

    • friend: relates two distinct Person instances (no self-reference)
    • status: connects to the most recent StatusUpdate
  • StatusUpdate

    • next: points to the next StatusUpdate in the chain, which was posted before the current one

Status graph instance

The StatusUpdate list for a Person is a linked list. The head of the list (the most recent status) is found by following status. Each subsequent StatusUpdate is connected by next.

Here’s an example where Andreas Kollegger micro-blogged his way to work in the morning:

andreas-status-updates.svg

To read the status updates, we can create a traversal, like so:

TraversalDescription traversal = graphDb().traversalDescription()
        .depthFirst()
        .relationships( NEXT );

This gives us a traverser that will start at one StatusUpdate, and will follow the chain of updates until they run out. Traversers are lazy loading, so it’s performant even when dealing with thousands of statuses — they are not loaded until we actually consume them.

Activity stream

Once we have friends, and they have status messages, we might want to read our friends status' messages, in reverse time order — latest first. To do this, we go through these steps:

  1. Gather all friend’s status update iterators in a list — latest date first.
  2. Sort the list.
  3. Return the first item in the list.
  4. If the first iterator is exhausted, remove it from the list. Otherwise, get the next item in that iterator.
  5. Go to step 2 until there are no iterators left in the list.

Animated, the sequence looks like this.

The code looks like:

PositionedIterator<StatusUpdate> first = statuses.get(0);
StatusUpdate returnVal = first.current();

if ( !first.hasNext() )
{
    statuses.remove( 0 );
}
else
{
    first.next();
    sort();
}

return returnVal;




http://neo4j.com/docs/stable/tutorials-java-embedded-traversal.html

'NoSQL' 카테고리의 다른 글

Neo4j - 인덱스 사용하기  (0) 2015.10.30
Neo4j 인사이드 : 파일 스토리지  (0) 2015.10.30
시계열 DB (OpenTSDB , 인플럭스 DB , Graphite ) 정리  (0) 2015.10.22
MongoDB vs Couchbase (2)  (0) 2015.09.03
MongoDB vs Couchbase (1)  (0) 2015.09.03


Neo4j 의 인덱스를 만들고  데이터 넣고, 검색하기 


- 일단 서버부터 시작하자.

GraphDatabaseService graphDb = new GraphDatabaseFactory().newEmbeddedDatabase( DB_PATH );


- 이름으로  유저를 인덱스하기위해 데이타베이스를 설정해야한다. 한번에 완료되야 한다.

IndexDefinition indexDefinition;
try ( Transaction tx = graphDb.beginTx() )
{
    Schema schema = graphDb.schema();
    indexDefinition = schema.indexFor( DynamicLabel.label( "User" ) )
            .on( "username" )
            .create();
    tx.success();
}


-인덱스는 처음 생성될때 비동기적으로 구성된다.  완료되기 기다리길 원할때  코어 API 를 사용할수있다.

try ( Transaction tx = graphDb.beginTx() )
{
    Schema schema = graphDb.schema();
     ....

    schema.awaitIndexOnline( indexDefinition, 10, TimeUnit.SECONDS );
}


- User 를  추가해 보자.

try ( Transaction tx = graphDb.beginTx() )
{
    Label label = DynamicLabel.label( "User" );

    // Create some users
    for ( int id = 0; id < 100; id++ )
    {
        Node userNode = graphDb.createNode( label );
        userNode.setProperty( "username", "user" + id + "@neo4j.org" );
    }
    System.out.println( "Users created" );
    tx.success();
}

- id 로 User 를 찾는 방법.
Label label = DynamicLabel.label( "User" );
int idToFind = 45;
String nameToFind = "user" + idToFind + "@neo4j.org";
try ( Transaction tx = graphDb.beginTx() )
{
    try ( ResourceIterator<Node> users =
            graphDb.findNodes( label, "username", nameToFind ) )
    {
        ArrayList<Node> userNodes = new ArrayList<>();
        while ( users.hasNext() )
        {
            userNodes.add( users.next() );
        }

        for ( Node node : userNodes )
        {
            System.out.println( "The username of user " + idToFind + " is " + node.getProperty( "username" ) );
        }
    }
}


- User 의 이름을 업데이트 할때, 인덱스도 업데이트 됨.

try ( Transaction tx = graphDb.beginTx() )
{
    Label label = DynamicLabel.label( "User" );
    int idToFind = 45;
    String nameToFind = "user" + idToFind + "@neo4j.org";

    for ( Node node : loop( graphDb.findNodes( label, "username", nameToFind ) ) )
    {
        node.setProperty( "username", "user" + ( idToFind + 1 ) + "@neo4j.org" );
    }
    tx.success();
}


 - User 를 삭제하면 자동적으로 인덱스에서 삭제됨.

try ( Transaction tx = graphDb.beginTx() )
{
    Label label = DynamicLabel.label( "User" );
    int idToFind = 46;
    String nameToFind = "user" + idToFind + "@neo4j.org";

    for ( Node node : loop( graphDb.findNodes( label, "username", nameToFind ) ) )
    {
        node.delete();
    }
    tx.success();
}


- 데이터 모델을 바꿀때  , 필요없어진 인덱스를 드랍 할수있다.

try ( Transaction tx = graphDb.beginTx() )
{
    Label label = DynamicLabel.label( "User" );
    for ( IndexDefinition indexDefinition : graphDb.schema()
            .getIndexes( label ) )
    {
        // There is only one index
        indexDefinition.drop();
    }

    tx.success();
}



http://neo4j.com/docs/stable/tutorials-java-embedded-new-index.html

'NoSQL' 카테고리의 다른 글

Neo4j - Traversal  (0) 2015.10.30
Neo4j 인사이드 : 파일 스토리지  (0) 2015.10.30
시계열 DB (OpenTSDB , 인플럭스 DB , Graphite ) 정리  (0) 2015.10.22
MongoDB vs Couchbase (2)  (0) 2015.09.03
MongoDB vs Couchbase (1)  (0) 2015.09.03

Neo4j Hello world~!!


인사이드를 살펴보기전에 Neo4j 의 사용 예를 간단하게 체크하자.


-  Relationship 타입을 enum 으로 만든다. 

private static enum RelTypes implements RelationshipType { KNOWS // 아는 관계 ( 친구 , 가족 뭐 그런..) }


- 관련 변수 선언 

GraphDatabaseService graphDb;
Node firstNode;
Node secondNode;
Relationship relationship;


- 데이터 베이스 시작 

graphDb = new GraphDatabaseFactory().newEmbeddedDatabase( DB_PATH );
registerShutdownHook( graphDb );


- 트랜잭션으로 todo list  감싸기  

try ( Transaction tx = graphDb.beginTx() )
{
    // Database operations go here
    tx.success();
}


- 작은 그래프 만들어보자.

firstNode = graphDb.createNode();
firstNode.setProperty( "message", "Hello, " );
secondNode = graphDb.createNode();
secondNode.setProperty( "message", "World!" );

relationship = firstNode.createRelationshipTo( secondNode, RelTypes.KNOWS );
relationship.setProperty( "message", "brave Neo4j " );

  



- 그래프를 출력해보고 

System.out.print( firstNode.getProperty( "message" ) );
System.out.print( relationship.getProperty( "message" ) );
System.out.print( secondNode.getProperty( "message" ) );

- 그래프  데이터를 삭제해보자

// let's remove the data
firstNode.getSingleRelationship( RelTypes.KNOWS, Direction.OUTGOING ).delete();
firstNode.delete();
secondNode.delete();


위의 소스를 보면 중요한 3가지 특성이 있다는것을 알수있다.

1. 노드  (firstNode, SecondNode) : '노드 속성을 가지고있다'

2. 관계  (relationship) : '관계 타입, 노드 속성 , 노드  방향을 가지고있다.'

3. 속성  ( 노드속성 :  "hello" , 'world'  /  관계 속성 : "brave neo4j")


이제 이정도만 기억해두고 이런 정보들을 과연 파일에 어떻게 저장하는지에 관해 알아보자.


Neo4j Internals: File Storage



1.저장되는  파일 이름들 

   - 노드 .db  파일  (neostore.nodestore.db)
   - 관계 .db  파일  (neostore.relationshipstore.db)
   - 노드 .id   파일  (neostore.nodestore.db.id)
   - .propertystore   파일  (neostore.propertystore.db)
   - .index   파일  (neostore.propertystore.db.index)


2. 재 사용되는 Ids

    neo 의 데이터가 동일한 사이즈의 청크로 저장된다고 가정해보자. 이걸 "chunks records" 라고 말한다고 치고, 
각각 레코드들은 그것과 연계된 id 를 가지고있다고하자.  파일안에서 데이터를 어떻게 찾을수있을까?
만약 데이타베이스에 대해서 좀 알고있다면 , 다양한 파일 엑세스 기법들에 대해서 떠오를것이다.
제일 쉬운방법은 id 를 자동적으로 하나씩 증가하게하면, 파일내에서 id 의 위치는 id * 레코드사이즈가 될것이다.
이때 문제가 하나 생기는데, 중간에 id 들이 삭제되면 어떻게 될냐는건데, 새로운 노드를 그 중간에 붕 뜬 자리에 
넣어주면되는데, 위에서 말한바와 같이 id 와 파일에서의 offset 은 id * 레코드사이즈라는 관계가 있다는 문제가 있다.
그래서 이걸 해결하기위한 방법은 지워진 id 를 기억해두었다가, 새로운 레코드에 적용시켜주면 되는것이다.
결국 id 를 요청하면 , 서버는 재사용되는 id 가 있으면 할당해주고, 없으면 새로운 id 를 만들어주는 전력을 취하면된다.


3. idGenerator 와 데이터 파일 

    - neo 서버가 영원히 메모리위에 있을수가 없기때문에, 저런 재사용 ids 를 파일에 저장해두어야한다.
 - org.neo4j.kernel.impl.nioneo.store.IdGenerator 와 이것의 구현체인 IdGeneratorImpl 클래스가 사용된다.
 - 모든 데이터파일의 관리자는 IdGenerator 를 유지해야한다. 
 - "id" 파일은 ".db" 파일과  나란히 연결되있다.
 - 위에 헬로월드를 실행시키고 (삭제하기전에)  neostore.nodestore.db.id 파일을 살펴보면 , 9바이트를 볼수있는데
   처음 0 은 클린 셧다운을 말하고, 나머지 8 바이트는 3 의 long 타입이다. 이 3은 다음 사용될 id 를 나타낸다. 
   1과 2는 이미 사용되고있다. 지워진 노드가 없기때문에 재사용 ids 도 물론 없다. 

4. Neo4j 의 두가지 저장법.

      가.org.neo4j.kernel.impl.nioneo.store.AbstractStore
           -  고정된 사이즈의 레코드를 다룬다.

      나. org.neo4j.kernel.impl.nioneo.store.AbstractDynamicStore
          -  다양한 사이즈의 레코드를 블럭안에 저장한다. 그리고 LinkedList 같이 디스크상에서 연결해서  저장한다. 

       - 두가지 모두 그들의 빌딩블록들과 연결된 ids 를 사용한다. 차이점은 AbstractStore 는 바깥 세상과 ids 를 가지고
         소통한다는 점이고, DynamicStore 의 ids 는 오직 클래스안에서 의미를 가진다는점이다. 



Record - oriented storage : The primitives


 5. 노드 스토어 

        - org.neo4j.kernel.impl.nioneo.store.NodeStore 는 neostore.nodestore.db 파일을 만든다.
        - 모든 노드는 여기에 저장된다.
        - 간단하게 9 바이트의 고정 레코드를 살펴보면, 첫바이트는 사용 플레그이고, 그 다음 4바이트 integer 는
          relationships 의 첫번째 id 이고, 마지막 4 byte 는 이 노드가 가지고있는 첫번째 속성의 id 이다.  
        - AbstractStore 안의 레코드들은 ids 를 저장하지 않는다.  그들의 파일에서의 오프셋은 그들의 id 를 정의하며 
          그들의 id 는 그들의 파일에서의 오프셋을 정의한다. 


 6.  관계 스토어 

         - org.neo4j.kernel.impl.nioneo.store.RelationshipStore 는 neostore.relationshipstore.db 파일을 만든다.
         - 33  바이트의 관계 레코드에서, 
                * 첫번째 바이트는 사용플레그
                * 4 바이트 :  첫번째 노드의 id 
                * 4 바이트 :  두번째 노드의 id
                * 4 바이트 :  이 관계의 타입을 나타내는 레코드의 id   
                * 4 바이트 * 4  : 첫번째 노드의 이전관계 id  /  첫번째 노드의 다음 관계 id  /   
                                          두번째 노드의 이전관계 id  /  두번째 노드의 다음 관계 id  
                * 4 바이트 : 이 관계의 첫번째 속성의 id 
            - 관계는 디스크상에서 이중링크드리스트의 형태이다.

 7.  관계 타입  스토어 

         org.neo4j.kernel.impl.nioneo.store.RelationshipTypeStore 는 neostore.relationshipTypestore.db 파일 생성. 

         - 관계 타입 이름을 저장하기위한 장소가 필요한데 neostore.relationshipTypestore.db.names 파일이다.    


  8 . 속성  스토어  

         org.neo4j.kernel.impl.nioneo.store.PropertyStore 는 neostore.propertystore.db 파일을 만든다. 

           - 각각의 속성 레코드들은 in_use 바이트과 속성의 타입을 저장하는 integer 로 시작된다.


  Block - oriented storage: Strings and arrays


 * 이전 스토리지엔진들은 고정크기의 레코드들을 가지고, 다른 레코드들로의 포인터 및 작은 데이터로  구성되었다. 
   당신의  소중하고, 다양한크기를 가진 문자열 또는 배열 속성 값들은 다른 방식으로 다루어져야한다.  
   이것이 DynamicStores 가 만들어진 이유이다. 우리는 이미 PropertyStore 나 RelaltionshipTypeStore 의 부분으로 
   그것의 사용법을 살펴보았는데, 모두다 .Strings 나 배열같은 자바 기본 요소가 필요하다.
   이제 그들의 구조를 살펴보자.

   9.   일반 블록 구조 

    다양한 사이즈의 임의의 데이터는  dynamic store 파일안에 이중 링크드 블럭 구조에 의해  수용되어진다.
   여기서 필요로
 되어지는것은 in_use byte /  이전 블럭 / 다음 블럭으로의 포인터 / 데이타의 크기 및 데이터이다. 
   이것의 크기는  파일안에서의 오프셋을 발견하기 위해 가능한 맥시점 블럭사이즈 에 의해 한계가 지어진다. 만약 하나의
   데이터가  blockSize 보다 더 큰 
공간을 요구하면, 그 만큼의 블럭들이 그것의 체인에 알맞게 추가된다.
   그러나 체인의 마지막 블럭은 (만약 하나라면 첫번째)
 할당된 양보다 적게  사용가능하다. 

    각각 블럭헤더의 레이아웃은 in_use byte, 이전 블럭의 int id (만약 -1 이면 첫번째) , 길이 , 다음블럭의 int id,  이 
    13바이트 
이후에는 맥스 blockSize 크기의 데이터가 온다.전체 블럭의 크기는 고로 13 + blockSize 이다. 


  10.   Strings vs Arrays 


org.neo4j.kernel.impl.nioneo.store.DynamicArrayStore 

org.neo4j.kernel.impl.nioneo.store.DynamicStringStore 


  11.   개인적 노트



  12. 마무리

  몇몇 부분 정확한 이해없이 대략 정리한것이기때문에 잘못된것이 있을수 있습니다. 

  하기 레퍼런스를 보시면 더 정확한 정보를 알수있습니다.           



레퍼런스:

http://neo4j.com/docs/stable/tutorials-java-embedded-hello-world.html

http://digitalstain.blogspot.kr/2010/10/neo4j-internals-file-storage.html

'NoSQL' 카테고리의 다른 글

Neo4j - Traversal  (0) 2015.10.30
Neo4j - 인덱스 사용하기  (0) 2015.10.30
시계열 DB (OpenTSDB , 인플럭스 DB , Graphite ) 정리  (0) 2015.10.22
MongoDB vs Couchbase (2)  (0) 2015.09.03
MongoDB vs Couchbase (1)  (0) 2015.09.03

무들에서 하나씩 문제를 생산하는 삽질대신해서, 대규모로 문제를 업로드시킬수있는 방법이 있습니다. 
무들은 그런 문제들을 질문은행으로 대규모 임포트하는데 사용될수있는 여러가지 포맷들을 지원합니다.
여기에서는 3가지 방법을 알아보겠습니다.

방법1. 간단한 Aiken format 을 사용하여 문제를 임포트하기. 이 방법은 오직 선다형에서만 사용할수있음.

스텝1.  원하는 문제를 Aiken format 으로 변경합니다. 그리고 .txt 파일로 저장합니다.



위는 Akien format 의 예입니다.  질문은 오직 한라인이어야만 합니다. 각각의 대답은 단일 문자로 시작되며,   .  or )  로 끝나구요. 공백하나 같은후에 답변이 이어집니다.   정답은 ANSWER: 로 시작하며 답변에 적절한 문자를 넣어줍니다.  너무 쉽긴한데  피드백을 어떻게 넣는지는 모르겠네요.  안되는거 같기도 하구요. 


스텝2.  무들 페이지로가서  강좌관리-질문은행-가져오기로 갑니다.

스텝3.  파일형식에서 Aiken 형식으로 선택해주고 , 파일에서 질문 가져오기에 해당 파일을 넣어주면 됩니다.

             (중간에 일반 설정은 별 영향을 미치지 않습니다) 



방법2.  선다형 문제 말고 다른게 필요하다면,  XML 로 퀴즈문제를 바꾸기위해 Moodle XML 컨버터를 이용할수있습니다. 그리고나서 XML 파일을 무들로 임포트하는것이죠.  


스텝1.  문제를 올바른 .txt  포맷으로 준비합니다. 만약 문제가  word 문서로 되있다면 , .txt 포맷으로 저장해줍니다.  

각각의 질문 타입은 아래의 예를 따릅니다.


multichoice 
1. When must a Post Implementation Review take place? 
A. after every Change 
B. at the request of the person who submitted the Change request 
C. in case of emergency changes 
D. if another incident of the same type occurs again after a Change has been made 
Answer: A 
feedback: Correct! The PIR must be a part of every change process. 

2. When an organization decides to control the flow of incident information within the IT organization, which ITIL process would it be putting in place? 
A) Availability Management 
B) Change Management 
C) Incident Management 
D) Problem Management 
Answer: C 
feed.1. Incorrect! 
feed.2. Another try! 
feed.3. Correct! 

essay 
3. Which activity is not the responsibility of IT Service Continuity Management? 
feed. write something 

shortanswer 
4. According to the Deming quality circle a number of steps must be performed repeatedly in order to ensure good performance. Specify the correct sequence for these steps. 
A. Act 
B. Check 
C. Do 
D. Plan 
Answer: DCBA 

truefalse 
5. Infrastructure Monitoring will provide support teams with alerts directly allowing for faster resolution. Such alerts do not need to be recorded in the Incident Management tool as there is little added value in this. Typically the incident will be resolved automatically before the customer recognises it. 
Answer: False 
feedback: False, it is good practice to automatically record the alert in the Incident Management tool and assign this directly to the support team concerned. 

description 
6. A Service Desk brings many benefits to an organization. Key to these is increased customer satisfaction & perception. Customers benefit from a single point of contact which prevents the need to chase organization teams for updates or resolutions etc. A service desk will be able to produce more accurate information for management, and ensure that support resources are more productively used. Successful implementation of changes is not a benefit determined by the Service Desk function. 

cloze 
7. Infrastructure {1:MULTICHOICE:=Monitoring~Controlling~Service} will provide support teams with alerts directly allowing for faster resolution. Such alerts do not need to be recorded in the Incident Management tool as there is little added value in this {1:MULTICHOICE:=true~false}. Typically the incident will be resolved automatically before the customer recognizes it. 

numerical 
8. How many days are there in May? Give answer only with number 
Answer: 31 


스텝2. Moodle XML converter 로 가서 위의 텍스트파일을 업로드하거나 input window 로 텍스트를 붙혀넣고

스텝3. Download XML 버튼을 클릭하고, XML 퀴즈파일을 데스크탑에 저장합니다.

스텝4.  무들 페이지로가서  강좌관리-질문은행-가져오기로 갑니다.

스텝5.  파일형식에서 Moodle XML  형식으로 선택해주고 , 파일에서 질문 가져오기에 해당 파일을 넣어주면 됩니다.




        



방법3.  무들에서 지원하는 아무 포맷에 맞춰서 올리면 됩니다. (파일이름에 한글있으면 안되는듯?)

개인적으로는  디비에 있는 문제를 가져와서 자동적으로 무들 xml 형식을 생성하는 임포트툴을 만들었습니다.

원래 DB and 원래 엑셀  ->  임포트툴  ->  무들XML  -> 무들서버 


사실 d3 라이브러리를 사용하는것은 OpenGL 이나 SVG/GDI+ 를 사용하는것 만큼 나름 로우레벨 작업이다. 이름 자체가 나타내듯이 Data-Driven Documents 이지 그래프 / 차트 라이브러리가 아니기때문이다.개인적으로는 아주 가끔 가다가 차트작업을 했었고, 차트에 그려질 그래프 스타일이 그때 마다 가지각색 이었기때문에 chart FX 나 Teechart 를 사용하기보다는  GDI+  로 직접 그렸던것 같다.  (차트라이브러리를 공부해서 사용하는것보다 밑바닥부터 그린게  편하긴한데.. 대략 일주일정도 소모됬던것 같다) 

d3 라이브러리 경우 자유성은 분명 높지만, 간단하면서 아주 일반적인 차트를 그리는데 있어서는 공부도 좀 해야하고 신경써야할 부분이 많다. 이럴때  d3 를 이용하여 어느정도 템플릿화한 라이브러리가 필요한데 그런것들을 소개해본다.



그 전에 d3.js 로 작업한것을 잠시 살펴보자.


d3.js (http://d3js.org/)



- d3.js 를 그대로 이용하여 애니매이션 그래프 개발   (주의 : 동영상 클릭시 음악나옴) 

- d3 는  scale  및  axis 를 이용하여  x/y 축 그린다. 

- d3 에 transition 특성을 이용하여 애니매이션해준다. 

- d3 에 표시되길 원하는 데이터를 바인딩하는 방법만 알면된다. 

- d3 에서는 툴팁같은것도 직접 작성해야한다. 

- d3 는 SVG 기반의  2d 라이브러리이다.  3d 그래프를 그리려면 webGL 기반을 사용해야한다. 

- d3 는 highchart 나 google charts  와는 전혀 다른 성질의 것이다.


C3.js (http://c3js.org/)



- d3.js 를 이용한 범용 차트 라이브러리 

- d3.js 를 배워야하는 러닝커브를 완화시킨다.



NVD3.js (http://nvd3.org/index.html)




- d3.js 를 이용한 범용 차트 라이브러리 

- c3.js 와 비교되는데 이게 더 편하다는 글들이 인터넷에 많은듯~



Cubism.js (https://square.github.io/cubism/)



- d3.js 를 이용한 타임시리즈 데이터 특화 라이브러리 

- Graphite 같은 타임시리즈특화 NoSQL 의 데이터를 가시화할수있다.  (InfluxDB 도 가능한지는 모름)



MetricsGraphics.js (http://metricsgraphicsjs.org)



- d3.js 를 이용한 타임시리즈 데이터 특화 라이브러리 

+ Recent posts