Google Firestore: Truy vấn về chuỗi con của giá trị thuộc tính (tìm kiếm văn bản)


103

Tôi đang tìm cách thêm một trường tìm kiếm đơn giản, tôi muốn sử dụng một thứ gì đó như

collectionRef.where('name', 'contains', 'searchTerm')

Tôi đã thử sử dụng where('name', '==', '%searchTerm%'), nhưng nó không trả lại bất cứ điều gì.


2
xin vui lòng có bạn tìm thấy bất kỳ giải pháp. tôi đã được tìm kiếm trong nhiều ngày cho một cái gì đó tương tự với không thiếu
suulisin

1
Firebase hiện hỗ trợ điều này. Vui lòng cập nhật câu trả lời: stackoverflow.com/a/52715590/2057171
Albert Renshaw

1
Tôi nghĩ cách tốt nhất là tạo một tập lệnh lập chỉ mục từng tài liệu theo cách thủ công. Sau đó truy vấn các chỉ mục đó. hãy kiểm tra điều này: anglefirebase.com/lessons/…
Kiblawi_Rabee

Câu trả lời:


34

Không có nhà điều hành như vậy, những người cho phép là ==, <, <=, >, >=.

Bạn chỉ có thể lọc theo tiền tố, ví dụ: đối với mọi thứ bắt đầu từ giữa barfoobạn có thể sử dụng

collectionRef.where('name', '>=', 'bar').where('name', '<=', 'foo')

Bạn có thể sử dụng dịch vụ bên ngoài như Algolia hoặc ElasticSearch cho điều đó.


5
Đó không hoàn toàn là những gì tôi đang tìm kiếm. Tôi có một danh sách lớn các sản phẩm với tiêu đề dài. "Vợt tennis Rebok dành cho nam". Người dùng có thể tìm kiếm tennis, nhưng dựa trên các toán tử truy vấn có sẵn, không có cách nào để có được những kết quả đó. Kết hợp >=<=không hoạt động. Dĩ nhiên tôi có thể sử dụng Algolia, nhưng tôi cũng có thể chỉ cần sử dụng nó với căn cứ hỏa lực để làm hầu hết các truy vấn và không cần phải chuyển sang FireStore ...
tehfailsafe

4
@tehfailsafe Vâng, câu hỏi của bạn là 'làm thế nào để truy vấn nếu một trường chứa một chuỗi' và câu trả lời là 'bạn không thể làm điều đó'.
Kuba

20
@ A.Chakroun chính xác thì câu trả lời của tôi là gì?
Kuba

16
tbh đây là một cái gì đó thực sự cần thiết. Tôi không hiểu tại sao nhóm của Firebase lại không nghĩ đến điều này
Dani

2
Thực sự ngạc nhiên rằng Firebase quá yếu trong việc truy vấn. Không thể tin rằng có rất nhiều người đang sử dụng nó nếu nó không thể hỗ trợ một truy vấn đơn giản như vậy.
Bagusflyer

43

Mặc dù câu trả lời của Kuba là đúng khi có giới hạn, bạn có thể mô phỏng một phần điều này với cấu trúc giống như một tập hợp:

{
  'terms': {
    'reebok': true,
    'mens': true,
    'tennis': true,
    'racket': true
  }
}

Bây giờ bạn có thể truy vấn với

collectionRef.where('terms.tennis', '==', true)

Điều này hoạt động vì Firestore sẽ tự động tạo chỉ mục cho mọi trường. Thật không may, điều này không hoạt động trực tiếp cho các truy vấn phức hợp vì Firestore không tự động tạo chỉ mục tổng hợp.

Bạn vẫn có thể giải quyết vấn đề này bằng cách lưu trữ các tổ hợp từ nhưng điều này trở nên xấu đi nhanh chóng.

Bạn vẫn có thể tốt hơn với tìm kiếm toàn văn bên ngoài .


Có thể sử dụng chức năng đám mây với cloud.google.com/appengine/docs/standard/java/search không?
Henry

1
Nếu bạn đang hỏi điều này như một phần tiếp theo cho câu trả lời này thì: Tìm kiếm toàn văn của AppEngine hoàn toàn tách biệt với Firestore và vì vậy điều này sẽ không trực tiếp giúp bạn. Bạn có thể sao chép dữ liệu của mình bằng cách sử dụng chức năng đám mây, nhưng về cơ bản đó là gợi ý sử dụng tìm kiếm toàn văn bên ngoài. Nếu bạn đang hỏi điều gì khác, vui lòng bắt đầu một câu hỏi mới.
Gil Gilbert

1
Trong FireStore, Bạn cần chỉ mục tất cả các điều khoản trước khi sử dụngwhere
Husam

4
Như Husam đã đề cập, tất cả các trường này sẽ cần được lập chỉ mục. Tôi muốn cho phép tìm kiếm bất kỳ thuật ngữ nào mà tên sản phẩm của tôi có. Vì vậy, tôi đã tạo thuộc tính kiểu 'object' trên tài liệu của mình với các khóa là các phần của tên sản phẩm, mỗi khóa có giá trị 'true' được gán cho nó, hy vọng rằng việc tìm kiếm ở đâu ('nameSearies.tennis', '==', true) sẽ hoạt động, nhưng firestore đề xuất tạo chỉ mục cho nameSe mảnh.tennis, giống nhau cho mọi thuật ngữ khác. Vì có thể có vô số thuật ngữ, nên câu trả lời này chỉ có thể sử dụng được cho một hệ thống rất hạn chế khi tất cả các cụm từ tìm kiếm được xác định trước.
Slawoj

2
@epeleg Truy vấn sẽ hoạt động, sau khi bạn tạo chỉ mục cho nó, nhưng không khả thi để tạo chỉ mục cho từng thuật ngữ có thể mà tên sản phẩm của bạn chứa, vì vậy, đối với tìm kiếm văn bản các cụm từ trong tên sản phẩm, phương pháp này không hoạt động đối với trường hợp của tôi.
Slawoj

43

Tôi đồng ý với câu trả lời của @ Kuba, Tuy nhiên, nó cần phải thêm một thay đổi nhỏ để hoạt động hoàn hảo cho tìm kiếm theo tiền tố. ở đây những gì đã làm việc cho tôi

Để tìm kiếm các bản ghi bắt đầu bằng tên queryText

collectionRef.where('name', '>=', queryText).where('name', '<=', queryText+ '\uf8ff').

Ký tự \uf8ffđược sử dụng trong truy vấn là một điểm mã rất cao trong phạm vi Unicode (nó là mã Khu vực Sử dụng Riêng [PUA]). Vì nó đứng sau hầu hết các ký tự thông thường trong Unicode, nên truy vấn khớp với tất cả các giá trị bắt đầu bằng queryText.


1
Câu trả lời hay !, điều này rất hiệu quả khi tìm kiếm văn bản tiền tố. Để tìm kiếm các từ trong văn bản, có thể thử triển khai "array-chứa" như được giải thích trong bài đăng này media.com/@ken11zer01/…
guildlefd

Chỉ cần suy nghĩ, nhưng về mặt lý thuyết, bạn có thể khớp tất cả các giá trị kết thúc bằng queryTest bằng cách tạo một trường khác và đảo ngược dữ liệu ...
Jonathan

Có @Jonathan, điều đó cũng có thể.
Ankit Prajapati

30

Mặc dù Firebase không hỗ trợ rõ ràng việc tìm kiếm cụm từ trong một chuỗi,

Firebase hiện (bây giờ) hỗ trợ những điều sau sẽ giải quyết được trường hợp của bạn và nhiều trường hợp khác:

Kể từ tháng 8 năm 2018, họ hỗ trợ array-containstruy vấn. Xem: https://firebase.googleblog.com/2018/08/better-arrays-in-cloud-firestore.html

Bây giờ bạn có thể đặt tất cả các thuật ngữ chính của mình thành một mảng dưới dạng một trường, sau đó truy vấn tất cả các tài liệu có một mảng chứa 'X'. Bạn có thể sử dụng logic AND để so sánh thêm cho các truy vấn bổ sung. (Điều này là do firebase hiện không hỗ trợ các truy vấn phức hợp cho nhiều truy vấn chứa mảng vì vậy các truy vấn sắp xếp 'VÀ' sẽ phải được thực hiện ở đầu máy khách)

Sử dụng các mảng theo phong cách này sẽ cho phép chúng được tối ưu hóa để ghi đồng thời, điều này rất hay! Chưa thử nghiệm xem nó có hỗ trợ yêu cầu hàng loạt không (tài liệu không nói) nhưng tôi đánh cuộc nó sẽ làm được vì nó là giải pháp chính thức.


Sử dụng:

collection("collectionPath").
    where("searchTermsArray", "array-contains", "term").get()

12
Đây là một giải pháp tốt. Tuy nhiên, hãy sửa cho tôi nếu tôi sai, nhưng tôi nghĩ nó không cho phép bạn làm những gì @tehfailsafe đã yêu cầu. Ví dụ: nếu bạn muốn lấy tất cả các tên có chứa chuỗi "abc", bạn sẽ không nhận được bất kỳ thành công nào với mảng-chứa, vì nó sẽ chỉ trả về các tài liệu có tên chính xác là "abc", nhưng "abcD" hoặc "0abc" sẽ bị loại bỏ.
Yulian

1
@Yulian Trong thế giới lập trình, Search termthường được hiểu là một thuật ngữ toàn bộ được phân tách bằng dấu cách, dấu chấm câu, v.v. ở cả hai phía. Nếu bạn google abcdengay bây giờ, bạn sẽ chỉ tìm thấy kết quả cho những thứ giống như %20abcde.hoặc ,abcde!nhưng không abcdefghijk... mặc dù chắc chắn toàn bộ gõ bảng chữ cái được phổ biến hơn nhiều để được tìm thấy trên internet, việc tìm kiếm không phải dành cho abcde * nó cho một abcde bị cô lập
Albert Renshaw

1
Tôi thấy quan điểm của bạn và tôi đồng ý với nó, nhưng có lẽ tôi đã bị hiểu nhầm bởi từ này 'contains', có nghĩa là chính xác những gì tôi đang đề cập đến trong nhiều ngôn ngữ lập trình. Điều tương tự cũng xảy '%searchTerm%'ra theo quan điểm SQL.
Yulian

2
@Yulian Vâng, tôi hiểu rồi. Mặc dù vậy, Firebase là NoSQL nên nó thực sự tốt trong việc thực hiện các loại hoạt động này nhanh chóng và hiệu quả, ngay cả khi chúng có thể bị hạn chế đối với một số vấn đề ngoài phạm vi như tìm kiếm chuỗi ký tự đại diện.
Albert Renshaw

2
Chà, bạn có thể tạo một trường riêng biệt cho từng trường với đại diện của các từ được tách ra như titleArray: ['this', 'is', 'a', 'title'] mỗi khi bạn cập nhật tài liệu. Và sau đó tìm kiếm sẽ dựa trên trường đó thay vì tiêu đề. Bạn tạo một bộ ba onUpdate để tạo các trường này. Rất nhiều công việc cho văn bản dựa trên tìm kiếm nhưng tôi muốn cải thiện hiệu suất của NoSQL.
sfratini

14

Theo tài liệu của Firestore , Cloud Firestore không hỗ trợ lập chỉ mục gốc hoặc tìm kiếm các trường văn bản trong tài liệu. Ngoài ra, tải xuống toàn bộ bộ sưu tập để tìm kiếm các trường phía máy khách là không thực tế.

Các giải pháp tìm kiếm của bên thứ ba như AlgoliaElastic Search được khuyến nghị.


46
Tôi đã đọc các tài liệu, mặc dù nó không lý tưởng. Hạn chế là Algolia và Firestore có các mô hình định giá khác nhau ... Tôi rất vui có thể có 600.000 tài liệu trong Firestore (miễn là tôi không truy vấn quá nhiều mỗi ngày). Khi tôi đẩy họ đến Algolia để tìm kiếm, bây giờ tôi phải trả cho Algolia 310 đô la mỗi tháng chỉ để có thể thực hiện tìm kiếm tiêu đề trên các tài liệu Firestore của tôi.
tehfailsafe

2
vấn đề là điều này không miễn phí
Dani

Đây là câu trả lời chính xác cho câu hỏi được đặt ra và nên được chấp nhận là tốt nhất.
briznad

11

Một vài lưu ý ở đây:

1.) \uf8ff hoạt động giống như~

2.) Bạn có thể sử dụng mệnh đề where hoặc mệnh đề bắt đầu kết thúc:

ref.orderBy('title').startAt(term).endAt(term + '~');

giống hệt như

ref.where('title', '>=', term).where('title', '<=', term + '~');

3.) Không, nó không hoạt động nếu bạn đảo ngược startAt()endAt()trong mọi kết hợp, tuy nhiên, bạn có thể đạt được cùng một kết quả bằng cách tạo trường tìm kiếm thứ hai được đảo ngược và kết hợp các kết quả.

Ví dụ: Đầu tiên bạn phải lưu một phiên bản đảo ngược của trường khi trường được tạo. Một cái gì đó như thế này:

// 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);
}

Với điều này, bạn có thể tìm kiếm các chữ cái cuối cùng của trường chuỗi và chữ cái đầu tiên , không phải chữ cái giữa hoặc nhóm chữ cái ngẫu nhiên. Điều này gần với kết quả mong muốn hơn. Tuy nhiên, điều này sẽ không thực sự giúp ích cho chúng ta khi chúng ta muốn các chữ cái hoặc từ ở giữa ngẫu nhiên. Ngoài ra, hãy nhớ lưu mọi thứ bằng chữ thường hoặc bản sao chữ thường để tìm kiếm, vì vậy trường hợp không thành vấn đề.

4.) Nếu bạn chỉ có một vài từ, Phương pháp của Ken Tan sẽ làm mọi thứ bạn muốn, hoặc ít nhất là sau khi bạn sửa đổi nó một chút. Tuy nhiên, chỉ với một đoạn văn bản, bạn sẽ tạo ra hơn 1MB dữ liệu theo cấp số nhân, lớn hơn giới hạn kích thước tài liệu của firestore (tôi biết, tôi đã thử nghiệm nó).

5.) Nếu bạn có thể kết hợp mảng chứa (hoặc một số dạng mảng) với \uf8ffthủ thuật này, bạn có thể có một tìm kiếm khả thi mà không đạt đến giới hạn. Tôi đã thử mọi cách kết hợp, ngay cả với bản đồ và không đi được. Bất cứ ai tìm ra điều này, đăng nó ở đây.

6.) Nếu bạn phải thoát khỏi TÌM KIẾM ALGOLIA và ELASTIC, và tôi không đổ lỗi cho bạn, bạn luôn có thể sử dụng mySQL, postSQL hoặc neo4Js trên Google Cloud. Cả 3 đều dễ thiết lập và có các cấp miễn phí. Bạn sẽ có một hàm đám mây để lưu dữ liệu onCreate () và một hàm onCall () khác để tìm kiếm dữ liệu. Đơn giản ... là được. Tại sao không chuyển sang mySQL sau đó? Tất nhiên là dữ liệu thời gian thực! Khi ai đó viết DGraph bằng websocks để lấy dữ liệu thời gian thực, hãy tính tôi!

Algolia và ElasticSearch được xây dựng để trở thành các dbs chỉ dành cho tìm kiếm, vì vậy, không có gì nhanh bằng ... nhưng bạn phải trả tiền cho nó. Google, tại sao bạn dẫn chúng tôi ra khỏi Google, và bạn không theo dõi MongoDB noSQL và cho phép tìm kiếm?

CẬP NHẬT - TÔI ĐÃ TẠO GIẢI PHÁP:

https://fireblog.io/blog/post/firestore-full-text-search


Một tổng quan tuyệt vời và rất hữu ích.
RedFilter

Tuyệt vời! ủng hộ cho một phản hồi có cấu trúc tốt và nhiều thông tin.
King Of The Jungle

thật tuyệt, cảm ơn bạn
Giang

10

Câu trả lời muộn nhưng đối với bất kỳ ai vẫn đang tìm kiếm câu trả lời, Giả sử chúng tôi có một tập hợp người dùng và trong mỗi tài liệu của bộ sưu tập, chúng tôi có một trường "tên người dùng", vì vậy nếu muốn tìm tài liệu có tên người dùng bắt đầu bằng "al" chúng ta có thể làm một cái gì đó như

 FirebaseFirestore.getInstance().collection("users").whereGreaterThanOrEqualTo("username", "al")

đây là một giải pháp tuyệt vời dễ dàng cảm ơn bạn. Nhưng nếu bạn muốn kiểm tra nhiều hơn một trường thì sao. Giống như "tên" và "mô tả" được kết nối bằng HOẶC?
Hãy dùng thử

Tôi không nghĩ rằng bạn có thể truy vấn dựa trên hai lĩnh vực, buồn bã căn cứ hỏa lực là xấu khi nói đến truy vấn, bạn có thể kiểm tra hy vọng này nó giúp stackoverflow.com/questions/26700924/...
MoTahir

1
Đã xác nhận, @MoTahir. Không có "HOẶC" trong Firestore.
Rap

giải pháp đó không khớp với tên người dùng bắt đầu bằng "al" ... ví dụ: "hello" sẽ được khớp ("hello"> "al")
antoine129

Truy vấn bằng HOẶC đơn giản là vấn đề kết hợp hai kết quả tìm kiếm. Sắp xếp những kết quả đó là một vấn đề khác ...
Jonathan

7

Tôi chắc chắn rằng Firebase sẽ sớm ra mắt "string-contains" để nắm bắt bất kỳ chỉ mục nào [i] startAt trong chuỗi ... Nhưng tôi đã nghiên cứu các trang web và nhận thấy giải pháp này do người khác nghĩ ra khi thiết lập dữ liệu của bạn như điều này

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
})

nhập mô tả hình ảnh ở đây

truy vấn như thế này

firebase
.firestore()
.collection("clubs")
.where(
 "titleAsArray",
 "array-contains",
 this.state.userQuery.toLowerCase()
)

Không được khuyến khích ở tất cả. Vì tài liệu có giới hạn 20k dòng, vì vậy bạn không thể sử dụng nó theo cách này, cho đến khi bạn chắc chắn rằng tài liệu của bạn sẽ không bao giờ đạt đến giới hạn như vậy
Sandeep

Đó là lựa chọn tốt nhất tại thời điểm này, còn điều gì khác được khuyến nghị?
Nick Carducci

1
@Sandeep Tôi khá chắc chắn rằng kích thước được giới hạn ở kích thước 1MB và 20 cấp độ sâu cho mỗi tài liệu. 20k dòng có nghĩa là gì? Đây hiện là giải pháp tốt nhất nếu sử dụng Algolia hoặc ElasticSearch thì khỏi phải bàn
ppicom

5

Nếu bạn không muốn sử dụng dịch vụ của bên thứ ba như Algolia, thì Firebase Cloud Functions là một sự thay thế tuyệt vời. Bạn có thể tạo một hàm có thể nhận một tham số đầu vào, xử lý thông qua các bản ghi phía máy chủ và sau đó trả về các bản ghi phù hợp với tiêu chí của bạn.


1
Còn android thì sao?
Pratik Butani

Bạn có đề xuất rằng mọi người lặp lại từng bản ghi trong một bộ sưu tập không?
DarkNeuron

Không hẳn vậy. Tôi sẽ sử dụng Array.prototype. * - Như .every (), .some (), .map (), .filter (), v.v. Điều này được thực hiện trong Node trên máy chủ trong một Hàm Firebase trước khi trả về các giá trị cho khách hàng.
Rap

3
Bạn vẫn sẽ phải đọc TẤT CẢ các tài liệu để tìm kiếm chúng, điều này phát sinh chi phí và tốn kém về thời gian.
Jonathan

3

Tôi thực sự nghĩ rằng giải pháp tốt nhất để làm điều này trong Firestore là đặt tất cả các chuỗi con trong một mảng và chỉ thực hiện một truy vấn array_contains. Điều này cho phép bạn thực hiện đối sánh chuỗi con. Hơi quá mức cần thiết để lưu trữ tất cả các chuỗi con nhưng nếu cụm từ tìm kiếm của bạn ngắn thì điều đó rất hợp lý.


2

Tôi chỉ gặp vấn đề này và đưa ra một giải pháp khá đơn giản.

String search = "ca";
Firestore.instance.collection("categories").orderBy("name").where("name",isGreaterThanOrEqualTo: search).where("name",isLessThanOrEqualTo: search+"z")

IsGreaterThanOrEqualTo cho phép chúng tôi lọc ra phần đầu của tìm kiếm và bằng cách thêm "z" vào cuối isLessThanOrEqualĐể chúng tôi giới hạn tìm kiếm của mình để không chuyển sang các tài liệu tiếp theo.


3
Tôi đã thử giải pháp này nhưng đối với tôi nó chỉ hoạt động khi nhập chuỗi đầy đủ. Ví dụ: nếu tôi muốn nhận cụm từ "miễn phí", nếu tôi bắt đầu nhập "fr" sẽ không có gì trả lại. Khi tôi nhập "miễn phí" thì thuật ngữ này sẽ cho tôi ảnh chụp nhanh của nó.
Chris

Bạn có đang sử dụng cùng một định dạng mã không? Và thuật ngữ có phải là một chuỗi trong firestore không? Tôi biết rằng bạn không thể lọc theo documentId.
Jacob Bonk

2

Câu trả lời đã chọn chỉ hoạt động cho các tìm kiếm chính xác và không phải là hành vi tìm kiếm tự nhiên của người dùng (tìm kiếm "apple" trong "Joe ate an apple today" sẽ không hoạt động).

Tôi nghĩ câu trả lời trên của Dan Fein nên được xếp hạng cao hơn. Nếu dữ liệu Chuỗi mà bạn đang tìm kiếm ngắn, bạn có thể lưu tất cả các chuỗi con của chuỗi vào một mảng trong Tài liệu của mình và sau đó tìm kiếm trong mảng bằng truy vấn array_contains của Firebase. Tài liệu Firebase được giới hạn ở 1 MiB (1.048.576 byte) (Định mức và giới hạn của Firebase ), khoảng 1 triệu ký tự được lưu trong tài liệu (tôi nghĩ 1 ký tự ~ = 1 byte). Lưu trữ các chuỗi con cũng được miễn là tài liệu của bạn không đạt gần mốc 1 triệu.

Ví dụ để tìm kiếm tên người dùng:

Bước 1: Thêm phần mở rộng Chuỗi sau vào dự án của bạn. Điều này cho phép bạn dễ dàng chia một chuỗi thành các chuỗi con. ( Tôi tìm thấy cái này ở đây ).

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])
}

Bước 2: Khi bạn lưu trữ tên người dùng, hãy lưu trữ kết quả của hàm này dưới dạng một mảng trong cùng một Tài liệu. Điều này tạo ra tất cả các biến thể của văn bản gốc và lưu trữ chúng trong một mảng. Ví dụ: đầu vào văn bản "Apple" sẽ tạo ra mảng sau: ["a", "p", "p", "l", "e", "ap", "pp", "pl", "le "," app "," ppl "," ple "," appl "," pple "," apple "], phải bao gồm tất cả các tiêu chí tìm kiếm mà người dùng có thể nhập. Bạn có thể để MaximumStringSize là con số không nếu bạn muốn có tất cả kết quả, tuy nhiên, nếu có văn bản dài, tôi khuyên bạn nên giới hạn nó trước khi kích thước tài liệu quá lớn - đối với tôi thì khoảng 15 là tốt (hầu hết mọi người không tìm kiếm các cụm từ dài ).

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
}

Bước 3: Bạn có thể sử dụng hàm array_contains của Firebase!

[yourDatabasePath].whereField([savedSubstringArray], arrayContains: searchText).getDocuments....

0

Với Firestore, bạn có thể triển khai tìm kiếm toàn văn nhưng sẽ vẫn tốn nhiều lần đọc hơn so với cách khác và bạn cũng cần nhập và lập chỉ mục dữ liệu theo một cách cụ thể, Vì vậy, trong cách tiếp cận này, bạn có thể sử dụng các chức năng đám mây của firebase để tokenise và sau đó băm văn bản đầu vào của bạn trong khi chọn một hàm băm tuyến tính h(x)thỏa mãn điều kiện sau - nếu x < y < z then h(x) < h (y) < h(z). Đối với mã hóa, bạn có thể chọn một số Thư viện NLP nhẹ để giữ cho thời gian bắt đầu lạnh của chức năng ở mức thấp có thể loại bỏ các từ không cần thiết khỏi câu của bạn. Sau đó, bạn có thể chạy một truy vấn với toán tử nhỏ hơn và lớn hơn trong Firestore. Trong khi lưu trữ dữ liệu của bạn cũng vậy, bạn sẽ phải đảm bảo rằng bạn băm văn bản trước khi lưu trữ nó và lưu trữ văn bản thuần túy cũng giống như nếu bạn thay đổi văn bản thuần túy, giá trị băm cũng sẽ thay đổi.


0

Điều này hoàn toàn phù hợp với tôi nhưng có thể gây ra các vấn đề về hiệu suất.

Làm điều này khi truy vấn firestore:

   Future<QuerySnapshot> searchResults = collectionRef
        .where('property', isGreaterThanOrEqualTo: searchQuery.toUpperCase())
        .getDocuments();

Làm điều này trong FutureBuilder của bạn:

    return FutureBuilder(
          future: searchResults,
          builder: (context, snapshot) {           
            List<Model> searchResults = [];
            snapshot.data.documents.forEach((doc) {
              Model model = Model.fromDocumet(doc);
              if (searchQuery.isNotEmpty &&
                  !model.property.toLowerCase().contains(searchQuery.toLowerCase())) {
                return;
              }

              searchResults.add(model);
            })
   };

0

Tính đến ngày hôm nay, về cơ bản có 3 cách giải quyết khác nhau, được các chuyên gia gợi ý, làm câu trả lời cho câu hỏi.

Tôi đã thử tất cả. Tôi nghĩ có thể hữu ích khi ghi lại kinh nghiệm của tôi với từng người trong số họ.

Phương pháp-A: Sử dụng: (dbField "> =" searchString) & (dbField "<=" searchString + "\ uf8ff")

Đề xuất bởi @Kuba & @Ankit Prajapati

.where("dbField1", ">=", searchString)
.where("dbField1", "<=", searchString + "\uf8ff");

A.1 Các truy vấn Firestore chỉ có thể thực hiện các bộ lọc phạm vi (>, <,> =, <=) trên một trường duy nhất. Các truy vấn có bộ lọc phạm vi trên nhiều trường không được hỗ trợ. Bằng cách sử dụng phương pháp này, bạn không thể có toán tử phạm vi trong bất kỳ trường nào khác trên db, ví dụ trường ngày.

A.2. Phương pháp này KHÔNG hoạt động để tìm kiếm trong nhiều trường cùng một lúc. Ví dụ: bạn không thể kiểm tra xem một chuỗi tìm kiếm có nằm trong bất kỳ tệp nào không (tên, ghi chú và địa chỉ).

Phương pháp-B: Sử dụng MAP các chuỗi tìm kiếm với "true" cho mỗi mục nhập trong bản đồ và sử dụng toán tử "==" trong các truy vấn

Do @Gil Gilbert đề xuất

document1 = {
  'searchKeywordsMap': {
    'Jam': true,
    'Butter': true,
    'Muhamed': true,
    'Green District': true,
    'Muhamed, Green District': true,
  }
}

.where(`searchKeywordsMap.${searchString}`, "==", true);

B.1 Rõ ràng, phương pháp này yêu cầu xử lý thêm mỗi khi dữ liệu được lưu vào db, và quan trọng hơn, cần thêm không gian để lưu trữ bản đồ các chuỗi tìm kiếm.

B.2 Nếu một truy vấn Firestore có một điều kiện giống như điều kiện ở trên, thì không cần tạo chỉ mục trước. Giải pháp này sẽ hoạt động tốt trong trường hợp này.

B.3 Tuy nhiên, nếu truy vấn có một điều kiện khác, ví dụ (trạng thái === "hoạt động",) thì có vẻ như một chỉ mục được yêu cầu cho mỗi "chuỗi tìm kiếm" mà người dùng nhập vào. Nói cách khác, nếu một người dùng tìm kiếm "Mứt" và một người dùng khác tìm kiếm "Bơ", một chỉ mục nên được tạo trước cho chuỗi "Mứt" và một chỉ mục khác cho "Bơ", v.v. Trừ khi bạn có thể dự đoán tất cả. chuỗi tìm kiếm của người dùng, điều này KHÔNG hoạt động - trong trường hợp truy vấn có các điều kiện khác!

.where(searchKeywordsMap["Jam"], "==", true); // requires an index on searchKeywordsMap["Jam"]
.where("status", "==", "active");

** Phương thức-C: Sử dụng phương pháp ARRAY của các chuỗi tìm kiếm và toán tử "mảng chứa"

Do @Albert Renshaw đề xuất và @Nick Carducci thể hiện

document1 = {
  'searchKeywordsArray': [
    'Jam',
    'Butter',
    'Muhamed',
    'Green District',
    'Muhamed, Green District',
  ]
}

.where("searchKeywordsArray", "array-contains", searchString); 

C.1 Tương tự như Phương pháp-B, phương pháp này yêu cầu xử lý thêm mỗi khi dữ liệu được lưu vào db và quan trọng hơn, yêu cầu thêm không gian để lưu trữ mảng chuỗi tìm kiếm.

C.2 Truy vấn Firestore có thể bao gồm nhiều nhất một mệnh đề "mảng-chứa" hoặc "mảng-chứa-bất kỳ" trong một truy vấn ghép.

Hạn chế chung:

  1. Không có giải pháp nào trong số này dường như hỗ trợ tìm kiếm các chuỗi một phần. Ví dụ: nếu trường db chứa "1 Peter St, Green District", bạn không thể tìm kiếm chuỗi "nghiêm ngặt".
  2. Hầu như không thể bao gồm tất cả các kết hợp có thể có của các chuỗi tìm kiếm mong đợi. Ví dụ: nếu trường db chứa "1 Mohamed St, Green District", bạn KHÔNG thể tìm kiếm chuỗi "Green Mohamed", là một chuỗi có các từ theo thứ tự khác với thứ tự được sử dụng trong db cánh đồng.

Không có một giải pháp phù hợp với tất cả. Mỗi cách giải quyết đều có những hạn chế của nó. Tôi hy vọng thông tin trên có thể giúp bạn trong quá trình lựa chọn giữa các cách giải quyết này.

Để biết danh sách các điều kiện truy vấn Firestore, vui lòng xem tài liệu https://firebase.google.com/docs/firestore/query-data/queries .

Tôi chưa thử https://fireblog.io/blog/post/firestore-full-text-search , được gợi ý bởi @Jonathan.


-10

Chúng ta có thể sử dụng dấu tích để in ra giá trị của một chuỗi. Điều này sẽ hoạt động:

where('name', '==', `${searchTerm}`)

Cảm ơn, nhưng câu hỏi này là về việc nhận các giá trị không chính xác. Ví dụ, ví dụ trong câu hỏi có tác dụng tìm tên có chính xác hay không. Nếu tôi có tài liệu với tên: "Thử nghiệm" và sau đó tôi tìm kiếm "Thử nghiệm", nó hoạt động. Nhưng tôi mong đợi có thể tìm kiếm "tes" hoặc "est" và vẫn nhận được kết quả "Test". Hãy tưởng tượng một trường hợp sử dụng với tên sách. Mọi người thường tìm kiếm tên sách từng phần thay vì tiêu đề chính xác hoàn toàn.
tehfailsafe

12
@suulisin bạn nói đúng, tôi đã không đọc kỹ vì tôi muốn chia sẻ những gì tôi đã tìm thấy. Cảm ơn bạn đã nỗ lực của bạn để trỏ mà ra, và tôi sẽ cẩn thận hơn
Zach J
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.