我正在尝试检索名称中包含字符串first
的所有任务文档。
我目前有以下代码,但是仅当我输入确切的名称时,它才有效:
res, err := db.client.Query(
f.Map(
f.Paginate(f.MatchTerm(f.Index("tasks_by_name"), "My first task")),
f.Lambda("ref", f.Get(f.Var("ref"))),
),
)
我想我可以在某处使用ContainsStr()
,但我不知道如何在查询中使用它。
而且,有没有不用Filter()
的方法吗?我问是因为它似乎在分页之后过滤,并且弄乱了页面
FaunaDB提供了许多结构,这使其功能强大,但是您有很多选择。强大的能力会带来小的学习曲线:)。
根据docs的基本用法
ContainsStr('Fauna', 'a')
当然,这适用于特定值,因此为了使它起作用,您[[需要过滤器和过滤器仅适用于分页集。这意味着我们首先需要获得一个分页的集合。获取分页文档的一种方法是:
Map(
Paginate(Documents(Collection('tasks'))),
Lambda(['ref'], Get(Var('ref')))
)
但是我们可以更有效地执行此操作,因为一次读取===一次,并且我们不需要文档,我们将过滤掉很多文档。有趣的是,一个索引页也是一个读取页,因此我们可以如下定义索引:
{ name: "tasks_name_and_ref", unique: false, serialized: true, source: "tasks", terms: [], values: [ { field: ["data", "name"] }, { field: ["ref"] } ] }
并且由于我们将name和ref添加到值中,因此索引将返回name和ref的页面,可用于过滤。例如,我们可以对索引执行类似的操作,在索引上进行映射,这将为我们返回一个布尔数组。
Map( Paginate(Match(Index('tasks_name_and_ref'))), Lambda(['name', 'ref'], ContainsStr(Var('name'), 'first')) )
由于Filter也适用于数组,因此实际上我们可以简单地将替换为filter。我们还将添加一个小写字母以忽略大小写,我们需要:Map
Filter(
Paginate(Match(Index('tasks_name_and_ref'))),
Lambda(['name', 'ref'], ContainsStr(LowerCase(Var('name')), 'first'))
)
就我而言,结果是:
{
"data": [
[
"Firstly, we'll have to go and refactor this!",
Ref(Collection("tasks"), "267120709035098631")
],
[
"go to a big rock-concert abroad, but let's not dive in headfirst",
Ref(Collection("tasks"), "267120846106001926")
],
[
"The first thing to do is dance!",
Ref(Collection("tasks"), "267120677201379847")
]
]
}
过滤并缩小页面大小
选项2:索引!
绑定
我们没有字符串拆分功能,但由于FQL易于扩展,因此我们可以将其自己编写为使用宿主语言(在这种情况下为javascript)绑定到变量,或使用以下社区驱动的库中的一个:https://github.com/shiftx/faunadb-fql-lib
function StringSplit(string: ExprArg, delimiter = " "){
return If(
Not(IsString(string)),
Abort("SplitString only accept strings"),
Map(
FindStrRegex(string, Concat(["[^\\", delimiter, "]+"])),
Lambda("res", LowerCase(Select(["data"], Var("res"))))
)
)
)
并在我们的绑定中使用它。
CreateIndex({
name: 'tasks_by_words',
source: [
{
collection: Collection('tasks'),
fields: {
words: Query(Lambda('task', StringSplit(Select(['data', 'name']))))
}
}
],
terms: [
{
binding: 'words'
}
]
})
提示,如果不确定是否正确,则可以始终将绑定放在中,而不是用术语,然后在fauna dashboard中将看到索引是否实际上包含值:] >我们做了什么?我们只是编写了一个绑定,该绑定将在编写文档时将值转换为values
值数组
。当您在FaunaDB中对文档数组进行索引时,这些值分别是索引,但全部指向同一文档,这对于我们的搜索实现非常有用。 我们现在可以通过使用以下查询来查找包含字符串“ first”作为其单词之一的任务:Map(
Paginate(Match(Index('tasks_by_words'), 'first')),
Lambda('ref', Get(Var('ref')))
)
谁会给我这个文件,名字是:“要做的第一件事就是跳舞!”其他两个文档没有确切的单词,那么我们该怎么做?
选项3:索引和Ngram(精确包含匹配项)
不过我们会根据您的用例进行调整。在搜索时,所有这些都是性能和存储的权衡,在FaunaDB中,用户可以选择权衡。请注意,在以前的方法中,我们将每个单词分开存储,使用Ngrams可以进一步拆分单词以提供某种形式的模糊匹配。不利的一面是,如果您选择错误,索引大小可能会变得非常大(对于搜索引擎来说同样如此,因此为什么它们让您定义不同的算法)。
NGram本质上所做的是获取一定长度的字符串的子字符串。例如:
将返回:
NGram('lalala', 3, 3)
[如果我们知道我们搜索的字符串长度不会超过特定长度,则假设长度为10(这是一个折衷,增加大小会增加存储要求,但允许您查询更长的字符串),可以编写以下Ngram生成器。
然后您可以按照以下步骤编写索引:
function GenerateNgrams(Phrase) { return Distinct( Union( Let( { // Reduce this array if you want less ngrams per word. indexes: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], indexesFiltered: Filter( Var('indexes'), // filter out the ones below 0 Lambda('l', GT(Var('l'), 0)) ), ngramsArray: q.Map(Var('indexesFiltered'), Lambda('l', NGram(LowerCase(Var('Phrase')), Var('l'), Var('l')))) }, Var('ngramsArray') ) ) ) }
并且您有一个索引支持的搜索,其中您的页面就是您所请求的大小。
CreateIndex({ name: 'tasks_by_ngrams_exact', // we actually want to sort to get the shortest word that matches first source: [ { // If your collections have the same property tht you want to access you can pass a list to the collection collection: [Collection('tasks')], fields: { wordparts: Query(Lambda('task', GenerateNgrams(Select(['data', 'name'], Var('task'))))) } } ], terms: [ { binding: 'wordparts' } ] })
选项4:大小为3或三字母组合(模糊匹配)的索引和Ngrams如果要模糊搜索
Map( Paginate(Match(Index('tasks_by_ngrams_exact'), 'first')), Lambda('ref', Get(Var('ref'))) )
,在这种情况下,我们的索引将很容易,因此我们将不使用外部函数。
在这种方法中,我们在索引侧和查询侧都使用两个三元组。例如,我们现在可以如下进行模糊搜索:
CreateIndex({ name: 'tasks_by_ngrams', source: { collection: Collection('tasks'), fields: { ngrams: Query(Lambda('task', Distinct(NGram(LowerCase(Select(['data', 'name'], Var('task'))), 3, 3)))) } }, terms: [ { binding: 'ngrams' } ] })
这将返回所有包含第一个句子的句子。,由于存在一个匹配的三字母组,我们仍然会匹配所有这三个字符。Map( Paginate(Union(Map(NGram('first', 3, 3), Lambda('ngram', Match(Index('tasks_by_ngrams'), Var('ngram')))))), Lambda('ref', Get(Var('ref'))) )
但是如果我们拼写错误并且写了[[frst