아래는 연구실의 구성원을 테이블 형식으로 나타낸 것이다.
(이름은 가상의 이름을 사용했다.)
이름 | 학력 | 성별 | 상주유무 | 조 |
---|---|---|---|---|
김영희 | 박사 | 여 | N | 0 |
김철수 | 대학생 | 남 | N | 2 |
김민희 | 대학생 | 여 | N | 2 |
이현지 | 대학생 | 여 | Y | 1 |
박수현 | 대학생 | 여 | Y | 1 |
박민우 | 대학생 | 남 | Y | 1 |
박동철 | 대학생 | 남 | Y | 1 |
최영규 | 대학생 | 남 | Y | 3 |
정우경 | 대학생 | 남 | N | 3 |
👉 위를 Neo4j 그래프DB로 구성하는 과정을 알아본다.
📝각 사람 노드의 속성으로 이름, 학력, 성별, 상주유무, 조를 표현한다.
📝관계 설정에서 시작하는 노드 쪽에는 ‘ - ‘를 작성하고 관계가 끝나는 노드 쪽에는 ‘ -> ‘ 를 작성한다.
create (연구실:lab{name:'LAB'}),
(우경:person{name:'정우경',학력:'대학생',성별:'남',상주유무:'NO',조:'3'}),
(영규:person{name:'최영규',학력:'대학생',성별:'남',상주유무:'YES',조:'3'}),
(현지:person{name:'이현지',학력:'대학생',성별:'여',상주유무:'YES',조:'1'}),
(수현:person{name:'박수현',학력:'대학생',성별:'여',상주유무:'YES',조:'1'}),
(민우:person{name:'박민우',학력:'대학생',성별:'남',상주유무:'YES',조:'1'}),
(동철:person{name:'박동철',학력:'대학생',성별:'남',상주유무:'YES',조:'1'}),
(철수:person{name:'김철수',학력:'대학생',성별:'남',상주유무:'NO',조:'2'}),
(민희:person{name:'김민희',학력:'대학생',성별:'여',상주유무:'NO',조:'2'}),
(영희:person{name:'김영희',학력:'박사',성별:'여',상주유무:'NO',조:'0'}),
(g1:group{name:'1조'}),
(g2:group{name:'2조'}),
(g3:group{name:'3조'}),
(영희)-[:belongs_to]->(연구실),
(g1)-[:belongs_to]->(연구실),
(g2)-[:belongs_to]->(연구실),
(g3)-[:belongs_to]->(연구실),
(철수)-[:belongs_to]->(g2),
(민희)-[:belongs_to]->(g2),
(현지)-[:belongs_to]->(g1),
(수현)-[:belongs_to]->(g1),
(민우)-[:belongs_to]->(g1),
(동철)-[:belongs_to]->(g1),
(영규)-[:belongs_to]->(g3),
(우경)-[:belongs_to]->(g3)
match (m:person)
where m.name=~ '박.*'
return count(m.name)
💡 설명
1. match에서 쿼리 작성을 위한 변수 m 을 지정하고, 이 m이 person 노드들 중 하나라고 저장한다.
2. Where을 통해 검색할 패턴에 추가적인 제약을 넣는다. m.name으로 m노드들 중 name property를 지정하고, m.name=~ ‘박.*’ 을 통해 m에 name이 ‘박’으로 시작하는 노드만 저장한다.
3. 개수를 반환하는 count() 함수를 이용하여 return절에서 m.name의 개수를 반환한다.
실행 결과 ⬇
: 아래와 같이 3이라는 결과를 얻을 수 있다.
MATCH (m:person)
WHERE m.조 = '2'
RETURN m.name, m.조
실행 결과 ⬇
: 아래와 같이 결과를 얻을 수 있다.
A,B,C,D라는 사람이 있고, 각각 좋아하는 영화들로 관계를 생성하여 movie 데이터베이스를 만들었다.
create를 사용해 전체 노드&관계 생성을 한번에 하도록 했다. 해당 코드는 아래와 같다.
create (A:Person {name:'A'}), (B:Person {name:'B'}), (C:Person {name:'C'}), (D:Person {name:'D'}),
(영화:Base {name:'영화'}),
(애니:Genre {name:'애니'}),(판타지:Genre {name:'판타지'}),(로맨스:Genre {name:'로맨스'}),(액션스릴러:Genre {name:'액션/스릴러'}),(음악영화:Genre {name:'음악영화'}),
(타란티노:Director {name:'쿠엔틴타란티노영화'}),(디즈니픽사:Production {name:'디즈니/픽사'}),(일본만화:Production {name:'일본만화'}),(외국:Production {name:'외국액션/스릴러'}),(한국:Production {name:'한국액션/스릴러'}),(히어로물:Director {name:'마블/dc'}),
(토이스토리:movie {name:'토이스토리'}),(코코:movie {name:'코코'}),(알라딘:movie {name:'알라딘'}),(크루엘라:movie {name:'크루엘라'}),(미녀와야수:movie {name:'미녀와야수'}),(짱구:movie {name:'짱구'}),
(센치행:movie {name:'센과치히로의행방불명'}),(하울:movie {name:'하울의움직이는성'}),(귀멸의칼날:movie {name:'귀멸의칼날'}),(해리포터:movie {name:'해리포터'}),(반지의제왕:movie {name:'반지의제왕'}),(신과함께:movie {name:'신과함께'}),(비긴어게인:movie {name:'비긴어게인'}),(내가널사랑할수없는:movie {name:'내가널사랑할수없는10가지이유'}),(노트북:movie {name:'노트북'}),(타이타닉:movie {name:'타이타닉'}),(어바웃타임:movie {name:'어바웃타임'}),(범죄와의전쟁:movie {name:'범죄와의전쟁'}),(범죄도시:movie {name:'범죄도시'}), (타짜:movie {name:'타짜'}),(신세계:movie {name:'신세계'}),
(셔터아일랜드:movie {name:'셔터아일랜드'}),(킹스맨:movie {name:'킹스맨'}),(바스터즈:movie {name:'바스터즈:거친녀석들'}),
(킬빌:movie {name:'킬빌'}), (펄프픽션:movie {name:'펄프픽션'}),(아이언맨:movie {name:'아이언맨'}),(스파이더맨:movie {name:'스파이더맨'}),(토르:movie {name:'토르'}),
(영화)-[:include]->(애니),(영화)-[:include]->(판타지),(영화)-[:include]->(로맨스),(영화)-[:include]->(액션스릴러),(영화)-[:include]->(음악영화),
(애니)-[:include]->(디즈니픽사),(애니)-[:include]->(일본만화),
(디즈니픽사)-[:include]->(토이스토리),(디즈니픽사)-[:include]->(코코),(디즈니픽사)-[:include]->(알라딘), (디즈니픽사)-[:include]->(크루엘라),(디즈니픽사)-[:include]->(미녀와야수),
(일본만화)-[:include]->(짱구),(일본만화)-[:include]->(센치행), (일본만화)-[:include]->(하울),(일본만화)-[:include]->(귀멸의칼날),
(판타지)-[:include]->(해리포터),(판타지)-[:include]->(반지의제왕),
(판타지)-[:include]->(신과함께),
(로맨스)-[:include]->(비긴어게인),(로맨스)-[:include]->(노트북)(로맨스)-[:include]->(타이타닉),(로맨스)-[:include]->(어바웃타임),(로맨스)-[:include]->(내가널사랑할수없는),
(액션스릴러)-[:include]->(외국),(액션스릴러)-[:include]->(한국),(외국)-[:include]->(셔터아일랜드),
(외국)-[:include]->(킹스맨),(외국)-[:include]->(타란티노),(외국)-[:include]->(히어로물),(히어로물)-[:include]->(아이언맨),(히어로물)-[:include]->(토르),(히어로물)-[:include]->(스파이더맨),
(타란티노)-[:include]->(펄프픽션),(타란티노)-[:include]->(킬빌),(타란티노)-[:include]->(바스터즈),
(한국)-[:include]->(범죄와의전쟁),(한국)-[:include]->(범죄도시),(한국)-[:include]->(타짜),(한국)-[:include]->(신세계),(음악영화)-[:include]->(알라딘),(음악영화)-[:include]->(미녀와야수),
(음악영화)-[:include]->(비긴어게인),
(A)-[:LIKE]->(토이스토리),(A)-[:LIKE]->(킬빌),(A)-[:LIKE]->(펄프픽션),(A)-[:LIKE]->(바스터즈),(A)-[:LIKE]->(타짜),(A)-[:LIKE]->(해리포터),(A)-[:LIKE]->(코코),
(B)-[:LIKE]->(짱구),(B)-[:LIKE]->(센치행),(B)-[:LIKE]->(비긴어게인),(B)-[:LIKE]->(타이타닉),(B)-[:LIKE]->(셔터아일랜드),
(C)-[:LIKE]->(아이언맨),(C)-[:LIKE]->(토르),(C)-[:LIKE]->(내가널사랑할수없는),(C)-[:LIKE]->(스파이더맨),
(D)-[:LIKE]->(해리포터),(D)-[:LIKE]->(크루엘라),(D)-[:LIKE]->(알라딘),(D)-[:LIKE]->(미녀와야수)
위의 코드를 실행하면 나타나는 Graph는 다음과 같다.
match (p:Person)-[l:LIKE]-(m:movie) where p.name='A' return m
match (p:Person)-[l:LIKE]->(m:movie)<-[i:LIKE]-(e:Person)
where p.name='A' and e.name='D' return m
match (p:Person)-[l:LIKE]-(m:movie) where m.name='타이타닉' return p
match (p:Person)-[:LIKE]->(m:movie)<-[:LIKE]-(e:Person)
where p.name='A' return e.name, count(e)
match (p:Person)-[:LIKE]->(m:movie)<-[:LIKE]-(e:Person)-[:LIKE]->(o:movie)
where p.name='A' and NOT (p)-[:LIKE]->(o)
return DISTINCT o.name
💡 설명
1. match 절 : p와 e가 좋아하는 영화의 합집합 : m, e가 좋아하는 영화의 집합 : o
2. where 절 : p의 이름 설정, p가 o라는 영화를 좋아하는 경우는 제외
3. return : 영화 o의 이름 리턴, 중복을 피하기 위해 distinct 사용
👉 특정 인물 p가 좋아하는 영화 m을 다른 인물 e도 좋아하고, 그 e가 좋아하는 또 다른 영화 o 중에서 p가 아직 좋아하지 않은 영화 o의 이름을 반환
영화 추천 로직은 다음과 같이 설정했다. 아래는 그림으로 표현한 것이다.