翻译自:这里
在本教程中,我们将在Dgraph中构建tweets的Graph图,并使用它,我们将学习更多关于Dgraph中的字符串索引。
我们将具体学习:
- 在Dgraph中构建Tweet图
- 在Dgraph中使用String索引
- 使用哈希索引hash查询twitter用户。
- 使用精确索引exact比较字符串。
- 使用术语索引term基于关键字搜索tweet。
让我们开始分析真实推文的结构,并找出如何在Dgraph中建构建Tweet的Graph图。
在Graph中构建Tweet图
这里有一个例子。
让我们仔细分析一下上面的推文。以下是这条推文的组成部分:
-
作者 这条推文的作者是用户@hackintoshrao
-
主体 这个组件就是tweet的内容。
-
标签 这里是推文中的标签:#GraphQL和#GraphDB。
-
提及(@) 一条推文可以提到其他推特用户。这里是上面提到的:@dgraphlabs和@francesc。
在我们使用这些组件在Dgraph中构建tweet之前,让我们回顾一下图形模型的设计原则: 节点和边是图形模型的构建块。它可能是一个销售,一条tweet,用户信息,任何概念或实体表示为一个节点。如果有两个节点是相关的,则通过在它们之间创建一条边来表示。
考虑到以上设计原则,让我们看看tweet的组成部分,看看如何将它们融入Dgraph中。
- 作者 推特的作者是推特用户。我们应该用一个节点来表示它。
- 主体 我们应该将每个tweet表示为一个节点。
- 标签 将一个标签表示为它自己的节点是有好处的。在查询时,它为我们提供了更好的灵活性。虽然你可以从推文正文中搜索标签,但这样做并不高效。创建唯一的节点来表示一个标签,允许您编写如下的性能查询:Hey Dgraph,给我所有带有#graphql标签的tweet
- 提及(@) 一个提及代表一个twitter用户,我们已经将一个用户建模为一个节点。因此,我们将提及表示为一条tweet和所提到的用户之间的一个谓词边。
构建关系
我们有三种类型的节点:User、Tweet和Hashtag。
让我们看看这些节点如何相互关联,并将它们的关系建模为它们之间的边。
User和Tweet节点
Tweet和User节点之间存在双向关系。
每一条推文都是由一个用户编写的,一个用户可以编写许多推文。让我们将表示这种关系的边命名为authored。边authored从User节点指向Tweet节点。
一条推文可以提到很多用户,用户也可以在很多推文中被提到。让我们命名代表这个关系的边为mentioned。从一个Tweet节点到一个User节点的边mentioned。这些用户就是在推特中提到的那些人。
推特和标签节点
一条推文可以有一个或多个标签。让我们将表示这种关系的边命名为tagged_with。从Tweet节点到Hashtag节点的tagged_with边。这些标签节点对应于推文中的标签。
Author节点和hashtag节点
作者和标签节点之间没有直接关系。因此,我们不需要它们之间的直接边。
我们的推文图形模型准备好了!这是它。
这是我们的推文样本图。
让我们在列表中添加几个tweet。
我们将使用这两个tweet和一开始的示例tweet,作为我们的数据集。打开Ratel,转到突变选项卡,粘贴突变,然后单击Run。
{
"set": [
{
"user_handle": "hackintoshrao",
"user_name": "Karthic Rao",
"uid": "_:hackintoshrao",
"authored": [
{
"tweet": "Test tweet for the fifth episode of getting started series with @dgraphlabs. Wait for the video of the fourth one by @francesc the coming Wednesday!\n#GraphDB #GraphQL",
"tagged_with": [
{
"uid": "_:graphql",
"hashtag": "GraphQL"
},
{
"uid": "_:graphdb",
"hashtag": "GraphDB"
}
],
"mentioned": [
{
"uid": "_:francesc"
},
{
"uid": "_:dgraphlabs"
}
]
}
]
},
{
"user_handle": "francesc",
"user_name": "Francesc Campoy",
"uid": "_:francesc",
"authored": [
{
"tweet": "So many good talks at #graphqlconf, next year I'll make sure to be *at least* in the audience!\nAlso huge thanks to the live tweeting by @dgraphlabs for alleviating the FOMO\n#GraphDB ♥️ #GraphQL",
"tagged_with": [
{
"uid": "_:graphql"
},
{
"uid": "_:graphdb"
},
{
"hashtag": "graphqlconf"
}
],
"mentioned": [
{
"uid": "_:dgraphlabs"
}
]
}
]
},
{
"user_handle": "dgraphlabs",
"user_name": "Dgraph Labs",
"uid": "_:dgraphlabs",
"authored": [
{
"tweet": "Let's Go and catch @francesc at @Gopherpalooza today, as he scans into Go source code by building its Graph in Dgraph!\nBe there, as he Goes through analyzing Go source code, using a Go program, that stores data in the GraphDB built in Go!\n#golang #GraphDB #Databases #Dgraph ",
"tagged_with": [
{
"hashtag": "golang"
},
{
"uid": "_:graphdb"
},
{
"hashtag": "Databases"
},
{
"hashtag": "Dgraph"
}
],
"mentioned": [
{
"uid": "_:francesc"
},
{
"uid": "_:dgraphlabs"
}
]
},
{
"uid": "_:gopherpalooza",
"user_handle": "gopherpalooza",
"user_name": "Gopherpalooza"
}
]
}
]
}
这是我们画的图。(注意:官方教程说明这里有两个错误, 一个是第三条推特中提及到的人是 @francesc 和 @Gopherpalooza, 但是官方的内容里, mentioned指向的却是francesc节点和dgraphlabs节点, 突变语句中的_:dgraphlabs应该改为_:gopherpalooza节点; 还有个错误, 就是最后User节点gopherpalooza的突变语句在json中的位置不对, 应该与其他User节点并列, 而不是在dgraphlabs节点的authored关系中)
我们的图:
- 蓝色的twitter用户节点。
- 绿色的节点是tweets。
- 蓝色的是标签。
注意:官方教程说明这里有两个错误, 一个是第三条推特中提及到的人是 @francesc 和 @Gopherpalooza, 但是官方的内容里, mentioned指向的却是francesc节点和dgraphlabs节点, 突变语句中的_:dgraphlabs应该改为_:gopherpalooza节点; 还有个错误, 就是最后User节点gopherpalooza的突变语句在json中的位置不对, 应该与其他User节点并列, 而不是在dgraphlabs节点的authored关系中. 修改之后个正确突变语句和Graph图如下:
{
"set": [
{
"user_handle": "hackintoshrao",
"user_name": "Karthic Rao",
"uid": "_:hackintoshrao",
"authored": [
{
"tweet": "Test tweet for the fifth episode of getting started series with @dgraphlabs. Wait for the video of the fourth one by @francesc the coming Wednesday!\n#GraphDB #GraphQL",
"tagged_with": [
{
"uid": "_:graphql",
"hashtag": "GraphQL"
},
{
"uid": "_:graphdb",
"hashtag": "GraphDB"
}
],
"mentioned": [
{
"uid": "_:francesc"
},
{
"uid": "_:dgraphlabs"
}
]
}
]
},
{
"user_handle": "francesc",
"user_name": "Francesc Campoy",
"uid": "_:francesc",
"authored": [
{
"tweet": "So many good talks at #graphqlconf, next year I'll make sure to be *at least* in the audience!\nAlso huge thanks to the live tweeting by @dgraphlabs for alleviating the FOMO\n#GraphDB ♥️ #GraphQL",
"tagged_with": [
{
"uid": "_:graphql"
},
{
"uid": "_:graphdb"
},
{
"hashtag": "graphqlconf"
}
],
"mentioned": [
{
"uid": "_:dgraphlabs"
}
]
}
]
},
{
"user_handle": "dgraphlabs",
"user_name": "Dgraph Labs",
"uid": "_:dgraphlabs",
"authored": [
{
"tweet": "Let's Go and catch @francesc at @Gopherpalooza today, as he scans into Go source code by building its Graph in Dgraph!\nBe there, as he Goes through analyzing Go source code, using a Go program, that stores data in the GraphDB built in Go!\n#golang #GraphDB #Databases #Dgraph ",
"tagged_with": [
{
"hashtag": "golang"
},
{
"uid": "_:graphdb"
},
{
"hashtag": "Databases"
},
{
"hashtag": "Dgraph"
}
],
"mentioned": [
{
"uid": "_:francesc"
},
{
"uid": "_:gopherpalooza"
}
]
}
]
},
{
"uid": "_:gopherpalooza",
"user_handle": "gopherpalooza",
"user_name": "Gopherpalooza"
}
]
}
正确突变后的Graph图如下:
让我们通过查询数据库中的twitter用户来开始我们的tweet探索。
{
tweet_graph(func: has(user_handle)) {
user_handle
}
}
我们有4个twitter用户:@hackintoshrao、@francesc、@dgraphlabs 和 @gopherpalooza。
现在,让我们找到他们的推文和标签
{
tweet_graph(func: has(user_handle)) {
user_name
authored {
tweet
tagged_with {
hashtag
}
}
}
}
在开始查询图表之前,让我们通过一个简单的类比来了解一下数据库索引。
什么是索引?
索引是一种通过最小化处理查询时所需的磁盘访问数量来优化数据库性能的方法。
考虑一本600页的“书”,分成30个部分。假设每个部分有不同的页数。
现在,如果没有索引页,要找到以字母“F”开头的特定部分,你别无选择,只能浏览整本书。即:600页。
但是在开头有一个索引页可以更容易地访问预期的信息。您只需要查看索引页,在找到匹配的索引之后,您可以跳过其他部分有效地跳转到该部分。
但是请记住,索引页也会占用磁盘空间!只在必要的时候使用它们。
接下来,让我们学习一些twitter图表上有趣的查询。
字符串索引和查询
Hash索引
让我们编写一个查询,它说:Hey Dgraph, 找到推特作者是hackintoshrao的所有推特。
在此之前,首先需要向user_handle谓词添加一个哈希索引。我们知道有5种类型的字符串索引:散列索引hash、精确索引exact、术语索引term、全文索引full-text和三元索引trigram。
要使用的字符串索引的类型取决于希望在字符串谓词上运行的查询类型。
在本例中,我们希望根据谓词的确切字符串值搜索节点。对于像这样的用例,建议使用哈希索引。
让我们首先将哈希索引添加到user_handle谓词。
现在,让我们使用eq比较器函数来查找hackintoshrao的所有tweet。转到查询选项卡,输入查询,然后单击Run。
{
tweet_graph(func: eq(user_handle, "hackintoshrao")) {
user_name
authored {
tweet
}
}
}
让我们扩展最后一个查询来获取标签和提及。
{
tweet_graph(func: eq(user_handle, "hackintoshrao")) {
user_name
authored {
tweet
tagged_with {
hashtag
}
mentioned {
user_name
}
}
}
}
您知道Dgraph中的字符串值也可以使用大于或小于之类的比较器进行比较吗?
在接下来,让我们看看如何在字符串谓词上运行equals to (eq)以外的比较函数。
Exact索引
我们在前面的教程中讨论了Dgraph中有5个比较器函数。
简单回顾一下:
比较器函数名 | 全称 |
---|---|
eq | equals to |
lt | less than |
le | less than or equal to |
gt | greater than |
ge | greater than or equal to |
注意: 所有5个比较器函数都可以应用于字符串谓词。
我们已经使用了eq算子。其他四个对于依赖于字符串的字母顺序的操作是有用的。
让我们通过一个简单的例子来了解它。
让我们以字母顺序查找dgraphlabs之后的twitter帐户。
{
using_greater_than(func: gt(user_handle, "dgraphlabs")) {
user_handle
}
}
我们有一个错误!从错误中可以看到,user_handle谓词上的当前哈希索引不支持gt函数。
为了能够执行如上所述的字符串比较操作,您需要首先设置字符串谓词的精确索引。
精确索引exact是唯一允许您在字符串谓词上使用ge、gt、le、lt比较器的字符串索引。
提示: 精准索引exact还允许您使用equals to (eq)比较器。但是,如果您只想在字符串谓词上使用equals to (eq)比较器,那么使用精确的索引将是多余的。哈希索引hash是一个更好的选择,因为它通常更节省空间。
让我们看看实际的索引。
我们又有一个错误!
虽然字符串谓词可以有多个索引,但其中一些索引彼此不兼容。其中一个例子就是散列hash和精确索引exact的组合。
user_handle谓词已经有了哈希索引,因此试图设置精确的索引会给您带来错误。
让我们取消选中user_handle谓词的哈希索引,选择准确的索引,然后单击update。
虽然Dgraph允许更改谓词的索引类型,但只在必要时才这样做。当索引改变时,需要重新索引数据,这需要一些计算,因此可能需要一些时间。当重新索引操作运行时,所有的突变都将被搁置。
现在,让我们重新运行查询。
结果包含三个:francesc、gopherpalooza和hackintoshrao。按字母顺序排序,这些值都大于dgraphlabs。
有些推文比其他的更能吸引我们。例如,我喜欢Graphs 和 Go。因此,我肯定会喜欢与这些主题相关的tweet。基于关键词的搜索是一种查找相关信息的有用方法。
Term索引
术语索引允许您基于一个或多个关键字搜索字符串谓词。这些关键字称为术语。
为了能够搜索具有特定关键字或术语的tweet,我们需要首先设置tweet上的术语索引。
添加术语索引类似于添加任何其他字符串索引。
Dgraph提供了两个内置函数专门用于搜索术语:allofterms和anyofterms。
除了这两个函数,term索引只支持eq比较器。这意味着在带有术语索引的字符串谓词上运行任何其他查询函数(如lt、gt、le…)都会失败。
我们很快就会看到包含字符串索引及其支持的查询函数的表。但是首先,让我们学习如何使用anyofterms和allofterms查询函数。让我们编写一个查询来查找所有带有Go或Graph中的术语或关键字的tweet。
{
find_tweets(func: anyofterms(tweet, "Go Graph")) {
tweet
}
}
下面是来自查询响应的匹配推文:
{
"tweet": "Let's Go and catch @francesc at @Gopherpalooza today, as he scans into Go source code by building its Graph in Dgraph!\nBe there, as he Goes through analyzing Go source code, using a Go program, that stores data in the GraphDB built in Go!\n#golang #GraphDB #Databases #Dgraph "
}
anyofterms函数返回具有Go或Graph关键字的tweet。
在本例中,我们只使用了两个术语进行搜索(Go和Graph),但是您可以扩展任意数量的术语进行搜索或匹配。
结果包含数据库中三个tweet中的一个。另外两个tweet没有到达结果,因为它们没有Go或Graph这两个术语。
同样需要注意的是,术语搜索函数(anyofterms和allofterms)对大小写和特殊字符不敏感。
这意味着,如果搜索术语GraphQL,查询将返回与在tweet中找到的以下所有术语的正匹配:graphql, graphQL, #graphql, #GraphQL。
现在,让我们找到其中包含Go或GraphQL两个术语的tweet。
{
find_tweets(func: anyofterms(tweet, "Go GraphQL")) {
tweet
}
}
哦,哇,我们在结果中有所有的三个tweet。这意味着,所有三个tweet都使用Go或GraphQL中的任何一个术语。
现在,如何找到其中包含Go和GraphQL术语的tweet。我们可以用allofterms函数来做。
{
find_tweets(func: allofterms(tweet, "Go GraphQL")) {
tweet
}
}
我们得到一个空结果。没有一条推文同时包含Go和GraphQL。
除了Go和Graph,我也是GraphQL和GraphDB的粉丝。
让我们找出其中包含GraphQL和GraphDB关键字的tweet。
在一个结果中,我们有两个tweets,其中包含两个术语GraphQL和GraphDB。
{
"tweet": "Test tweet for the fifth episode of getting started series with @dgraphlabs. Wait for the video of the fourth one by @francesc the coming Wednesday!\n#GraphDB #GraphQL"
},
{
"tweet": "So many good talks at #graphqlconf, next year I'll make sure to be *at least* in the audience!\nAlso huge thanks to the live tweeting by @dgraphlabs for alleviating the FOMO\n#GraphDB ♥️ #GraphQL"
}
在结束之前,下面是包含我们学过的三个字符串索引及其兼容的内置函数的表。
索引 | 有效的查询功能 |
---|---|
hash | eq |
exact | eq, lt, gt, le, ge |
term | eq, allofterms, anyofterms |