AQL:展平全文查询的文档

时间:2016-10-27 23:29:57

标签: full-text-search graph-databases arangodb aql

我对AQL中的全文功能有一些复杂的用例。我有一个大的分层文档,作为图遍历的结果返回。这构造了类似社交网络提要的东西。它类似于各种类别的帖子,其中评论作为包含其自身结构的子文档。返回的数据如下所示:

[
  {
     "data": {
       "_key": "",
       "_id": "someCollection/someKey",
       "_rev": "",
       "userID": "12345",
       "otherAttributeOfFeedEvent": "",
       .
       .
       .
     },
     "date": "2016-10-25",
     "category": "",
     "children": [
       {
         "category": "",
         "child": "myCollection/childDocumentKey",
         "date": "2016-10-26"
       },
       { sameStructureAsAbove },
       { anotherChildLikeAbove },
    ]
  },
  { etc }
]

当然,对于进入Feed的每种事件类型,将通过全文搜索的属性是不同的且很多,并且对于给定的用户输入,我需要同时搜索它们。我最初的想法是,由于每个文档的_key,无论Feed中的父项或子项是否保证在此结构中列出,我都可以创建某种包含所有文档的集合。通过他们的钥匙识别。

一个挑战是这个全文搜索需要保留层次结构。回到社交网络评论类比,如果用户搜索评论中存在的术语(即子事件),则查询应返回父事件,并在每个与该术语匹配的子事件上带有标志,以便接口可以显示搜索结果的上下文(否则,需要使用辅助查询来获取上下文)。

如上定义的这种层次结构是通过图形遍历在图形上生成的,其结构看起来像这样:

profile ---> event ---> childEvent
     |                    ^
     |                    |
      \------------------/

生成数据的查询如下所示:

let events = (
    for v, e, p in 1..3 outbound @profileKey graph 'myGraph' options { "uniqueEdges": "global"}
        filter e.type == "hasEvent"
        filter p.edges[0].category in ["cat1", "cat2", "cat3"]
        filter e.category in ["cat1", "cat2", "cat3"]

        let children = (
           for v1, e1, p1 in outbound v._id graph 'myGraph'
              filter e1.type =="hasEvent" or e1.isChildEvent == "True"
              sort (e1.date) desc
              return {category: e1.category, child: v1._id, date: e1.date }
        )

        let date = e.date
        let category = e.category
        let data = v
        return distinct { data: data, date: date, category: category, children: children }
)

for event in events
   sort(event.date) desc
   return event

底线

总而言之:我需要编写一个AQL,它将对所描述的Feed中显示的每个文档中的几个属性执行全文搜索,并返回结构化结果,或者可以在结构化结果中使用的内容,显示与上述结构相同的Feed,其中仅包含与全文搜索结果匹配或具有与全文搜索结果匹配的子项的事件。

在我的测试中,我尝试创建这样的查询:

    let events = (
            FOR v, e, p in 1..3 OUTBOUND 'myCollection/myDocument' GRAPH 'myGraph' OPTIONS { "uniqueEdges": "global" }
                FILTER e.type == "hasEvent"
                FILTER (p.edges[0].category in ["cat1", "cat2", "cat3"] )  
                FILTER (e.category in ["cat1","cat2","cat3] ) 



            LET children = ( 
                FOR v1, e1, p1 in OUTBOUND v._id GRAPH 'myGraph'
                    FILTER e1.type == "hasEvent" OR e1.isChildEvent == "True"
                    SORT(e1.date) DESC
                    RETURN {category: e1.category, _id: v1._id, date: e1.date}
            )

            let date = e.date
            let category = e.category
            let data = v
            RETURN DISTINCT {data: data, date: date, category: category, children: children}    
    )

    let eventIds = (
        for event in events
            return event.data._id
        )
    let childEventIds = (
        for event in events
            for child in event.children
                return child._id
        )
    let allIds = append(eventIds, childEventIds)

    let allDocs = (for doc in allIds
        return document(doc))

    let firstAttributeMatches = (for doc in fulltext(allDocs, "firstAttribute", @queryTerm)
                                return doc._id)
    let secondAttributeMatches = (for doc in fulltext(allDocs, "secondAttribute", @queryTerm)
                                return doc._id)
    let nthAttributeMatches = (for doc in fulltext(allDocs, "nthAttribute", @queryTerm)
                                return doc._id)
    let results = union_distinct(firstAttributeMatches,secondAttributeMatches,nthAttributeMatches)
    return results

但这有错误:Query: invalid argument type in call to function 'FULLTEXT()' (while executing)

据推测,即使我使用的所有属性都有全文索引,因为我已将所有这些文档收集到一个新的集合中,而这个集合也没有全文索引,我不能简单地调用fulltext()他们。这是否意味着我最好的选择是获取我的第一个查询返回的所有文档集合的列表,对这些集合执行全局全文搜索,然后将结果内部连接到我的第一个查询的结果?这听起来非常复杂且耗时。是否有一些更简单的方法来做我以后的事情?

我的下一次尝试看起来更像是这样:

let events = (
        FOR v, e, p in 1..3 OUTBOUND 'myCollection/myDocument' GRAPH 'myGraph' OPTIONS { "uniqueEdges": "global" }
            FILTER e.type == "hasEvent"
            FILTER (p.edges[0].category in ["cat1", "cat2", "cat3"] )  
                 FILTER (e.category in ["cat1", "cat2", "cat3"] ) 



            LET children = ( 
                FOR v1, e1, p1 in OUTBOUND v._id GRAPH 'myGraph'
                    FILTER e1.type == "hasEvent" OR e1.isChildEvent == "True"
                    SORT(e1.date) DESC
                    RETURN {category: e1.category, _id: v1._id, date: e1.date}
            )

            let date = e.date
            let category = e.category
            let data = v
            RETURN DISTINCT {data: data, date: date, category: category, children: children}    
    )

    let eventIds = (
        for event in events
            return event.data._id
        )
    let childEventIds = (
        for event in events
            for child in event.children
                return child._id
        )
    let allIds = append(eventIds, childEventIds)

    let losCollections = (for id in allIds
        return distinct parse_identifier(id).collection)

    let searchAttrs = ["attr1","attr2","attr3","attrN"]

    for col in losCollections
        for attr in searchAttrs
            return (for doc in fulltext(col, attr, @queryTerm) return doc._id)

但是,只要它尝试了一个不是集合中全文索引的属性,这似乎就失败了。也许在AQL中有一种方法可以检查属性是否具有全文索引,那么在这种情况下只执行查询?

1 个答案:

答案 0 :(得分:1)

首先是一些一般性评论:

  1. 目前,全文索引只能索引一个集合中的文档,并且只能查看单个属性的字符串值。 AQL中相应的FULLTEXT搜索只能使用单个索引,因此只能查看一个集合和一个属性。如果这还不够,则必须运行多个FULLTEXT查询并统一结果。

  2. 如果不必构建完整路径,图表查询会更快,因此不是

    for v, e, p in 1..3 outbound @profileKey graph 'myGraph' options {"uniqueEdges": "global"}
      filter e.type == "hasEvent"
      filter p.edges[0].category in ["cat1", "cat2", "cat3"]
      filter e.category in ["cat1", "cat2", "cat3"]
    

    应该写一个

    for v, e in 1..3 outbound @profileKey graph 'myGraph' options {"uniqueEdges": "global"}
      filter e.type == "hasEvent"
      filter e.category in ["cat1", "cat2", "cat3"]
    

    相当但更快(最后一个过滤器意味着中间过滤器)。

  3. 如果您对表单有疑问

    let events = (... return xyz)
    for event in events
      sort event.date desc
      return event
    

    通常通过编写

    来避免子查询更好
    ...
    let event=xyz
    sort event.date desc
      return event
    

    因为在开始使用bottom for语句之前,查询引擎不会被强制计算整个子查询的结果。

  4. 现在我要回答您手头的具体问题:您的方法都失败了,因为AQL中的FULLTEXT函数只能用于具有现有全文索引的现有集合。特别是,它不能用于对先前在AQL查询中生成的中间结果执行全文搜索。也就是说,因为对于有效的全文搜索,需要全文索引结构,而中间结果则不存在。

    因此,我的预感是,如果你想同时对个人资料,事件和子事件进行全文搜索,你必须首先使用索引执行全文搜索,然后从每个结果放入根据需要使用图形查询将层次结构组合在一起。

    我看到了两种基本方法。第一种是对每个现有集合执行三次独立的全文搜索,然后为每个结果运行单独的图形查询以将层次结构组合在一起。根据您的全文搜索是否找到个人资料,事件或子事件,这必须有所不同。使用子查询,这三种方法都可以在一个AQL查询中完成。

    第二个是有一个额外的全文搜索集合,其中将有一个文档用于所有其他三个集合中的每个文档,其中包含要进行全文搜索的属性。是的,这是一个数据非规范化,在保存和更新数据时需要额外的内存空间和额外的工作,但它可能会加速全文搜索。

    我想提到的另一个想法是,你的查询的复杂性达到了一个应该考虑用Javascript编写它的程度(在服务器上运行,可能在Foxx应用程序中)。在那里以程序方式实现查询逻辑是相对简单的。我的预感是,甚至可以通过这种方式提高性能,即使JS代码必须发出多个AQL查询。至少我希望代码更容易理解。