5. Modelowanie i eksploracja grafowych baz danych 69
5.5. Przykładowa implementacja grafowej bazy danych
5.5.2. Implementacja
Aby umo˙zliwi´c u˙zytkownikom komunikacj˛e z baz ˛a danych został stworzony program napisany w j˛ezyku Python 2, wykorzystuj ˛acy frameworkBulbflowwraz z j˛ezykiem zapyta ´n Gremlindo komunikacji z baz ˛a danychNeo4j. Stworzony program umo˙zliwia wyszukanie:
• wszystkich prac danego autora, • wszystkich autorów danej pracy, • najcz˛e´sciej cytowanych prac, • najcz˛e´sciej cytowanych autorów,
• autorów, którzy opieraj ˛a swoje prace na tych samych pracach,
• autorów, którzy opieraj ˛a swoje prace na pracach tych samych autorów.
Tworzenie bazy danych
Na rys. 5.6 pokazano prost ˛a baz˛e danych zło˙zon ˛a z 8 prac i 8 autorów. Baza ta została stworzona za pomoc ˛a skryptu napisanego w j˛ezyku Python 2. W tym celu utworzono 2 klasy odpowiedzialne za wierzchołki w grafie: PaperiPerson
modeluj ˛ace, odpowiednio, publikacje i autorów. Kraw˛edzie w grafie s ˛a modelo-wane przez klasyWroteiCite. KlasaWrotejest przeznaczona do ł ˛aczenia wierz-chołków pochodz ˛acych z klasyPersonz wierzchołkamiPaper, a wi˛ec okre´slenia autorów prac. Kraw˛ed´z klasyWroteskierowana jest w stron˛e wierzchołka klasy
Bartek Grzegorz Marcin Andrzej Wojtek Karol Marek Tomek P2 P0 P1 P4 P3 P5 P6 P7
5.5. Przykładowa implementacja grafowej bazy danych
Paper. KlasaCiteł ˛aczy ze sob ˛a instancje klasyPaper, modeluj ˛ac zale˙zno´sci bi-bliograficzne pomi˛edzy pracami. Wierzchołek odpowiadaj ˛acy pracy, która cytuje drug ˛a prac˛e, jest wyj´sciem kraw˛edzi.
from bulbs . model im port Node , R e l a t i o n s h i p from bulbs . p r o p e r t y im port S tring
class Per son ( Node ):
e l e m e n t _ t y p e = " pe rson "
name = Str ing ( n u l l a b l e = False ) u n i v e r s i t y = S tring ( n u l l a b l e = True )
class Paper ( Node ):
e l e m e n t _ t y p e = " paper "
name = Str ing ( n u l l a b l e = False )
class Wrote ( R e l a t i o n s h i p ): label = " wrote "
class Cite ( R e l a t i o n s h i p ): label = " cite "
Skrypt odpowiedzialny za utworzenie przykładowej bazy danych tworzy naj-pierw wszystkie wierzchołki (zarówno odpowiedzialne za prace jak i za autorów), a nast˛epnie ł ˛aczy je ze sob ˛a, tworz ˛ac zamierzony graf reprezentuj ˛acy niewielk ˛a kolekcje prac naukowych. Przyj˛eto tak ˛a wła´snie metod˛e, gdy˙z nie wymaga ona sprawdzania, czy istnieje ju˙z dany autor w bazie danych przy dodawaniu nowych publikacji. Jednak nic nie stałoby na przeszkodzie, aby została zaimplemento-wana metoda dodaj ˛aca prac˛e i autorów, sprawdzaj ˛aca czy dane prace lub autorzy znajduj ˛a si˛e ju˙z w bazie danych.
#!/ usr / bin / env pytho n # * co ding : utf 8 *
-from l i b r a r y C l a s s e s im port Person , Paper , Wrote , Cite from bulbs . n e o 4 j s e r v e r i mport Graph
impo rt o p e r a t o r impo rt os
def c r e a t e N e w G r a p h ():
p e r s o n L i s t = [" G r z e g o r z " , " Marcin " , " Tomek " , " Bartek " , " A n d r z e j " , " Wo jtek " , " Marek " , " Karol "] p a p e r s L i s t = [" P0 " , " P1 " , " P2 " , " P3 " , " P4 " , " P5 " , " P6 " , " P7 "] p e o p l e L i s t = [[0 , 1] , [0 , 2] , [3 , 4] , [0 , 1 , 2] , [0] , [6 , 7] , [6 , 7] , [5 , 6 , 7]] c i t e L i s t = [ None , [0] , [0 , 1] , [6] , [0 ,1 ,2] , [3 ,4] , [0 ,1 ,2 ,4 ,5] , [4]]
g = Graph () g . clear () g . a d d _ p r o x y (" pe rson " , P erson ) g . a d d _ p r o x y (" paper " , Paper ) g . a d d _ p r o x y (" wrote " , Wrote ) g . a d d _ p r o x y (" cite " , Cite ) p e r s o n A d d e d = [] p a p e r A d d e d = [] for x in p e r s o n L i s t :
p e r s o n A d d e d . appen d ( g . perso n . crea te ( name = x ) )
for x in p a p e r s L i s t :
p a p e r A d d e d . a ppend ( g . paper . create ( name = x ) )
for i ~ in range ( len ( p a p e r s L i s t )): for x in p e o p l e L i s t [ i ]: g . wrote . cr eate ( p e r s o n A d d e d [ x ] , p a p e r A d d e d [ i ]) if c i t e L i s t [ i ] != None : for x in c i t e L i s t [ i ]: g . cite . cr eate ( p a p e r A d d e d [ i ] , p a p e r A d d e d [ x ]) Trawersowanie
Przeszukiwanie grafowej bazy danych odbywa si˛e za pomoc ˛a przechodzenia z jednego wierzchołka do drugiego poprzez ł ˛acz ˛ace je kraw˛edzie grafu. Zacho-wanie takie jest okre´slane jako trawersoZacho-wanie bazy danych. Obecnie nie istniej ˛a łatwe w obsłudze ani dobrze spopularyzowane frameworki słu˙z ˛ace do tego celu, które wspierałyby j˛ezyk Python. Dlatego wybrano Bulbflow, który umo˙zliwia bezpo´srednie wykonywanie skryptów napisanych w j˛ezykuGremlinz poziomu programu. Pozwala to sprawnie i szybko porusza´c si˛e po grafie. Na potrzeby przykładu zostały napisane nast˛epuj ˛ace skrypty Gremlin-Groovy, wykonuj ˛ace za-ło˙zone zadania:
1. allPapers- funkcja zwraca list˛e warto´sci polanamewszystkich wierzchołków okre´slaj ˛acych publikacj˛e, czyli tych których poleelement_typema warto´s´c
paper.
def a l l P a p e r s () {
retu rn g . V . has ( ’ ele ment_t ype ’ , ’ paper ’). name . t oList () }
2. allPeople - identycznie jak w allPapersz t ˛a ró˙znic ˛a, ˙ze teraz szukani s ˛a autorzy publikacji.
def a l l P e o p l e () {
retu rn g . V . has ( ’ ele ment_t ype ’ , ’ person ’). name . t oList () }
5.5. Przykładowa implementacja grafowej bazy danych 3. personPapersByName - funkcja wyszukuje najpierw wierzchołki nale˙z ˛ace do autorów o zadanym nazwisku, po czym przemieszcza si˛e po wszystkich kraw˛edziach o warto´sci pola label = wrote do prac powi ˛azanych z auto-rami. Na ko ´ncu zwraca tytułu (polename) publikacji.
def p e r s o n P a p e r s B y N a m e ( name ) {
retu rn g . V . has ( ’ ele ment_t ype ’ , ’ person ’) . has ( ’ name ’ , name ). out ( ’ wrote ’). name }
4. authorsOfPaperByName- podobnie jak w przypadkupersonPapersByName
najpierw wyszukuje prace o odpowiednim tytule, po czym przemieszcza si˛e po kraw˛edzi wchodz ˛acej do tych wierzchołków o warto´sci polalabel = wrotedo wierzchołków odpowiedzialnych za ich autorów. Funkcja zwraca warto´sci pólname.
def a u t h o r s O f P a p e r B y N a m e ( name ) {
retu rn g . V . has ( ’ ele ment_t ype ’ , ’ paper ’) . has ( ’ name ’ , name ). in ( ’ wrote ’). name }
5. mostCitedPapers- funkcja dla wszystkich wierzchołków zawieraj ˛acych pu-blikacje wyszukuje ich referencje (out(’cite’)), po czym zlicza ich nazwy
name, a wynik zapisuje w mapiem. def m o s t C i t e d P a p e r s () {
m = [:]
g . V . out ( ’ cite ’). name . g r o u p C o u n t ( m ). t oList () m = m . sort {a , b -> a . value <= > b . value } retu rn m
}
6. mostCitedAuthors - działa podobnie jak mostCitedPapers, z tym wyj ˛ at-kiem, ˙ze tym razem wyszukiwani i zliczani s ˛a autorzy prac.
def m o s t C i t e d A u t h o r s () { m = [:]
a ~= g . V . in ( ’ wrote ’). name . g r o u p C o u n t ( m ). t oList () m = m . sort {a , b -> a . value <= > b . value }
retu rn m }
7. sameReferancesAsByName - funkcja wyszukuje autorów prac, którzy pisz ˛ac swoje prace wykorzystuj ˛a te same pozycje bibliograficzne co zadany autor. W tym celu najpierw jest wyszukiwany wierzchołek odpowiedzialny za au-tora z prawidłow ˛a nazw ˛a, po czym zapytanie przesuwa si˛e do referencji jego prac (zapisuj ˛ac do zmiennejxprace, które on sam napisał). Nast˛epnie znaj-dywane s ˛a prace, które wykorzystuj ˛a te same referencje, wykluczaj ˛ac z nich prace zadanego autora (in(’cite’).except(x)). Potem funkcja przesuwa si˛e do wierzchołków odpowiedzialnych za autorów tych prac, zlicza ich wy-st ˛apienia i zapisuje wynik w mapiem. Metodadedupusuwa duplikaty z wy-niku.
def s a m e R e f e r a n c e s A s B y N a m e ( name ) { m = [:]
x = []
g . V . has ( ’ name ’ , name )
. out ( ’ wrote ’). store ( x ) . out ( ’ cite ’)
. in ( ’ cite ’). ex cept ( x ). dedup ()
. in ( ’ wrote ’). name . g r o u p C o u n t ( m ). t oList () m = m . sort {a , b -> a . value <= > b . value }
retu rn m }
8. sameAuthorReferancesAsByName - działa podobnie do funkcji
sameReferancesAsByName. Jej analiz˛e pozostawiono jako proste ´cwiczenie dla czytelnika.
def s a m e A u t h o r R e f e r a n c e s A s B y N a m e ( name ) { m = [:]
x = [] y = []
g . V . has ( ’ name ’ , name ). store ( x ) . out ( ’ wrote ’). store ( y ) . out ( ’ cite ’)
. in ( ’ wrote ’). ex cept ( x ) . out ( ’ wrote ’)
. in ( ’ cite ’). ex cept ( y ). dedup ()
. in ( ’ wrote ’). name . g r o u p C o u n t ( m ). t oList () m = m . sort {a , b -> a . value <= > b . value }
retu rn m }
Wymienione funkcje znajduj ˛a si˛e w plikugremlin.groovy. Same skrypty wy-konywane s ˛a za pomoc ˛a interfejsu udost˛epnionego przezBulbflowprzez odpo-wiednie funkcje, które dalej operuj ˛a na danych np.:
def s a m e R e f e r a n c e s A s B y N a m e ( Name ): g = Graph ()
g . s c r i p t s . up date ( ’ g r e m l i n . groovy ’)
scri pt = g . s c r i p t s . get ( ’ s a m e R e f e r a n c e s A s B y N a m e ’) items = g . g r e m l i n . e x e c u t e ( script , dict ( name = Name )) m = items . c o n t e n t if ( len ( m ) ): s o r t e d _ m = so rted ( m . i t e r i t e m s () , key = o p e r a t o r . i t e m g e t t e r (1)) retu rn s o r t e d _ m else : retu rn None
5.6. Podsumowanie Do ka˙zdej wcze´sniej omówionej funkcji napisanej w groovy została stworzona funkcja w Pythonie, która odbiera, przetwarza i udost˛epnia stworzonemu UI od-powiednie dane. Wszystkie te funkcje umieszczone s ˛a w plikulibrary.py.
Interfejs u˙zytkownika
Na potrzeby demonstracji napisano w Pythonie minimalistyczny konsolowy interfejs u˙zytkownika. Wykorzystuje on bibliotek˛ecursesdo stworzenia menu, które pozwala wybra´c polecenie dla bazy. Klasa menu została pobrana zhttp: //www.promisc.org/blog/?p=33. Funkcje, klasy i metody odpowiedzialne za menu zamieszczono wlibrary.py,menu.py. Odpowiedzi na zapytania s ˛a wy-´swietlane w konsoli. Wywołanie programu znajduje si˛e w plikumainLibrary.py.