# riot
**Repository Path**: veni0/riot
## Basic Information
- **Project Name**: riot
- **Description**: 开源,分布式,简单高效的搜索引擎
- **Primary Language**: Go
- **License**: Apache-2.0
- **Default Branch**: main
- **Homepage**: https://www.oschina.net/p/riotsearch
- **GVP Project**: No
## Statistics
- **Stars**: 198
- **Forks**: 0
- **Created**: 2017-08-03
- **Last Updated**: 2026-09-16
## Categories & Tags
**Categories**: search-engine
**Tags**: None
## README
#  Riot
[](https://pkg.go.dev/github.com/vcaesar/riot)
[](https://github.com/vcaesar/riot/actions/workflows/tests.yml?query=event%3Apush+branch%3Amain)
[](https://github.com/vcaesar/riot/actions/workflows/lint.yml?query=event%3Apush+branch%3Amain)
English | [简体中文](langs/README.zh.md) | [繁體中文](langs/README.zht.md) | [日本語](langs/README.ja.md) | [한국어](langs/README.ko.md) | [Français](langs/README.fr.md) | [Deutsch](langs/README.de.md) | [Español](langs/README.es.md) | [Русский](langs/README.ru.md) | [Português](langs/README.pt.md)
The fast modern text indexing in go, fork form the [bluge](https://github.com/blugelabs/bluge)
## Features
- Supported field types:
- Text, Numeric, Date, Boolean, IP, Geo Point, Vector
- Supported query types:
- Term, Phrase, Match, Match Phrase, Prefix, Regexp, Wildcard, Fuzzy
- Conjunction, Disjunction, Boolean
- Numeric Range, Date Range, Term Range, IP Range
- Geo Bounding Box, Geo Distance, Geo Polygon, KNN
- BM25 Similarity/Scoring with pluggable interfaces
- Search result match highlighting
- Extendable Aggregations:
- Bucketing
- Terms
- Numeric Range
- Date Range
- Metrics
- Min/Max/Count/Sum
- Avg/Weighted Avg
- Cardinality Estimation ([HyperLogLog++](https://github.com/axiomhq/hyperloglog))
- Quantile Approximation ([T-Digest](https://github.com/caio/go-tdigest))
## Installation
```sh
go get -u github.com/vcaesar/riot
```
## Usage
Runnable versions of all three programs live in [`test/readme_demo`](test/readme_demo).
### Indexing
Save as `write/main.go` and run with `go run ./write`:
```go
package main
import (
"log"
riot "github.com/vcaesar/riot"
)
func main() {
config := riot.DefaultConfig("./riot_index")
writer, err := riot.OpenWriter(config)
if err != nil {
log.Fatalf("error opening writer: %v", err)
}
defer writer.Close()
doc := riot.NewDocument("example").
AddField(riot.NewTextField("name", "riot"))
err = writer.Update(doc.ID(), doc)
if err != nil {
log.Fatalf("error updating document: %v", err)
}
log.Printf("indexed: %s", doc.ID())
}
```
### Querying
Save as `read/main.go` and run with `go run ./read` against the index written above.
If you index and search in the same process, use `writer.Reader()` instead of `riot.OpenReader`
(or `riot.InMemoryOnlyConfig()` for an in-memory index):
```go
package main
import (
"context"
"fmt"
"log"
riot "github.com/vcaesar/riot"
)
func main() {
config := riot.DefaultConfig("./riot_index")
reader, err := riot.OpenReader(config)
if err != nil {
log.Fatalf("error opening reader: %v", err)
}
defer reader.Close()
query := riot.NewMatchQuery("riot").SetField("name")
request := riot.NewTopNSearch(10, query).
WithStandardAggregations()
documentMatchIterator, err := reader.Search(context.Background(), request)
if err != nil {
log.Fatalf("error executing search: %v", err)
}
match, err := documentMatchIterator.Next()
for err == nil && match != nil {
err = match.VisitStoredFields(func(field string, value []byte) bool {
if field == "_id" {
fmt.Printf("match: %s\n", string(value))
}
return true
})
if err != nil {
log.Fatalf("error loading stored fields: %v", err)
}
match, err = documentMatchIterator.Next()
}
if err != nil {
log.Fatalf("error iterator document matches: %v", err)
}
}
```
Output:
```
match: example
```
### Chinese / Japanese with gse
The [`gse`](gse) package wraps riot with a [gse](https://github.com/go-ego/gse) tokenizer for
CJK text, plus query-string search and highlighting. Save as `gse/main.go` and run with
`go run ./gse`:
```go
package main
import (
"fmt"
"log"
"os"
"github.com/vcaesar/riot/gse"
)
func main() {
opt := gse.Option{
Index: "test.riot",
Dicts: "embed, ja", // or "embed, zh"
Opt: "search-hmm",
}
// Option{Lang: "en"} skips gse and uses a riot analysis/lang analyzer
// instead ("en", "cjk", "de", ...; see gse.Langs()).
defer os.RemoveAll(opt.Index)
index, err := gse.New(opt)
if err != nil {
log.Fatalf("error opening gse index: %v", err)
}
defer index.Close()
text := `見解では、謙虚なヴォードヴィリアンのベテランは、運命の犠牲者と悪役の両方の変遷として代償を払っています`
docs := map[string]string{
"1": text,
"3": text + "浮き沈み",
"4": `In view, a humble vaudevillian veteran cast vicariously as both victim and villain vicissitudes of fate.`,
"2": `It's difficult to understand the sum of a person's life.`,
"5": `Riot 是用 Go 语言编写的全文搜索引擎`,
}
for id, doc := range docs {
if err = index.Index(id, doc); err != nil {
log.Fatalf("error indexing %s: %v", id, err)
}
}
for _, query := range []string{"運命の犠牲者", "搜索引擎", "vaudevillian"} {
req := gse.NewQueryString(query, true)
res, err := index.Search(req)
if err != nil {
log.Fatalf("error searching %q: %v", query, err)
}
fmt.Printf("query %q: %d hits in %v\n", query, res.Total, res.Took)
for _, hit := range res.Hits {
fmt.Printf(" %s (%.3f) %v\n", hit.ID, hit.Score, hit.Fragments["text"])
}
}
}
```
Output:
```
query "運命の犠牲者": 2 hits in 14.5µs
1 (1.909) [見解では、謙虚なヴォードヴィリアンのベテランは、運命の犠牲者と悪役の両方の変遷として代償を払っています]
3 (1.846) [見解では、謙虚なヴォードヴィリアンのベテランは、運命の犠牲者と悪役の両方の変遷として代償を払っています浮き沈み]
query "搜索引擎": 1 hits in 19.792µs
5 (1.536) [Riot 是用 Go 语言编写的全文搜索引擎]
query "vaudevillian": 1 hits in 1.958µs
4 (0.657) [In view, humble vaudevillian veteran cast vicariously as both victim and villain vicissitudes of fate.]
```
## License
Apache License Version 2.0