diff --git a/CONTRIBUTORS b/CONTRIBUTORS index 2b16e99..544da55 100644 --- a/CONTRIBUTORS +++ b/CONTRIBUTORS @@ -7,3 +7,4 @@ Dustin Sallings Jason Mooberry Sergey Shepelev Alex Edwards +Minudika Malshan diff --git a/cache.go b/cache.go index db88d2f..d79359f 100644 --- a/cache.go +++ b/cache.go @@ -6,6 +6,7 @@ import ( "io" "os" "runtime" + "strings" "sync" "time" ) @@ -165,6 +166,43 @@ func (c *cache) GetWithExpiration(k string) (interface{}, time.Time, bool) { return item.Object, time.Time{}, true } +// Keys retrieves all non-expired keys in the cache that contain the given substring pattern. +// It returns a slice of keys matching the pattern. +func (c *cache) Keys(pattern string) []interface{} { + c.mu.RLock() + defer c.mu.RUnlock() + + var matchedKeys []interface{} + for k, i := range c.items { + if strings.Contains(k, pattern) { + if i.Expiration <= 0 || time.Now().UnixNano() < i.Expiration { + matchedKeys = append(matchedKeys, k) + } + } + } + + return matchedKeys +} + +// Scan searches for and retrieves all non-expired cached items associated with keys that +// contain the given substring pattern. +// It returns a slice of matching cached items. +func (c *cache) Scan(pattern string) []interface{} { + c.mu.RLock() + defer c.mu.RUnlock() + + var matchedObjects []interface{} + for k, i := range c.items { + if strings.Contains(k, pattern) { + if i.Expiration <= 0 || time.Now().UnixNano() < i.Expiration { + matchedObjects = append(matchedObjects, i.Object) + } + } + } + + return matchedObjects +} + func (c *cache) get(k string) (interface{}, bool) { item, found := c.items[k] if !found { diff --git a/cache_test.go b/cache_test.go index de3e9d6..1a443a4 100644 --- a/cache_test.go +++ b/cache_test.go @@ -1769,3 +1769,50 @@ func TestGetWithExpiration(t *testing.T) { t.Error("expiration for e is in the past") } } + +func TestKeys(t *testing.T) { + tc := New(DefaultExpiration, 0) + + tc.Set("jane_001", "admin", 50*time.Millisecond) // Will expire after 50 milliseconds + tc.Set("john_002", "user", NoExpiration) // Never expires + tc.Set("alice_003", "editor", 10*time.Millisecond) // Expires immediately due to DefaultExpiration = 0 + + keys := tc.Keys("jane") + if len(keys) != 1 || keys[0] != "jane_001" { + t.Error("Expected to find 'jane_001' but got:", keys) + } + + keys = tc.Keys("002") + if len(keys) != 1 || keys[0] != "john_002" { + t.Error("Expected to find 'john_002' but got:", keys) + } + + keys = tc.Keys("003") + if len(keys) != 0 { + t.Error("Expected no keys for 'alice_003' as it should have expired, but got:", keys) + } +} + +func TestScan(t *testing.T) { + tc := New(DefaultExpiration, 0) + + tc.Set("jane_001", "admin", 50*time.Millisecond) // Will expire after 50 milliseconds + tc.Set("john_002", "user", NoExpiration) // Never expires + tc.Set("alice_003", "editor", 10*time.Millisecond) // Expires immediately due to DefaultExpiration = 0 + + roles := tc.Scan("jane") + if len(roles) != 1 || roles[0].(string) != "admin" { + t.Error("Expected to find role 'admin' for 'jane_001' but got:", roles) + } + + roles = tc.Scan("002") + if len(roles) != 1 || roles[0].(string) != "user" { + t.Error("Expected to find role 'user' for 'john_002' but got:", roles) + } + + <-time.After(25 * time.Millisecond) + roles = tc.Scan("003") + if len(roles) != 0 { + t.Error("Expected no roles for 'alice_003' as it should have expired, but got:", roles) + } +}