From 8bdb474972d9c86bbd7a287bd17619957261f52b Mon Sep 17 00:00:00 2001 From: yah01 Date: Wed, 8 Jun 2022 15:29:40 +0800 Subject: [PATCH] Support searching with multiple indexes Signed-off-by: yah01 --- map.go | 11 ++++++ search.go | 95 ++++++++++++++++++++++++++++++++++++++++++++++++++ search_test.go | 7 ++++ utils/utils.go | 23 ++++++++++++ 4 files changed, 136 insertions(+) create mode 100644 search.go create mode 100644 search_test.go create mode 100644 utils/utils.go diff --git a/map.go b/map.go index af1776c..d6b3227 100644 --- a/map.go +++ b/map.go @@ -75,6 +75,17 @@ func (imap *IndexMap[K, V]) GetAllBy(indexName string, key any) []*V { return values.Collect() } +// Return all values the seeked by the key, +// nil if index or key not exists. +func (imap *IndexMap[K, V]) GetAllByMulti(indexName string, key any) []*V { + values := imap.getAllBy(indexName, key) + if values == nil { + return nil + } + + return values.Collect() +} + // Return true if the value with given key exists, // false otherwise. func (imap *IndexMap[K, V]) Contain(key K) bool { diff --git a/search.go b/search.go new file mode 100644 index 0000000..c41b2ca --- /dev/null +++ b/search.go @@ -0,0 +1,95 @@ +package indexmap + +import ( + "github.com/yah01/container" + "github.com/yah01/indexmap/utils" +) + +const ( + lookupTypeNone lookupType = iota + lookupTypeAnd + lookupTypeOr + lookupTypeExclude +) + +type lookupType int + +type indexLookup struct { + lookupType lookupType + indexName string + keys []any +} + +type SearchStream[K comparable, V any] struct { + chain []*indexLookup + + imap *IndexMap[K, V] +} + +func (stream *SearchStream[K, V]) And(indexName string, keys ...any) *SearchStream[K, V] { + stream.chain = append(stream.chain, &indexLookup{ + lookupType: lookupTypeAnd, + indexName: indexName, + keys: keys, + }) + + return stream +} + +func (stream *SearchStream[K, V]) Or(indexName string, keys ...any) *SearchStream[K, V] { + stream.chain = append(stream.chain, &indexLookup{ + lookupType: lookupTypeOr, + indexName: indexName, + keys: keys, + }) + + return stream +} + +func (stream *SearchStream[K, V]) Exclude(indexName string, keys ...any) *SearchStream[K, V] { + stream.chain = append(stream.chain, &indexLookup{ + lookupType: lookupTypeExclude, + indexName: indexName, + keys: keys, + }) + + return stream +} + +func (stream *SearchStream[K, V]) Excute() []*V { + values := make(container.Set[*V]) + + for _, lookup := range stream.chain { + result := stream.executeLookup(lookup) + + switch lookup.lookupType { + case lookupTypeAnd: + utils.IntersectSet(values, result) + + case lookupTypeOr: + utils.UnionSet(values, result) + + case lookupTypeExclude: + utils.ExceptSet(values, result) + } + } + + return values.Collect() +} + +func (stream *SearchStream[K, V]) executeLookup(lookup *indexLookup) container.Set[*V] { + result := make(container.Set[*V]) + for i := range lookup.keys { + utils.UnionSet(result, + stream.imap.getAllBy(lookup.indexName, lookup.keys[i])) + } + + return result +} + +func (imap *IndexMap[K, V]) Search() *SearchStream[K, V] { + return &SearchStream[K, V]{ + chain: make([]*indexLookup, 0), + imap: imap, + } +} diff --git a/search_test.go b/search_test.go new file mode 100644 index 0000000..33f8bf6 --- /dev/null +++ b/search_test.go @@ -0,0 +1,7 @@ +package indexmap + +import "testing" + +func TestSearch(t *testing.T) { + +} \ No newline at end of file diff --git a/utils/utils.go b/utils/utils.go new file mode 100644 index 0000000..5ffbd19 --- /dev/null +++ b/utils/utils.go @@ -0,0 +1,23 @@ +package utils + +import "github.com/yah01/container" + +func UnionSet[T comparable](dst container.Set[T], src container.Set[T]) { + for elem := range src { + dst.Insert(elem) + } +} + +func IntersectSet[T comparable](dst container.Set[T], src container.Set[T]) { + for elem := range dst { + if !src.Contain(elem) { + delete(dst, elem) + } + } +} + +func ExceptSet[T comparable](dst container.Set[T], src container.Set[T]) { + for elem := range src { + delete(dst, elem) + } +}