JanusGraph入坑笔记(五)- GraphTraversal (Vertex centric)

本文是JanusGraph入坑笔记的第五篇,重点探讨GraphTraversal的Terminal Step和Vertex Step(Vertex centric)。首先解释了Terminal Step如何终止traversal并启动图查询,接着详细阐述Vertex Step,特别是从API查询开始,解析traverser的生成、遍历过程以及结果的获取,深入到存储层的操作。

目录

一、Terminal Step

二、Vertex Step (Vertex centric)


首先我们假定读者对Gremlin语句有一定的了解,那么Traversal就是对gremlin语句用java语法中的方法进行拼接。看一下JanusGraph官方文档中对traversal的使用:

JanusGraph graph = JanusGraphFactory.open('conf/janusgraph-cql.properties')
GraphTraversalSource g = graph.traversal()
saturn = g.V().has('name', 'saturn').next()

一、Terminal Step

traversal方法首先返回一个GraphTraversalSource类型,这个类型可以执行图相关的搜索,也就是根据条件从全图进行搜索,然后返回traversal,在后续若干个traversal后,最终结果在next方法后返回。这个方法被称为ternimal step,与他作用相同的一共有9个,他们的作用是终止traversal的添加过程,转而对前面设置的条件开始图查询。

g.V().out('created').hasNext()
g.V().out('created').next() 
g.V().out('created').next(2) 
g.V().out('nothing').tryNext() 
g.V().out('created').toList() 
g.V().out('created').toSet() 
g.V().out('created').toBulkSet() 
results = ['blah',3]
g.V().out('created').fill(results) 
g.addV('person').iterate()

本节中,我们重点关注GraphTraversalSource类在全局搜索时的工作方式。

二、Vertex Step (Vertex centric)

(1) 从一个简单的api查询开始

// Traversal.toList
g.V().toList()

// Traversal.fill()
final Traverser<E> traverser = endStep.next();
TraversalHelper.addToCollection(collection, traverser.get(),          traverser.bulk());

// AbstractStep.next();
final Traverser.Admin<E> traverser = this.processNextStart();
if (null != traverser.get() && 0 != traverser.bulk())
    return this.prepareTraversalForNextStep(traverser);

  • 前面我们介绍了final step,toLIst属于一个final setp,所以与前面的查询链返回traverser不同,这次调用的是fill方法。也就是将结果填入到给出的collection继承类中返回。
  • 每一次endStep.next方法返回一个traverser,添加到结果集,所以接下来我们关注这个traverser是怎么生成的

(2) 

// AbstractStep.processNextStart
this.head = this.starts.next();     
this.iterator = this.flatMap(this.head);
return this.head.split(this.iterator.next(), this);
  • 将 (ExpandableStepIterator<S> implements Iterator<Traverser.Admin<S>>)的next()方法返回值给(Traverser) head
  • iterator 保存了flatMap返回的result
  • 然后我们返回值是一个由原traverser clone出的新traverser,但是将result中的next添加了进去,clone iterator的同时,指针也向后移动了
  • 此时可以看到两个关键节点,head和 iterator,分别记录了traverser 和 result iterator

(2.1)我们关注在 this.starts.next() 发生了什么,怎么返回的结果

// ExpandableStepIterator.next
if (this.hostStep.getPreviousStep().hasNext())
    return this.hostStep.getPreviousStep().next();
return this.traverserSet.remove();

从字面意义上看,先查询了前一个step时候有结果,如果有的话,返回这个结果。但是hasNext方法却不只是简单的判断。

// AbstractStep.hasNext

public boolean hasNext() {                                                          
    if (null != this.nextEnd)                                                       
        return true;                                                                
    else {                                                                          
        try {                                                                       
            while (true) {                                                          
                if (Thread.interrupted()) throw new TraversalInterruptedException();
                this.nextEnd = this.processNextStart();                             
                if (null != this.nextEnd.get() && 0 != this.nextEnd.bulk())         
                    return true;                                                    
                else                                                                
                    this.nextEnd = null;                                            
            }                                                                       
        } catch (final NoSuchElementException e) {                                  
            return false;                                                           
        }                                                                           
    }                                                                               
}                                                                                   
  • 总结一下就是将上一个step的processNextStart方法的返回值给他的nextEnd属性
public Traverser.Admin<E> next() {                                              
    if (null != this.nextEnd) {                                                 
        try {                                                                   
            return this.prepareTraversalForNextStep(this.nextEnd);              
        } finally {                                                             
            this.nextEnd = null;                                                
        } 
    }                                                                      
}                                                                  
  • 然后返回这个traverser

(3)那么看到result是如何被一次次拿出来的,加下来关注result是如何生成的

// AbstractStep.flatMap(this.head)

Iterator<E> flatMap(final Traverser.Admin<Vertex> traverser)
    //



JanusGraphVertexStep.flatMap ==> query.vertices()

(4) 中间一些复杂的封装

VertexCentricQueryBuilder.vertices ===> execute()
VertexCentricQueryBuilder.execute ===>  return resultConstructor.getResult(vertex,bq);
VertexConstructor.getResult ===> return executeVertices(v,bq);
BasicVertexCentricQueryBuilder.executeVertices ===> return executeIndividualVertices(vertex,baseQuery);
SimpleVertexQueryProcessor.vertexIds ===> Iterables.transform(
CacheVertex.loadRelations ===> lookup.get(query)
SimpleVertexQueryProcessor.getBasicIterator ===> tx.getGraph().edgeQuery()

(4) JanusGraph

StandardJanusGraph.edgeQuery ===> tx.edgeStoreQuery()

(5) 存储层的操作

// Backend
BackendTransaction.edgeStoreQuery ===> call
KCVSProxy.getSlice ===> store.getSlice(query, unwrapTx(txh))
HBaseKeyColumnValueStore.getSlice ===> getHelper()
HBaseKeyColumnValueStore.getHelper ===> table.get(requests);

<think>好的,我需要帮助用户在JanusGraph中创建和使用索引。根据提供的引用资料,先理清步骤。首先,用户提到了组合索引和混合索引两种类型,引用[4]详细说明了两者的区别,所以需要分别讲解这两种索引的创建方法。 组合索引需要预定义属性组合,支持唯一约束,但排序在内存中。混合索引更灵活,支持更多查询类型,但需要配置后端如Elasticsearch。接下来,引用[2]和引用[3]提到了创建组合索引的具体步骤,比如使用ManagementSystem,注册索引并启用。而混合索引的创建可能需要不同的步骤,比如指定索引后端。 然后要考虑用户可能的疑问,比如如何选择索引类型,或者创建后如何启用和重新索引数据。引用[3]提到注册索引后可能需要重新索引现有数据,这点很重要,应该包含在步骤中。另外,引用[1]提到创建索引是为了提高查询速度,所以在使用场景中需要强调这一点。 需要确保步骤清晰,分点说明组合索引和混合索引的创建流程,并给出示例代码,如Gremlin命令。同时,要提到注意事项,比如索引状态检查和性能影响。最后,根据用户提供的示例中的相关问题部分,生成相关的问题,如比较两种索引的优缺点,如何优化索引性能等。 现在检查是否有遗漏的信息,比如索引的唯一性约束和启用索引的方法。确保引用资料中的关键点都被涵盖,比如组合索引的ENABLED状态和重新索引的操作。可能还需要提醒用户注意索引后端配置对混合索引的影响,以及处理现有数据时需要重新索引的情况。</think>在JanusGraph中,索引的创建和使用是优化查询性能的关键操作。以下是具体方法分步说明: ### 一、索引类型选择 JanusGraph支持两种索引(引用[4]): 1. **组合索引(Composite Index)** - 通过预定义属性组合进行等值检索 - 支持唯一性约束(`unique`) - 适用于固定属性组合的精确查询 2. **混合索引(Mixed Index)** - 支持多条件谓词(范围查询/全文检索等) - 需配置外部索引后端(如Elasticsearch) - 排序效率更高 ### 二、组合索引创建步骤 1. **定义属性键** ```groovy mgmt = graph.openManagement() person_id = mgmt.makePropertyKey('person_id').dataType(Integer.class).make() mgmt.commit() ``` 2. **创建组合索引** ```groovy mgmt = graph.openManagement() person_id = mgmt.getPropertyKey('person_id') mgmt.buildIndex('byPerson_idComposite', Vertex.class).addKey(person_id).unique().buildCompositeIndex() mgmt.commit() // 提交事务后索引状态为INSTALLED ``` 3. **启用索引(引用[2][3])** ```groovy mgmt.updateIndex(mgmt.getGraphIndex("byPerson_idComposite"), SchemaAction.REINDEX).get() mgmt.commit() // 状态变为ENABLED ``` ### 三、混合索引创建步骤 1. **配置索引后端** - 在`janusgraph.properties`中添加: ```properties index.search.backend=elasticsearch index.search.hostname=127.0.0.1 ``` 2. **创建索引映射** ```groovy mgmt = graph.openManagement() age = mgmt.makePropertyKey('age').dataType(Integer.class).make() mgmt.buildIndex('ageMixed', Vertex.class).addKey(age, Mapping.DEFAULT.asParameter()).buildMixedIndex("search") mgmt.commit() ``` ### 四、索引使用场景 - **组合索引**:`g.V().has('person_id', 123)`(精确匹配) - **混合索引**:`g.V().has('age', gt(30))`(范围查询) ### 、注意事项 1. 新数据自动索引,**存量数据需执行REINDEX操作**(引用[3]) 2. 检查索引状态:`index.getIndexStatus(propertyKey)`(引用[2]) 3. 混合索引不支持唯一性约束 4. 组合索引查询时**必须包含所有定义属性** $$ \text{查询性能公式:} T_{query} = \frac{N}{\log_2 M} \quad (M为索引覆盖数据量) $$
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值