【问题标题】:Titan cassandra does not use defined indexes for custom gremlin stepsTitan cassandra 不为自定义 gremlin 步骤使用定义的索引
【发布时间】:2023-09-30 06:35:02
【问题描述】:

我们在下面的代码块中使用 Titan cassandra 定义了 5 个索引

 def mgmt = g.managementSystem;
 try {
     if (!mgmt.containsGraphIndex("byId")) {
         def key = mgmt.makePropertyKey('__id').dataType(String.class).make()
         mgmt.buildIndex("byId",Vertex.class).addKey(key).buildCompositeIndex()
     }
     if (!mgmt.containsGraphIndex("byType")) {
          def key = mgmt.makePropertyKey('__type').dataType(String.class).make()
         mgmt.buildIndex("byType",Vertex.class).addKey(key).buildCompositeIndex()
     }
     if (!mgmt.containsGraphIndex("lastName")) {
         def key = mgmt.makePropertyKey('lastName').dataType(String.class).make()
         mgmt.buildIndex('lastName',Vertex.class).addKey(key).buildMixedIndex(INDEX_NAME)
     }
     if (!mgmt.containsGraphIndex("firstName")) {
         def key = mgmt.makePropertyKey('firstName').dataType(String.class).make()
         mgmt.buildIndex('firstName',Vertex.class).addKey(key).buildMixedIndex(INDEX_NAME)
     }
     if (!mgmt.containsGraphIndex("vin")) {
         def key = mgmt.makePropertyKey('vin').dataType(String.class).make()
         mgmt.buildIndex('vin',Vertex.class).addKey(key).buildMixedIndex(INDEX_NAME)
     }
     mgmt.commit()
 } catch (Exception e) {
     System.err.println("An error occurred initializing indices")
     e.printStackTrace()
 }

然后我们执行下面的查询

g.V.has('__id','49fb8bae5f994cf5825b849a5dd9b49a')

这会产生一个警告,通知我们:

“查询需要遍历所有顶点 [{}]。为了获得更好的性能,请使用索引”

我很困惑,因为根据文档,这些索引设置正确,但由于某种原因,泰坦没有使用它们。

索引是在图表中的任何数据之前创建的,因此不需要重新索引。非常感谢任何帮助。

更新- 我设法将其分解为一个非常简单的测试。在我们的代码中,我们开发了一个自定义 gremlin 步骤以用于所述查询

Gremlin.defineStep('hasId', [Vertex,Pipe], { String id ->
    _().has('__id', id)
})

然后从我们的代码中调用

g.V.hasId(id)

看来,当我们使用自定义 gremlin 步骤时,查询不使用索引,但在使用 vanilla gremlin 调用时使用索引。

在这篇文章中似乎注意到了类似的怪事 https://groups.google.com/forum/#!topic/aureliusgraphs/6DqMG13_4EQ

【问题讨论】:

    标签: indexing cassandra gremlin titan


    【解决方案1】:

    我更愿意检查属性键是否存在,这意味着您将检查调整为:

    if (!mgmt.containsRelationType("__id")) {
    

    我在 Titan Gremlin 控制台中试用了您的代码,但没有发现问题:

    gremlin> g  = TitanFactory.open("conf/titan-cassandra.properties")
    ==>titangraph[cassandrathrift:[127.0.0.1]]
    gremlin> mgmt = g.managementSystem
    ==>com.thinkaurelius.titan.graphdb.database.management.ManagementSystem@2227a6c1
    gremlin> key = mgmt.makePropertyKey('__id').dataType(String.class).make()
    ==>__id
    gremlin> mgmt.buildIndex("byId",Vertex.class).addKey(key).buildCompositeIndex()
    ==>com.thinkaurelius.titan.graphdb.database.management.TitanGraphIndexWrapper@6d4c273c
    gremlin> mgmt.commit()
    ==>null
    gremlin> mgmt = g.managementSystem
    ==>com.thinkaurelius.titan.graphdb.database.management.ManagementSystem@79d743e6
    gremlin> mgmt.containsGraphIndex("byId")
    ==>true
    gremlin> mgmt.rollback()
    ==>null
    gremlin> v = g.addVertex()
    ==>v[256]
    gremlin> v.setProperty("__id","123")
    ==>null
    gremlin> g.commit()
    ==>null
    gremlin> g.V
    12:56:45 WARN  com.thinkaurelius.titan.graphdb.transaction.StandardTitanTx  - Query requires iterating over all vertices [()]. For better performance, use indexes
    ==>v[256]
    gremlin> g.V("__id","123")
    ==>v[256]
    gremlin> g.V.has("__id","123")
    ==>v[256]
    

    请注意,我没有收到任何关于“...使用索引”的丑陋消息。或许您可以在此处尝试我的示例,看看它的行为是否符合预期,然后再返回您的代码。

    更新:回答上面关于自定义步骤的更新问题。正如您发现的帖子所指出的,Titan 的查询优化器似乎无法解决这个问题。我认为在这个例子中很容易看出原因:

    gremlin> g = TinkerGraphFactory.createTinkerGraph()
    ==>tinkergraph[vertices:6 edges:6]
    gremlin> Gremlin.defineStep('hasName', [Vertex,Pipe], { n -> _().has('name',n) })
    ==>null
    gremlin> g.V.hasName('marko')
    ==>v[1]
    gremlin> g.V.hasName('marko').toString()
    ==>[GremlinStartPipe, GraphQueryPipe(vertex), [GremlinStartPipe, PropertyFilterPipe(name,EQUAL,marko)]]
    

    “编译”的 Gremlin 看起来像上面的最后一行。请注意,自定义步骤编译为带有新 GremlinStartPipe 的“内部”管道。将其与没有自定义步骤的情况进行比较:

    gremlin> g.V.has('name','marko').toString()
    ==>[GremlinStartPipe, GraphQueryPipe(has,vertex), IdentityPipe]
    

    Titan 可以使用嵌入的has 优化“GraphQueryPipe”,但自定义步骤的签名似乎并非如此。我认为解决方法(至少对于这种特定情况是编写一个返回管道的函数。

    gremlin> def hasName(g,n){g.V.has('name',n)}  
    ==>true
    gremlin> hasName(g,'marko')
    ==>v[1]
    gremlin> hasName(g,'marko').toString()
    ==>[GremlinStartPipe, GraphQueryPipe(has,vertex), IdentityPipe]
    

    传递“g”有点臭。也许编写你的 DSL 以便将 'g' 包装在一个类中,然后让你这样做:

    with(g).hasName('marko')
    

    最后一个想法是使用 Groovy 元编程工具:

    gremlin> Graph.metaClass.hasName = { n -> delegate.V.has('name',n) }
    ==>groovysh_evaluate$_run_closure1@600b9d27
    gremlin> g.hasName("marko").toString()                              
    ==>[GremlinStartPipe, GraphQueryPipe(has,vertex), IdentityPipe]
    gremlin> g.hasName("marko")                                         
    ==>v[1]
    

    【讨论】:

    • 对此有何解释?文档说containsGraphIndex 检查“图形是否具有使用给定名称定义的图形索引”,containsRelationType 检查是否存在具有指定名称的类型。这不是很有启发性。
    • 更改该条件会导致每次图初始化时都创建索引。这第二次产生异常。 “为键 [system%&%SchemaName] 和值 [rt%__id] 添加此属性违反了唯一性约束 [com.thinkaurelius.titan.graphdb.types.system.BaseKey$1@d35feb2]”
    • 我们也可以通过这段代码进行调试,看看索引正在构建中。我们只是不知道为什么在执行我们的查询时没有使用它。
    • 嗯 - 对不起 - 我完全误读了你的问题。我以为您正在检查属性键是否存在,但您正在检查索引本身是否存在,所以我想这是对的。在上面编辑...也许这次我做对了。
    • 您需要将其定义为唯一吗?它几乎是直接从here复制而来的。