Fullstack GraphQL Book Club - Chapters 2 & 3
Neo4j, Cypher, & Apollo Server
Each week we'll review a chapter from the Fullstack GraphQL Applications With GRANDstack Essential Excerpts ebook and work through the exercises. Download the free ebook to follow along: bit.ly/fullstack-graphql-ebook.This week we tackle Chapters 2 & 3, looking at using Neo4j, Cypher, the Neo4j JavaScript driver, and Apollo Server to build a GraphQL API.
Links And Resources#
- Download the free 3 chapter book excerpt, Fullstack GraphQL Applications
- Arrows online graph data modeling tool
- Open movies GraphQL API
- OpenStreetMap Neo4j Sandbox
- Cypher Refcard
- Apollo Server documentation
hey folks 04:09 welcome to the neo4j live stream 04:12 my name is will and this week 04:15 we are continuing on with the full stack 04:19 graphql book club series 04:23 um last week we covered chapter one 04:28 so if you are following along 04:32 uh today we will cover uh sort of 04:35 chapters two and 04:36 three i'll talk a bit about what i mean 04:39 by 04:40 sort of chapter two and three but uh 04:42 first of all if you don't 04:43 have yet a copy of 04:47 the book you can download it for free 04:51 here i'll drop a link in the chat 04:58 and so we're working through 05:02 the chapters and exercises of this book 05:06 full stack graphql applications with 05:08 grand 05:09 this book is published by manning 05:13 and we are working through um 05:17 a free three chapter excerpt of it 05:21 that is sponsored by neo4j so neo4j has 05:24 sponsored this book 05:25 uh to provide three chapters for free 05:28 um so if we look at the full 05:33 here's the full table of contents for 05:36 the book 05:36 which i'll drop in the chat 05:40 so this book is is that an early release 05:44 last week we covered chapter one 05:47 which was sort of an overview of the 05:50 different components of the grand stack 05:52 so graphql react apollo and 05:55 neo4j database chapter two 06:00 covers this this idea that we call graph 06:03 thinking 06:03 with graphql really this is about taking 06:07 business requirements for an application 06:10 and how do you model that in a 06:14 graph and then how do you go from a 06:17 graph model to implementing 06:20 a graphql api using apollo server 06:24 in chapter 2 just uses mock data 06:28 and then in chapter 3 we take a deeper 06:31 dive 06:32 on neo4j looking at 06:35 the cipher query language how to use 06:37 client drivers 06:38 the neo4j tooling that we use including 06:42 your desktop and neo4j browser to build 06:44 applications 06:46 so chapter 2 is not included though 06:49 in the excerpt ebook 06:53 that we're working with so we're going 06:55 to sort of 06:56 skip over chapter 2 but i actually kind 06:58 of combined some of the concepts 07:00 today which i think will be okay 07:03 now if you remember last time we 07:05 actually talked quite a bit about 07:07 this concept of of graph thinking with 07:10 graphql 07:11 and we went so far as to take the 07:15 business requirements 07:16 if you remember i think let's see if we 07:19 still have this loaded in arrows 07:22 yeah there we go so this is arrows 07:26 this is a diagramming and modeling tool 07:31 for building graph models in neo4j and 07:33 this is where we left off last time so 07:35 we went through the process of 07:40 thinking of the business requirements 07:42 for our application 07:44 that we're building which is a simple 07:47 business reviews application so we have 07:49 users users 07:51 create reviews that review a business 07:54 and then the idea is i can then search 07:56 for businesses in a category and view 07:58 reviews and recommendations 08:02 from other users 08:06 so we went through the business 08:09 requirements of our application 08:11 which were things like 08:14 as a user i want to be able to search 08:16 for businesses by category 08:17 as a user i want to view personalized 08:20 recommendations 08:21 of a business based on my reviews based 08:24 on the categories that i'm interested in 08:26 that sort of thing we translated that 08:29 into this 08:31 graph model we talked about this idea of 08:33 graph data modeling as this iterative 08:36 process 08:37 where we go through and identify what 08:38 are the entities those become the nodes 08:41 how are they connected those become the 08:43 relationships 08:45 and then we think of okay what are the 08:47 questions that i have 08:50 of the data can i think of a traversal 08:53 through the graph 08:54 that answers my question if i can 08:57 and if i can do that for all of the 08:59 business requirements of my application 09:01 then this model looks pretty good and 09:02 we're ready to start implementing our 09:04 application 09:05 if not then we might want to 09:10 make some iterative adjustments to our 09:12 model to take those into account and so 09:14 we 09:15 we looked at the idea of adding 09:17 categories 09:18 and talked about should category be 09:21 modeled as a property or a node 09:23 that sort of thing so if you missed that 09:26 and you're interested in 09:27 this idea of graph thinking and graph 09:29 data modeling 09:30 definitely check out last week's stream 09:34 which you can find the recording of in 09:36 the neo4j youtube 09:38 channel okay and then last week 09:41 we we actually then went a step further 09:44 and translated these into 09:46 graphql type definitions which we'll 09:48 talk more about today 09:52 we looked a little bit at react in 09:54 chapter one 09:56 and then the other thing we did is we 09:57 looked at a 10:02 sample 10:06 graphql api so this demo api which is 10:10 running here 10:11 at movies.grandstack.io i'll link that 10:14 in the chat as well 10:16 and we went through uh the exercises 10:19 from last time were 10:20 actually this is i think the the last 10:22 exercise that we left off on 10:23 which is find the movie with the highest 10:26 imdb rating we had to figure out how to 10:29 filter 10:30 out movies that had a null imdb rating 10:33 value 10:35 and so on so we we learned how to write 10:37 graphql queries 10:39 how to use graphql playground 10:42 uh inspect the documentation 10:46 for the graphql api 10:49 and then we also took a look at neo4j 10:52 so we looked at neo4j desktop 10:56 and how to create databases 11:00 how to write cipher queries i think we 11:03 looked at a 11:04 data set from openstreetmap which 11:07 by the way is available on neo4j sandbox 11:10 if you're interested in specifically the 11:13 one we looked at last time 11:15 here's the link for that 11:19 in neofj sandbox and i think we looked 11:21 at 11:22 writing cipher queries for routing 11:24 between points of interest 11:26 in the 11:30 openstreetmap dataset 11:33 cool so that's what we covered last time 11:35 um 11:36 what are we going to do today well 11:40 if we if we look at chapter two so 11:43 chapter two we're kind of skipping 11:45 um we actually i think went a bit 11:48 further last time 11:49 then uh or maybe a bit more in depth 11:51 than um 11:53 than what the book covers so i think 11:54 we've actually covered a lot of the 11:56 concepts 11:56 uh from chapters two and and three 11:58 already um 12:00 but that's fine since we're skipping 12:02 chapter two 12:04 i think the goal for today what we'll 12:06 try to do is 12:08 take a data set in neo4j so 12:11 we'll start with a sample data set of 12:13 businesses and reviews 12:15 and we'll learn how to write some cipher 12:17 queries to query 12:19 that database and then what i want to do 12:23 is see how to use the javascript 12:26 client driver for neo4j so oftentimes 12:28 when we're 12:29 writing applications we don't actually 12:32 want to use 12:33 tooling like neo4j browser instead we 12:36 want to use the client drivers 12:38 to actually in the language that we're 12:40 interested in if that's javascript 12:42 python 12:43 java.net whatever it is and we want to 12:46 be able to 12:47 talk to the database connect to the 12:48 database and query the database 12:51 that way using our client driver so 12:54 today we'll look at how can we 12:56 query this database using the javascript 13:00 driver for neo4j and then i want to go a 13:03 step further 13:04 and picking up some of the concepts from 13:06 chapter 2 that we're skipping 13:09 see how we can use apollo server 13:12 to build a graphql api that uses 13:15 the neo4j javascript driver to query 13:18 neo4j so that'll be the goal for 13:22 uh for today chance says hello everyone 13:26 hi chance 13:27 where are you joining us from today 13:33 okay so let's 13:37 go ahead and dig in in that case 13:40 so we'll jump to chapter three so 13:43 chapter three 13:44 this is a bit of a deeper dive on neo4j 13:49 the property graph data model which we 13:53 we talked about quite a bit 13:56 last time this idea of nodes 14:00 nodes have labels that group nodes 14:05 and relationships that connect them and 14:07 we store properties 14:10 as the attributes those are the 14:13 individual attributes that we store as 14:15 either properties on 14:16 nodes or relationships 14:20 we talked a little bit about cipher last 14:22 time so we'll go a bit deeper and cypher 14:25 this time and then also in chapter 3 we 14:29 introduce client drivers for neo4j 14:34 so chance is calling in from kenya we've 14:36 got zoltan from hungary 14:38 great welcome thanks for joining 14:45 let's see in our section on 14:49 neo4j overview i think one 14:53 concept that is important uh to think 14:56 about 14:56 is the differences of a graph database 15:00 with other databases that you may be 15:03 familiar with 15:06 in my mind that there are several 15:09 differences of course but in in my mind 15:11 the data model that you're working with 15:14 is one of the most prominent 15:15 right so in a relational database 15:19 the data model that we work with 15:21 consists of tables 15:23 and relations basically 15:27 foreign key references how those tables 15:29 are connected 15:32 we have a strict schema in a relational 15:34 database that defines 15:36 what are the attributes of each table 15:39 what is the type of each attribute 15:42 that sort of thing and we typically use 15:45 sql 15:46 to write queries and work with 15:48 relational data 15:50 this concept of a join is very important 15:53 in 15:54 uh in the relational world so if i have 15:57 a 15:58 foreign key that's referencing an entry 16:00 in another table 16:02 i need to do a join operation 16:04 essentially 16:05 seeing where those two tables overlap 16:09 this set operation that's using an index 16:11 to 16:12 compare where those two tables overlap 16:15 to give me the result of my 16:16 of my join that's kind of like finding a 16:18 relationship 16:21 this this join operation the set 16:24 comparison operation 16:25 that's using an index to see where these 16:27 two tables overlap 16:29 um the performance of that starts to 16:32 uh starts to break down when i have lots 16:34 and lots of data when those tables are 16:36 very large 16:37 those joins become very slow because i'm 16:40 comparing where the 16:41 uh the entire two sets the entire two 16:43 tables 16:44 overlap so i have to use this global 16:45 index to do that 16:48 so this is a a common problem or 16:51 a common thing to think about at least 16:54 with graphql 16:55 because we're often constructing these 16:56 very nested 16:59 queries that result in several joins 17:03 in a relational database with graphql 17:06 so then a document database is 17:09 something like or 17:12 couchbase where i have collections 17:16 and documents uh 17:20 another term generically for for this 17:23 is i think nosql when people think of 17:25 nosql databases they're often thinking 17:27 of 17:28 of document databases that's not quite a 17:30 perfect mapping there's also key value 17:32 stores and things like that out there 17:33 but that's 17:34 you know maybe i think most of the time 17:36 what folks are thinking of 17:38 and in a document database you may 17:42 reference another collection type with 17:45 an id 17:46 in the document so then your application 17:50 is then responsible for resolving those 17:53 ids and making another query 17:55 to the collection to do that equivalent 17:57 of a join or 17:58 you may just embed that additional 18:01 information 18:02 in the document 18:05 which results in sort of duplicating 18:07 that so 18:09 for example if you have maybe 18:12 um a document of blog posts 18:16 and you have information about the 18:18 author 18:19 you can either have a separate author 18:20 collection or you can embed 18:22 all of the author detail information in 18:25 each blog post document and duplicate 18:27 that 18:28 in each document then we come to 18:31 graph databases like neo4j where we're 18:33 using the 18:34 property graph model which we've talked 18:37 about a bit 18:38 already so i won't go into too much 18:40 detail there 18:41 we have nodes these are the entities the 18:44 objects connected by relationships 18:46 relationships are a first-class citizen 18:49 of 18:50 of the database so these relationships 18:52 are persisted 18:53 and part of the data model whereas if 18:56 you look at 18:57 the relational model those 19:00 those joins are essentially finding 19:03 relationships at query time 19:06 so not necessarily stored in the 19:08 database themselves 19:09 so anyway with the sort of graph 19:12 nature in graphql where we are often 19:15 traversing a data graph in 19:18 a graph database to me i think this is 19:21 really a perfect 19:23 match up because one from the data 19:26 modeling aspect 19:27 there's a very close one-to-one mapping 19:29 of how we think about the data 19:31 as a graph in graphql and how we 19:35 model store and query that data in 19:38 the database but the other aspect is the 19:41 performance consideration 19:43 so because in graphql like i said we're 19:45 often creating these nested 19:47 queries that are referencing other 19:49 objects 19:51 a graph database like neo4j is optimized 19:54 for 19:55 doing that traversal to traverse from 19:57 one node 19:58 to any other is a very fast and constant 20:01 time 20:02 operation in a graph database like neo4j 20:05 so unlike relational databases where 20:07 we're comparing 20:10 sets comparing where entire tables 20:12 overlap 20:13 to execute a join in a graph database 20:16 you're essentially just chasing 20:18 a pointer to an offset on disk and 20:20 that's very fast it's not dependent on 20:22 the overall 20:23 size of the data so anyway that those i 20:26 think are 20:27 are some important things to think about 20:29 uh when we're looking at 20:31 the backend database for our application 20:34 and in this case with graphql 20:40 so we talked a bit about about the data 20:42 modeling considerations already so 20:44 i might just i might just skip over that 20:48 we also looked last time a little bit at 20:50 neo4j desktop 20:52 in neo4j browser which is a query 20:55 workbench 20:55 we'll dive into that in the exercises so 20:58 i'm going to skip over those sections as 20:59 well 21:00 and then we come to a section on 21:04 cipher and here we're referencing the 21:08 cipher manual and the cipher ref card so 21:12 let's bring this up so cipher 21:16 ref card 21:20 this is a great reference document let 21:23 me drop a link 21:24 in the chat it's a great reference 21:30 document 21:31 for learning and 21:35 referring to the different operations 21:39 the different clauses the different 21:40 syntax for these different operations 21:42 that i can do in cipher 21:44 fundamentally cipher is all about 21:46 pattern matching 21:48 you can think of of cipher as 21:51 query language for graphs think of it as 21:53 maybe sql for graphs if you're familiar 21:55 with sql 21:56 but pattern matching is a fundamental 21:59 part of cipher 22:00 uh do we have a an example here we 22:04 this example is kind of 22:08 yeah here's here's a good one so with 22:11 with cipher we create these patterns 22:15 here we're defining a pattern 22:19 for a single node so nodes are 22:21 represented in 22:23 parentheses so this uh is a single node 22:26 with a label 22:27 business and assigning it to the 22:31 variable b 22:32 which we can then refer to later to this 22:34 k in this case 22:35 set the name property of this business 22:38 node and this is a crate operation 22:40 so this creates one node in the database 22:47 let's let's go ahead and skip to the 22:50 exercises here and just get our 22:53 get our hands dirty there's 22:56 oh right and then there's also um client 22:59 drivers which we'll talk about in a 23:01 second 23:02 i'm just going to bring up the 23:05 developer guide so this is the developer 23:08 guides 23:08 for neo4j which is a good resource for 23:12 learning how to use neo4j many different 23:15 aspects of neo4j 23:17 and how to use neo4j with different 23:20 tooling and we are interested in the 23:24 drivers and language guides and 23:26 specifically 23:28 javascript for nodejs 23:32 so we'll refer to this later on but 23:34 basically we install 23:36 the neo4j driver package import that 23:40 we can then create a driver instance to 23:42 connect 23:43 to our neo4j instance we create a 23:46 session 23:48 object and then that session can send 23:52 cipher queries that then give us results 23:54 to work with in our application 23:58 okay so let's jump over to the exercises 24:02 and just start off here 24:06 with number one oh first first though we 24:09 need a data set so this says 24:12 to complete the following exercises run 24:14 the following command in neo4j browser 24:17 grand stack delete a browser guide and 24:19 embedded queries 24:22 then we load this larger more complete 24:25 sample data set of businesses and 24:26 reviews after writing the query 24:29 to load the data in neo4j then work 24:31 through these exercises so 24:32 great let's switch over to neo4j desktop 24:37 i'm going to go ahead and create a new 24:39 database 24:40 we'll call this chapter 3. 24:43 i need to set a password 24:47 it can be whatever i want but i want to 24:50 remember 24:51 what it is since i'll need that when 24:55 we start using the client driver 25:03 and let's say run this command colon 25:06 play grand 25:07 stack uh i'm gonna s 25:11 we can we can click here to open ufj 25:14 browser 25:16 in desktop but it's also running 25:20 locally at localhost 7474 25:25 so i'm just going to switch to that 25:27 since i already have my 25:29 my tiled windows set up here 25:33 okay so this is neo4j browser which we 25:35 used last time 25:36 last time we were using a pre-loaded 25:39 data set 25:40 so if i run a query now that says 25:43 match in return count in this is saying 25:48 match so find find 25:52 parts of the graph that match some 25:54 pattern what's the pattern 25:55 well 25:58 open close parenthesis means a node 26:02 we're not specifying any qualifiers any 26:04 predicates here 26:06 so it's just going to match on any node 26:07 regardless of the label 26:09 assign that to the variable in 26:12 and then we're going to return what 26:14 we're going to return 26:15 well passing in into this count 26:18 aggregation function that will tell us 26:20 that we have 26:21 zero nodes in the database so there's 26:23 nothing nothing in the database 26:25 we want to run colon play 26:29 grand stack and has this 26:33 embedded query which we can click on and 26:36 if i hit escape i can get the 26:40 full view of the query um 26:44 what's going on here well this is 26:46 loading a 26:47 csv file that has some sample data 26:51 and then creating 26:54 data for each row 26:57 in this case we're creating businesses 26:59 users reviews 27:01 connecting those businesses reviews 27:04 and categories as well so we'll run that 27:09 it says okay you've now created 36 nodes 27:13 100 and some properties 41 relationships 27:15 great so now we've got some data 27:17 and we can start working through our 27:19 exercises 27:23 okay first off run the command call 27:25 db.schema to inspect the data model 27:27 what are the node labels used what are 27:29 the relationship types 27:31 so call db.schema 27:35 in this case we'll do visualization 27:36 since we want the 27:38 the visual representation of it there's 27:40 also i think a tabular 27:42 view as well but let's look at this 27:46 visual representation of it so here's 27:48 here's our 27:49 data model that we're working with we 27:50 have user nodes 27:53 so in other words nodes with the label 27:55 user so label is a way that we 27:58 group and categorize nodes so we have 28:01 user 28:01 review business category 28:04 okay those are our labels uh the other 28:07 question what are the relationship 28:09 types so every relationship in neo4j has 28:13 a single 28:14 type and direction so we have 28:18 rote which connects users to reviews we 28:21 have reviews 28:22 that connects reviews to businesses and 28:25 in category 28:26 that create that connects businesses and 28:30 categories 28:33 okay cool that 28:36 is number one number two 28:41 let me zoom in a little bit there we go 28:44 write a cipher query 28:45 to find all the users in the database 28:48 how many users are there and what are 28:51 their names 28:53 okay so we're going to write a 28:57 match make this a bit bigger too this is 29:00 going to be a match 29:01 statement so match is find some pattern 29:05 and then return something so what's the 29:09 pattern 29:10 in this case we're going to look for 29:11 nodes so we start with a 29:13 open and close parenthesis that's find 29:15 me a node pattern 29:17 and then colon user 29:21 is sort of a filter here we're saying 29:24 only 29:24 find nodes with the label user 29:29 and then we want to be able to refer to 29:31 those user nodes later on 29:33 so we'll add the variable u and then 29:36 let's 29:37 return u so find all user nodes in the 29:40 database 29:41 and return those it looks like we have 29:44 four users 29:46 if we click on these we can see the 29:49 properties 29:50 so a user has a name and a user id 29:55 we also have this internal id this is 29:58 just 29:59 uh the internal neo4j id which is not 30:02 something we want to use in 30:03 some external system it's just sort of 30:05 like telling us where 30:08 um where on disk this node record is 30:12 stored 30:15 okay so great those are the users we can 30:17 also double click on these 30:19 to start to traverse out to see 30:22 what are the reviews this user has 30:24 written 30:26 what businesses 30:29 what category what are other businesses 30:31 in that category so we can 30:32 sort of visually traverse through the 30:35 graph that way 30:37 but the next part of our question is how 30:39 many users 30:41 are there well if i run this again 30:44 to clear this i can see there's four i 30:47 can count those 30:49 that's that's probably not good enough i 30:50 think we want to write a cipher query 30:53 to do this so we saw this count 30:57 function earlier so count is this 31:00 aggregation function 31:02 so we can say match all the users 31:05 then return the count and there's four 31:08 okay 31:08 great that's how we do 31:12 aggregations 31:16 the next part is what are their names 31:21 so in this case 31:26 let's instead of returning 31:29 an aggregation function we will refer to 31:33 the property so return u dot name so for 31:36 every 31:38 every user node return the name property 31:41 in just that so now we get a 31:44 tabular results so in the fj browser we 31:46 can return 31:49 nodes relationships paths or we can just 31:51 return 31:52 tabular data with properties 31:58 okay number three find all the reviews 32:00 written by the user named will 32:01 what's the average rating given by this 32:04 user 32:06 okay well if i do shift enter that's 32:08 going to give me the multi-line editor 32:10 and now i'm going to need to define a 32:11 more complex pattern 32:13 so previously i was just defining this 32:17 node with the label user pattern 32:20 in this case now i want to traverse out 32:23 from this user 32:26 to find 32:30 user wrote 32:33 review and let's add an 32:37 r here so now u is going to refer to the 32:39 user r is going to refer 32:41 to the review and 32:44 i only want 32:47 the reviews written by this user named 32:49 will 32:51 so let's add a where clause and a 32:54 predicate 32:55 where u.name equals will 32:58 and let's do a return star so return 33:00 starts will return 33:02 um return 33:05 star this will return everything that 33:08 we've assigned 33:09 to a variable here 33:15 let's let's style oops let's style our 33:19 graph a little bit so if i click on 33:21 review 33:23 i can choose the caption that i want to 33:25 show let's 33:26 display the stars so that's 33:31 the rating for the review okay so will's 33:34 written 33:34 five reviews but i want to know what are 33:36 the average 33:37 rating given by this user well 33:41 okay we saw how we can do the count 33:44 aggregation function in this case we're 33:46 going to use the average 33:49 aggregation and instead of just passing 33:53 in 33:54 a node we're going to pass in just a 33:55 property so 33:57 give me the average of the stars 34:00 not on u though on r on the review node 34:06 so on average this user will 34:10 there's a 4.2 rating across his reviews 34:18 okay next up find all the businesses 34:20 reviewed by 34:22 the user well what's the most common 34:24 category oh okay 34:25 so this let's start with the query we 34:28 had previously 34:31 so find all the businesses reviewed 34:35 by the user will so we've got the 34:37 reviews 34:39 uh if we go back to our our model 34:42 we scroll down here a little bit we've 34:44 gone from the user 34:45 to the review well now we just need 34:48 again 34:49 a bit more complex graph pattern and we 34:52 can say 34:52 go from the review 34:57 to the business 35:02 so one thing that's really nice about 35:04 cipher is we can 35:05 define these arbitrarily complex graph 35:08 patterns 35:09 and they're fairly 35:12 fairly straightforward to construct 35:15 using this sort of ascii art 35:17 notation i can also move up my 35:20 predicate in curly braces here and i can 35:23 say 35:24 name will 35:28 and remove this 35:31 it comes comes a bit a bit much to 35:35 leave on all one line especially because 35:37 i 35:39 i'm zoomed in let me zoom out a little 35:41 bit there we go 35:46 uh okay so 35:50 that will give me all the businesses 35:52 that this user will has reviewed 35:54 but now so let's return a 35:58 star here and we can see all of these 36:03 so here's this user well here's the 36:05 reviews 36:06 and here's the businesses so he's 36:08 written a review of 36:10 this business if we click on the 36:11 business we can see we have the address 36:14 the business id city location which is 36:17 the latitude and longitude 36:19 the name of the business and the state 36:20 that it's in 36:22 now if we traverse out from these 36:25 business nodes 36:27 we see the category 36:32 and that is what we're after in this 36:34 case it's asking us 36:36 the category so we actually need to add 36:38 a little bit here to our pattern 36:41 so now we need to say business 36:44 in category 36:49 so now we traverse out to the category 36:52 okay so now 36:53 that's the subgraph we're working with 36:54 now we're out to the categories 36:56 and the question is what are the most 37:00 common categories 37:07 uh yeah so like like chance says in the 37:09 chat yep instead of using the 37:11 instead of using the where clause we can 37:14 just move that 37:15 into in the curly braces yep exactly 37:22 well we can return 37:27 the category 37:31 okay so here's all the categories but it 37:33 says the most common 37:34 what do i mean by that well if we see 37:38 here 37:38 that we have categories that 37:43 maybe are showing up more than once 37:45 right so 37:48 although in this case we may not 37:50 actually have that we may just have 37:53 uh one category or one entry for each 37:56 category 37:56 but anyway let's write our query so um 38:00 in the relational world or in sql 38:03 what we what we would do here is called 38:06 a group by 38:07 so we would do something like group by 38:11 category return count or something like 38:13 that 38:14 in cypher and neo4j there's an implicit 38:17 group by 38:18 any time we return something along with 38:20 an aggregation 38:21 we get an implicit group by whatever 38:23 we're returning 38:24 alongside that aggregation so in this 38:27 case 38:28 we can return c dot name 38:32 so that's going to be the the name of 38:34 the category 38:36 and then let's return 38:39 uh count 38:42 star as num 38:47 so num is the number of times that that 38:50 category shows up 38:52 uh in this case that's not very exciting 38:54 because all these categories only 38:56 show up once 39:00 okay now write a query to recommend 39:03 businesses 39:04 to the user named will that he hasn't 39:06 previously reviewed 39:08 okay cool so in this case 39:12 um we've got 39:15 all the way to the category so we know 39:17 what what categories 39:19 of businesses will likes so a 39:21 recommendation query might go 39:23 something like this uh so c 39:27 so c now in the second match is 39:29 referring to 39:32 the category of businesses that this 39:34 user has written reviews of 39:38 so let's find other businesses 39:43 in that category call that rec 39:51 but we want to say where not 39:54 exists because we don't want 39:57 uh we don't want to recommend a business 40:01 that will has already reviewed 40:05 so where this pattern where the will 40:08 wrote a 40:13 review 40:19 and that review 40:24 reviews the recommendation 40:28 all right let me zoom out a little more 40:32 make that a little 40:34 a little more readable just some long 40:35 paths and then 40:38 return that recommendation 40:44 okay so here are three 40:47 businesses that this user might be 40:48 interested in uh hanabi 40:51 which is a ramen 40:54 restaurant and imagination brewing which 40:59 is a 41:00 brewery so this is a 41:04 fairly simple recommendation 41:08 um where we're looking at the categories 41:12 this user is interested in 41:14 finding other businesses that are in 41:15 that same category we could make this 41:17 more complex 41:19 to include 41:23 comparing the ratings of other users 41:26 so what users have similar ratings to 41:30 this user will what businesses are they 41:32 reviewing and so on 41:34 those kind of queries are are really 41:37 nice to write in in cypher 41:38 and graph databases 41:44 if you're interested in learning more 41:47 about 41:47 those type of recommendation queries 41:49 there's a neo4j sandbox instance 41:53 called recommendations 41:57 which has the movie lens data set so a 42:00 data set of uh 42:03 i got like 100 000 user ratings of 42:06 movies 42:07 and then some movie data information 42:11 and shows how to generate 42:12 recommendations so i'll link that's just 42:15 use case 42:18 recommendations i'll drop a link to that 42:22 as well 42:26 okay cool so that is all the exercises 42:29 from chapter three 42:32 now what i want to do though is 42:35 see how we can use apollo server 42:38 which we talked about just kind of 42:41 briefly last time 42:45 uh yep here's the docs link these in the 42:48 chat 42:51 so apollo server allows us to take 42:54 graphql type definitions the type 42:57 definitions 42:58 define the data available in our graphql 43:02 api 43:03 take those and the resolvers the 43:05 resolvers are functions 43:06 that have the logic for actually 43:08 fetching this data from the data layer 43:10 so apollo server allows us to take 43:12 typedefs resolvers 43:14 combine those together create a graphql 43:18 executable schema object serve that 43:21 over the network layer take incoming 43:23 graphql requests 43:25 execute that against our schema 43:28 and resolvers and send back results 43:32 apollo server also has some 43:35 functionality for hooking into 43:39 to middleware uh for using different 43:42 uh network 43:46 layers uh different so different 43:48 frameworks by default i think we use 43:49 express with apollo server 43:50 but there's also like a lambda version 43:53 or different 43:54 http frameworks that we can use you can 43:58 also use 44:00 apollo's data source abstraction 44:03 which gives us server-side caching and 44:06 things like that in apollo server so 44:07 there's a lot more 44:08 to apollo server than what we're going 44:09 to look at today but today what i want 44:12 to do is just see how we can 44:14 use the javascript driver to build a 44:16 simple 44:18 graphql api 44:21 hey isaac thanks for joining thanks for 44:23 the message 44:26 uh okay so where to get started well 44:27 let's start 44:29 with creating a new node project 44:32 uh so 44:36 there's my terminal 44:40 where is my terminal good question 44:44 there we go uh 44:47 okay so let's call this 44:51 chapter three 44:55 and do an npm 45:02 init 45:04 and then npm install what are we going 45:07 to install 45:07 how about neo4j driver apollo 45:11 server and i think we need the 45:15 graphql implementation 45:20 so we'll install those three packages 45:22 and the first thing we're going to do 45:23 is basically 45:26 replicate this kind of result here where 45:29 we're using 45:31 the uh javascript driver just to execute 45:35 a query 45:35 and log the results 45:39 so let's open this up in 45:43 vs code should i do this side uh 45:47 now let's leave this open let's cover up 45:49 our 45:50 our book here 45:58 and let's get a terminal 46:02 make that a bit bigger 46:09 and create index.js 46:16 okay so first 46:20 let's pull in ufj driver 46:25 let's go ahead and pull in 46:34 apollo server 46:37 and we'll also pull in gql which is 46:42 a parser 46:45 gql template tag allows us to parse 46:48 graphql type definitions 46:51 from apollo server 46:55 okay and then we can create a driver 46:57 instance 46:59 so this will be neufj.driver 47:03 and the connection string by default 47:06 so i want to connect to the database 47:07 that i have running locally that i 47:09 started in 47:10 desktop that's going to run 47:14 with the bolt protocol on localhost 7687 47:18 and then there's different ways that i 47:20 can authenticate into the database but i 47:23 just want to use uh neo4j.auth.basic 47:31 which just takes a username 47:36 and password 47:44 okay great i've got my driver instance 47:47 let's just come up with some 47:50 query uh how about match in return 47:53 count in so just tell me how many nodes 47:55 are in the database 47:57 then with the driver we create a 48:01 session 48:05 so a session is kind of like a 48:08 an object that's going to do a unit of 48:10 work 48:11 um in this case that work is going to be 48:14 running a query 48:14 uh but we want to make sure that we we 48:17 close 48:19 queer closed sessions as soon as we're 48:21 done with them 48:22 since that represents sort of a 48:24 connection to the database as we don't 48:26 want to leave those hanging around 48:28 so what in we also 48:31 can use await async 48:34 here so what i'm going to do is create a 48:36 new async function 48:41 called main and then in main 48:46 i'm going to have a try block and 48:49 finally 48:51 session dot close so this will ensure 48:54 that our session.close 48:57 function is going to call it so 49:01 in here execute 49:04 the query uh log 49:07 results um 49:10 this so why do this in a in a try catch 49:13 without a catch well this means that 49:17 session.close will always be called 49:20 regardless of what happens when i run 49:23 the query so that that 49:24 session connection to the database gets 49:27 closed 49:29 calvin says thanks for the ebook 49:32 lifesaver at literally the perfect time 49:33 great cool 49:34 glad uh glad you found it 49:37 um calvin tell us a little bit about 49:40 what you're building maybe 49:41 are you working on a on a project 49:43 wouldn't mind telling us a little bit 49:44 about that 49:47 okay so what do we want to do well we 49:50 want to run the query 49:51 so we'll say the result is going to be 49:55 await session dot run and pass in the 49:58 query 50:01 so oh wait it's just a nice way of 50:03 working with 50:04 promises in async so because this is an 50:06 async function 50:08 you can say await here and driver will 50:11 go out to the database 50:13 asynchronously get the results back but 50:16 because i'm saying 50:17 await we won't we won't move on to the 50:20 next line until we actually have our 50:22 result and then 50:25 just using using this example we have on 50:27 the right we'll say 50:28 our single record is going to be result 50:32 dot records 50:35 which is an array so cipher returns 50:39 array of results array of records so 50:42 let's just grab the first one 50:44 and then let's say the 50:48 r the result is going to be 50:51 single record dot get so 50:55 a record is like a result set or a 50:58 result 50:58 object and we want to then 51:02 grab columns so in our 51:05 query here we're returning tabular data 51:09 so count in we can alias that 51:12 to num or we don't we can give it no 51:14 name so we could say get 51:16 num here or in like in the example here 51:19 it's just grabbing 51:21 the first the first column if we don't 51:24 care about what it's called in this case 51:26 we'll grab a column 51:28 called num and then this is going to be 51:32 an integer to represent 51:35 the double precision integers or the 51:39 64-bit integers that neo4j uses we can 51:42 cast those to a 51:45 javascript number type and then console 51:49 dot log r 51:53 and then caller main function 51:57 why did we create this main function 51:59 well because we wanted to use 52:00 async await and that needs to be wrapped 52:04 in a 52:04 async function so let's run this 52:08 node index.js is this going to work 52:13 no it did not lock anything 52:17 why not 52:22 what went wrong here 52:25 uh nifty driver 52:32 we have our query 52:37 the result 52:44 session.run 52:50 result.records 52:55 singlerecord.get we do return a 53:00 named column don't we 53:03 oh there we go 53:07 okay so it says there are 36 so i was 53:11 just not grabbing the right 53:14 column i guess okay cool 53:20 so we have 36 nodes in the database 53:24 okay so what i want to do now is modify 53:27 this to 53:31 work with apollo server and 53:36 use this 53:39 use these this javascript driver to 53:41 execute a query 53:43 but in a graphql server implementation 53:49 so let's get rid of 53:52 this part 53:56 let's bring in um 54:01 our type definitions from last time so i 54:04 have these 54:06 save where they ah here they are so 54:10 now we're going to say our typedefs so 54:13 this is what we wrote last time 54:16 wrap these in the gql template tag 54:19 so we said okay here's our type 54:22 definitions 54:23 user review business category 54:26 query type search for businesses 54:30 that's what we came up last time for the 54:33 type definitions that map to our 54:35 business requirements 54:39 and then 54:42 we said that apollo server allows us to 54:44 take type definitions 54:46 and resolvers combine those together to 54:49 get an executable graphql schema so now 54:52 we need some resolvers 54:58 let's just leave that see if we can get 55:00 away leaving that blank for now 55:02 and then the next thing we want is to 55:04 create 55:05 a server so we'll say server equals 55:09 new apollo server 55:14 type defs resolvers 55:18 and i'm also going to specify a context 55:22 object that takes our driver what's 55:26 going on there 55:27 well each one of these there's no 55:29 resolver functions 55:31 quite just yet but each one of those 55:34 resolver functions is past 55:36 this context object so this context 55:39 object 55:40 is a great place to inject like database 55:44 connections or if we have like api 55:50 data fetching logic layer to inject that 55:53 into the context 55:54 object so then in our resolver functions 55:57 we can access 55:59 those things and then 56:02 uh server dot 56:06 listen and then i think we can 56:12 uh this is returns a promise 56:16 that we can 56:19 log the url so we know 56:23 what port it's listening on 56:26 let's give that a try 56:29 it says localhost 4000 if we go to 56:31 localhost 4000 56:33 we should see 4 000 not 3 000. we should 56:37 see 56:38 graphql playgrounds 56:42 and if we go to our docs tab we can see 56:46 [Music] 56:47 yep this matches uh the type definition 56:51 so the query type 56:52 has two entry points current user and a 56:55 business search 56:56 so what we want to do is implement 56:57 business search because right now 57:00 if we write a query that says business 57:01 search 57:03 and a search string i don't search for 57:05 the library 57:07 and give me the name of the library we 57:10 get back null because we didn't write a 57:11 resolver function 57:13 for that so let's do that 57:16 now 57:20 i just need to 57:25 let someone know i'm going to be a 57:26 little bit late for my 57:28 meetings we want to finish this out 57:29 before we drop 57:40 there we go that's fine finish this out 57:43 so um okay we need to write a resolver 57:46 function 57:47 for the business search 57:51 field on the query type 57:55 well uh resolvers this is like a like a 57:58 map 57:59 of resolver functions 58:03 uh and so we're going to have a 58:06 query object that's going to have 58:10 resolver functions for all of the query 58:13 fields 58:14 so this is going to be business search 58:16 it's going to be an async function 58:19 and every resolver is past a few things 58:23 the first thing that it's passed is 58:26 an object and because resolvers are 58:29 called in this nested 58:31 fashion we're passing along whatever 58:35 we've resolved 58:37 thus far and in this case because this 58:39 is a root resolver 58:40 we haven't resolved anything uh so 58:44 in this case this object this is going 58:46 to be blank are going to be empty 58:48 so we don't really care about that at 58:49 the root so we'll just 58:51 call that underscore um but we will be 58:54 passed a 58:55 params object next and these are 58:58 the field parameters so any any 59:01 arguments parameters that we pass in 59:03 here 59:04 so in this case we have to pass a search 59:06 string 59:07 so we definitely want to know the params 59:11 and then we also are past this context 59:13 object which is where we have injected 59:16 our driver instance 59:21 uh okay cool so now 59:25 this is gonna be a function let's 59:29 format this so it's a little readable 59:31 cool okay 59:32 so the first thing we'll do is write our 59:34 query and now in 59:36 in most cases you're probably going to 59:38 want to like 59:39 have some uh some data 59:42 abstraction layer so you're not actually 59:45 writing 59:46 queries inside your resolvers but this 59:49 will 59:49 this will work for us we'll say 59:53 match business 59:57 where bdot name contains 60:03 a search string return 60:07 b what is this saying this says 60:11 let's make this a little wider 60:14 let's go maybe half and half here 60:18 something like that 60:21 there we go so it says find business 60:24 nodes 60:25 where the name has 60:29 this value search string so this is a 60:31 parameter in 60:32 cipher and where is that going to come 60:34 from that's going to come from the 60:36 params 60:37 object that's passed into our resolver 60:40 which is going to be 60:41 this search string when we write the 60:44 graphql query 60:47 okay so that's the query that we want to 60:49 run 60:50 next we need to create a session 60:54 object so context dot driver is where we 60:57 injected the driver 61:00 call the session function to get us a 61:01 session connection to the to 61:04 the database from the connection pool 61:05 and then we'll put this in our 61:07 try block so in the finally 61:11 block we can say session dot 61:14 close and then in the try 61:18 let's say results 61:21 equals it's going to be await 61:25 session dot run now i'm using 61:28 session.run 61:30 [Music] 61:32 as kind of just an easy 61:35 implied way of dealing with transactions 61:37 in neo4j 61:38 there are these things called 61:40 transaction functions 61:41 that actually have things like um like 61:44 retrying from the client 61:46 uh and actually refetching the routing 61:50 table if we're working with 61:52 with a neo4j cluster so in a production 61:55 application we don't want to use 61:57 this session.run instead we want to 61:59 explicitly work with transaction 62:01 functions 62:02 so we can control the transaction a bit 62:04 better but this is a nice convenient way 62:07 to work with this in our case so we're 62:09 going to say session.run 62:11 the query and then the next thing we 62:13 pass in are the 62:15 parameters and in this case we said what 62:18 search 62:19 string is going to be params 62:23 dot search so that's 62:27 the search argument here in the params 62:31 object that's passed into our resolver 62:36 okay so that gets me results 62:40 and then what i want to return 62:43 from this function well b is going to be 62:47 a node object so there's a specific 62:53 class and type called a node with a 62:56 javascript driver 62:57 that has properties and metadata and 63:00 other things and what i want to return 63:03 is basically just grab all the 63:04 properties into a javascript object and 63:07 return that 63:08 that maps up to this structure 63:11 so what i'm going to do i'm going to say 63:16 return return 63:21 results dot records so for every 63:25 record that's returned map 63:28 so let's apply a function to that 63:31 so for every record 63:36 i want to 63:39 record dot get b so b 63:43 this is the business node so i'm 63:44 returning b 63:47 and then i'm going to call dot 63:49 properties on it dot properties 63:52 this is the javascript object 63:54 representation of the node so that 63:56 basically just grabs 63:58 all of the properties of the node 64:01 and returns that 64:06 so let's restart our server 64:10 and now if we go back to graphql 64:13 playground on the right and now if i run 64:15 my query 64:16 i'm searching for library and it says oh 64:18 yep here's the missoula public 64:20 library if i search for i don't know if 64:23 i remove 64:24 everything and just pass an empty string 64:27 i get all the businesses 64:28 that's fun 64:32 um okay what about if i add 64:36 categories so categories 64:40 is or category i should say is 64:43 an object that has just a name 64:47 but i get back a null here so i'm not 64:50 getting any data why why is that well 64:53 remember in 64:54 the database categories 64:58 are nodes themselves so category is not 65:01 stored 65:02 as a property on the node 65:06 rather it's stored as 65:13 a node itself so i need to traverse from 65:16 the business 65:18 to the category 65:22 so how do we do that well 65:26 if we look at our type definitions here 65:30 the business type has a categories field 65:34 so i need to implement a categories 65:37 resolver uh 65:40 or the business type 65:44 so here's the query 65:47 now for the 65:50 business type 65:55 we need to implement a resolver called 65:58 categories 66:00 and this is going to be kind of similar 66:02 so an async 66:04 function it is passed remember these 66:07 resolvers are called in a nested fashion 66:09 so it's past whatever we've resolved 66:14 previously so we've we've resolved the 66:16 business object 66:18 so we also bring in any parameters and 66:21 the context object 66:26 okay now let's write our query what's 66:28 our query going to be 66:29 well let's match 66:32 uh from the business 66:36 and we know the name of the business 66:38 because remember we've resolved the 66:40 business 66:40 so far so we know the business node 66:42 we'll look it up by name 66:44 but we want to traverse out 66:48 to 66:52 category any category 66:57 nodes and let's this time let's just 66:59 return the name 67:01 of the category and elast that to cat 67:06 okay then just like before we need a 67:09 session 67:10 context dot driver dot 67:14 session 67:17 and in our try catch so in the finally 67:22 session close 67:26 and in the try here well results 67:30 is going to be await session.run 67:35 pass in the query and then we need to 67:38 also be sure to grab 67:39 the name value and pass that as a 67:43 parameter so 67:44 name is going to be in this case 67:47 business 67:48 so the business object that we've 67:50 resolved already 67:52 and the name property that 67:57 um okay cool and then 68:01 i'm gonna return just like we did before 68:04 results dot records and let's map 68:08 over each of those records 68:11 because we want to sort of massage the 68:15 data that we get back from the database 68:16 we want to 68:17 massage that a little bit to get that in 68:18 the right format that apollo server is 68:20 expecting 68:22 for a list of category objects 68:25 and for that we're just going to return 68:28 an object so category is simple it just 68:29 has 68:30 a name so we'll return an object 68:34 with the key name and the value 68:38 record dot get cat 68:41 which is what we returned the name of 68:44 the category as 68:46 here 68:49 okay let's restart our server and see if 68:53 that works 68:55 back to apollo server so business search 68:57 search for the library 69:00 give me the name of the library give me 69:02 the categories previously 69:03 we were getting null for categories if 69:06 we run it now 69:07 hey now we get the name is library 69:10 and if we remove the search criteria 69:13 let's work for every 69:16 business yes it will so here's all the 69:18 businesses we have and all of the 69:20 categories 69:22 if you notice when i run this 69:25 well the first time i ran it it was a 69:26 bit slow 69:28 and why was that well it's because it 69:30 it's making 69:31 now multiple round trips to the database 69:35 so first we execute the 69:38 business search resolver and 69:41 we get back this list of businesses and 69:44 then because we requested 69:45 categories for every business then we 69:48 have to go back 69:49 and for every business make another 69:51 query to the database 69:53 to execute this query to fetch the 69:56 business 69:57 which is not quite ideal 70:02 so in the next 70:06 chapter we'll see how to implement a 70:11 faster smarter version 70:14 of this looking at the 70:18 neo4j graphql integration 70:21 um you can think of think of what we did 70:24 here with apollo server 70:26 as maybe a naive implementation 70:30 of a uh graphql server 70:34 um because we have this in plus one 70:36 query problem 70:38 uh where we're making multiple round 70:40 trips uh 70:41 to the database instead it would be nice 70:45 if we could 70:47 create a single query to send to the 70:49 database for 70:50 any arbitrary graphql 70:54 query graphical operation really that 70:56 we're making we just want to make one 70:58 request to the database 71:00 and then also writing these resolvers 71:04 this wasn't super fun um these look very 71:07 similar 71:08 there's a lot of boilerplate here uh 71:11 you know i would prefer not to have to 71:13 write this for every 71:14 uh type for in every field uh wouldn't 71:18 it be nice if we could 71:19 have some auto generation functionality 71:22 there that we're generating these 71:25 resolvers and 71:26 generating this data fetching logic for 71:29 me that would be 71:30 that would be nice so that's what we'll 71:33 look at 71:34 in the next chapter uh 71:37 which we will dig into next 71:40 thursday same time 2 p.m 71:44 pacific uh 71:47 if you don't have the book yet i'll 71:51 link it again here full stack 71:55 graphql ebook this is a free download of 71:57 um 71:58 three chapters of the o'reilly book 72:00 focused mostly 72:01 uh on the backend aspects 72:05 um cool so today 72:08 we had a bit of an overview of cipher 72:12 uh we worked with a data set 72:15 of businesses and reviews figuring out 72:18 how to write some cipher queries 72:20 to query that we 72:23 also looked at using the javascript 72:28 driver for neo4j and then we created a 72:32 simple apollo server graphql server 72:36 implementation 72:37 using the neo4j javascript driver 72:42 and we queried some data 72:46 to search for businesses by name find 72:48 categories 72:49 and so on so next week we'll take a 72:52 deeper dive 72:53 into the neo4j graphql implementation 72:56 and build out 72:57 a much more functional complete version 73:00 of this api thanks for joining 73:04 today and hope you can join next week 73:07 as well cheers
Subscribe To Will's Newsletter
Want to know when the next blog post or video is published? Subscribe now!