gremlin是一个通用的图查询插件,尽管在neo4j上可以使用Cypher语言进行查询,但我仍想调研一下通过gremlin查询neo4j。
1. 安装
插件下载地址:https://archive.apache.org/dist/tinkerpop/,这里下载了3.4.4版本的console和server压缩包
gremlin的参考文档:https://tinkerpop.apache.org/docs/3.6.0/tutorials/getting-started/
2. 配置Neo4j连接
Tinkerpop Server 3.4.4支持的Neo4j版本是3.2.3。
- 安装neo4j依赖
cd /opt/gremlin/apache-tinkerpop-gremlin-server-3.4.4
bin/gremlin-server.sh install org.apache.tinkerpop neo4j-gremlin 3.4.4
- 配置conf/gremlin-server-neo4j.yaml和neo4j-empty.properties
在gremlin-server-neo4j.yaml配置需要连接的neo4j服务的ip和port
hosts: [10.30.239.205]
port: 7687
在neo4j-empty.properties配置需要连接的neo4j的数据库
gremlin.neo4j.directory=/opt/neo4j/data/databases/graph.db
- 启动Tinkerpop Server
bin/gremlin-server.sh conf/gremlin-server-neo4j.yaml
发现报错,无法连接:
排查后,关闭neo4j的服务,再次启动,成功
./neo4j stop
- 通过console访问
进入console…/conf目录,在remote.yaml文件中添加需要连接的neo4j库的host和port:
hosts: [10.30.239.205]
port: 7687
启动console客户端
cd /opt/gremlin/apache-tinkerpop-gremlin-console-3.4.4
./bin/gremlin.sh
执行如下命令,连接已配置好的neo4j数据库
:remote connect tinkerpop.server ./conf/remote.yaml
:remote console
# 确认连接是否成功
g
退出console
# 退出
:exit
3. 常用语法
gremlin的语法参考文档:https://blog.youkuaiyun.com/javeme/article/details/82631834,http://tinkerpop-gremlin.cn/#where-step
此处仅列出一些基础知识和个人在项目中可能使用的一些查询模板,V是节点的关键字,E是边的关键字。
- V()、E()、id()、label()、properties()、valueMap()、values()
- out()、in()、both()、outE()、inE()、bothE()、outV()、inV()、bothV()、otherV()
- hasLabel(labels…)、hasId(ids…)、has(key, value)、has(label, key, value)、has(key, predicate)、hasKey(keys…)、hasValue(values…)、has(key)、hasNot(key)
- count()、range()、limit()、tail()、skip()
- path()、simplePath()、cyclicPath()
- repeat()、times()、until()、emit()、loops()
- order()、by()
- group()、groupCount()、dedup()、by()
- where()、filter()
and、or、eq、neq、lt、lte、gt、gte、inside、outside、between、within、without、not - is()、and()、or()、not()
- sum()、max()、min()、mean()
- math()
- as()+select()、as()+where()、as()+match()、as()+dedup()
- choose()、branch()
- coalesce()、optional()、union()
- aggregate()、store()、unfold()、fold()
- match()
- sample()、coin()、constant()、inject()
- sack()
- barrier()
- local()、count()、max()、mean()、min()、sum()、order()、tail()、limit()、range()、sample()、skip()、limit()、dedup()
- hasNext()、next()、tryNext()、toList()、toSet()、toBulkSet()、fill()、iterate()
- map、flatMap()
- sideEffect()
- profile()、explain()
3.1 基础知识-v/e/p
# 查所有的节点
g.V()
# 查所有的边
g.E()
# 查某个节点id的所有属性
g.V(0).properties()
# 查所有节点的title属性值
g.V().properties('title')
# 查所有节点的属性
g.V().valueMap()
3.2 遍历
通过顶点来访问与其有关联边的邻接顶点.
# 查询id为0的节点所有相连的节点,可以通过both('')来限制相邻的边的类型
# 可以在语句中追加使用.out()、.in()来进行N度查询
g.V(0).both()
# barrier(),栅栏,可以强制之前的step都执行完毕后才会执行后面的step
# barrier()可以优化语法的执行效率
# LazyBarrierStrategy是默认策略,该策略会在合适的地方插入barrier,即隐式的自动使用barrier
g.V().both().barrier().both()
3.3 条件过滤
has语句是filter类型语句的代表,能够以顶点和边的属性作为过滤条件,决定哪些对象可以通过
# has(label, key, value),通过label和属性的名字和值过滤顶点和边
# 问题1:value需要完全相等,能不能部分相等
g.V().has('Actor', 'name', 'Keanu Reeves')
# 问题2:有多个label的节点查不出来
g.V().hasLabel('Movie::Sequel')
# 多个label的节点可以通过此语法查询
g.V().filter {it.get().label() == 'Movie::Sequel'}
3.4 路径
# 需要注意的是path()只能接在V后面使用
# simplePath():只保留路径中不含有环路的对象
# cyclicPath():只保留路径中含有环路的对象
g.V().has('Actor', 'name', 'Keanu Reeves').bothE().otherV().path()
3.5 分支
类似于if…else…then,或者switch…case…这样的功能,主要通过choose关键字实现
3.6 模式匹配
gremlin通过match语法实现模式匹配功能
// 对每一个顶点,用以下模式去匹配,满足则生成一个map<String, Object>,不满足则过滤掉
// 模式1:“a”对应当前顶点,且创建了软件“HugeGraph”
// 模式2:“b”对应顶点软件“HugeGraph”
// 模式3:“c”对应创建软件“HugeGraph”的年龄为29的person顶点
g.V().match(__.as('a').out('created').has('name', 'HugeGraph').as('b'),
__.as('b').in('created').has('age', 29).as('c'))
3.7 local
挺让人费解的一个用法,不太明白它的含义和它实现的功能。
local是对单个对象的局部操作,很多操作是针对传递过来的对象流中的全部对象进行操作,但也有很多时候需要针对对象流中的单个对象而非对象流中的全部对象进行一些操作。
# 输出所有节点的第一个属性
g.V().valueMap().limit(local, 1)
# 输出第一个节点的属性
g.V().valueMap().limit(1)
# 所有顶点一步邻居中所有的Actor
g.V().both().group().by(label).select('Actor').dedup(local)
# 所有顶点一步邻居中所有的Actor
g.V().both().group().by(label).select('Actor').dedup()
4. python访问
上面都是通过gremlin的console界面进行查询,此处,将介绍使用python来进行gremlin查询。
安装gremlin-python,pip3 install gremlinpython==3.6.0,连接服务器:
conn = DriverRemoteConnection('ws://%s:%s/gremlin' % (self.host, self.port), 'g')
graph = traversal().withRemote(conn)
常用的算子都在process下:
from gremlin_python.driver.driver_remote_connection import \
DriverRemoteConnection
from gremlin_python.process.anonymous_traversal import traversal
from gremlin_python.process.graph_traversal import both, in_, out, as_, label, \
count, valueMap, has, bothE, hasId, __, local, values, unfold
from gremlin_python.process.traversal import neq, lte, eq, within, gt, \
containing
from gremlin_python.process.traversal import Scope, WithOptions
一些常用的查询语法:
graph.V(22).out(a[2]).in_().where(out().count().is_(gt(10))).values('name').toList()
graph.V(22).repeat(__.both().simplePath()).until(__.hasId(434).or_().loops().is_(eq(2))).hasId(434).path().toList()
graph.V().label().dedup().toList()
# count(Scope.local):统计list内部元素的数量,15700
# count():统计有多少List,1
g.V().has('name', containing('0')).fold().count(Scope.local).toList()
# 将节点按label分组统计
graph.V().groupCount().by(label()).toList()
# 返回某个节点的属性,体会以下两个语法的不同
# valueMap():每key维护着一个值的list,[{name:..., tel:...}, {...}]
graph.V(1).valueMap().toList()
# 下列语法等效
g.V(1).properties().value().toList()
graph.V(1).values().toList()
# by(unfold()):将值的list转换为一个单一值
graph.V().has('name', containing('0')).limit(2).valueMap().by(unfold()).toList()
# valueMap():只列出属性,若需要输出id、label等需使用with
graph.V().has('name', containing('0')).limit(2).valueMap().with_(WithOptions.tokens).toList()
graph.V(1).out('电话').aggregate('x').by('name').cap('x').toList()
graph.V().values('count').order().by(Order.desc).toList()