Настоящую любовь нетрудно найти с RedisJSON
В первом выпуске этой серии мы рассмотрели важность JSON, баз данных JSON и RedisJSON, установили Redis Cloud, Redis Stack, and Redis Insight, а также то, как мы можем хранить все типы данных (скалярные, объективные, массив объектов) в RedisJSON. Мы приближались с каждым шагом к нашей цели - найти идеальные пары для вернувшихся на свободу заключенных. В конце концов, каждый может найти настоящую любовь. Давайте сделаем еще один шаг к нашей цели в этой статье.
Время для творчества
Как мы можем подготовить наши измерения данных для наших совпадений с помощью кода? С помощью Golang мы изучили, как беспрепятственно взаимодействовать с нашей базой данных RedisJSON и позволить освободившимся заключенным без труда указать свои интересы.
Все это описано в первом нашем уроке. Изучить его вы можете пройдя по ссылке ниже,
Итак, наше руководство по созданию инструмента знакомств (2 части)::
Творим, создаем, претворяем в жизнь
Мы будем использовать простую структуру каталогов и шаблон расположения кода (насколько это возможно). В более серьезной реализации рекомендуется использовать больше идиоматических архитектурных стилей Golang. Однако мы хотели бы разделить проблемы в самых простых формах.Также мы будем использовать стандарт REST API. Этот код будет построен как монолит, чтобы избежать сложностей, но позже его можно будет масштабировать до гораздо более сложных архитектур. К микроуслугам и лучшим практикам Lords:
Давайте создадим каталог для нашего кода. В UNIX-подобных системах мы можем сделать:
mkdir dating-app && cd dating-app
Было бы здорово начать с настройки и приведения в порядок некоторых наших зависимостей. Запустите это в корневом проекте вашего терминала:
#go mod init {your-repo-name}
#For me I have:
go mod init github.com/femolacaster/dating-app
#Tidy things up
go mod tidy
#Call on your Redis Soldiers
go get github.com/gomodule/redigo/redis
go get github.com/nitishm/go-rejson/v4
#Let us include MUX for our API routing
go get -u github.com/gorilla/mux
Последующим шагом будет создание следующих маршрутов в папке с именем routes в корневом каталоге нашего приложения:
[route-dir]/routes/routes.go
package routes
import (
"github.com/femolacaster/dating-app/controllers"
"github.com/gorilla/mux"
)
func Init() *mux.Router {
route := mux.NewRouter()
route.HandleFunc("/api/v1/criteria", controllers.ShowAll)
route.HandleFunc("/api/v1/criteria", controllers.Add).Methods("POST")
route.HandleFunc("/api/v1/criteria/ {id}/dimension", controllers.ShowDimension)
return route
}
В приведенном выше коде показана простая маршрутизация. Функция Init возвращает 3 экспортированных маршрута, которые позволяют добавить новые критерии для бывших заключенных, которые используют метод POST, отображают все различные критерии знакомства заключенных в приложении и возвращают размерность определенных критериев (случайных (Casual) или серьезных (Serious)), используя метод GET.
Хорошим следующим шагом было бы создание помощников для нашего кода. Помощники - это функции, которые вы постоянно используете в своем коде. Две вспомогательные функции, идентифицированные в этом случае, - «RenderErrorResponse» и «RenderResponse» соответственно. Эти функции помогают отображать вывод данных нашего API в простом формате в зависимости от того, является это ошибкой или нет.
Что мы имеем в:
[route-dir]/helpers/dating.go
package helpers
import (
"encoding/json"
"net/http"
)
type ErrorResponse struct {
Error string `json:"error"`
}
func RenderErrorResponse(w http.ResponseWriter, msg string, status int) {
RenderResponse(w, ErrorResponse{Error: msg}, status)
}
func RenderResponse(w http.ResponseWriter, res interface{}, status int) {
w.Header().Set("Content-Type", "application/json")
content, err := json.Marshal(res)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
return
}
w.WriteHeader(status)
if _, err = w.Write(content); err != nil {
}
}
Мы можем добавить еще одну вспомогательную функцию. Все что он делает - это подключается к нашей локальной базе данных RedisJSON и выводит экземпляр подключения клиента Redigo, который мы можем использовать для нашей логики:
func NewRedisConn() *rejson.Handler {
var addr = flag.String("Server", "localhost:6379", "Redis server address")
rh := rejson.NewReJSONHandler()
flag.Parse()
// Redigo Client
conn, err := redis.Dial("tcp", *addr)
if err != nil {
log.Fatalf("Failed to connect to redis-server @ %s", *addr)
}
defer func() {
_, err = conn.Do("FLUSHALL")
err = conn.Close()
if err != nil {
log.Fatalf("Failed to communicate to redis-server @ %v", err)
}
}()
rh.SetRedigoClient(conn)
return rh
}
Давайте создадим логику для наших маршрутов.
Мы создаем новый файл:
[route-dir]/controllers/dating.go
Этот файл будет иметь три функции, которые определяют нашу логику. Первый позволит добавить новые критерии для освободившихся заключенных, второй будет отображать в приложении все различные критерии знакомства заключенных в заявке, а последний позволит фильтровать критерии (Casual или Serious).
Первое, что нужно сделать в этом разделе, - сохранить различные интересы в структуре, а затем внедрить интересы и другие детали для формирования критериев заключенного, как показано в этой структуре:
type Criteria struct {
ID int `json:"id"`
Name string `json:"name"`
Height float32 `json:"height"` //height in feet and inches
WeightKG int `json:"weight"`
SexualOrientation string `json:"sexualOrientation"`
Age int `json:"age"`
CasualInterest CasualInterest `json:"casualInterest"`
SeriousInterest SeriousInterest `json:"seriousInterest"`
}
type SeriousInterest struct {
Career bool `json:"career"`
Children bool `json:"children "`
Communication bool `json:"communication"`
Humanity bool `json:"humanity"`
Investment bool `json:"investment"`
Marriage bool `json:"marriage"`
Religion bool `json:"religion"`
Politics bool `json:"politics"`
}
type CasualInterest struct {
Entertainment bool `json:"entertainment"`
Gym bool `json:"gym"`
Jewellries bool `json:"jewellries"`
OneNight bool `json:"oneNight"`
Restaurant bool `json:"restaurant"`
Swimming bool `json:"swimming"`
Travel bool `json:"travel"`
Yolo bool `json:"yolo"`
}
Во всех наших логических функциях мы использовали возвращаемый экземпляр rejson Golang в функции helpers. Функция NewRedisConn будет использоваться для связи с нашей базой данных RedisJSON.
rh := helpers.NewRedisConn()
Rejson - это модуль Redis, который реализует ECMA-404, стандарт обмена данными JSON в качестве собственного типа данных и позволяет хранить, обновлять и извлекать значения JSON из ключей Redis, который также поддерживает два популярных клиента Golang: Redigo и go-redis.
Вот в чем разница между Redigo и go-redis, чтобы сделать свой собственный осознанный выбор:
Redigo | go-redis |
Это менее безопасно для типов | Это более безопасно для типов |
Это может быть быстрее и проще в использовании | Это может быть медленнее и может быть не проще в использовании, чем Redigo |
Не используйте его, если планируете масштабировать базу данных до высокодоступного кластера | Идеально подходит для кластеризации |
Выбор остается за вами. В этой статье мы выбрали более простой вариант - Redigo, и вы увидите его использование в функциях controller.
Для нашей первой функции, которая добавляет критерии для заключенного:
func Add(w http.ResponseWriter, r *http.Request) {
var req Criteria
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
helpers.RenderErrorResponse(w, "invalid request", http.StatusBadRequest)
return
}
defer r.Body.Close()
rh := helpers.NewRedisConn()
res, err := rh.JSONSet("criteria", ".", &req)
if err != nil {
log.Fatalf("Failed to JSONSet")
return
}
if res.(string) == "OK" {
fmt.Printf("Success: %s\n", res)
helpers.RenderResponse(w, helpers.ErrorResponse{Error: "Successfully inserted new Criteria to Database"}, http.StatusCreated)
} else {
fmt.Println("Failed to Set: ")
helpers.RenderErrorResponse(w, "invalid request", http.StatusBadRequest)
}
}
Вторая конечная точка, которая показывает все критерии, показана ниже:
func ShowAll(w http.ResponseWriter, r *http.Request) {
rh := helpers.NewRedisConn()
criteriaJSON, err := redis.Bytes(rh.JSONGet("criteria", "."))
if err != nil {
log.Fatalf(("Failed to get JSON"))
return
}
readCriteria := Criteria{}
err = json.Unmarshal(criteriaJSON, &readCriteria)
if err != nil {
fmt.Printf("JSON Unmarshal Failed")
helpers.RenderErrorResponse(w, "invalid request", http.StatusBadRequest)
}
fmt.Printf("Student read from RedisJSON:%#v\n", readCriteria)
helpers.RenderResponse(w, helpers.ErrorResponse{Error: "Successful retrieval of criterias"}, http.StatusOK)
}
Так же вы самостоятельно можете узнать являются ли критерии для заключенного Casual или Serious.
Совет
Получите все критерии от RedisJSON, точно также как показано в функции ShowAll, но на этот раз с использованием ключа, который является идентификатором для получения этих критериев. Затем, поскольку структуры CasualInterest и SeriousInterest имеют логические поля bool, сравните два отдельных значения структуры, чтобы определить какое из них имеет наибольшее значение «1» или «true». Таким образом, вы можете определить заключенного, который склонен искать что-то серьезное или случайное.
В нашем корневом каталоге main.go мы можем создать наш сервер:
package main
import (
"errors"
"fmt"
"log"
"net/http"
"os"
"time"
"github.com/femolacaster/dating-app/routes"
"github.com/ichtrojan/thoth"
"github.com/joho/godotenv"
)
func main() {
logger, thothErr := thoth.Init("log")
if thothErr != nil {
log.Fatal(thothErr)
}
//warning, error, log, trace, metrics
if envLoadErr := godotenv.Load(); envLoadErr != nil {
logger.Log(errors.New("There was a problem loading an environmental file. Please check file is present."))
log.Fatal("Error:::There was a problem loading an environmental file. Please check file is present.")
}
appPort, appPortExist := os.LookupEnv("APPPORT")
if !appPortExist {
logger.Log(errors.New("There was no Port variable for the application in the env file"))
log.Fatal("Error:::There was no Port variable for the application in the env file")
}
address := ":" + appPort
srv := &http.Server{
Handler: routes.Init(),
Addr: address,
ReadTimeout: 1 * time.Second,
ReadHeaderTimeout: 1 * time.Second,
WriteTimeout: 1 * time.Second,
IdleTimeout: 1 * time.Second,
}
log.Println("Starting server", address)
fmt.Println("Go to localhost:" + appPort + " to view application")
log.Fatal(srv.ListenAndServe())
}
Итак, запускаем наш сервер:
В корне вашего проекта запустите код выполнив эту команду:
go run main.go
Мы успешно настроили простой API для освобожденных заключенных, чтобы получить их совпадения.
Это означает, что любая система может подключиться к нему и использовать информацию базы данных в своими средствами, стилем и т.д.
Давайте углубимся в это утверждение. Убедитесь, что у вас включен экземпляр базы данных Redis и запустите RedisInsight, чтобы иметь представление о том, что происходит.
- Рассмотрим простой вариант использования: MR Peter, который когда-то был заключенным, хочет заявить о своем удивительном профиле, демонстрирующем множество качеств, и надеется, что кто-то примет и полюбит его таким, какой он есть. С помощью нашего API MR Peter может выполнять эту задачу с помощью мобильного клиента, IoT устройства, своего браузера и т.д. Возможно, говоря, набирая текст и т.д., что переводится следующим образом:
curl -X POST localhost :9000 /api/v1/criteria
-H "Content-Type: application/json"
-d ' {
"id":DATIN00025,
"name":"Mr Peter Griffin",
"height":6.4,
"weight":120,
"sexualOrientation":"straight",
"age":45,
"casualInterest":{
"entertainment":true,
"gym":false,
"jewellries":false,
"oneNight":false,
"restaurant":true,
"swimming":false,
"travel":false,
"yolo":true
},
"seriousInterest":{
"career":false,
"children ":true,
"communication":false,
"humanity":false,
"investment":false,
"marriage":false,
"religion":false,
"politics":true
}
}
‘
- Другой вариант использования: Mrs. Lois. Mrs. Lois желает связаться с кем-то кто сможет ее понять, кто сможет понять, что такое быть за решеткой, поскольку она тоже была в такой ситуации. Ей нужен этот мужчина, излучающий мужественность и энергию. Вызов нашего API через ее клиент, как показано ниже, творит волшебство, показывая ей всех мужчин, доступных для ее выбора:
curl localhost :9000 /api/v1/criteria
-H "Accept: application/json"
- И еще один вариант: Miss Meg, желающая видеть обе стороны медали на непринужденном уровне. Никаких серьезных обязательств она не хочет. Вероятно, она хочет знать соответствует ли конкретная сладкая партия этой потребности. Она видит профиль Peter Griffin ранее и хочет определить есть ли у него какие-то случайные или серьезные намерения. Miss Meg нажимает кнопку на своем мобильном телефоне, и все, что нужно сделать с ее мобильного телефона - это позвониь в нашу нереализованную конечную точку showDimension для Peter Griffin, чтобы узнать является ли он случайным кандидатом в аналогичном вызове, таким как:
curl localhost :9000 /api/v1/criteria/ DATIN00025/dimension
-H "Accept: application/json"
Итак, в случаях с этими вариантами, Mr.Peter, Mrs. Lois и Miss. Meg мы разобрались. Мы смогли с легкостью найти идеальные пары.
Мы создали полноценный инструмент знакомств для людей, которые вышли из привычного образа жизни в тюрьме, и помогли им вырваться из системы.
Полным руководством вы можете ознакомиться пройдя по ссылке ниже: