Google Firestore: запрос по подстроке значения свойства (текстовый поиск)


107

Я хочу добавить простое поле поиска, хотел бы использовать что-то вроде

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

Я пробовал использовать where('name', '==', '%searchTerm%'), но ничего не вернуло.


1
Firebase теперь поддерживает это. Обновите ответ: stackoverflow.com/a/52715590/2057171
Альберт Реншоу,

1
Я думаю, что лучший способ - создать скрипт, который вручную индексирует каждый документ. Затем запросите эти индексы. проверьте это: angularfirebase.com/lessons/…
Kiblawi_Rabee

Ответы:


39

Там нет такого оператора, разрешенных из них является ==, <, <=, >, >=.

Вы можете фильтровать только по префиксам, например, для всего, что начинается между ними, barи fooвы можете использовать

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

Для этого вы можете использовать внешний сервис, такой как Algolia или ElasticSearch.


5
Это не совсем то, что я ищу. У меня большой список товаров с длинными названиями. "Ракетка теннисная мужская Ребок". Пользователь может искать tennis, но на основе доступных операторов запроса получить эти результаты невозможно. Комбинировать так >=и <=не получается. Конечно, я могу использовать Algolia, но я мог бы просто использовать его с Firebase для выполнения большинства запросов, и мне не нужно было переключаться на Firestore ...
tehfailsafe

4
@tehfailsafe Что ж, ваш вопрос - «как запросить, если поле содержит строку», а ответ - «вы не можете этого сделать».
Куба

20
@ A.Chakroun, что в моем ответе грубо?
Куба

19
tbh это то, что действительно необходимо. Я не могу понять, почему команда Firebase не подумала об этом
Дэни

2
Действительно удивительно, что Firebase настолько слаба в запросах. Не могу поверить, что его используют так много людей, если он не может поддерживать такой простой запрос.
Bagusflyer

46

Я согласен с ответом @Kuba, но все же необходимо добавить небольшое изменение, чтобы он отлично работал для поиска по префиксу. вот что сработало для меня

Для поиска записей, начинающихся с имени queryText

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

\uf8ffИспользуемый в запросе символ - это очень высокий код в диапазоне Unicode (это код области частного использования [PUA]). Поскольку он находится после большинства обычных символов в Юникоде, запрос соответствует всем значениям, начинающимся с queryText.


1
Хороший ответ !, это отлично подходит для поиска префиксного текста. Для поиска слов в тексте, может попытаться «массив содержит» реализацию , как описано в этом посте medium.com/@ken11zer01/...
guillefd

Просто подумал, но теоретически вы можете сопоставить все значения, заканчивающиеся на queryTest , создав другое поле и инвертируя данные ...
Джонатан,

Да, @Jonathan, это тоже возможно.
Анкит Праджапати,

43

Хотя ответ Кубы верен в том, что касается ограничений, вы можете частично эмулировать это с помощью структуры, подобной множеству:

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

Теперь вы можете запросить

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

Это работает, потому что Firestore автоматически создает индекс для каждого поля. К сожалению, это не работает напрямую для составных запросов, потому что Firestore не создает составные индексы автоматически.

Вы все еще можете обойти это, сохраняя комбинации слов, но это быстро становится ужасно.

Вам все же, вероятно, будет лучше с внешним полнотекстовым поиском .


Можно ли использовать облачную функцию с cloud.google.com/appengine/docs/standard/java/search ?
Генри

1
Если вы спрашиваете об этом как о продолжении этого ответа, то: Полнотекстовый поиск AppEngine полностью отделен от Firestore, поэтому напрямую вам это не поможет. Вы можете реплицировать свои данные с помощью облачной функции, но, по сути, это и есть предложение использовать внешний полнотекстовый поиск. Если вы спрашиваете что-то еще, задайте новый вопрос.
Гил Гилберт

1
В Firestore вам необходимо проиндексировать все термины перед использованиемwhere
Хусам,

4
Как упоминал Хусам, все эти поля необходимо проиндексировать. Я хотел включить поиск по любому слову, которое содержится в названии моего продукта. Поэтому я создал в своем документе свойство типа «объект» с ключами, являющимися частями названия продукта, каждому из которых присвоено значение «true», надеясь, что поиск where ('nameSegments.tennis', '==', true) будет работают, но firestore предлагает создать индекс для nameSegments.tennis, как и для всех остальных терминов. Поскольку может быть бесконечное количество терминов, этот ответ можно использовать только для очень ограниченного сценария, когда все условия поиска определены заранее.
Slawoj

2
@epeleg Запрос будет работать после того, как вы создадите для него индекс, но невозможно создать индекс для каждого возможного термина, содержащегося в вашем названии продукта, поэтому для текстового поиска терминов в названиях продуктов этот подход не работал в моем случае.
Slawoj 05

30

Хотя Firebase явно не поддерживает поиск термина в строке,

Firebase (сейчас) поддерживает следующее, которое решит как в вашем, так и во многих других случаях:

По состоянию на август 2018 года они поддерживают array-containsзапросы. Видеть: Https://firebase.googleblog.com/2018/08/better-arrays-in-cloud-firestore.html

Теперь вы можете установить все ключевые термины в массив в виде поля, а затем запросить все документы, в которых есть массив, содержащий «X». Вы можете использовать логическое И для дальнейших сравнений для дополнительных запросов. (Это связано с тем, что firebase в настоящее время не поддерживает составные запросы для нескольких запросов, содержащих массив поэтому запросы сортировки `` И '' должны выполняться на стороне клиента)

Использование массивов в этом стиле позволит оптимизировать их для одновременной записи, что приятно! Не проверял, поддерживает ли он пакетные запросы (документы не говорят), но я бы поспорил, что он поддерживает, так как это официальное решение.


Использование:

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

12
Это хорошее решение. Однако поправьте меня, если я ошибаюсь, но я думаю, что это не позволяет вам делать то, о чем просил @tehfailsafe. Например, если вы хотите получить все имена, содержащие строку «abc», вы не добьетесь успеха с помощью array-contains, поскольку он вернет только те документы, которые имеют точное имя «abc», но «abcD» или "0abc" не будет.
Юлиан

1
@Yulian В мире программирования Search termобычно понимается как целый термин, разделенный пробелами, знаками препинания и т. Д. С обеих сторон. Если вы abcdeсейчас погуглите, вы найдете результаты только для таких вещей, как %20abcde.или, ,abcde!но не abcdefghijk... хотя, безусловно, весь набранный алфавит гораздо чаще можно найти в Интернете, поиск ведется не по abcde *, а по изолированному abcde
Альберт Реншоу

1
Я понимаю вашу точку зрения и согласен с ней, но, вероятно, меня ввело в заблуждение это слово 'contains', которое означает именно то, что я имею в виду во многих языках программирования. То же самое и '%searchTerm%'с точки зрения SQL.
Юлиан

2
@Yulian Да, я понял. Firebase - это NoSQL, поэтому он действительно хорош в выполнении этих типов операций быстрыми и эффективными, даже если они могут быть ограничены из-за некоторых проблем, выходящих за рамки, таких как поиск строк с подстановочными знаками.
Альберт Реншоу

2
Что ж, вы можете создать отдельное поле для каждого из них с представлением слов, разделенных как titleArray: ['this', 'is', 'a', 'title'] каждый раз, когда вы обновляете документ. И тогда поиск будет основан на этом поле, а не на заголовке. Вы можете создать триггер onUpdate для создания этих полей. Много работы для текста на основе поиска, но я предпочитаю улучшения производительности NoSQL.
sfratini 01

15

Согласно документации Firestore , Cloud Firestore не поддерживает собственное индексирование или поиск текстовых полей в документах. Кроме того, загрузка всей коллекции для поиска полей на стороне клиента нецелесообразна.

Рекомендуются сторонние поисковые решения, такие как Algolia и Elastic Search .


46
Я прочитал документы, хотя это не идеально. Недостатком является то, что у Algolia и Firestore разные модели ценообразования ... Я с радостью могу иметь 600 000 документов в Firestore (если я не запрашиваю слишком много документов в день). Когда я отправляю их в Algolia для поиска, я теперь должен платить Algolia 310 долларов в месяц только за то, чтобы иметь возможность выполнять поиск по заголовку в моих документах Firestore.
tehfailsafe

2
проблема в том, что это не бесплатно
Дани

Это правильный ответ на поставленный вопрос, и его следует считать лучшим.
briznad

13

Несколько примечаний здесь:

1.) \uf8ff работает так же, как~

2.) Вы можете использовать предложение where или предложения start end:

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

точно так же, как

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

3.) Нет, это не сработает, если вы перевернете startAt()и endAt()в каждой комбинации, однако вы можете добиться того же результата, создав второе поле поиска, которое перевернуто, и объединив результаты.

Пример: сначала вы должны сохранить инвертированную версию поля при создании поля. Что-то вроде этого:

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

С его помощью вы можете искать последние буквы строкового поля и первые , а не случайные средние буквы или группы букв. Это ближе к желаемому результату. Однако это не особо поможет, когда нам нужны случайные средние буквы или слова. Кроме того, не забудьте сохранить все в нижнем регистре или копию в нижнем регистре для поиска, так что регистр не будет проблемой.

4.) Если у вас есть только несколько слов, метод Кена Тана сделает все, что вы хотите, или, по крайней мере, после того, как вы немного его измените. Однако, имея всего лишь абзац текста, вы в геометрической прогрессии создадите более 1 МБ данных, что больше, чем ограничение на размер документа firestore (я знаю, я это тестировал).

5.) Если бы вы могли комбинировать array-contains (или некоторую форму массивов) с \uf8ffуловкой, вы могли бы получить жизнеспособный поиск, который не достигнет пределов. Я пробовал каждую комбинацию, даже с картами, и ничего не вышло. Кто-нибудь это поймет, разместите здесь.

6.) Если вам нужно уйти от АЛГОЛИИ и ЭЛАСТИЧЕСКОГО ПОИСКА, и я вас ни в чем не виню, вы всегда можете использовать mySQL, postSQL или neo4Js в Google Cloud. Их все 3 легко настроить, и у них есть бесплатные уровни. У вас будет одна облачная функция для сохранения данных onCreate () и другая функция onCall () для поиска данных. Просто ... иш. Почему бы тогда просто не перейти на mySQL? Конечно же, данные в реальном времени! Когда кто-то пишет DGraph с помощью websocks для данных в реальном времени, посчитайте меня!

Algolia и ElasticSearch созданы для работы с базами данных только для поиска, поэтому нет ничего более быстрого ... но вы платите за это. Google, почему вы уводите нас от Google, не следите за MongoDB noSQL и не разрешаете поиск?

ОБНОВЛЕНИЕ - Я СОЗДАЛ РЕШЕНИЕ:

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


Отличный обзор и очень полезный.
RedFilter

Потрясающие! Проголосуйте за хорошо структурированный и информативный ответ.
King Of The Jungle

10

Поздний ответ, но для тех, кто все еще ищет ответ, скажем, у нас есть коллекция пользователей, и в каждом документе коллекции у нас есть поле «имя пользователя», поэтому, если вы хотите найти документ, в котором имя пользователя начинается с «al» мы можем сделать что-то вроде

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

это отличное простое решение, спасибо. Но что, если вы хотите проверить более одного поля. Как «имя» и «описание», соединенные оператором ИЛИ?
Попробуйте

Я не думаю, что вы можете запрашивать на основе двух полей, к сожалению, firebase плохая, когда дело доходит до запросов, вы можете проверить эту надежду, что это поможет stackoverflow.com/questions/26700924/…
MoTahir

1
Подтверждено, @MoTahir. В Firestore нет «ИЛИ».
Rap

это решение не соответствует именам пользователей, начинающимся с "al" ... например, "hello" будет сопоставлено ("hello"> "al")
antoine129

Запрос по ИЛИ - это просто объединение двух результатов поиска. Сортировка этих результатов - другая проблема ...
Джонатан,

7

Я уверен, что Firebase скоро выпустит "string-contains" для захвата любого index [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
})

введите описание изображения здесь

запрос вроде этого

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

Совершенно не рекомендуется. Поскольку документы имеют ограничение в 20 тыс. Строк, вы просто не можете использовать его таким образом, пока не убедитесь, что ваш документ никогда не достигнет таких ограничений
Сандип

На данный момент это лучший вариант, что еще рекомендуется?
Ник Кардуччи

1
@Sandeep Я почти уверен, что размер ограничен 1 МБ и 20 уровнями глубины для каждого документа. Что вы имеете в виду под 20k строками? В настоящее время это лучший обходной путь, если использование Algolia или ElasticSearch не подходит
ppicom

6

Если вы не хотите использовать сторонний сервис, такой как Algolia, облачные функции Firebase - отличная альтернатива. Вы можете создать функцию, которая может получать входной параметр, обрабатывать записи на стороне сервера и затем возвращать те, которые соответствуют вашим критериям.


1
А что с андроидом?
Pratik Butani

Вы предлагаете, чтобы люди перебирали каждую запись в коллекции?
DarkNeuron

На самом деле, нет. Я бы использовал Array.prototype. * - например, .every (), .some (), .map (), .filter () и т. Д. Это делается в Node на сервере в функции Firebase перед возвратом значений в клиент.
Рэп

4
Вам все равно придется читать ВСЕ документы, чтобы искать их, что требует затрат и требует больших затрат времени.
Джонатан

4

Выбранный ответ работает только для точного поиска и не является естественным поведением пользователя при поиске (поиск «яблоко» в «Джо съел яблоко сегодня» не сработает).

Я думаю, что ответ Дэна Фейна выше следует поставить выше. Если данные String, которые вы ищете, короткие, вы можете сохранить все подстроки строки в массиве в своем документе, а затем выполнить поиск по массиву с помощью запроса Firebase array_contains. Документы Firebase ограничены 1 МиБ (1048576 байтов) ( квоты и ограничения Firebase ), что составляет около 1 миллиона символов, сохраненных в документе (я думаю, 1 символ ~ = 1 байт). Хранить подстроки можно, если ваш документ не приближается к отметке в 1 миллион.

Пример поиска по именам пользователей:

Шаг 1. Добавьте в проект следующее расширение String. Это позволяет легко разбить строку на подстроки. ( Я нашел это здесь ).

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

Шаг 2: Когда вы сохраняете имя пользователя, также сохраните результат этой функции в виде массива в том же документе. Это создает все варианты исходного текста и сохраняет их в массиве. Например, при вводе текста «Apple» будет создан следующий массив: [«a», «p», «p», «l», «e», «ap», «pp», «pl», «le "," app "," ppl "," ple "," app "," 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
}

Шаг 3: вы можете использовать функцию array_contains Firebase!

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

3

Я действительно думаю, что лучшее решение для этого в Firestore - это поместить все подстроки в массив и просто выполнить запрос array_contains. Это позволяет вам выполнять сопоставление подстрок. Немного избыточно хранить все подстроки, но если ваши условия поиска короткие, это очень разумно.


2

У меня была эта проблема, и я нашел довольно простое решение.

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

IsGreaterThanOrEqualTo позволяет нам отфильтровать начало нашего поиска и, добавив «z» в конец isLessThanOrEqualTo, мы ограничиваем наш поиск, чтобы не переходить к следующим документам.


3
Я пробовал это решение, но для меня оно работает только при вводе полной строки. Например, если я хочу получить термин «бесплатно», если я начну вводить «fr», ничего не вернется. Как только я набираю «бесплатно», этот термин дает мне снимок.
Крис

Вы используете тот же формат кода? И термин строка в пожарном магазине? Я знаю, что вы не можете фильтровать по documentId.
Джейкоб Бонк

0

С Firestore вы можете реализовать полнотекстовый поиск, но это все равно будет стоить больше операций чтения, чем было бы в противном случае, а также вам нужно будет вводить и индексировать данные определенным образом. Таким образом, в этом подходе вы можете использовать облачные функции firebase для tokenise, а затем хэшировать ваш входной текст, выбирая линейную хеш-функцию, h(x)которая удовлетворяет следующему - если x < y < z then h(x) < h (y) < h(z). Для токенизации вы можете выбрать несколько облегченных библиотек NLP, чтобы сократить время холодного запуска вашей функции, что может удалить ненужные слова из вашего предложения. Затем вы можете запустить запрос с операторами «меньше» и «больше» в Firestore. При сохранении ваших данных вам также необходимо убедиться, что вы хешируете текст перед его сохранением, и сохраните простой текст также, как если бы вы изменили простой текст, хешированное значение также изменится.


0

У меня это сработало отлично, но могло вызвать проблемы с производительностью.

Сделайте это при запросе firestore:

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

Сделайте это в своем FutureBuilder:

    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

На сегодняшний день существует в основном 3 различных обходных пути, которые были предложены экспертами в качестве ответов на вопрос.

Я перепробовал их все. Я подумал, что было бы полезно задокументировать свой опыт работы с каждым из них.

Метод A: Использование: (dbField "> =" searchString) & (dbField "<=" searchString + "\ uf8ff")

Предложено @Kuba и @Ankit Prajapati

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

A.1 Запросы Firestore могут выполнять фильтры диапазона (>, <,> =, <=) только для одного поля. Запросы с фильтрами диапазона для нескольких полей не поддерживаются. Используя этот метод, вы не можете иметь оператор диапазона в любом другом поле базы данных, например, поле даты.

А.2. Этот метод НЕ работает для поиска в нескольких полях одновременно. Например, вы не можете проверить, находится ли строка поиска в каком-либо из полей (имя, примечания и адрес).

Метод-B: использование MAP строк поиска с "true" для каждой записи на карте и использование оператора "==" в запросах.

Предложено @Gil Gilbert

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

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

B.1 Очевидно, этот метод требует дополнительной обработки каждый раз, когда данные сохраняются в базу данных, и, что более важно, требует дополнительного места для хранения карты строк поиска.

B.2 Если в запросе Firestore есть одно условие, подобное приведенному выше, то заранее создавать индекс не требуется. В этом случае это решение подойдет.

B.3 Однако, если запрос имеет другое условие, например (status === «active»,), кажется, что индекс требуется для каждой «строки поиска», которую вводит пользователь. Другими словами, если пользователь ищет «Джем», а другой пользователь ищет «Масло», необходимо заранее создать индекс для строки «Джем», а другой - для «Масло» и т. Д. Если вы не можете предсказать все возможные строки поиска пользователей, это НЕ работает - если в запросе есть другие условия!

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

** Метод-C: использование ARRAY строк поиска и оператора "array-contains".

Предложено @Albert Renshaw и продемонстрировано @Nick Carducci

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

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

C.1 Подобно методу-B, этот метод требует дополнительной обработки каждый раз, когда данные сохраняются в базу данных, и, что более важно, требует дополнительного места для хранения массива строк поиска.

C.2 Запросы Firestore могут включать не более одного предложения "array-contains" или "array-contains-any" в составном запросе.

Общие ограничения:

  1. Ни одно из этих решений, похоже, не поддерживает поиск частичных строк. Например, если поле db содержит «1 Peter St, Green District», вы не можете искать строку «strict».
  2. Практически невозможно охватить все возможные комбинации ожидаемых строк поиска. Например, если поле db содержит «1 Mohamed St, Green District», вы НЕ сможете найти строку «Green Mohamed», которая представляет собой строку, в которой порядок слов отличается от порядка, используемого в db. поле.

Не существует универсального решения. У каждого обходного пути есть свои ограничения. Я надеюсь, что приведенная выше информация поможет вам в процессе выбора между этими обходными путями.

Список условий запроса Firestore см. В документации https://firebase.google.com/docs/firestore/query-data/queries .

Я не пробовал https://fireblog.io/blog/post/firestore-full-text-search , который предлагает @Jonathan.


-10

Мы можем использовать обратную галочку, чтобы распечатать значение строки. Это должно работать:

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

Спасибо, но этот вопрос касается получения неточных значений. Например, в рассматриваемом примере действительно работает поиск, если имя точное. Если у меня есть документ с именем «Тест», а затем я ищу «Тест», он работает. Но я бы ожидал, что смогу выполнить поиск «tes» или «est» и все равно получить результат «Test». Представьте себе вариант использования с названиями книг. Люди часто ищут частичные названия книг, а не полное название.
tehfailsafe

13
@suulisin, вы правы, я не читал внимательно, потому что мне не терпелось поделиться тем, что я нашел. Спасибо за ваше усилие, чтобы указать на это, и я буду более осторожен
Zach J
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.