teh*_*afe 78 firebase google-cloud-firestore
我想添加一个简单的搜索字段,想使用类似的东西
collectionRef.where('name', 'contains', 'searchTerm')
我尝试过使用where('name', '==', '%searchTerm%'),但它没有返回任何东西.
Gil*_*ert 36
虽然就限制而言Kuba的答案是正确的,但您可以使用类似集合的结构来部分模拟它:
{
'terms': {
'reebok': true,
'mens': true,
'tennis': true,
'racket': true
}
}
Run Code Online (Sandbox Code Playgroud)
现在您可以查询
collectionRef.where('terms.tennis', '==', true)
Run Code Online (Sandbox Code Playgroud)
这是有效的,因为Firestore会自动为每个字段创建索引.不幸的是,这不能直接用于复合查询,因为Firestore不会自动创建复合索引.
您仍然可以通过存储单词组合来解决这个问题,但这会很快变得难看.
使用外置全文搜索可能还是比较好的.
Jon*_*han 36
UPDATE - 2/17/21 - I created several new Full Text Search Options.
See Fireblog.io for details.
Also, side note, dgraph now has websockets for realtime... wow, never saw that coming, what a treat! Slash Dgraph - Amazing!
1.) \uf8ff works the same way as ~
2.) You can use a where clause or start end clauses:
ref.orderBy('title').startAt(term).endAt(term + '~');
Run Code Online (Sandbox Code Playgroud)
is exactly the same as
ref.where('title', '>=', term).where('title', '<=', term + '~');
Run Code Online (Sandbox Code Playgroud)
3.) No, it does not work if you reverse startAt() and endAt() in every combination, however, you can achieve the same result by creating a second search field that is reversed, and combining the results.
Example: First you have to save a reversed version of the field when the field is created. Something like this:
// collection
const postRef = db.collection('posts')
async function searchTitle(term) {
// reverse term
const termR = term.split("").reverse().join("");
// define queries
const titles = postRef.orderBy('title').startAt(term).endAt(term + '~').get();
const titlesR = postRef.orderBy('titleRev').startAt(termR).endAt(termR + '~').get();
// get queries
const [titleSnap, titlesRSnap] = await Promise.all([
titles,
titlesR
]);
return (titleSnap.docs).concat(titlesRSnap.docs);
}
Run Code Online (Sandbox Code Playgroud)
With this, you can search the last letters of a string field and the first, just not random middle letters or groups of letters. This is closer to the desired result. However, this won't really help us when we want random middle letters or words. Also, remember to save everything lowercase, or a lowercase copy for searching, so case won't be an issue.
4.) If you have only a few words, Ken Tan's Method will do everything you want, or at least after you modify it slightly. However, with only a paragraph of text, you will exponentially create more than 1MB of data, which is bigger than firestore's document size limit (I know, I tested it).
5.) If you could combine array-contains (or some form of arrays) with the \uf8ff trick, you might could have a viable search that does not reach the limits. I tried every combination, even with maps, and a no go. Anyone figures this out, post it here.
6.) If you must get away from ALGOLIA and ELASTIC SEARCH, and I don't blame you at all, you could always use mySQL, postSQL, or neo4Js on Google Cloud. They are all 3 easy to set up, and they have free tiers. You would have one cloud function to save the data onCreate() and another onCall() function to search the data. Simple...ish. Why not just switch to mySQL then? The real-time data of course! When someone writes DGraph with websocks for real-time data, count me in!
Algolia and ElasticSearch were built to be search-only dbs, so there is nothing as quick... but you pay for it. Google, why do you lead us away from Google, and don't you follow MongoDB noSQL and allow searches?
Kub*_*uba 25
有没有这样的运营商,允许的有==,<,<=,>,>=.
您可以通过前缀只能过滤,例如对于之间开始的一切bar,并foo可以使用
collectionRef.where('name', '>=', 'bar').where('name', '<=', 'foo')
您可以使用Algolia或ElasticSearch 等外部服务.
Alb*_*haw 22
Firebase不明确支持在字符串中搜索字词...
但Firebase现在支持以下内容,这将解决您的案例和许多其他问题:
截至2018年8月,他们支持array-contains查询.请参阅:https://firebase.googleblog.com/2018/08/better-arrays-in-cloud-firestore.html
您现在可以将所有关键术语设置为数组作为字段,然后查询具有包含"X"的数组的所有文档.您可以使用逻辑AND进一步比较其他查询.(这是因为firebase 目前本身不支持多个包含数组的查询的复合查询,所以'AND'排序查询必须在客户端完成)
使用这种风格的数组将允许它们针对并发写入进行优化,这很好!没有测试它支持批量请求(文档没有说),但我打赌它自从它的官方解决方案.
collection("collectionPath").
where("searchTermsArray", "array-contains", "term").get()
Run Code Online (Sandbox Code Playgroud)
Ank*_*ati 11
我同意@Kuba的回答,但是,仍然需要添加一个小的更改以完美地进行前缀搜索。这对我有用
用于搜索以名称开头的记录 queryText
collectionRef.where('name', '>=', queryText).where('name', '<=', queryText+ '\uf8ff')。
\uf8ff查询中使用的字符是Unicode范围内的一个很高的代码点(它是专用使用区[PUA]代码)。由于该查询位于Unicode中大多数常规字符之后,因此它匹配以开头的所有值queryText。
bho*_*ben 10
根据Firestore文档,Cloud Firestore不支持本机索引或搜索文档中的文本字段.此外,下载整个集合以搜索客户端字段是不切实际的.
建议使用Algolia和Elastic Search等第三方搜索解决方案.
我相信 Firebase 很快就会推出“字符串包含”来捕获字符串中的任何索引 [i] startAt ......但我已经研究了网络并发现其他人想到的这个解决方案设置了你的数据,例如这个
state = {title:"Knitting"}
...
const c = this.state.title.toLowerCase()
var array = [];
for (let i = 1; i < c.length + 1; i++) {
array.push(c.substring(0, i));
}
firebase
.firestore()
.collection("clubs")
.doc(documentId)
.update({
title: this.state.title,
titleAsArray: array
})
Run Code Online (Sandbox Code Playgroud)
像这样查询
firebase
.firestore()
.collection("clubs")
.where(
"titleAsArray",
"array-contains",
this.state.userQuery.toLowerCase()
)
Run Code Online (Sandbox Code Playgroud)
答案较晚,但对于仍在寻找答案的任何人,假设我们有一个用户集合,并且在该集合的每个文档中都有一个“用户名”字段,因此,如果要查找用户名以“ al”开头的文档我们可以做类似的事情
FirebaseFirestore.getInstance().collection("users").whereGreaterThanOrEqualTo("username", "al")
Run Code Online (Sandbox Code Playgroud)
就像乔纳森说的那样,我使用了卦。
三元组是存储在数据库中以帮助搜索的 3 个字母的组。因此,如果我有用户数据,并且我想查询唐纳德·特朗普的“trum”,我必须以这种方式存储它
我只是这样回忆
onPressed: () {
//LET SAY YOU TYPE FOR 'tru' for trump
List<String> search = ['tru', 'rum'];
Future<QuerySnapshot> inst = FirebaseFirestore.instance
.collection("users")
.where('trigram', arrayContainsAny: search)
.get();
print('result=');
inst.then((value) {
for (var i in value.docs) {
print(i.data()['name']);
}
});
Run Code Online (Sandbox Code Playgroud)
无论如何都会得到正确的结果

编辑 05/2021:
Google Firebase 现在有一个扩展来使用 Algolia 实现搜索。Algolia 是一个全文搜索平台,具有广泛的功能列表。你需要在 Firebase 上有一个“Blaze”计划,并且有与 Algolia 查询相关的费用,但这将是我推荐的生产应用程序的方法。如果您更喜欢免费的基本搜索,请参阅下面我的原始答案。
https://firebase.google.com/products/extensions/firestore-algolia-search https://www.algolia.com
原始答案:
所选答案仅适用于精确搜索,而不是自然的用户搜索行为(在“今天乔吃了一个苹果”中搜索“苹果”是行不通的)。
我认为上面丹费恩的回答应该排名更高。如果您搜索的字符串数据很短,您可以将字符串的所有子字符串保存在文档中的数组中,然后使用 Firebase 的 array_contains 查询搜索该数组。Firebase 文档限制为 1 MiB(1,048,576 字节)(Firebase Quotas and Limits),即文档中保存了大约 100 万个字符(我认为 1 个字符 ~= 1 个字节)。只要您的文档不接近 100 万个标记,就可以存储子字符串。
搜索用户名的示例:
步骤 1:将以下字符串扩展添加到您的项目中。这使您可以轻松地将字符串分解为子字符串。(我在这里找到了这个)。
extension String {
var length: Int {
return count
}
subscript (i: Int) -> String {
return self[i ..< i + 1]
}
func substring(fromIndex: Int) -> String {
return self[min(fromIndex, length) ..< length]
}
func substring(toIndex: Int) -> String {
return self[0 ..< max(0, toIndex)]
}
subscript (r: Range<Int>) -> String {
let range = Range(uncheckedBounds: (lower: max(0, min(length, r.lowerBound)),
upper: min(length, max(0, r.upperBound))))
let start = index(startIndex, offsetBy: range.lowerBound)
let end = index(start, offsetBy: range.upperBound - range.lowerBound)
return String(self[start ..< end])
}
Run Code Online (Sandbox Code Playgroud)
第 2 步:当您存储用户名时,也将这个函数的结果作为数组存储在同一个 Document 中。这将创建原始文本的所有变体并将它们存储在一个数组中。例如,文本输入“Apple”将创建以下数组:["a", "p", "p", "l", "e", "ap", "pp", "pl", "le ", "app", "ppl", "ple", "appl", "pple", "apple"],应该包含用户可能输入的所有搜索条件。如果您想要所有结果,您可以将 maximumStringSize 保留为 nil,但是,如果有长文本,我建议在文档大小变得太大之前将其设置为上限 - 大约 15 对我来说很好用(大多数人无论如何都不会搜索长短语)。
func createSubstringArray(forText text: String, maximumStringSize: Int?) -> [String] {
var substringArray = [String]()
var characterCounter = 1
let textLowercased = text.lowercased()
let characterCount = text.count
for _ in 0...characterCount {
for x in 0...characterCount {
let lastCharacter = x + characterCounter
if lastCharacter <= characterCount {
let substring = textLowercased[x..<lastCharacter]
substringArray.append(substring)
}
}
characterCounter += 1
if let max = maximumStringSize, characterCounter > max {
break
}
}
print(substringArray)
return substringArray
}
Run Code Online (Sandbox Code Playgroud)
第 3 步:您可以使用 Firebase 的 array_contains 函数!
[yourDatabasePath].whereField([savedSubstringArray], arrayContains: searchText).getDocuments....
Run Code Online (Sandbox Code Playgroud)
实际上,我认为在 Firestore 中执行此操作的最佳解决方案是将所有子字符串放入一个数组中,然后执行 array_contains 查询。这允许您进行子字符串匹配。存储所有子字符串有点矫枉过正,但如果您的搜索词很短,那么这是非常非常合理的。
截至今天,专家建议的基本上有 3 种不同的解决方法作为问题的答案。
我都试过了。我认为记录我对每个人的体验可能会很有用。
方法-A:使用: (dbField ">=" searchString) & (dbField "<=" searchString + "\uf8ff")
由@Kuba 和@Ankit Prajapati 推荐
.where("dbField1", ">=", searchString)
.where("dbField1", "<=", searchString + "\uf8ff");
Run Code Online (Sandbox Code Playgroud)
A.1 Firestore 查询只能对单个字段执行范围过滤器(>、<、>=、<=)。不支持对多个字段使用范围过滤器的查询。通过使用此方法,您不能在数据库的任何其他字段(例如日期字段)中使用范围运算符。
A2。此方法不适用于同时在多个字段中搜索。例如,您无法检查搜索字符串是否在任何字段(姓名、注释和地址)中。
方法 B:对映射中的每个条目使用带有“true”的搜索字符串 MAP,并在查询中使用“==”运算符
由@Gil Gilbert 推荐
document1 = {
'searchKeywordsMap': {
'Jam': true,
'Butter': true,
'Muhamed': true,
'Green District': true,
'Muhamed, Green District': true,
}
}
.where(`searchKeywordsMap.${searchString}`, "==", true);
Run Code Online (Sandbox Code Playgroud)
B.1 显然,这种方法每次将数据保存到数据库时都需要额外的处理,更重要的是,需要额外的空间来存储搜索字符串的映射。
B.2 如果一个 Firestore 查询只有一个类似上面的条件,则不需要事先创建索引。在这种情况下,此解决方案可以正常工作。
B.3 但是,如果查询有另一个条件,例如 (status === "active",) 似乎用户输入的每个“搜索字符串”都需要一个索引。换句话说,如果一个用户搜索“Jam”而另一个用户搜索“Butter”,则应事先为字符串“Jam”创建一个索引,并为“Butter”等创建另一个索引。除非您可以预测所有可能的用户的搜索字符串,这不起作用 - 如果查询有其他条件!
.where(searchKeywordsMap["Jam"], "==", true); // requires an index on searchKeywordsMap["Jam"]
.where("status", "==", "active");
Run Code Online (Sandbox Code Playgroud)
**方法-C:使用搜索字符串数组和“数组包含”运算符
由@Albert Renshaw 推荐并由@Nick Carducci 演示
document1 = {
'searchKeywordsArray': [
'Jam',
'Butter',
'Muhamed',
'Green District',
'Muhamed, Green District',
]
}
.where("searchKeywordsArray", "array-contains", searchString);
Run Code Online (Sandbox Code Playgroud)
C.1 与方法B类似,该方法每次将数据保存到数据库时都需要额外的处理,更重要的是,需要额外的空间来存储搜索字符串数组。
C.2 Firestore 查询在复合查询中最多可以包含一个“array-contains”或“array-contains-any”子句。
一般限制:
没有一种解决方案适合所有人。每种解决方法都有其局限性。我希望以上信息可以在这些变通方法之间的选择过程中帮助您。
有关 Firestore 查询条件的列表,请查看文档https://firebase.google.com/docs/firestore/query-data/queries。
我没试过https://fireblog.io/blog/post/firestore-full-text-search,这是由@Jonathan建议。
截至2023 年 3 月,Firestore 的新OR查询允许消除前缀搜索区分大小写的问题(在某种程度上):
query(
collection(DB, 'some/collection'),
or(
// query as-is:
and(
where('name', '>=', queryString),
where('name', '<=', queryString + '\uf8ff')
),
// capitalize first letter:
and(
where('name', '>=', queryString.charAt(0).toUpperCase() + queryString.slice(1)),
where('name', '<=', queryString.charAt(0).toUpperCase() + queryString.slice(1) + '\uf8ff')
),
// lowercase:
and(
where('name', '>=', queryString.toLowerCase()),
where('name', '<=', queryString.toLowerCase() + '\uf8ff')
)
)
);
Run Code Online (Sandbox Code Playgroud)
小智 5
我刚刚遇到了这个问题,并提出了一个非常简单的解决方案。
String search = "ca";
Firestore.instance.collection("categories").orderBy("name").where("name",isGreaterThanOrEqualTo: search).where("name",isLessThanOrEqualTo: search+"z")
Run Code Online (Sandbox Code Playgroud)
isGreaterThanOrEqualTo 让我们过滤掉搜索的开头,并通过在 isLessThanOrEqualTo 的末尾添加一个“z”来限制我们的搜索,使其不会滚动到下一个文档。
如果您不想使用像 Algolia 这样的第三方服务,Firebase Cloud Functions是一个不错的选择。您可以创建一个可以接收输入参数的函数,通过服务器端的记录进行处理,然后返回符合您条件的记录。
| 归档时间: |
|
| 查看次数: |
33497 次 |
| 最近记录: |