Skip to content

Repository files navigation

Mind

Fast, in-memory indexed collections for Go — filter your data like a database.

GoDocBuild StatusLicenseStars

Mind (Multi INDex list) lets you query in-memory collections by multiple fields using indexes, just like a database — but without one. It is particularly well suited where data is read more often than written.

⚠️ Mind is in an early stage of development and the API may change.

Installation

go get github.com/lima1909/mind

Features

Index Types

IndexBacked byDatatypeSupported operations
MapIndexHashMapcomparable=, !=, In
SortedIndexSkipListordered=, != , >, >=, <, <=, Between, In
RangeIndexuint8 sliceuint8=, != , >, >=, <, <=, Between, In
RangeEncodedIndexRange Encoded sliceuint8>, >=, <, <=, Between
FenwickIndexFenwickuint, int>, >=, <, <=, Between
TrigramIndexTrigramIndexstringLike
PhoneticIndexAmerican SoundexstringSounds
FuzzyIndexFuzzy search (BK-tree for Levenshtein-distance)stringFuzzy, Fuzzy([string], [distance])
StringIndexSkipList Or HashMap (can combined with Trigram, Phonetic, Fuzzy)stringSortedIndex or MapIndex and combined Index

All operations can be combined with AND, OR and NOT.

Like means:

  • '%' or '%%' => all
  • 'ab%' => startsWith (prefix): 'ab'
  • '%ab' => endsWith (suffix): 'ab'
  • '%ab%' => contains: 'ab'
  • 'abc' => equals
  • '%ab%cd%' => contains: 'ab' and 'cd', in this order

Trade-offs

Advantages

  • Zero dependencies
  • Generic — works with any struct type
  • Fast reads via bitmap-accelerated index intersection
  • SQL-like query language (with optimizer)

Disadvantages

  • Higher memory usage: indexes store additional data alongside user data
  • Slower writes: every mutation updates all registered indexes

Examples

List

package main
import (
"fmt""github.com/lima1909/mind""github.com/lima1909/mind/index""github.com/lima1909/mind/query"
)
typeCarstruct {
namestringageuint8tags []string
}
func (c*Car) Name() string { returnc.name }
func (c*Car) Age() uint8 { returnc.age }
func (c*Car) Tags() []string { returnc.tags }
funcmain() {
l:=mind.NewList[Car]()
err:=l.CreateIndex("name", index.NewMapIndex((*Car).Name))
iferr!=nil {
panic(err)
}
err=l.CreateIndex("age", index.NewSortedIndex((*Car).Age))
iferr!=nil {
panic(err)
}
err=l.CreateIndex("tag", index.NewSortedIndexSlice((*Car).Tags))
iferr!=nil {
panic(err)
}
l.Insert(Car{name: "Dacia", age: 2, tags: []string{"blue", "new"}})
l.Insert(Car{name: "Opel", age: 12, tags: []string{"old", "red"}})
l.Insert(Car{name: "Mercedes", age: 5})
l.Insert(Car{name: "Dacia", age: 22, tags: []string{"blue", "old"}})
t:=&query.Tracer{}
values, _:=l.QueryStr(
`(name = "Opel" or name = "Dacia") and age >= 2 and tag = "old"`,
query.WithTracer(t),
).Values()
fmt.Println(values)
// Output:// [{Opel 12 [old red]} {Dacia 22 [blue old]}fmt.Println()
fmt.Println("Trace:")
fmt.Println(t.PrettyString())
// Output:// Trace:// └── name = Opel OR name = Dacia AND age >= 2 AND tag = old [5.695µs] (2 matches)// ├── name = Opel OR name = Dacia AND age >= 2 [4.754µs] (3 matches)// │ ├── name = Opel OR name = Dacia [2.483µs] (3 matches)// │ │ ├── name = Opel [1.289µs] (1 matches)// │ │ └── name = Dacia [131ns] (2 matches)// │ └── age >= 2 [1.924µs] (4 matches)// └── tag = old [733ns] (2 matches)
}

List with ID

package main
import (
"fmt""github.com/lima1909/mind""github.com/lima1909/mind/index""github.com/lima1909/mind/query"
)
typeCarstruct {
iduintnamestringageuint8
}
func (c*Car) ID() uint { returnc.id }
func (c*Car) Name() string { returnc.name }
func (c*Car) Age() uint8 { returnc.age }
funcmain() {
l:=mind.NewIDList((*Car).ID)
// ignore error_=l.CreateIndex("name", index.NewMapIndex((*Car).Name))
_=l.CreateIndex("age", index.NewSortedIndex((*Car).Age))
l.Insert(Car{id: 1, name: "Dacia", age: 2})
l.Insert(Car{id: 2, name: "Opel", age: 12})
l.Insert(Car{id: 3, name: "Mercedes", age: 5})
l.Insert(Car{id: 4, name: "Dacia", age: 22})
// ignore errormercedes, _:=l.Get(3)
fmt.Println(mercedes)
// Output:// {3 Mercedes 5removed, _, _:=l.Remove(4)
fmt.Println("Removed:", removed)
// Output:// truet:=&query.Tracer{}
values, _:=l.Query(
query.Or(query.Eq("name", "Opel"), query.Lt("age", 10)),
query.WithTracer(t),
).Values()
fmt.Println(values)
// Output:// [{1 Dacia 2} {2 Opel 12} {3 Mercedes 5}]fmt.Println()
fmt.Println("Trace:")
fmt.Println(t.PrettyString())
// Output:// Trace:// └── name = Opel OR age < 10 [1.85µs] (3 matches)// ├── name = Opel [620ns] (1 matches)// └── age < 10 [790ns] (2 matches)
}

About

Mind (Multi Index List) finding list items faster by using indexes.

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages