Grafovske baze podataka – Neo4j
Iako su relacione baze podataka i dalje najpopularnija vrsta baza podataka, poslednjih godina se pojavilo nekoliko „alternativnih“, takozvanih NoSQL baza podataka. To su baze podataka koje nisu bazirane na relacinom modelu podataka. NoSQL sistemi nastali su iz novih zahteva za većom fleksibilnošću i boljim performansama za smeštanje velike količine podataka. Razlog tome je uglavnom zbog popularnost Interneta i informacionih tehnologija i sve veće količine podataka koja se generiše. Jedna od popularnih vrsta NoSQL baza podataka su grafovske baze podataka, koje će biti tema nastavka ovog teksta.
Sadržaj
Šta su grafovske baze podataka?
Graf baze podataka umesto relacionog koriste koncept grafa kao model podataka. Graf je struktura podataka koja se sastoji od čvorova i grana između njih. Sami čvorovi služe sa čuvanje podataka organizovanih kao skup ključ/vrednost parova, dok grane predstavljaju veze odnosno relacije između podataka. Izvršavanje upita kod ovakvih sistema sastoji se od praćenja putanja, odnosno veza među čvorovima. Na primer, ako nas zanima koga vole Zoranovi prijatelji, možemo jednostavno krenuti od Zorana i tražiti sve veze Prijatelj. U našem slučaju to je samo Žika. Onda od Žike trazimo sve čvorove sa vezom Voli. Na taj način dolazimo do rezultujućeg čvora Ana. Graf baze podataka su vrlo efikasne za ovakve probleme, jer rade sa direktnim vezama među čvorovima.
Neo4j – Zašto i kada ga koristiti?
Neo4j je jedna od vodećih svetskih graf baza podataka otvorenog koda razvijena upotrebom Java tehnologije. Neo4j omogućava:
- Fleksiblina šema – Nema potrebe za fiksnim modelom podataka. Atributi se mogu dodavati i uklanjati po potrebi.
- Skalabilnost – Omogućava povećanje broja upisa/čitanja kao i veličine samo baze bez uticaja na brzinu izvršavanja upita.
- Replikacija – Omogućava replikaciju podataka obezbeđujući time maksimalnu sigurnost i pouzdanost.
- ACID (Atomicity, Consistency, Isolation, Durability) model transakcija
- Ugrađena web aplikacija – Upite za kreiranje i pretragu baze možete kreirati kroz jednostavan web interfejs.
- Jednostavno modeliranje (Whiteboard Friendly) – Za Neo4j se kaže da je whiteboard friendly jer omogućava da model baze napravite jednostavnim crtanjem čvorova i veza.
- Podržava indekse
Neki od slučajeva kada je najpogodnije koristiti Neo4j bazu podataka:
- Otkrivanje i prevencija prevara – Velike kompanije gube milijarde dolara godišnje zbog prevaranata koji se služe raznim sofisticiranim trikovima i prevarama kao što su, krađa identiteta, lažno predstavljanje, prevare sa kreditnim karticama i pranje novca. Neo4j pomaže u njihovom otkrivanju. U nastavku teksta demonstiraćemo jedan primer suzbijanja prevare.
- Praćenje mrežne infrastrukture
- Sistem za preporuke u realnom vremenu – Posedujete online prodavnicu. Uz pomoć Neo4j baze podataka lako možete korisnicima preporučiti dodatne artikle iz ponude na osnovu onoga što pretražuju.
- Društvene mreže – Omogućava ubrzavanje kako razvoja tako i same aplikacije.
- Prava pristupa i kontrola identiteta
Klikom ovde možete pogledati primer za kreiranje sistema za obaveštavanje klijenata putem emaila upotrebom Neo4j baze podataka.
Cypher – Upitni jezik
Cypher je deklarativni upitni jezik inspirisan SQL-om. Omogućava nam da navedemo šta želimo da selektujemo, dodamo, izmenimo ili obrišemo iz grafa bez eksplicitnog navođenja načina na koji če to biti učinjeno.
MATCH (node1:Label1)-[:relation]->(node2:Label2)
WHERE node1.propertyA = {value}
RETURN node2.propertyA, node2.propertyB
Kreiranje čvorova
CREATE (you:Person {name:"Jovica"}) RETURN you
Ovaj upit kreira novi čvor označen kao you tipa Person i sadrži podatak sa ključem name čija je vrednost „Jovica“.
Kreiranje relacija
MATCH (you:Person {name:"Jovica"})
CREATE (you)-[like:LIKE]->(neo:Database
{name:"Neo4j" })
RETURN you,like,neo
Naredbom MATCH pronalazimo podatak tipa Person kome se vrednost ključa poklapa sa zadatom vrednosti „Jovica“ i dodeljuje mu oznaku you preko koje će se referincirati na taj podatak. Naredbom CREATE kreira se relacija LIKE ka drugom podatku tipa Database sa podatkom koji za ključ name ima vrednost „Neo4j“.
MATCH (you:Person {name:"Jovica"})
FOREACH (name in ["Pera","Zika","Ana","Laza","Dragan"] |
CREATE (you)-[:FRIEND]->(:Person {name:name}))
Ova komanda kreira nove podatke tipa Person sa imenima navedenim u FOREACH delu i prema svima kreira vezu tipa FRIEND.
Pretraga prijatelja
MATCH (you {name:"Jovica"})-[:FRIEND]->(yourFriends)
RETURN you, yourFriends
Ovaj upit kao rezultat vraća sve podatke prema kojima postoji veza tipa FRIEND od podatka kome za ključ name odgovara zadata vrednost „Jovica“.
Kreiranje prijatelja našeg prijatelja
MATCH (neo:Database {name:"Neo4j"})
MATCH (ana:Person {name:"Ana"})
CREATE (ana)-[:FRIEND]->(:Person:Expert
{name:"Dragana"})-[:WORKED_WITH]->(neo)
Nalaženje najkraćeg puta
MATCH (you {name:"Jovica"})
MATCH (expert)-[:WORKED_WITH]->(db:Database
{name:"Neo4j"})
MATCH path = shortestPath( (you)-[:FRIEND*..5]- (expert) )
RETURN db,expert,path
Ovaj upit prolazi rekurzivno kroz sve naše prijatelje maksimalno do dubine 5 zaključno sa osobom koja je expert.
Kao što možemo videti Neo4j ima ugradjenu funkciju shortestPath koja u pozadini koristi Dijkstra algoritam za pronalaženje najkraćeg puta između čvorova. U poređenju sa relacionim bazama podataka, ovakva pretraga kod graf baza podataka traje neuporedivo kraće.
Brisanje čvora
MATCH (n:Useless)
DELETE n
Brisanje relacija
MATCH (n { name: 'Andrew' })-[r:FRIEND]->()
DELETE r
Brisanje čvora i svih njegovih relacija
MATCH (n { name: 'Andrew' })
DETACH DELETE n
Izmena i dodavanje atributa
MATCH (n { name: 'Jovica' })
SET n.surname = 'Andrić' RETURN n
Uklanjanje atributa
MATCH (andres { name: 'Andres' })
REMOVE andres.age
RETURN andres
Kreiranje indeksa
CREATE INDEX ON :Person(firstname)
Kreiranje kompozitnog indeksa
CREATE INDEX ON :Person(firstname,
surname)
Brisanje indeksa
DROP INDEX ON :Person(firstname)
Izlistavanje svih indeksa
CALL db.indexes
Primer upotrebe Neo4j baze podataka za otkrivanje prevaranata
Jedan od načina na koji se problem sa prevarantima manifestuje prati sledeći scenario. Grupa prevaranata kreira u nekoj banci veliki broj bankovnih računa. Sve račune otvaraju koristeći kombinacije imena, prezimena, brojeva telefona, adresa, brojeva socijalnog osiguranja itd. Nakon otvaranja, račune koriste normalno kako niko ne bi posumnjao na prevaru, što podrazumeva redovne prilive i odlive novca sa računa, javljanje na pozive bankarskih službenika, dostavljanje potrebne dokumentacije, primanje pošte koju banka šalje, itd. Nakon nekog vremena, svi računi odlaze u dozvoljeni minus i nestaju. Više se ne javljaju i banka ne može da stupi u kontakt sa njima. Dug se otpisuje i banka gubi ogromnu količinu novca.
Kako bismo demonstrirali način na koji Neo4j pomaže u suzbijanju ovakvog problema potrebno je pripremiti radno okruženje.
To je moguće odraditi preko online alata ili instalacijom Neo4j paketa na lokalnu mašinu. Uputstvo za instalaciju možete pronaći ovde
Nakon što je radno okruženje podešeno, potrebno je učitati testni set podataka.
To možete uraditi sledećom komandom:
// Create account holders
CREATE (accountHolder1:AccountHolder {
FirstName: "John",
LastName: "Doe",
UniqueId: "JohnDoe" })
CREATE (accountHolder2:AccountHolder {
FirstName: "Jane",
LastName: "Appleseed",
UniqueId: "JaneAppleseed" })
CREATE (accountHolder3:AccountHolder {
FirstName: "Matt",
LastName: "Smith",
UniqueId: "MattSmith" })
// Create Address
CREATE (address1:Address {
Street: "123 NW 1st Street",
City: "San Francisco",
State: "California",
ZipCode: "94101" })
// Connect 3 account holders to 1 address
CREATE (accountHolder1)-[:HAS_ADDRESS]->(address1),
(accountHolder2)-[:HAS_ADDRESS]->(address1),
(accountHolder3)-[:HAS_ADDRESS]->(address1)
// Create Phone Number
CREATE (phoneNumber1:PhoneNumber { PhoneNumber: "555-555-5555" })
// Connect 2 account holders to 1 phone number
CREATE (accountHolder1)-[:HAS_PHONENUMBER]->(phoneNumber1),
(accountHolder2)-[:HAS_PHONENUMBER]->(phoneNumber1)
// Create SSN
CREATE (ssn1:SSN { SSN: "241-23-1234" })
// Connect 2 account holders to 1 SSN
CREATE (accountHolder2)-[:HAS_SSN]->(ssn1),
(accountHolder3)-[:HAS_SSN]->(ssn1)
// Create SSN and connect 1 account holder
CREATE (ssn2:SSN { SSN: "241-23-4567" })<-[:HAS_SSN]-(accountHolder1)
// Create Credit Card and connect 1 account holder
CREATE (creditCard1:CreditCard {
AccountNumber: "1234567890123456",
Limit: 5000, Balance: 1442.23,
ExpirationDate: "01-20",
SecurityCode: "123" })<-[:HAS_CREDITCARD]-(accountHolder1)
// Create Bank Account and connect 1 account holder
CREATE (bankAccount1:BankAccount {
AccountNumber: "2345678901234567",
Balance: 7054.43 })<-[:HAS_BANKACCOUNT]-(accountHolder1)
// Create Credit Card and connect 1 account holder
CREATE (creditCard2:CreditCard {
AccountNumber: "1234567890123456",
Limit: 4000, Balance: 2345.56,
ExpirationDate: "02-20",
SecurityCode: "456" })<-[:HAS_CREDITCARD]-(accountHolder2)
// Create Bank Account and connect 1 account holder
CREATE (bankAccount2:BankAccount {
AccountNumber: "3456789012345678",
Balance: 4231.12 })<-[:HAS_BANKACCOUNT]-(accountHolder2)
// Create Unsecured Loan and connect 1 account holder
CREATE (unsecuredLoan2:UnsecuredLoan {
AccountNumber: "4567890123456789-0",
Balance: 9045.53,
APR: .0541,
LoanAmount: 12000.00 })<-[:HAS_UNSECUREDLOAN]-(accountHolder2)
// Create Bank Account and connect 1 account holder
CREATE (bankAccount3:BankAccount {
AccountNumber: "4567890123456789",
Balance: 12345.45 })<-[:HAS_BANKACCOUNT]-(accountHolder3)
// Create Unsecured Loan and connect 1 account holder
CREATE (unsecuredLoan3:UnsecuredLoan {
AccountNumber: "5678901234567890-0",
Balance: 16341.95, APR: .0341,
LoanAmount: 22000.00 })<-[:HAS_UNSECUREDLOAN]-(accountHolder3)
// Create Phone Number and connect 1 account holder
CREATE (phoneNumber2:PhoneNumber {
PhoneNumber: "555-555-1234" })<-[:HAS_PHONENUMBER]-(accountHolder3)
RETURN *
Rezultat izvršavanja ovog upita prikazan je na Slici 3.
Nakon što smo učitali testne podatke, treba da nađemo sve vlasnike računa kojima se neki podaci poklapaju.
To ćemo učiniti sledećim upitom:
MATCH (accountHolder:AccountHolder)- []->(contactInformation)
WITH contactInformation, count(accountHolder) AS RingSize
MATCH (contactInformation) 1
RETURN AccountHolders AS FraudRing,
labels(contactInformation) AS
ContactType,
RingSize
ORDER BY RingSize DESC
Sada kada smo našli sve vlasnike računa koji su potencijalni prevaranti, treba da otkrijemo koliki maksimalan gubitak svaki od njih donosi ukoliko načini prevaru.
To ćemo saznati čim izvršimo sledeći upit.
MATCH (accountHolder:AccountHolder)-[]->(contactInformation)
WITH contactInformation,
count(accountHolder) AS RingSize
MATCH (contactInformation)(unsecuredAccount)
WITH collect(DISTINCT accountHolder.UniqueId) AS AccountHolders,
contactInformation, RingSize,
SUM(CASE type(r)
WHEN 'HAS_CREDITCARD' THEN unsecuredAccount.LIMIT
WHEN 'HAS_UNSECUREDLOAN' THEN
unsecuredAccount.Balance
ELSE 0
END) AS FinancialRisk
WHERE RingSize > 1
RETURN AccountHolders AS FraudRing,
labels(contactInformation) AS ContactType,
RingSize,
round(FinancialRisk) AS FinancialRisk
ORDER BY FinancialRisk DESC
Krajnji rezultat ovog našeg primera može se prikazati u obliku sledeće tabele
Literatura
- https://neo4j.com
- https://rubygarage.org/blog/neo4j-database-guide-with-use-cases
- https://neo4j.com/cypher-graph-query-language/
- https://neo4j.com/docs/operations-manual/current/installation/
- https://www.tutorialspoint.com/neo4j/neo4j_overview.htm