
Bleve предоставляет индексации любых структур данных Go, он поддерживает различные типы полей: текст, числа, даты и логические значения, а также разнообразие запросов: от простых терминов до фраз и сложных булевых запросов.
Чтобы начать работу с Bleve, нужно установить саму библиотеку в рабочее пространство Go. Процесс установки выполняется с помощью команды go get:
go get -u github.com/blevesearch/bleve
Bleve
Создание индекса начинается с определения маппинга. Маппинг — это описание структуры данных, которое указывает Bleve, как индексировать и хранить различные поля документов:
import "github.com/blevesearch/bleve" func createIndex() { // определение базового маппинга mapping := bleve.NewIndexMapping() // создание индекса с базовым маппингом index, err := bleve.New("example.bleve", mapping) if err != nil { panic(err) } }
После создания индекса можно добавить в него данные. Это делается с помощью метода Index, принимающего идентификатор документа и сам документ:
type BlogPost struct { Title string Content string Tags []string Author string } func indexData(index bleve.Index) { post := BlogPost{ Title: "Bleve: индексация в Go", Content: "Bleve предоставляет разработчикам...", Tags: []string{"поиск", "индексация", "go"}, Author: "Кот", } err := index.Index("post_1", post) if err != nil { log.Fatal(err) } }
Для выполнения поиска в индексе Bleve используется метод Search. Пример поиска документов по тексту:
func searchIndex(index bleve.Index) { query := bleve.NewMatchQuery("индексация") search := bleve.NewSearchRequest(query) searchResults, err := index.Search(search) if err != nil { log.Fatal(err) } log.Printf("Найдено документов: %d", searchResults.Total) }
Можно настроить маппинг для текстового поля с использованием конкретного анализатора для обработки текста на английском языке:
textFieldMapping := bleve.NewTextFieldMapping() // текстовый тип поля textFieldMapping.Analyzer = "en" docMapping := bleve.NewDocumentMapping() docMapping.AddFieldMappingsAt("description", textFieldMapping) mapping := bleve.NewIndexMapping() mapping.AddDocumentMapping("myDoc", docMapping) index, err := bleve.New("path/to/index.bleve", mapping) if err != nil { log.Fatal(err) }
Создаём маппинг для документа с типом «myDoc«, в котором есть текстовое поле «description«, обрабатываемое английским анализатором
В первой строчке здесь юзаем текстовый тип поля. Можно объявить числовое поле с помощью bleve.NewNumericFieldMapping() или к примеру поле даты bleve.NewDateTimeFieldMapping() .
Динамические маппинги позволяют автоматически определять типы полей и применять к ним соответствующие настройки маппинга:
dynamicMapping := bleve.NewDocumentMapping() // настройка динамического маппинга для обработки всех текстовых полей dynamicMapping.DefaultTextFieldMapping = bleve.NewTextFieldMapping() dynamicMapping.DefaultTextFieldMapping.Analyzer = "en" mapping := bleve.NewIndexMapping() mapping.DefaultMapping = dynamicMapping index, err := bleve.New("example.bleve", mapping) if err != nil { panic(err) }
Запросы
TermQuery ищет документы, содержащие точное значение термина в указанном поле. Это самый базовый тип запроса:
query := bleve.NewTermQuery("specific term") query.SetField("fieldName") searchRequest := bleve.NewSearchRequest(query) searchResult, err := index.Search(searchRequest) if err != nil { log.Fatalf("error executing search: %v", err) } fmt.Printf("Found %d documents matching 'specific term'\n", searchResult.Total)
MatchQuery предназначен для поиска документов, содержащих термины, соответствующие запросу, с учётом анализатора поля, звучит намного круче чем termquery:
query := bleve.NewMatchQuery("text to match") query.SetField("content") searchRequest := bleve.NewSearchRequest(query) searchResult, err := index.Search(searchRequest) if err != nil { log.Fatalf("error executing search: %v", err) } fmt.Printf("Found %d documents matching 'text to match'\n", searchResult.Total)
PrefixQuery ищет документы, в которых поля содержат термины, начинающиеся с указанного префикса:
query := bleve.NewPrefixQuery("pre") query.SetField("fieldName") searchRequest := bleve.NewSearchRequest(query) searchResult, err := index.Search(searchRequest) if err != nil { log.Fatalf("error executing search: %v", err) } fmt.Printf("Found %d documents with terms starting with 'pre'\n", searchResult.Total)
Анализаторы в Bleve — это комбинации токенайзеров и фильтров (токенов и символов), предназначенные для обработки текста определенным образом. Bleve предлагает несколько встроенных анализаторов, например, Пример использования встроенного анализатора: Токенайзеры разбивают текст на токены (обычно слова или фразы), которые затем индексируются. В Bleve доступны различные встроенные токенайзеры, к примеру Фильтры токенов применяются к каждому токену отдельно, позволяя модифицировать, добавлять или удалять токены. Например, фильтр Можно создать кастомный анализатор: Также есть символьный фильтр. Он применяется к тексту перед его разбиением на токены. Эти фильтры работают на уровне отдельных символов, позволяя изменять, удалять или добавлять символы в исходный текст. Символьные фильтры могут быть использованы для предварительной обработки текста, например, для удаления специальных символов, приведения текста к нижнему регистру или замены символов. В Bleve напрямую символьные фильтры не выделяются как отдельная категория для настройки анализаторов, в отличие от токенайзеров и фильтров токенов. Но все эт можно реализовать на уровне кастомных анализаторов или в процессе предварительной обработки данных перед индексацией. Предположим, мы хотим создать символьный фильтр, который удаляет все знаки препинания из текста перед токенизацией. Это можно сделать, определив функцию предварительной обработки текста: Bleve позволяет строить высокопроизводительные и функционально богатые приложения. В завершение хочу порекомендовать вам бесплатный вебинар курса Highload Architect про асинхронную обработку данных и ее использование в высоконагруженных проектах.FuzzyQuery поддерживает поиск по схожим терминам, опираясь на заданное query := bleve.NewFuzzyQuery("fuzze term") query.SetFuzziness(2) query.SetField("fieldName") searchRequest := bleve.NewSearchRequest(query) searchResult, err := index.Search(searchRequest) if err != nil { log.Fatalf("error executing search: %v", err) } fmt.Printf("Found %d documents with terms similar to 'fuzze term'\n", searchResult.Total)
BoolQuery позволяет комбинировать другие запросы с использованием логических операторов:query1 := bleve.NewMatchQuery("first term") query2 := bleve.NewMatchQuery("second term") boolQuery := bleve.NewBooleanQuery() boolQuery.AddMust(query1) boolQuery.AddShould(query2) searchRequest := bleve.NewSearchRequest(boolQuery) searchResult, err := index.Search(searchRequest) if err != nil { log.Fatalf("error executing search: %v", err) } fmt.Printf("Found %d documents matching conditions\n", searchResult.Total) PhraseQuery используется для поиска точной последовательности слов в документе:phraseQuery := bleve.NewPhraseQuery([]string{"search", "phrase"}, "fieldName") searchRequest := bleve.NewSearchRequest(phraseQuery) searchResult, err := index.Search(searchRequest) if err != nil { log.Fatalf("error executing search: %v", err) } fmt.Printf("Found %d documents matching the exact phrase\n", searchResult.Total) DateRangeQuery позволяет искать документы с датами в определённом диапазоне. Этот тип запроса оч хорошо подходит для фильтрации событий, записей или любых данных, связанных с временными интервалами:startDate := "2000-01-01" endDate := "2024-12-31" dateRangeQuery := bleve.NewDateRangeQuery(startDate, endDate) dateRangeQuery.SetField("dateField") searchRequest := bleve.NewSearchRequest(dateRangeQuery) searchResult, err := index.Search(searchRequest) if err != nil { log.Fatalf("error executing search: %v", err) } fmt.Printf("Found %d documents within the date range\n", searchResult.Total)GeoDistanceQuery предназначен для поиска документов, находящихся в определённом радиусе от заданной географической точки:location := bleve.NewGeoPoint(37.7749, -122.4194) // Пример для Сан-Франциско geoQuery := bleve.NewGeoDistanceQuery(location.Lat, location.Lon, "100km") geoQuery.SetField("locationField") searchRequest := bleve.NewSearchRequest(geoQuery) searchResult, err := index.Search(searchRequest) if err != nil { log.Fatalf("error executing search: %v", err) } fmt.Printf("Found %d documents within 100km of the location\n", searchResult.Total)WildcardQuery поддерживает поиск с использованием подстановочных знаков, что позволяет находить документы с частичным совпадением:wildcardQuery := bleve.NewWildcardQuery("te*t") wildcardQuery.SetField("fieldName") searchRequest := bleve.NewSearchRequest(wildcardQuery) searchResult, err := index.Search(searchRequest) if err != nil { log.Fatalf("error executing search: %v", err) } fmt.Printf("Found %d documents matching the wildcard pattern\n", searchResult.Total)Анализаторы
en для английского языка, которые настраиваются для выполнения стемминга, удаления стоп-слов и т.дmapping := bleve.NewIndexMapping() textFieldMapping := bleve.NewTextFieldMapping() textFieldMapping.Analyzer = "en" mapping.DefaultMapping.AddFieldMappingsAt("description", textFieldMapping)unicode, который разбивает текст по пробелам и пунктуации:customAnalyzer := bleve.NewAnalyzer() customAnalyzer.SetTokenizer(bleve.NewUnicodeTokenizer())ToLower преобразует все токены к нижнему регистру:customAnalyzer := bleve.NewAnalyzer() customAnalyzer.SetTokenizer(bleve.NewUnicodeTokenizer()) customAnalyzer.AddTokenFilter(bleve.NewLowerCaseTokenFilter())indexMapping := bleve.NewIndexMapping() customAnalyzer := bleve.NewAnalyzerNamed("custom_analyzer") customAnalyzer.SetTokenizer(bleve.NewUnicodeTokenizer()) customAnalyzer.AddTokenFilter(bleve.NewLowerCaseTokenFilter()) customAnalyzer.AddTokenFilter(bleve.NewStopTokenFilter()) indexMapping.AddCustomAnalyzer("custom_analyzer", customAnalyzer) textFieldMapping := bleve.NewTextFieldMapping() textFieldMapping.Analyzer = "custom_analyzer" indexMapping.DefaultMapping.AddFieldMappingsAt("custom_text", textFieldMapping) import ( "github.com/blevesearch/bleve" "strings" "unicode" ) // функция для удаления знаков препинания из строки func removePunctuation(text string) string { return strings.Map(func(r rune) rune { if unicode.IsPunct(r) { return -1 // Удаление символа } return r }, text) } // пример использования в кастомном анализаторе func createCustomAnalyzerWithCharFilter(indexMapping *bleve.IndexMapping) { textMapping := bleve.NewTextFieldMapping() // допустм что customAnalyzer уже определён customAnalyzer := &bleve.CustomAnalyzer{ Tokenizer: bleve.NewUnicodeTokenizer(), TokenFilters: []string{"lower_case", "stop_en"}, CharFilters: []bleve.CharFilter{ // используем функцию для обработки текста здесь removePunctuation, }, } indexMapping.AddCustomAnalyzer("custom_with_char_filter", customAnalyzer) textMapping.Analyzer = "custom_with_char_filter" indexMapping.DefaultMapping.AddFieldMappingsAt("myTextField", textMapping) }
ссылка на оригинал статьи https://habr.com/ru/articles/793526/
Добавить комментарий