Gremlin -- 常用查詢用法

yoylee_web發表於2018-12-03

一:gremlin查詢過程

gremlin的查詢是流式查詢,一步一步的進行下去,當然這裡的“一步”可能是一個方法(g.V().has())也可能是多個方法組成的一步(g.V().order().by(desc,‘age’))。下面看一個案例

g.V().has('code','AUS').out().value('name','age').order().by('age',desc)

步驟解讀
第一步:g.V() 標明是對相簿中的所有節點進行操作的
第二步:has(‘code’,‘AUS’) 獲取包含屬性code並且該屬性的值為AUS的所有節點
第三步:out() 獲取上個結果集中所有節點的出邊對應的節點
第四步:value(‘name’,‘age’) 獲取上個結果集中所有節點的name和age屬性值
第五步:order().by(‘age’,desc) 對結果集根據age進行降序排序
從上面便可以看出gremlin流式執行的特徵,這使得gremlin的查詢語句可以十分的靈活,從而滿足我們的各種查詢需求。

1:想要了解更多全面的查詢方法,可以看英文官網:http://kelvinlawrence.net/book/Gremlin-Graph-Guide.html#_introduction
2:通過看本文章你會對gremlin查詢的形式方法有大體的瞭解,之後找方法在官網就可以了
3:本文章在使用過程會不斷更新

二:常用的查詢方法

  1. 首先,這裡的g.V()中的g為遍歷例項,其建立為:
graph = TinkerGraph.open()
g = graph.traversal() 
  1. V()與E()

在下面的例子中,你會發現幾乎每一個查詢的開始都會有他們的存在
V()代表檢視圖中的所有節點,接下來的操作是對節點進行操作的
E()代表圖中的所有邊,接下來的操作就是對邊操作的

  1. 使用value獲取節點的某一屬性值
g.V().has('code','AUS').out().value('name','age')
//獲取AUS的出邊對應節點的name和age屬性的值
g.V().has('code','AUS').out().value()
//顯示所有的屬性值
  1. 使用has、hasNot獲取(不)包含某一屬性值得節點
g.V().has('code','AUS')
//獲取擁有code屬性並且其屬性值為AUS的節點
g.V().has('user','code','AUS')
//相當於 g.V().hasLabel('user').has('code','AUS')
g.V().hasNot('name')
//獲取所有不包含name屬性的節點,等同於g.V().not(has('name'))
  1. 使用hasLabel獲取label為某值得節點
g.V().hasLabel("user")
//獲取label為user的節點
  1. 使用hasNext方法判斷兩個節點中是否有查詢的邊
  • 返回值為boolean型別引數,存在則返回true,不存在則false
g.V().has('code','AUS').out('route').has('code','DFW').hasNext()

true

g.V().has('code','AUS').out('route').has('code','SYD').hasNext()

false
  1. 使用range()獲取某一範圍內的資料
g.V().hasLabel('airport').range(0,2)
//輸出結果集中1,2個節點
g.V().hasLabel('airport').range(3,6)
//輸出結果集中4,5,6個節點
g.V().range(3500,-1)
//輸出結果集中3500往後的所有節點
  1. 使用skip跳躍節點查詢
g.V().has('region','US-TX').skip(5)
//跳過節點集中的前5條資料,從第六條開始,效果等同於下面的語句
g.V().has('region','US-TX').range(5,-1)
  1. 使用order對結果集進行排序
g.V().has('code','AUS').out().order().by()
g.V().has('code','AUS').out().order(local).
g.V().has('code','AUS').out()
g.V().has('code','AUS').out()

order(local)中local的作用:Notice also how local is used as a parameter to order. This is required so that the ordering is done while the final list is being constructed. If you do not specify local then order will have no effect as it will be applied to the entire result which is treated as a single entity at that point. 這是官網上的一句話,我翻譯了一下沒太明白,大體的意思我理解的是:加local引數的話會在最終結果生成前就完成排序。。大家可以翻譯一下

  1. 使用out,in進行查詢結點的出邊和入邊所對應的節點
g.V().has('code','AUS').out()
//獲取AUS的節點所有出邊對應的節點
g.V().has('code','AUS').out("brought")
//獲取AUS節點所有邊關係為“brought”的出邊對應的節點
g.V().has('code','AUS').in()
//獲取AUS的節點所有入邊對應的節點
g.V().has('code','AUS').in("brought")
//獲取AUS節點所有邊關係為“brought”的入邊對應的節點
  1. 使用count、groupCount對結果集計數
g.V().has('code','AUS').out().count()
//獲取AUS節點的出邊的個數
g.V().has('code','AUS').out().groupCount().by("name")
//根據結果集的name屬性的值進行分組計數,最終結果類似於:[a:1,b:3,r:6]
  1. 使用dedup進行去重
g.V().has('code','AUS').out().out().dedup().count()
//步驟解讀:
1:獲取AUS節點的兩度出節點,用dedup對結果進行去重
2:使用count()對結果集進行計數
  1. 使用aggregate建立一個臨時集合
//獲取AUS節點的大於兩度出度的節點個數,注意應該不包含一度的節點
g.V().has('code','AUS').out().aggregate('nonstop').
     out().where(without('nonstop')).dedup().count()
//第一行獲取一度的節點並將其結果集儲存為一個臨時集合命名為"nonstop"
//第二行獲取二度節點,並且使用臨時集合去除掉一度節點,去重,計數
  1. 使用limit、tail、timeLimit限制結果數量
g.V().hasLabel('airport').values('code').limit(20)
//只顯示前20個
g.V().hasLabel('airport').values('code').tail(20)
//只顯示最後20個
g.V().has('airport','code','AUS').
      repeat(timeLimit(10).out()).until(has('code','LHR')).path().by('code')
//上述作用:獲取在10毫秒內查詢到的結果
  1. 使用outE\inE outV\inV指定方向的邊
  • 平常用法:
g.V().has('code','AUS').outE().inV().path()
g.V().has('code','AUS').inE().outV().path()
//獲取出邊或者入邊
g.V().has('code','AUS').outE('brought').inV().path()
g.V().has('code','AUS').inE('brought').outV().path()
//獲取指定的邊關係的出邊入邊
  • 獲取兩個節點之間的邊
g.V().has('code','MIA').
    outE().as('e').inV().has('code','DFW').
    select('e')
//第一步:選擇源節點
//第二步:outE找到所有出邊as('e')將結果儲存為標籤e
//第三步:inV().has('code','DFW')找到前面結果集的邊入節點為code屬性為DFW值得節點
//將邊顯示出來:結果類似於:
e[4127][16-route->8]
這樣就獲取到了兩個節點之間的邊
  1. 使用as,select和project來引用遍歷步驟
  • as可以將前一個步驟結果集臨時儲存下來,便於下面使用
g.V().has('code','DFW').as('from').out().
      has('region','US-CA').as('to').
      select('from','to')
//has('code','DFW').as('from')   :將has('code','DFW')的結果集標識為from標籤的臨時結果,下面使用的時候直接使用from即可
//返回的結果型別
[from:v[8],to:v[13]]
[from:v[8],to:v[23]]
[from:v[8],to:v[24]]

g.V().has('type','airport').limit(10).as('a','b','c').
      select('a','b','c').
        by('code').by('region').by(out().count())
//返回結果為:
[a:ATL,b:US-GA,c:232]
[a:ANC,b:US-AK,c:39]
[a:AUS,b:US-TX,c:59]
  • project()相當於select和by共同使用的效果
g.V().has('type','airport').limit(10).
      project('a','b','c').
        by('code').by('region').by(out().count())
//效果等同於:只不過上面的寫法更加簡潔
g.V().has('type','airport').limit(10).as('a','b','c').
      select('a','b','c').
        by('code').by('region').by(out().count())
//輸出結果:
[a:ATL,b:US-GA,c:232]
[a:ANC,b:US-AK,c:39]
[a:AUS,b:US-TX,c:59]
  1. 相同標籤的處理方式,在select中使用first,last,all引數
g.V(1).as('a').V(2).as('a').select(first,'a')
v[1]
//選擇第一個a標籤
g.V(1).as('a').V(2).as('a').select(last,'a')
v[2]
//選擇最後一個a標籤
g.V(1).as('a').V(2).as('a').select(all,'a')
[v[1],v[2]]
//選擇所有a標籤
g.V().has('code','AUS').as('a').
      out().as('a').limit(10).
      select(last,'a').by('code')

g.V().has('code','AUS').as('a').
      out().as('a').limit(10).
      select(first,'a').by('code')

g.V().has('code','AUS').as('a').
      out().as('a').limit(10).
      select(all,'a').unfold().values('code')
  1. 使用valueMap獲取節點或者邊的屬性

返回結構:kv對陣列,key:屬性key,v:屬性的值列表(list,這樣可以顯示該屬性對應的多個值)

結構類似於:

[country:[US], code:[AUS], longest:[12248], city:[Austin], elev:[542], icao:[KAUS], lon:[-97.6698989868164], type:[airport], region:[US-TX], runways:[2], lat:[30.1944999694824], desc:[Austin Bergstrom International Airport]]

g.V().has('name','gremlin').valueMap()
//獲得節點的所有屬性
//valueMap在預設情況下不顯示ID和label值,必須新增true引數
g.V().has('name','gremlin').valueMap(true)
//返回的集合中包含ID和label值
g.V().has('code','AUS').valueMap(true,'region')
//返回id+label+region三個屬性的kv
g.E(5161).valueMap(true)
//返回id為5161邊的屬性

為了完整起見,還可以使用select來優化valueMap的結果

g.V().has('code','AUS').valueMap().select('code','icao','desc')
//返回的結果為 code+icao+desc屬性的kv

如果想要結果集合更容易展現,可以使用unfold方法將其展開,但是結果的結構就變了,只是為了在 console上更加容易看

g.V().has('code','AUS').valueMap(true,'code','icao','desc','city').unfold()
//輸出結果形式:
code=[AUS]
city=[Austin]
icao=[KAUS]
id=3
label=airport
desc=[Austin Bergstrom International Airport]
  1. 使用toList,toSet,bulkSet和fill建立集合
  • toList()建立結果集合為list集合,可重複,不排序,要想排序可以使用order方法
listr = g.V().has('airport','region','US-TX').
              values('runways').toList().join(',')
//此處的join(',')是將結果組合起來,用逗號分割,這樣最終的結果就是一個字串
//輸出結果:
2,7,5,3,4,3,3,3,3,4,2,3,2,3,2,2,3,2,1,3,2,3,4,3,4,2

//在我們專案中使用一般不會加join,因為最終結果只會是一個字串
listr = g.V().has('airport','region','US-TX').
        values('runways').toList()
//使用集合的一些操作:
-> listr[1]
7

-> listr.size()
26

-> listr[1,3]
7
3
  • toSet()建立結果集合為Set集合,不可重複
setr = g.V().has('airport','region','US-TX').
             values('runways').toSet().join(',')
//輸出結果:
1,2,3,4,5,7
  • toBulkSet()將結果集相同資料放在連續的位置,其餘與tolist相同
setb= g.V().has('airport','region','US-TX')
    .values('runways').toBulkSet().join(',')
//輸出結果:
2,2,2,2,2,2,2,2,7,5,3,3,3,3,3,3,3,3,3,3,3,4,4,4,4,1

//不使用join
setb= g.V().has('airport','region','US-TX').values('runways').toBulkSet()
//一些操作
setb.uniqueSize()   
6

setb.size()
26

setb.asBulk()
2=8
7=1
5=1
3=11
4=4
1=1
  • fill方法將結果集填充入進一個自定義集合中
//案例一:
a = []
g.V().has('airport','region','US-TX').values('runways').fill(a)
//操作
a.size()
26

a[1,3]
73
//案例二:
s = [] as Setg.V().has('airport','region','US-TX').values('runways').fill(s)
//操作
println s
[2, 7, 5, 3, 4, 1]

未完待續。。。。

三:java中如何使用呢?

1:建立源節點

private GraphTraversal createGraphTraversalByIdLabel(String id,String label,Map<String,Object> propertys){
    GraphTraversal graphTraversal = null;
    if(StringUtils.isNotEmpty(id)){
        graphTraversal = getGraphTraversalSource().V(id);
    }else{
        graphTraversal = getGraphTraversalSource().V();
    }
    if(StringUtils.isNotEmpty(label)){
        graphTraversal = graphTraversal.hasLabel(label);
    }
    if(propertys!=null&&propertys.size()>0){
        for (Object o : propertys.entrySet()) {
            Map.Entry entry = (Map.Entry) o;
            String key = entry.getKey().toString().trim();
            String value = entry.getValue().toString().trim();
            if(StringUtils.isNotEmpty(key) &&
                    StringUtils.isNotEmpty(value))
                graphTraversal = graphTraversal.has(key, value);
        }
    }
    return graphTraversal;
}

2:組裝語句

@Override
public GraphTraversal timeLineGraphPath(String id, String label,
                                        Map<String, Object> propertys,
                                        String[] edgeLabels, String type,
                                        String beginTime, String endTime) {
    GraphTraversal graphTraversal = createGraphTraversalByIdLabel(id,label,propertys);
    if(edgeLabels==null||edgeLabels.length==0)
        return graphTraversal.valueMap(true);
    else
        return                 graphTraversal.outE(edgeLabels).and(__.has(type,P.gte(beginTime)),__.has(type,P.lte(endTime))).outV().path().order().by(type);
}

其中的”_.“是填充字元,只是為了可以使用方法,比如and( _.has(type,P.gte(beginTime)。。中如果沒有__.的話,不能這寫吧,也不支援呀and(.has()。。

相關文章