Last active
October 5, 2022 15:10
-
-
Save schadokar/31cabcd968c0dccd3447df6575a45f9f to your computer and use it in GitHub Desktop.
redisrepo
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package redisrepo | |
import ( | |
"context" | |
"log" | |
"os" | |
"github.com/go-redis/redis/v8" | |
) | |
var redisClient *redis.Client | |
func InitialiseRedis() *redis.Client { | |
conn := redis.NewClient(&redis.Options{ | |
Addr: os.Getenv("REDIS_CONNECTION_STRING"), | |
Password: os.Getenv("REDIS_PASSWORD"), | |
DB: 0, | |
}) | |
// checking if redis is connected | |
pong, err := conn.Ping(context.Background()).Result() | |
if err != nil { | |
log.Fatal("Redis Connection Failed", | |
err) | |
} | |
log.Println("Redis Successfully Connected.", | |
"Ping", pong) | |
redisClient = conn | |
return redisClient | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package redisrepo | |
import ( | |
"encoding/json" | |
"gochatapp/model" | |
"log" | |
"github.com/go-redis/redis/v8" | |
) | |
type Document struct { | |
ID string `json:"_id"` | |
Payload []byte `json:"payload"` | |
Total int64 `json:"total"` | |
} | |
func Deserialise(res interface{}) []Document { | |
switch v := res.(type) { | |
case []interface{}: | |
if len(v) > 1 { | |
total := len(v) - 1 | |
var docs = make([]Document, 0, total/2) | |
for i := 1; i <= total; i = i + 2 { | |
arrOfValues := v[i+1].([]interface{}) | |
value := arrOfValues[len(arrOfValues)-1].(string) | |
// add _id in the response | |
doc := Document{ | |
ID: v[i].(string), | |
Payload: []byte(value), | |
Total: v[0].(int64), | |
} | |
docs = append(docs, doc) | |
} | |
return docs | |
} | |
default: | |
log.Printf("different response type otherthan []interface{}. type: %T", res) | |
return nil | |
} | |
return nil | |
} | |
func DeserialiseChat(docs []Document) []model.Chat { | |
chats := []model.Chat{} | |
for _, doc := range docs { | |
var c model.Chat | |
json.Unmarshal(doc.Payload, &c) | |
c.ID = doc.ID | |
chats = append(chats, c) | |
} | |
return chats | |
} | |
func DeserialiseContactList(contacts []redis.Z) []model.ContactList { | |
contactList := make([]model.ContactList, 0, len(contacts)) | |
// improvement tip: use switch to get type of contact.Member | |
// handle unknown type accordingly | |
for _, contact := range contacts { | |
contactList = append(contactList, model.ContactList{ | |
Username: contact.Member.(string), | |
LastActivity: int64(contact.Score), | |
}) | |
} | |
return contactList | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package redisrepo | |
import ( | |
"fmt" | |
"time" | |
) | |
func userSetKey() string { | |
return "users" | |
} | |
func sessionKey(client string) string { | |
return "session#" + client | |
} | |
func chatKey() string { | |
return fmt.Sprintf("chat#%d", time.Now().UnixMilli()) | |
} | |
func chatIndex() string { | |
return "idx#chats" | |
} | |
func contactListZKey(username string) string { | |
return "contacts:" + username | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package redisrepo | |
import ( | |
"context" | |
"encoding/json" | |
"fmt" | |
"log" | |
"strings" | |
"time" | |
"gochatapp/model" | |
"github.com/go-redis/redis/v8" | |
) | |
func RegisterNewUser(username, password string) error { | |
// redis-cli | |
// SYNTAX: SET key value | |
// SET username password | |
// register new username:password key-value pair | |
err := redisClient.Set(context.Background(), username, password, 0).Err() | |
if err != nil { | |
log.Println("error while adding new user", err) | |
return err | |
} | |
// redis-cli | |
// SYNTAX: SADD key value | |
// SADD users username | |
err = redisClient.SAdd(context.Background(), userSetKey(), username).Err() | |
if err != nil { | |
log.Println("error while adding user in set", err) | |
// redis-cli | |
// SYNTAX: DEL key | |
// DEL username | |
// drop the registered user | |
redisClient.Del(context.Background(), username) | |
return err | |
} | |
return nil | |
} | |
func IsUserExist(username string) bool { | |
// redis-cli | |
// SYNTAX: SISMEMBER key value | |
// SISMEMBER users username | |
return redisClient.SIsMember(context.Background(), userSetKey(), username).Val() | |
} | |
func IsUserAuthentic(username, password string) error { | |
// redis-cli | |
// SYNTAX: GET key | |
// GET username | |
p := redisClient.Get(context.Background(), username).Val() | |
if !strings.EqualFold(p, password) { | |
return fmt.Errorf("invalid username or password") | |
} | |
return nil | |
} | |
// UpdateContactList add contact to username's contact list | |
// if not present or update its timestamp as last contacted | |
func UpdateContactList(username, contact string) error { | |
zs := &redis.Z{Score: float64(time.Now().Unix()), Member: contact} | |
// redis-cli SCORE is always float or int | |
// SYNTAX: ZADD key SCORE MEMBER | |
// ZADD contacts:username 1661360942123 contact | |
err := redisClient.ZAdd(context.Background(), | |
contactListZKey(username), | |
zs, | |
).Err() | |
if err != nil { | |
log.Println("error while updating contact list. username: ", | |
username, "contact:", contact, err) | |
return err | |
} | |
return nil | |
} | |
func CreateChat(c *model.Chat) (string, error) { | |
chatKey := chatKey() | |
fmt.Println("chat key", chatKey) | |
by, _ := json.Marshal(c) | |
// redis-cli | |
// SYNTAX: JSON.SET key $ json_in_string | |
// JSON.SET chat#1661360942123 $ '{"from": "sun", "to":"earth","message":"good morning!"}' | |
res, err := redisClient.Do( | |
context.Background(), | |
"JSON.SET", | |
chatKey, | |
"$", | |
string(by), | |
).Result() | |
if err != nil { | |
log.Println("error while setting chat json", err) | |
return "", err | |
} | |
log.Println("chat successfully set", res) | |
// add contacts to both user's contact list | |
err = UpdateContactList(c.From, c.To) | |
if err != nil { | |
log.Println("error while updating contact list of", c.From) | |
} | |
err = UpdateContactList(c.To, c.From) | |
if err != nil { | |
log.Println("error while updating contact list of", c.To) | |
} | |
return chatKey, nil | |
} | |
func CreateFetchChatBetweenIndex() { | |
res, err := redisClient.Do(context.Background(), | |
"FT.CREATE", | |
chatIndex(), | |
"ON", "JSON", | |
"PREFIX", "1", "chat#", | |
"SCHEMA", "$.from", "AS", "from", "TAG", | |
"$.to", "AS", "to", "TAG", | |
"$.timestamp", "AS", "timestamp", "NUMERIC", "SORTABLE", | |
).Result() | |
fmt.Println(res, err) | |
} | |
func FetchChatBetween(username1, username2, fromTS, toTS string) ([]model.Chat, error) { | |
// redis-cli | |
// SYNTAX: FT.SEARCH index query | |
// FT.SEARCH idx#chats '@from:{user2|user1} @to:{user1|user2} @timestamp:[0 +inf]' | |
query := fmt.Sprintf("@from:{%s|%s} @to:{%s|%s} @timestamp:[%s %s]", | |
username1, username2, username1, username2, fromTS, toTS) | |
res, err := redisClient.Do(context.Background(), | |
"FT.SEARCH", | |
chatIndex(), | |
query, | |
"SORTBY", "timestamp", "DESC", | |
).Result() | |
if err != nil { | |
return nil, err | |
} | |
// deserialise redis data to map | |
data := Deserialise(res) | |
// deserialise data map to chat | |
chats := DeserialiseChat(data) | |
return chats, nil | |
} | |
// FetchContactList of the user. It includes all the messages sent to and received by contact | |
// It will return a sorted list by last activity with a contact | |
func FetchContactList(username string) ([]model.ContactList, error) { | |
zRangeArg := redis.ZRangeArgs{ | |
Key: contactListZKey(username), | |
Start: 0, | |
Stop: -1, | |
Rev: true, | |
} | |
// redis-cli | |
// SYNTAX: ZRANGE key from_index to_index REV WITHSCORES | |
// ZRANGE contacts:username 0 -1 REV WITHSCORES | |
res, err := redisClient.ZRangeArgsWithScores(context.Background(), zRangeArg).Result() | |
if err != nil { | |
log.Println("error while fetching contact list. username: ", | |
username, err) | |
return nil, err | |
} | |
contactList := DeserialiseContactList(res) | |
return contactList, nil | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment