Over a million developers have joined DZone.

Cypher vs. Gremlin in Neo4j

· Database Zone

To stay on top of the changing nature of the data connectivity world and to help enterprises navigate these changes, download this whitepaper from Progress Data Direct that explores the results of the 2016 Data Connectivity Outlook survey.

The Neo4jClient now supports Cypher as a query language with Neo4j. However I noticed the following:

  • Simple graph traversals are much more efficient when using Gremlin
  • Queries in Gremlin are 30-50% faster for simple traversals
  • Cypher is ideal for complex traversals where back tracking is required
  • Cypher is our choice of query language for reporting
  • Gremlin is our choice of query language for simple traversals where projections are not required
  • Cypher has intrinsic table projection model, where Gremlins table projection model relies on AS steps which can be cumbersome when backtracking e.g. Back(), As() and _CopySplit, where cypher is just comma separated matches
  • Cypher is much better suited for outer joins than Gremlin, to achieve similar results in gremlin requires parallel querying with CopySplit, where as in Cypher using the Match clause with optional relationships
  • Gremlin is ideal when you need to retrieve very simple data structures
  • Table projection in gremlin can be very powerful, however outer joins can be very verbose

So in a nutshell, we like to use Cypher when we need tabular data back from Neo4j and is especially useful in outer joins.

Here are two queries that return the exact same data from Neo4j, one in Cypher and one in Gremlin.

Cypher Report Query

var resultSet = graphClient.RootNode
                .StartCypher("root")
                .Match(@"root-[:HOSTS]->(agency)
                       <-[:USER_BELONGS_TO]-(user)-[:USER_LINKED_TO_PROGRAM]
                       ->(program)
                       <-[:HAS_PROGRAM]-(centre),
                       (program)<-[:HAS_SUGGESTED_PROGRAM]-(referralDecisionsSection)
                       <-[:REFERRAL_HAS_DECISIONS_SECTION]-(referral)-[:CREATED_BY]
                       ->(createdByUser), (referral)-[:REFERRAL_HAS_WHO_SECTION]
                       ->(whoSection)-[:HAS_PARTICIPANT]->(participant)")
                .Where<Agency>(agency => agency.Key == userIdentifier.AgencyKey)
                .And()
                .Where<User>(user => user.Username == userIdentifier.Username)
                .And()
                .Where<Referral>(referral => referral.Completed == false)
                .Return((user, program, centre, createdByUser, referral, whoSection, participant) => 
                new ReferralByGroup
                {
                    UserFamilyName = createdByUser.As<User>().FamilyName,
                    UserGivenName = createdByUser.As<User>().GivenName,
                    Program = program.As<Program>().Name,
                    Centre = centre.As<Centre>().Name,
                    ReferralId = referral.As<Referral>().UniqueId,
                    ReferralDate = whoSection.As<ReferralWhoSection>().ReferralDate,
                    ParticipantName = participant.As<ReferralParticipant>().Name,
                    ParticipantDisplayOrder = participant.As<ReferralParticipant>().DisplayOrder,
                })
                .Results
                .ToArray();

Gremlin Report Query using Table Projections

var resultSet = graphClient
    .RootNode
    .Out<Agency>(Hosts.TypeKey, a => a.Key == userIdentifier.AgencyKey)
    .In<User>(UserBelongsTo.TypeKey, u => u.Username == userIdentifier.Username)
    .Out<Program>(UserLinkedToProgram.TypeKey)
    .As("Program")
    .In<Centre>(HasProgram.TypeKey)
    .As("Centre")
    .BackV<Program>("Program")
    .In<ReferralDecisionsSection>(HasSuggestedProgram.TypeKey)
    .In<Referral>(ReferralHasDecisionsSection.TypeKey, r => r.Completed == false)
    .As("ReferralId")
    .Out<User>(CreatedBy.TypeKey)
    .As("UserGivenName")
    .As("UserFamilyName")
    .BackV<Referral>("ReferralId")
    .Out<ReferralWhoSection>(ReferralHasWhoSection.TypeKey)
    .As("ReferralDate")
    .Out<ReferralParticipant>(HasParticipant.TypeKey)
    .As("ParticipantDisplayOrder")
    .As("ParticipantName")
    .Table
    <ReferralByGroup, Program, Centre, Referral, User, User, ReferralWhoSection, ReferralParticipant,
        ReferralParticipant>(
            program => program.Name,
            centre => centre.Name,
            referral => referral.UniqueId,
            user => user.FamilyName,
            user => user.GivenName,
            who => who.ReferralDate,
            participant => participant.Name,
            participant => participant.DisplayOrder
    )
    .ToArray();

 Below is the converted parameterised script sent for cypher and gremlin respectively for those not familiar with the Neo4jClient.

Cypher


START root=node({p8})
MATCH root-[:HOSTS]->(agency)
                       <-[:USER_BELONGS_TO]-(user)-[:USER_LINKED_TO_PROGRAM]->(program)
                       <-[:HAS_PROGRAM]-(centre),
                       (program)<-[:HAS_SUGGESTED_PROGRAM]-(referralDecisionsSection)
                       <-[:REFERRAL_HAS_DECISIONS_SECTION]-(referral)-[:CREATED_BY]
                       ->(createdByUser), (referral)-[:REFERRAL_HAS_WHO_SECTION]
                       ->(whoSection)-[:HAS_PARTICIPANT]
                       ->(participant)
WHERE (agency.Key? = {p0}) AND (user.Username? = {p1}) AND (referral.Completed? = {p2})
RETURN createdByUser.FamilyName? AS UserFamilyName, createdByUser.GivenName? AS UserGivenName, program.Name? AS Program, centre.Name? AS Centre, referral.UniqueId? AS ReferralId, whoSection.ReferralDate? AS ReferralDate, participant.Name? AS ParticipantName, participant.DisplayOrder? AS ParticipantDisplayOrder

 

Gremlin

g.v(p0)
.out(p1).filter{ it[p2].equalsIgnoreCase(p3) }
.in(p4).filter{ it[p5].equalsIgnoreCase(p6) }
.out(p7).as(p8).in(p9).as(p10).back(p11)
.in(p12).in(p13).filter{ it[p14] == p15 }.as(p16)
.out(p17).as(p18).as(p19).back(p20)
.out(p21).as(p22).out(p23).as(p24).as(p25)
.table(new Table()){it[p26]}{it[p27]}{it[p28]}{it[p29]}{it[p30]}{it[p31]}{it[p32]}{it[p33]}
.cap

 I have included below the non-paramerterised cypher and gremlin query respectively.

Cypher

START root=node(0)
MATCH root-[:HOSTS]->(agency)<-[:USER_BELONGS_TO]-(user)-[:USER_LINKED_TO_PROGRAM]
->(program)
<-[:HAS_PROGRAM]-(centre),(program)
<-[:HAS_SUGGESTED_PROGRAM]-(referralDecisionsSection)
<-[:REFERRAL_HAS_DECISIONS_SECTION]-(referral)-[:CREATED_BY]->(createdByUser), (referral)-[:REFERRAL_HAS_WHO_SECTION]
->(whoSection)-[:HAS_PARTICIPANT]
->(participant)   WHERE (agency.Key? = romikoagency) AND (user.Username? = romiko.derbynew) AND (referral.Completed? = false)   
RETURN createdByUser.FamilyName? AS UserFamilyName, createdByUser.GivenName? AS UserGivenName, program.Name? AS Program, centre.Name? AS Centre, referral.UniqueId? AS ReferralId, whoSection.ReferralDate? AS ReferralDate, participant.Name? AS ParticipantName, participant.DisplayOrder? AS ParticipantDisplayOrder

 

Gremlin

g.v('0').out('HOSTS').filter{ it['Key'].equalsIgnoreCase('romikoagency') }
.in('USER_BELONGS_TO').filter{ it['Username'].equalsIgnoreCase('romiko.derbynew') }
.out('USER_LINKED_TO_PROGRAM').as('Program')
.in('HAS_PROGRAM').as('Centre').back('Program')
.in('HAS_SUGGESTED_PROGRAM')
.in('REFERRAL_HAS_DECISIONS_SECTION').filter{ it['Completed'] == false }.as('ReferralId')
.out('CREATED_BY').as('UserGivenName').as('UserFamilyName').back('ReferralId')
.out('REFERRAL_HAS_WHO_SECTION').as('ReferralDate')
.out('HAS_PARTICIPANT').as('ParticipantDisplayOrder').as('ParticipantName')
.table(new Table()){it['Name']}{it['Name']}{it['UniqueId']}{it['FamilyName']}{it['GivenName']}{it['ReferralDate']}{it['Name']}{it['DisplayOrder']}.cap

 


 

 

Turn Data Into a Powerful Asset, Not an Obstacle with Democratize Your Data, a Progress Data Direct whitepaper that explains how to provide data access for your users anywhere, anytime and from any source.

Topics:

Published at DZone with permission of Romiko Derbynew, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

The best of DZone straight to your inbox.

SEE AN EXAMPLE
Please provide a valid email address.

Thanks for subscribing!

Awesome! Check your inbox to verify your email so you can start receiving the latest in tech news and resources.
Subscribe

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}