├── .github
└── workflows
│ └── golangci-lint.yml
├── .golangci.yaml
├── Dockerfile
├── LICENSE
├── README.md
├── cache.go
├── clean.go
├── cloud
├── api
│ ├── handlers
│ │ ├── clean.go
│ │ ├── delete.go
│ │ ├── exists.go
│ │ ├── fulltext.go
│ │ ├── get.go
│ │ ├── indices.go
│ │ ├── info.go
│ │ ├── init.go
│ │ ├── keys.go
│ │ ├── length.go
│ │ ├── search.go
│ │ ├── set.go
│ │ └── values.go
│ ├── routes.go
│ └── utils
│ │ ├── decode.go
│ │ ├── http.go
│ │ └── params.go
├── app
│ ├── dev
│ │ └── .gitkeep
│ ├── prod
│ │ └── .gitkeep
│ └── server
│ │ ├── app.go
│ │ ├── go.mod
│ │ ├── go.sum
│ │ └── utils
│ │ └── args.go
├── database
│ └── .gitkeep
├── socket
│ ├── errors.go
│ ├── funcs.go
│ ├── handlers
│ │ ├── clean.go
│ │ ├── delete.go
│ │ ├── exists.go
│ │ ├── fulltext.go
│ │ ├── get.go
│ │ ├── indices.go
│ │ ├── info.go
│ │ ├── init.go
│ │ ├── keys.go
│ │ ├── length.go
│ │ ├── search.go
│ │ ├── set.go
│ │ └── values.go
│ ├── router.go
│ ├── socket.go
│ └── utils
│ │ ├── decode.go
│ │ ├── errors.go
│ │ ├── http.go
│ │ └── params.go
└── wrappers
│ ├── go
│ └── main.go
│ ├── npm
│ └── main.ts
│ ├── python
│ ├── LICENSE
│ ├── README.md
│ ├── dist
│ │ ├── hermescloud-0.0.6-py3-none-any.whl
│ │ └── hermescloud-0.0.6.tar.gz
│ ├── pyproject.toml
│ ├── setup.cfg
│ └── src
│ │ ├── hermescloud.egg-info
│ │ ├── PKG-INFO
│ │ ├── SOURCES.txt
│ │ ├── dependency_links.txt
│ │ └── top_level.txt
│ │ ├── hermescloud
│ │ ├── __init__.py
│ │ ├── __pycache__
│ │ │ ├── __init__.cpython-310.pyc
│ │ │ ├── cache.cpython-310.pyc
│ │ │ ├── hermes.cpython-310.pyc
│ │ │ └── utils.cpython-310.pyc
│ │ ├── cache.py
│ │ └── utils.py
│ │ ├── test_base.py
│ │ └── test_withjson.py
│ └── rust
│ ├── Cargo.lock
│ ├── Cargo.toml
│ ├── notes.txt
│ ├── src
│ └── main.rs
│ └── target
│ ├── .rustc_info.json
│ ├── CACHEDIR.TAG
│ └── debug
│ ├── .cargo-lock
│ ├── .fingerprint
│ ├── hermes-515e19fc54440f23
│ │ ├── dep-test-bin-hermes
│ │ ├── invoked.timestamp
│ │ ├── test-bin-hermes
│ │ └── test-bin-hermes.json
│ └── hermes-d80b4823bd3ba10f
│ │ ├── bin-hermes
│ │ ├── bin-hermes.json
│ │ ├── dep-bin-hermes
│ │ └── invoked.timestamp
│ ├── deps
│ ├── hermes-515e19fc54440f23.d
│ ├── hermes-d80b4823bd3ba10f.d
│ ├── libhermes-515e19fc54440f23.rmeta
│ └── libhermes-d80b4823bd3ba10f.rmeta
│ └── incremental
│ ├── hermes-1b6yi2bw8b899
│ ├── s-gkiytxaoyx-n4pevb-2nsnbq1tpmfaw
│ │ ├── dep-graph.bin
│ │ ├── query-cache.bin
│ │ └── work-products.bin
│ └── s-gkiytxaoyx-n4pevb.lock
│ └── hermes-1msokizf0w59t
│ ├── s-gkiytxaoyx-61kh8a-385m7v9kh6g64
│ ├── dep-graph.bin
│ ├── query-cache.bin
│ └── work-products.bin
│ └── s-gkiytxaoyx-61kh8a.lock
├── compression
├── gzip
│ └── gzip.go
└── zlib
│ └── zlib.go
├── delete.go
├── docker-compose.yml
├── examples
├── basic
│ └── basic.go
└── router
│ └── router.go
├── exists.go
├── fulltext.go
├── get.go
├── go.mod
├── go.sum
├── go.work
├── go.work.sum
├── indices.go
├── info.go
├── init.go
├── keys.go
├── length.go
├── nocache
├── examples
│ ├── basic
│ │ └── basic.go
│ └── router
│ │ └── router.go
├── fulltext.go
├── init.go
├── insert.go
├── search.go
├── searchoneword.go
├── searchparams.go
├── searchvalues.go
├── searchwithkey.go
└── withft.go
├── search.go
├── searchoneword.go
├── searchparams.go
├── searchvalues.go
├── searchwithkey.go
├── set.go
├── tempstorage.go
├── testing
├── api
│ ├── request.py
│ └── router.go
├── cache
│ └── cache.go
├── compression
│ └── compression.go
├── data
│ ├── data_array.json
│ ├── data_hash.json
│ └── data_stats.py
├── fulltext
│ ├── clean.go
│ ├── delete.go
│ ├── ft.go
│ ├── insert_key_merge.go
│ ├── main.go
│ ├── search.go
│ └── set.go
├── nocache
│ └── speed
│ │ └── speed.go
├── socket
│ └── router.go
└── speed
│ ├── func_speeds
│ └── fnspeeds.go
│ └── search_speeds
│ └── searchspeeds.go
├── utils
├── buffer.go
├── json.go
├── slices.go
└── strings.go
├── values.go
├── website
├── .editorconfig
├── .gitignore
├── .vscode
│ ├── extensions.json
│ ├── launch.json
│ └── tasks.json
├── README.md
├── angular.json
├── package-lock.json
├── package.json
├── src
│ ├── app
│ │ ├── app-routing.module.ts
│ │ ├── app.component.css
│ │ ├── app.component.html
│ │ ├── app.component.spec.ts
│ │ ├── app.component.ts
│ │ ├── app.module.ts
│ │ ├── code-example
│ │ │ ├── code-example.component.css
│ │ │ ├── code-example.component.html
│ │ │ ├── code-example.component.spec.ts
│ │ │ └── code-example.component.ts
│ │ ├── laptop
│ │ │ ├── laptop.component.html
│ │ │ ├── laptop.component.scss
│ │ │ ├── laptop.component.spec.ts
│ │ │ └── laptop.component.ts
│ │ ├── navbar-button
│ │ │ ├── navbar-button.component.css
│ │ │ ├── navbar-button.component.html
│ │ │ ├── navbar-button.component.spec.ts
│ │ │ └── navbar-button.component.ts
│ │ ├── navbar
│ │ │ ├── navbar.component.css
│ │ │ ├── navbar.component.html
│ │ │ ├── navbar.component.spec.ts
│ │ │ └── navbar.component.ts
│ │ └── terminal
│ │ │ ├── terminal.component.css
│ │ │ ├── terminal.component.html
│ │ │ ├── terminal.component.spec.ts
│ │ │ └── terminal.component.ts
│ ├── assets
│ │ ├── .gitkeep
│ │ └── images
│ │ │ ├── github.png
│ │ │ └── logo.png
│ ├── favicon.ico
│ ├── index.html
│ ├── main.ts
│ └── styles.css
├── tailwind.config.js
├── tsconfig.app.json
├── tsconfig.json
└── tsconfig.spec.json
└── withft.go
/.github/workflows/golangci-lint.yml:
--------------------------------------------------------------------------------
1 | name: golangci-lint
2 | on:
3 | push:
4 | tags:
5 | - v*
6 | branches:
7 | - master
8 | pull_request:
9 | permissions:
10 | contents: read
11 | jobs:
12 | golangci:
13 | name: lint
14 | runs-on: ubuntu-latest
15 | steps:
16 | - uses: actions/checkout@v3
17 | - uses: actions/setup-go@v4
18 | with:
19 | go-version: '1.20'
20 | cache: false
21 | - name: golangci-lint
22 | uses: golangci/golangci-lint-action@v3
23 | with:
24 | version: latest
25 |
--------------------------------------------------------------------------------
/.golangci.yaml:
--------------------------------------------------------------------------------
1 | linters:
2 | enable:
3 | - gosec
4 | - govet
5 | presets:
6 | - bugs
7 | - performance
8 | run:
9 | go: '1.20'
10 | issues:
11 | exclude-rules:
12 | - path: testing
13 | linters:
14 | - errcheck
15 |
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | # Use the offical golang image to create a binary.
2 | # This is based on Debian and sets the GOPATH to /go.
3 | # https://hub.docker.com/_/golang
4 | FROM golang:1.20-buster as builder
5 |
6 | # Create and change to the app directory.
7 | WORKDIR /app
8 |
9 | # Retrieve application dependencies.
10 | # This allows the container build to reuse cached dependencies.
11 | # Expecting to copy go.mod and if present go.sum.
12 | COPY /cloud/app/server/go.* ./
13 | RUN go mod download
14 |
15 | # Copy local code to the container image.
16 | COPY . ./
17 |
18 | # Build the binary.
19 | RUN go build -v -o server
20 |
21 | # Use the official Debian slim image for a lean production container.
22 | # https://hub.docker.com/_/debian
23 | # https://docs.docker.com/develop/develop-images/multistage-build/#use-multi-stage-builds
24 | FROM debian:buster-slim
25 | RUN set -x && apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y \
26 | ca-certificates && \
27 | rm -rf /var/lib/apt/lists/*
28 |
29 | # Copy the binary to the production image from the builder stage.
30 | COPY --from=builder /app/server /app/server
31 |
32 | # Run the web service on container startup.
33 | CMD ["/app/server serve -p 3000"]
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2023 Tristan
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/cache.go:
--------------------------------------------------------------------------------
1 | package hermes
2 |
3 | import (
4 | "sync"
5 | )
6 |
7 | // Cache is a struct that represents an in-memory cache of key-value pairs.
8 | // The cache can be used to store arbitrary data, and supports concurrent access through a mutex.
9 | // Additionally, the cache can be configured to support full-text search using a FullText index.
10 | // Fields:
11 | // - data (map[string]map[string]any): A map that stores the data in the cache. The keys of the map are strings that represent the cache keys, and the values are sub-maps that store the actual data under string keys.
12 | // - mutex (*sync.RWMutex): A RWMutex that guards access to the cache data.
13 | // - ft (*FullText): A FullText index that can be used for full-text search. If nil, full-text search is disabled.
14 | type Cache struct {
15 | data map[string]map[string]any
16 | mutex *sync.RWMutex
17 | ft *FullText
18 | }
19 |
--------------------------------------------------------------------------------
/clean.go:
--------------------------------------------------------------------------------
1 | package hermes
2 |
3 | import "errors"
4 |
5 | // Clean is a method of the Cache struct that clears the cache contents.
6 | // If the full-text index is initialized, it is also cleared.
7 | // This method is thread-safe.
8 | //
9 | // Parameters:
10 | // - None
11 | //
12 | // Returns:
13 | // - None
14 | func (c *Cache) Clean() {
15 | c.mutex.Lock()
16 | defer c.mutex.Unlock()
17 | c.clean()
18 | }
19 |
20 | // clean is a method of the Cache struct that clears the cache contents.
21 | // If the full-text index is initialized, it is also cleared.
22 | // This method is not thread-safe and should only be called from an exported function.
23 | //
24 | // Parameters:
25 | // - None
26 | //
27 | // Returns:
28 | // - None
29 | func (c *Cache) clean() {
30 | if c.ft != nil {
31 | c.ft.clean()
32 | }
33 | c.data = map[string]map[string]any{}
34 | }
35 |
36 | // FTClean is a method of the Cache struct that clears the full-text cache contents.
37 | // If the full-text index is not initialized, this method returns an error.
38 | // Otherwise, the full-text index storage and indices are cleared, and this method returns nil.
39 | // This method is thread-safe.
40 | //
41 | // Returns:
42 | // - error: An error object. If no error occurs, this will be nil.
43 | func (c *Cache) FTClean() error {
44 | c.mutex.Lock()
45 | defer c.mutex.Unlock()
46 |
47 | // Verify that the full text is initialized
48 | if c.ft == nil {
49 | return errors.New("full text is not initialized")
50 | }
51 |
52 | // Clean the ft cache
53 | c.ft.clean()
54 |
55 | // Return no error
56 | return nil
57 | }
58 |
59 | // clean is a method of the FullText struct that clears the full-text index storage and indices.
60 | // This method initializes a new empty storage map and indices map with the maximum length specified in the FullText struct.
61 | //
62 | // Parameters:
63 | // - None
64 | //
65 | // Returns:
66 | // - None
67 | func (ft *FullText) clean() {
68 | ft.storage = make(map[string]any)
69 | ft.indices = make(map[int]string)
70 | }
71 |
--------------------------------------------------------------------------------
/cloud/api/handlers/clean.go:
--------------------------------------------------------------------------------
1 | package handlers
2 |
3 | import (
4 | "github.com/gofiber/fiber/v2"
5 | hermes "github.com/realTristan/hermes"
6 | utils "github.com/realTristan/hermes/cloud/api/utils"
7 | )
8 |
9 | // Clean is a function that returns a fiber context handler function for cleaning the regular cache.
10 | // Parameters:
11 | // - c (*hermes.Cache): A pointer to a hermes.Cache struct.
12 | //
13 | // Returns:
14 | // - func(ctx *fiber.Ctx) error: A fiber context handler function that cleans the regular cache and returns a success message.
15 | func Clean(c *hermes.Cache) func(ctx *fiber.Ctx) error {
16 | return func(ctx *fiber.Ctx) error {
17 | c.Clean()
18 | return ctx.Send(utils.Success("null"))
19 | }
20 | }
21 |
22 | // FTClean is a function that returns a fiber context handler function for cleaning the full-text cache.
23 | // Parameters:
24 | // - c (*hermes.Cache): A pointer to a hermes.Cache struct.
25 | //
26 | // Returns:
27 | // - func(ctx *fiber.Ctx) error: A fiber context handler function that cleans the full-text cache and returns a success message or an error message if the cleaning fails.
28 | func FTClean(c *hermes.Cache) func(ctx *fiber.Ctx) error {
29 | return func(ctx *fiber.Ctx) error {
30 | if err := c.FTClean(); err != nil {
31 | return ctx.Send(utils.Error(err))
32 | }
33 | return ctx.Send(utils.Success("null"))
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/cloud/api/handlers/delete.go:
--------------------------------------------------------------------------------
1 | package handlers
2 |
3 | import (
4 | "github.com/gofiber/fiber/v2"
5 | hermes "github.com/realTristan/hermes"
6 | utils "github.com/realTristan/hermes/cloud/api/utils"
7 | )
8 |
9 | // Delete is a handler function that returns a fiber context handler function for deleting a key from the cache.
10 | // Parameters:
11 | // - c (*hermes.Cache): A pointer to a hermes.Cache struct.
12 | //
13 | // Returns:
14 | // - func(ctx *fiber.Ctx) error: A fiber context handler function that deletes a key from the cache and returns a success message or an error message if the key is not provided.
15 | func Delete(c *hermes.Cache) func(ctx *fiber.Ctx) error {
16 | return func(ctx *fiber.Ctx) error {
17 | // Get the key from the query
18 | var key string
19 | if key = ctx.Query("key"); len(key) == 0 {
20 | return ctx.Send(utils.Error("key not provided"))
21 | }
22 |
23 | // Delete the key from the cache
24 | c.Delete(key)
25 | return ctx.Send(utils.Success("null"))
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/cloud/api/handlers/exists.go:
--------------------------------------------------------------------------------
1 | package handlers
2 |
3 | import (
4 | "github.com/gofiber/fiber/v2"
5 | hermes "github.com/realTristan/hermes"
6 | utils "github.com/realTristan/hermes/cloud/api/utils"
7 | )
8 |
9 | // Exists is a handler function that returns a fiber context handler function for checking if a key exists in the cache.
10 | // Parameters:
11 | // - c (*hermes.Cache): A pointer to a hermes.Cache struct.
12 | //
13 | // Returns:
14 | // - func(ctx *fiber.Ctx) error: A fiber context handler function that checks if a key exists in the cache and returns a success message with a boolean value indicating whether the key exists or an error message if the key is not provided.
15 | func Exists(c *hermes.Cache) func(ctx *fiber.Ctx) error {
16 | return func(ctx *fiber.Ctx) error {
17 | // Get the key from the query
18 | var key string
19 | if key = ctx.Query("key"); len(key) == 0 {
20 | return ctx.Send(utils.Error("key not provided"))
21 | }
22 |
23 | // Return whether the key exists
24 | return ctx.Send(utils.Success(c.Exists(key)))
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/cloud/api/handlers/fulltext.go:
--------------------------------------------------------------------------------
1 | package handlers
2 |
3 | import (
4 | "encoding/json"
5 |
6 | "github.com/gofiber/fiber/v2"
7 | hermes "github.com/realTristan/hermes"
8 | utils "github.com/realTristan/hermes/cloud/api/utils"
9 | )
10 |
11 | // FTIsInitialized is a handler function that returns a fiber context handler function for checking if the full-text search is initialized.
12 | // Parameters:
13 | // - c (*hermes.Cache): A pointer to a hermes.Cache struct.
14 | //
15 | // Returns:
16 | // - func(ctx *fiber.Ctx) error: A fiber context handler function that checks if the full-text search is initialized and returns a success message with a boolean value indicating whether it is initialized.
17 | func FTIsInitialized(c *hermes.Cache) func(ctx *fiber.Ctx) error {
18 | return func(ctx *fiber.Ctx) error {
19 | return ctx.Send(utils.Success(c.FTIsInitialized()))
20 | }
21 | }
22 |
23 | // FTSetMaxBytes is a handler function that returns a fiber context handler function for setting the maximum number of bytes for full-text search.
24 | // Parameters:
25 | // - c (*hermes.Cache): A pointer to a hermes.Cache struct.
26 | //
27 | // Returns:
28 | // - func(ctx *fiber.Ctx) error: A fiber context handler function that sets the maximum number of bytes for full-text search and returns a success message or an error message if the value is not provided or if the setting fails.
29 | func FTSetMaxBytes(c *hermes.Cache) func(ctx *fiber.Ctx) error {
30 | return func(ctx *fiber.Ctx) error {
31 | // Get the value from the query
32 | var value int
33 | if err := utils.GetMaxBytesParam(ctx, &value); err != nil {
34 | return ctx.Send(utils.Error(err))
35 | }
36 |
37 | // Set the max bytes
38 | if err := c.FTSetMaxBytes(value); err != nil {
39 | return ctx.Send(utils.Error(err))
40 | }
41 | return ctx.Send(utils.Success("null"))
42 | }
43 | }
44 |
45 | // FTSetMaxSize is a handler function that returns a fiber context handler function for setting the maximum length for full-text search.
46 | // Parameters:
47 | // - c (*hermes.Cache): A pointer to a hermes.Cache struct.
48 | //
49 | // Returns:
50 | // - func(ctx *fiber.Ctx) error: A fiber context handler function that sets the maximum length for full-text search and returns a success message or an error message if the value is not provided or if the setting fails.
51 | func FTSetMaxSize(c *hermes.Cache) func(ctx *fiber.Ctx) error {
52 | return func(ctx *fiber.Ctx) error {
53 | // Get the value from the query
54 | var value int
55 | if err := utils.GetMaxSizeParam(ctx, &value); err != nil {
56 | return ctx.Send(utils.Error(err))
57 | }
58 |
59 | // Set the max length
60 | if err := c.FTSetMaxSize(value); err != nil {
61 | return ctx.Send(utils.Error(err))
62 | }
63 | return ctx.Send(utils.Success("null"))
64 | }
65 | }
66 |
67 | // FTStorage is a handler function that returns a fiber context handler function for getting the full-text storage.
68 | // Parameters:
69 | // - c (*hermes.Cache): A pointer to a hermes.Cache struct.
70 | //
71 | // Returns:
72 | // - func(ctx *fiber.Ctx) error: A fiber context handler function that gets the full-text storage and returns a JSON-encoded string of the data or an error message if the retrieval or encoding fails.
73 | func FTStorage(c *hermes.Cache) func(ctx *fiber.Ctx) error {
74 | return func(ctx *fiber.Ctx) error {
75 | if data, err := c.FTStorage(); err != nil {
76 | return ctx.Send(utils.Error(err))
77 | } else if data, err := json.Marshal(data); err != nil {
78 | return ctx.Send(utils.Error(err))
79 | } else {
80 | return ctx.Send(data)
81 | }
82 | }
83 | }
84 |
85 | // FTStorageLength is a handler function that returns a fiber context handler function for getting the length of the full-text storage.
86 | // Parameters:
87 | // - c (*hermes.Cache): A pointer to a hermes.Cache struct.
88 | //
89 | // Returns:
90 | // - func(ctx *fiber.Ctx) error: A fiber context handler function that gets the length of the full-text storage and returns a success message with the length or an error message if the retrieval fails.
91 | func FTStorageLength(c *hermes.Cache) func(ctx *fiber.Ctx) error {
92 | return func(ctx *fiber.Ctx) error {
93 | if length, err := c.FTStorageLength(); err != nil {
94 | return ctx.Send(utils.Error(err))
95 | } else {
96 | return ctx.Send(utils.Success(length))
97 | }
98 | }
99 | }
100 |
101 | // FTStorageSize is a handler function that returns a fiber context handler function for getting the size of the full-text storage.
102 | // Parameters:
103 | // - c (*hermes.Cache): A pointer to a hermes.Cache struct.
104 | //
105 | // Returns:
106 | // - func(ctx *fiber.Ctx) error: A fiber context handler function that gets the size of the full-text storage and returns a success message with the size or an error message if the retrieval fails.
107 | func FTStorageSize(c *hermes.Cache) func(ctx *fiber.Ctx) error {
108 | return func(ctx *fiber.Ctx) error {
109 | if size, err := c.FTStorageSize(); err != nil {
110 | return ctx.Send(utils.Error(err))
111 | } else {
112 | return ctx.Send(utils.Success(size))
113 | }
114 | }
115 | }
116 |
117 | // FTSetMinWordLength is a handler function that returns a fiber context handler function for setting the minimum word length for full-text search.
118 | // Parameters:
119 | // - c (*hermes.Cache): A pointer to a hermes.Cache struct.
120 | //
121 | // Returns:
122 | // - func(ctx *fiber.Ctx) error: A fiber context handler function that sets the minimum word length for full-text search and returns a success message or an error message if the value is not provided or if the setting fails.
123 | func FTSetMinWordLength(c *hermes.Cache) func(ctx *fiber.Ctx) error {
124 | return func(ctx *fiber.Ctx) error {
125 | // Get the min word length from the query
126 | var minWordLength int
127 | if err := utils.GetMinWordLengthParam(ctx, &minWordLength); err != nil {
128 | return ctx.Send(utils.Error(err))
129 | }
130 |
131 | // Update the min word length
132 | if err := c.FTSetMinWordLength(minWordLength); err != nil {
133 | return ctx.Send(utils.Error(err))
134 | }
135 |
136 | // Return null
137 | return ctx.Send(utils.Success("null"))
138 | }
139 | }
140 |
--------------------------------------------------------------------------------
/cloud/api/handlers/get.go:
--------------------------------------------------------------------------------
1 | package handlers
2 |
3 | import (
4 | "encoding/json"
5 |
6 | "github.com/gofiber/fiber/v2"
7 | hermes "github.com/realTristan/hermes"
8 | utils "github.com/realTristan/hermes/cloud/api/utils"
9 | )
10 |
11 | // Get is a handler function that returns a fiber context handler function for getting a value from the cache.
12 | // Parameters:
13 | // - c (*hermes.Cache): A pointer to a hermes.Cache struct.
14 | //
15 | // Returns:
16 | // - func(ctx *fiber.Ctx) error: A fiber context handler function that gets a value from the cache using a key provided in the query string and returns a JSON-encoded string of the value or an error message if the key is not provided or if the retrieval or encoding fails.
17 | func Get(c *hermes.Cache) func(ctx *fiber.Ctx) error {
18 | return func(ctx *fiber.Ctx) error {
19 | // Get the key from the query
20 | var key string
21 | if key = ctx.Query("key"); len(key) == 0 {
22 | return ctx.Send(utils.Error("key not provided"))
23 | }
24 |
25 | // Get the value from the cache
26 | if data, err := json.Marshal(c.Get(key)); err != nil {
27 | return ctx.Send(utils.Error(err))
28 | } else {
29 | return ctx.Send(data)
30 | }
31 | }
32 | }
33 |
34 | // GetAll is a handler function that returns a fiber context handler function for getting all the data from the cache.
35 | // Parameters:
36 | // - c (*hermes.Cache): A pointer to a hermes.Cache struct.
37 | //
38 | // Returns:
39 | // - func(ctx *fiber.Ctx) error: A fiber context handler function that gets all the data from the cache and returns a success message with a JSON-encoded string of the data or an error message if the retrieval or encoding fails.
40 | func GetAll(c *hermes.Cache) func(ctx *fiber.Ctx) error {
41 | return func(ctx *fiber.Ctx) error {
42 | return nil
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/cloud/api/handlers/indices.go:
--------------------------------------------------------------------------------
1 | package handlers
2 |
3 | import (
4 | "github.com/gofiber/fiber/v2"
5 | hermes "github.com/realTristan/hermes"
6 | utils "github.com/realTristan/hermes/cloud/api/utils"
7 | )
8 |
9 | // FTSequenceIndices is a handler function that returns a fiber context handler function for sequencing the full-text storage indices.
10 | // Parameters:
11 | // - c (*hermes.Cache): A pointer to a hermes.Cache struct.
12 | //
13 | // Returns:
14 | // - func(ctx *fiber.Ctx) error: A fiber context handler function that sequences the full-text storage indices and returns a success message or an error message if the sequencing fails.
15 | func FTSequenceIndices(c *hermes.Cache) func(ctx *fiber.Ctx) error {
16 | return func(ctx *fiber.Ctx) error {
17 | c.FTSequenceIndices()
18 | return ctx.Send(utils.Success("null"))
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/cloud/api/handlers/info.go:
--------------------------------------------------------------------------------
1 | package handlers
2 |
3 | import (
4 | "github.com/gofiber/fiber/v2"
5 | hermes "github.com/realTristan/hermes"
6 | utils "github.com/realTristan/hermes/cloud/api/utils"
7 | )
8 |
9 | // Info is a function that returns information about the cache.
10 | //
11 | // Parameters:
12 | // - c: A pointer to a hermes.Cache struct representing the cache to get information from.
13 | //
14 | // Returns:
15 | // - A function that takes a pointer to a fiber.Ctx struct and returns an error.
16 | // The function returns information about the cache.
17 | func Info(c *hermes.Cache) func(ctx *fiber.Ctx) error {
18 | return func(ctx *fiber.Ctx) error {
19 | if info, err := c.Info(); err != nil {
20 | return ctx.Send(utils.Error(err))
21 | } else {
22 | return ctx.Send(utils.Success(info))
23 | }
24 | }
25 | }
26 |
27 | // InfoForTesting is a function that returns information about the cache for testing purposes.
28 | //
29 | // Parameters:
30 | // - c: A pointer to a hermes.Cache struct representing the cache to get information from.
31 | //
32 | // Returns:
33 | // - A function that takes a pointer to a fiber.Ctx struct and returns an error.
34 | // The function returns information about the cache for testing purposes.
35 | func InfoForTesting(c *hermes.Cache) func(ctx *fiber.Ctx) error {
36 | return func(ctx *fiber.Ctx) error {
37 | if info, err := c.InfoForTesting(); err != nil {
38 | return ctx.Send(utils.Error(err))
39 | } else {
40 | return ctx.Send(utils.Success(info))
41 | }
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/cloud/api/handlers/init.go:
--------------------------------------------------------------------------------
1 | package handlers
2 |
3 | import (
4 | "github.com/gofiber/fiber/v2"
5 | hermes "github.com/realTristan/hermes"
6 | utils "github.com/realTristan/hermes/cloud/api/utils"
7 | )
8 |
9 | // FTInit is a handler function that returns a fiber context handler function for initializing the full-text search cache.
10 | // Parameters:
11 | // - c (*hermes.Cache): A pointer to a hermes.Cache struct.
12 | //
13 | // Returns:
14 | // - func(ctx *fiber.Ctx) error: A fiber context handler function that initializes the full-text search cache using the max length, max bytes, and min word length parameters provided in the query string and returns a success message or an error message if the parameters are not provided or if the initialization fails.
15 | func FTInit(c *hermes.Cache) func(ctx *fiber.Ctx) error {
16 | return func(ctx *fiber.Ctx) error {
17 | var (
18 | maxSize int
19 | maxBytes int
20 | minWordLength int
21 | )
22 |
23 | // Get the max length parameter
24 | if err := utils.GetMaxSizeParam(ctx, &maxSize); err != nil {
25 | return ctx.Send(utils.Error(err))
26 | }
27 |
28 | // Get the max bytes parameter
29 | if err := utils.GetMaxBytesParam(ctx, &maxBytes); err != nil {
30 | return ctx.Send(utils.Error(err))
31 | }
32 |
33 | // Get the min word length parameter
34 | if err := utils.GetMinWordLengthParam(ctx, &minWordLength); err != nil {
35 | return ctx.Send(utils.Error(err))
36 | }
37 |
38 | // Initialize the full-text cache
39 | if err := c.FTInit(maxSize, maxBytes, minWordLength); err != nil {
40 | return ctx.Send(utils.Error(err))
41 | }
42 | return ctx.Send(utils.Success("null"))
43 | }
44 | }
45 |
46 | // FTInitJson is a handler function that returns a fiber context handler function for initializing the full-text search cache with a JSON object.
47 | // Parameters:
48 | // - c (*hermes.Cache): A pointer to a hermes.Cache struct.
49 | //
50 | // Returns:
51 | // - func(ctx *fiber.Ctx) error: A fiber context handler function that initializes the full-text search cache using a JSON object, max length, max bytes, and min word length parameters provided in the query string and returns a success message or an error message if the parameters are not provided or if the initialization fails.
52 | func FTInitJson(c *hermes.Cache) func(ctx *fiber.Ctx) error {
53 | return func(ctx *fiber.Ctx) error {
54 | var (
55 | maxSize int
56 | maxBytes int
57 | minWordLength int
58 | json map[string]map[string]interface{}
59 | )
60 |
61 | // Get the max length from the query
62 | if err := utils.GetMaxSizeParam(ctx, &maxSize); err != nil {
63 | return ctx.Send(utils.Error(err))
64 | }
65 |
66 | // Get the max bytes from the query
67 | if err := utils.GetMaxBytesParam(ctx, &maxBytes); err != nil {
68 | return ctx.Send(utils.Error(err))
69 | }
70 |
71 | // Get the min word length from the query
72 | if err := utils.GetMinWordLengthParam(ctx, &minWordLength); err != nil {
73 | return ctx.Send(utils.Error(err))
74 | }
75 |
76 | // Get the JSON from the query
77 | if err := utils.GetJSONParam(ctx, &json); err != nil {
78 | return ctx.Send(utils.Error(err))
79 | }
80 |
81 | // Initialize the full-text cache
82 | if err := c.FTInitWithMap(json, maxSize, maxBytes, minWordLength); err != nil {
83 | return ctx.Send(utils.Error(err))
84 | }
85 |
86 | // Return success message
87 | return ctx.Send(utils.Success("null"))
88 | }
89 | }
90 |
--------------------------------------------------------------------------------
/cloud/api/handlers/keys.go:
--------------------------------------------------------------------------------
1 | package handlers
2 |
3 | import (
4 | "encoding/json"
5 |
6 | "github.com/gofiber/fiber/v2"
7 | hermes "github.com/realTristan/hermes"
8 | utils "github.com/realTristan/hermes/cloud/api/utils"
9 | )
10 |
11 | // Keys is a handler function that returns a fiber context handler function for getting all the keys from the cache.
12 | // Parameters:
13 | // - c (*hermes.Cache): A pointer to a hermes.Cache struct.
14 | //
15 | // Returns:
16 | // - func(ctx *fiber.Ctx) error: A fiber context handler function that gets all the keys from the cache and returns a JSON-encoded string of the keys or an error message if the retrieval or encoding fails.
17 | func Keys(c *hermes.Cache) func(ctx *fiber.Ctx) error {
18 | return func(ctx *fiber.Ctx) error {
19 | if keys, err := json.Marshal(c.Keys()); err != nil {
20 | return ctx.Send(utils.Error(err))
21 | } else {
22 | return ctx.Send(keys)
23 | }
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/cloud/api/handlers/length.go:
--------------------------------------------------------------------------------
1 | package handlers
2 |
3 | import (
4 | "github.com/gofiber/fiber/v2"
5 | hermes "github.com/realTristan/hermes"
6 | utils "github.com/realTristan/hermes/cloud/api/utils"
7 | )
8 |
9 | // Length is a handler function that returns a fiber context handler function for getting the length of the cache.
10 | // Parameters:
11 | // - c (*hermes.Cache): A pointer to a hermes.Cache struct.
12 | //
13 | // Returns:
14 | // - func(ctx *fiber.Ctx) error: A fiber context handler function that gets the length of the cache and returns a success message with the length or an error message if the retrieval fails.
15 | func Length(c *hermes.Cache) func(ctx *fiber.Ctx) error {
16 | return func(ctx *fiber.Ctx) error {
17 | return ctx.Send(utils.Success(c.Length()))
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/cloud/api/handlers/set.go:
--------------------------------------------------------------------------------
1 | package handlers
2 |
3 | import (
4 | "github.com/gofiber/fiber/v2"
5 | hermes "github.com/realTristan/hermes"
6 | utils "github.com/realTristan/hermes/cloud/api/utils"
7 | )
8 |
9 | // Set is a handler function that returns a fiber context handler function for setting a value in the cache.
10 | // Parameters:
11 | // - c (*hermes.Cache): A pointer to a hermes.Cache struct.
12 | //
13 | // Returns:
14 | // - func(ctx *fiber.Ctx) error: A fiber context handler function that sets a value in the cache using the key and value parameters provided in the query string and returns a success message or an error message if the set fails or if the parameters are not provided.
15 | func Set(c *hermes.Cache) func(ctx *fiber.Ctx) error {
16 | return func(ctx *fiber.Ctx) error {
17 | var (
18 | key string
19 | value map[string]interface{}
20 | )
21 | // Get the key from the query
22 | if key = ctx.Query("key"); len(key) == 0 {
23 | return ctx.Send(utils.Error("invalid key"))
24 | }
25 |
26 | // Get the value from the query
27 | if err := utils.GetValueParam(ctx, &value); err != nil {
28 | return ctx.Send(utils.Error(err))
29 | }
30 |
31 | // Set the value in the cache
32 | if err := c.Set(key, value); err != nil {
33 | return ctx.Send(utils.Error(err))
34 | }
35 | return ctx.Send(utils.Success("null"))
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/cloud/api/handlers/values.go:
--------------------------------------------------------------------------------
1 | package handlers
2 |
3 | import (
4 | "encoding/json"
5 |
6 | "github.com/gofiber/fiber/v2"
7 | hermes "github.com/realTristan/hermes"
8 | utils "github.com/realTristan/hermes/cloud/api/utils"
9 | )
10 |
11 | // Values is a handler function that returns a fiber context handler function for getting all values from the cache.
12 | // Parameters:
13 | // - c (*hermes.Cache): A pointer to a hermes.Cache struct.
14 | //
15 | // Returns:
16 | // - func(ctx *fiber.Ctx) error: A fiber context handler function that gets all values from the cache and returns a JSON-encoded string of the values or an error message if the retrieval fails.
17 | func Values(c *hermes.Cache) func(ctx *fiber.Ctx) error {
18 | return func(ctx *fiber.Ctx) error {
19 | if values, err := json.Marshal(c.Values()); err != nil {
20 | return ctx.Send(utils.Error(err))
21 | } else {
22 | return ctx.Send(values)
23 | }
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/cloud/api/routes.go:
--------------------------------------------------------------------------------
1 | package api
2 |
3 | import (
4 | "github.com/gofiber/fiber/v2"
5 | hermes "github.com/realTristan/hermes"
6 | "github.com/realTristan/hermes/cloud/api/handlers"
7 | )
8 |
9 | // SetRoutes is a function that sets the routes for the hermes Cache API.
10 | // Parameters:
11 | // - app (*fiber.App): A pointer to a fiber.App struct.
12 | // - cache (*hermes.Cache): A pointer to a hermes.Cache struct.
13 | //
14 | // Returns:
15 | // - void: This function does not return anything.
16 | func SetRoutes(app *fiber.App, cache *hermes.Cache) {
17 | // Dev Testing Handler
18 | app.Get("/dev/hermes", func(c *fiber.Ctx) error {
19 | return c.SendString("hermes Cache API Successfully Running!")
20 | })
21 |
22 | // Cache Handlers
23 | app.Get("/cache/values", handlers.Values(cache))
24 | app.Get("/cache/length", handlers.Length(cache))
25 | app.Post("/cache/clean", handlers.Clean(cache))
26 | app.Post("/cache/set", handlers.Set(cache))
27 | app.Delete("/cache/delete", handlers.Delete(cache))
28 | app.Get("/cache/get", handlers.Get(cache))
29 | app.Get("/cache/get/all", handlers.GetAll(cache))
30 | app.Get("/cache/keys", handlers.Keys(cache))
31 | app.Get("/cache/info", handlers.Info(cache))
32 | app.Get("/cache/info/testing", handlers.InfoForTesting(cache))
33 | app.Get("/cache/exists", handlers.Exists(cache))
34 |
35 | // Full-text Cache Handlers
36 | app.Post("/ft/init", handlers.FTInit(cache))
37 | app.Post("/ft/init/json", handlers.FTInitJson(cache))
38 | app.Post("/ft/clean", handlers.FTClean(cache))
39 | app.Get("/ft/search", handlers.Search(cache))
40 | app.Get("/ft/search/oneword", handlers.SearchOneWord(cache))
41 | app.Get("/ft/search/values", handlers.SearchValues(cache))
42 | app.Get("/ft/search/withkey", handlers.SearchWithKey(cache))
43 | app.Post("/ft/maxbytes", handlers.FTSetMaxBytes(cache))
44 | app.Post("/ft/maxsize", handlers.FTSetMaxSize(cache))
45 | app.Post("/ft/minwordlength", handlers.FTSetMinWordLength(cache))
46 | app.Get("/ft/storage", handlers.FTStorage(cache))
47 | app.Get("/ft/storage/size", handlers.FTStorageSize(cache))
48 | app.Get("/ft/storage/length", handlers.FTStorageLength(cache))
49 | app.Get("/ft/isinitialized", handlers.FTIsInitialized(cache))
50 | app.Post("/ft/indices/sequence", handlers.FTSequenceIndices(cache))
51 | }
52 |
--------------------------------------------------------------------------------
/cloud/api/utils/decode.go:
--------------------------------------------------------------------------------
1 | package utils
2 |
3 | import (
4 | "encoding/base64"
5 | "encoding/json"
6 | )
7 |
8 | // Decode is a function that decodes a base64-encoded string and then decodes the resulting JSON into a value of type T.
9 | // Parameters:
10 | // - s (string): The base64-encoded string to decode.
11 | // - v (*T): A pointer to a value of type T to store the decoded JSON.
12 | //
13 | // Returns:
14 | // - error: An error message if the decoding fails, or nil if the decoding is successful.
15 | func Decode[T any](s string, v *T) error {
16 | if data, err := base64.StdEncoding.DecodeString(s); err != nil {
17 | return err
18 | } else if err := json.Unmarshal(data, v); err != nil {
19 | return err
20 | }
21 | return nil
22 | }
23 |
--------------------------------------------------------------------------------
/cloud/api/utils/http.go:
--------------------------------------------------------------------------------
1 | package utils
2 |
3 | import (
4 | "fmt"
5 | )
6 |
7 | // Error is a function that returns an error message with the provided error.
8 | // Parameters:
9 | // - err (T): The error to include in the error message.
10 | //
11 | // Returns:
12 | // - []byte: A byte slice containing the error message with the provided error.
13 | func Error[T any](err T) []byte {
14 | return []byte(fmt.Sprintf(`{"success":false,"error":"%v"}`, err))
15 | }
16 |
17 | // Success is a function that returns a success message with the provided data.
18 | // Parameters:
19 | // - v (T): The data to include in the success message.
20 | //
21 | // Returns:
22 | // - []byte: A byte slice containing the success message with the provided data.
23 | func Success[T any](v T) []byte {
24 | return []byte(fmt.Sprintf(`{"success":true,"data":%v}`, v))
25 | }
26 |
--------------------------------------------------------------------------------
/cloud/api/utils/params.go:
--------------------------------------------------------------------------------
1 | package utils
2 |
3 | import (
4 | "errors"
5 | "fmt"
6 | "strconv"
7 |
8 | "github.com/gofiber/fiber/v2"
9 | )
10 |
11 | // GetValueParam is a function that retrieves a value from a query parameter in a Fiber context and decodes it into a value of type T.
12 | // Parameters:
13 | // - ctx (*fiber.Ctx): A pointer to a Fiber context.
14 | // - value (*T): A pointer to a value of type T to store the decoded value.
15 | //
16 | // Returns:
17 | // - error: An error message if the decoding fails or the query parameter is invalid, or nil if the decoding is successful.
18 | func GetValueParam[T any](ctx *fiber.Ctx, value *T) error {
19 | if v := ctx.Query("value"); len(v) == 0 {
20 | return errors.New("invalid value")
21 | } else if err := Decode(v, &value); err != nil {
22 | return err
23 | }
24 | return nil
25 | }
26 |
27 | // GetMaxSizeParam is a function that retrieves the "maxsize" query parameter from a Fiber context and stores it in an integer pointer.
28 | // Parameters:
29 | // - ctx (*fiber.Ctx): A pointer to a Fiber context.
30 | // - maxSize (*int): A pointer to an integer to store the "maxsize" query parameter.
31 | //
32 | // Returns:
33 | // - error: An error message if the "maxsize" query parameter is invalid or cannot be converted to an integer, or nil if the retrieval is successful.
34 | func GetMaxSizeParam(ctx *fiber.Ctx, maxSize *int) error {
35 | if s := ctx.Query("maxsize"); len(s) == 0 {
36 | fmt.Println(s)
37 | return errors.New("invalid maxsize")
38 | } else if i, err := strconv.Atoi(s); err != nil {
39 | return err
40 | } else {
41 | *maxSize = i
42 | }
43 | return nil
44 | }
45 |
46 | // GetMaxBytesParam is a function that retrieves the "maxbytes" query parameter from a Fiber context and stores it in an integer pointer.
47 | // Parameters:
48 | // - ctx (*fiber.Ctx): A pointer to a Fiber context.
49 | // - maxBytes (*int): A pointer to an integer to store the "maxbytes" query parameter.
50 | //
51 | // Returns:
52 | // - error: An error message if the "maxbytes" query parameter is invalid or cannot be converted to an integer, or nil if the retrieval is successful.
53 | func GetMaxBytesParam(ctx *fiber.Ctx, maxBytes *int) error {
54 | if s := ctx.Query("maxbytes"); len(s) == 0 {
55 | return errors.New("invalid maxbytes")
56 | } else if i, err := strconv.Atoi(s); err != nil {
57 | return err
58 | } else {
59 | *maxBytes = i
60 | }
61 | return nil
62 | }
63 |
64 | // Get the min word length url parameter
65 | func GetMinWordLengthParam(ctx *fiber.Ctx, minWordLength *int) error {
66 | if s := ctx.Query("minwordlength"); len(s) == 0 {
67 | return errors.New("invalid minwordlength")
68 | } else if i, err := strconv.Atoi(s); err != nil {
69 | return err
70 | } else {
71 | *minWordLength = i
72 | }
73 | return nil
74 | }
75 |
76 | // GetJSONParam is a function that retrieves a JSON-encoded value from a query parameter in a Fiber context and decodes it into a value of type T.
77 | // Parameters:
78 | // - ctx (*fiber.Ctx): A pointer to a Fiber context.
79 | // - json (*T): A pointer to a value of type T to store the decoded JSON.
80 | //
81 | // Returns:
82 | // - error: An error message if the decoding fails or the query parameter is invalid, or nil if the decoding is successful.
83 | func GetJSONParam[T any](ctx *fiber.Ctx, json *T) error {
84 | if s := ctx.Query("json"); len(s) == 0 {
85 | return errors.New("invalid json")
86 | } else if err := Decode(s, &json); err != nil {
87 | return err
88 | }
89 | return nil
90 | }
91 |
92 | // GetSchemaParam is a function that retrieves a schema from a query parameter in a Fiber context and decodes it into a map of string keys and boolean values.
93 | // Parameters:
94 | // - ctx (*fiber.Ctx): A pointer to a Fiber context.
95 | // - schema (*map[string]bool): A pointer to a map of string keys and boolean values to store the decoded schema.
96 | //
97 | // Returns:
98 | // - error: An error message if the decoding fails or the query parameter is invalid, or nil if the decoding is successful.
99 | func GetSchemaParam(ctx *fiber.Ctx, schema *map[string]bool) error {
100 | // Get the schema from the url params
101 | if s := ctx.Query("schema"); len(s) == 0 {
102 | return errors.New("invalid schema")
103 | } else if err := Decode(s, schema); err != nil {
104 | return err
105 | }
106 | return nil
107 | }
108 |
109 | // GetLimitParam is a function that retrieves the "limit" query parameter from a Fiber context and stores it in an integer pointer.
110 | // Parameters:
111 | // - ctx (*fiber.Ctx): A pointer to a Fiber context.
112 | // - limit (*int): A pointer to an integer to store the "limit" query parameter.
113 | //
114 | // Returns:
115 | // - error: An error message if the "limit" query parameter is invalid or cannot be converted to an integer, or nil if the retrieval is successful.
116 | func GetLimitParam(ctx *fiber.Ctx, limit *int) error {
117 | // Get the limit from the url params
118 | if s := ctx.Query("limit"); len(s) == 0 {
119 | return errors.New("invalid limit")
120 | } else if i, err := strconv.Atoi(s); err != nil {
121 | return err
122 | } else {
123 | *limit = i
124 | }
125 | return nil
126 | }
127 |
128 | // GetLimitParam is a function that retrieves the "limit" query parameter from a Fiber context and stores it in an integer pointer.
129 | // Parameters:
130 | // - ctx (*fiber.Ctx): A pointer to a Fiber context.
131 | // - limit (*int): A pointer to an integer to store the "limit" query parameter.
132 | //
133 | // Returns:
134 | // - error: An error message if the "limit" query parameter is invalid or cannot be converted to an integer, or nil if the retrieval is successful.
135 | func GetStrictParam(ctx *fiber.Ctx, strict *bool) error {
136 | // Get whether strict mode is enabled/disabled
137 | if s := ctx.Query("strict"); len(s) == 0 {
138 | return errors.New("invalid strict")
139 | } else if b, err := strconv.ParseBool(s); err != nil {
140 | return err
141 | } else {
142 | *strict = b
143 | }
144 | return nil
145 | }
146 |
--------------------------------------------------------------------------------
/cloud/app/dev/.gitkeep:
--------------------------------------------------------------------------------
1 | use a go gui framework
--------------------------------------------------------------------------------
/cloud/app/prod/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/realTristan/hermes/ad2f3c2255628af7372056976456e7395e9a7cb3/cloud/app/prod/.gitkeep
--------------------------------------------------------------------------------
/cloud/app/server/app.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "log"
5 | "os"
6 |
7 | utils "hermes/utils"
8 |
9 | "github.com/gofiber/fiber/v2"
10 | hermes "github.com/realTristan/hermes"
11 | Socket "github.com/realTristan/hermes/cloud/socket"
12 | )
13 |
14 | // Main function
15 | func main() {
16 | // Verify that the user is trying to serve the cache
17 | if len(os.Args) < 1 || os.Args[1] != "serve" {
18 | panic("incorrect usage. example: ./hermes serve -p {port}")
19 | }
20 |
21 | // Get the arg data
22 | var args, err = utils.GetArgData(os.Args)
23 | if err != nil || args.Port() == nil {
24 | panic("incorrect usage. example: ./hermes serve -p {port}")
25 | }
26 |
27 | // Get the port and json file
28 | var cache *hermes.Cache = hermes.InitCache()
29 |
30 | // Initialize a new fiber app
31 | var app *fiber.App = fiber.New(fiber.Config{
32 | Prefork: false,
33 | ServerHeader: "hermes",
34 | })
35 | Socket.SetRouter(app, cache)
36 |
37 | // Listen on the port
38 | log.Fatal(app.Listen(args.Port().(string)))
39 | }
40 |
--------------------------------------------------------------------------------
/cloud/app/server/go.mod:
--------------------------------------------------------------------------------
1 | module hermes
2 |
3 | go 1.20
4 |
5 | require github.com/realTristan/Hermes v1.6.7
6 |
7 | require (
8 | github.com/andybalholm/brotli v1.0.5 // indirect
9 | github.com/fasthttp/websocket v1.5.3 // indirect
10 | github.com/gofiber/fiber/v2 v2.45.0
11 | github.com/gofiber/websocket/v2 v2.2.0 // indirect
12 | github.com/google/uuid v1.3.0 // indirect
13 | github.com/klauspost/compress v1.16.5 // indirect
14 | github.com/mattn/go-colorable v0.1.13 // indirect
15 | github.com/mattn/go-isatty v0.0.18 // indirect
16 | github.com/mattn/go-runewidth v0.0.14 // indirect
17 | github.com/philhofer/fwd v1.1.2 // indirect
18 | github.com/rivo/uniseg v0.2.0 // indirect
19 | github.com/savsgio/dictpool v0.0.0-20221023140959-7bf2e61cea94 // indirect
20 | github.com/savsgio/gotils v0.0.0-20230208104028-c358bd845dee // indirect
21 | github.com/tinylib/msgp v1.1.8 // indirect
22 | github.com/valyala/bytebufferpool v1.0.0 // indirect
23 | github.com/valyala/fasthttp v1.47.0 // indirect
24 | github.com/valyala/tcplisten v1.0.0 // indirect
25 | golang.org/x/sys v0.8.0 // indirect
26 | )
27 |
--------------------------------------------------------------------------------
/cloud/app/server/utils/args.go:
--------------------------------------------------------------------------------
1 | package utils
2 |
3 | import (
4 | "errors"
5 | "strings"
6 | )
7 |
8 | // Data struct
9 | type Data struct {
10 | port any
11 | }
12 |
13 | // Get the port
14 | func (d *Data) Port() any {
15 | var copy any = d.port
16 | return copy
17 | }
18 |
19 | // Get the argument data in a map
20 | func GetArgData(args []string) (*Data, error) {
21 | var data *Data = &Data{
22 | port: nil,
23 | }
24 |
25 | // Iterate over the args
26 | for i := 2; i < len(args); i++ {
27 | // Port arg
28 | if args[i] == "-port" || args[i] == "-p" {
29 | if i+1 >= len(args) {
30 | return data, errors.New("invalid port")
31 | }
32 |
33 | // Add a ':' to the port
34 | data.port = ":" + strings.ReplaceAll(args[i+1], ":", "")
35 |
36 | // If all past index 1 isnt a number
37 | if !isNum(data.port.(string)[1:]) {
38 | return data, errors.New("invalid port")
39 | }
40 |
41 | // Increment i then continue
42 | i = i + 1
43 | continue
44 | }
45 | }
46 | return data, nil
47 | }
48 |
49 | // Check if a string is a number
50 | func isNum(s string) bool {
51 | for _, c := range s {
52 | if c < '0' || c > '9' {
53 | return false
54 | }
55 | }
56 | return true
57 | }
58 |
--------------------------------------------------------------------------------
/cloud/database/.gitkeep:
--------------------------------------------------------------------------------
1 | compress and store data in sqlite db
--------------------------------------------------------------------------------
/cloud/socket/errors.go:
--------------------------------------------------------------------------------
1 | package ws
2 |
3 | import (
4 | "strings"
5 | )
6 |
7 | // Check whether the error is a close error by converting
8 | // the error to a string and checking if it contains the
9 | // word "close".
10 | func IsCloseError(err error) bool {
11 | return strings.Contains(err.Error(), "close")
12 | }
13 |
--------------------------------------------------------------------------------
/cloud/socket/funcs.go:
--------------------------------------------------------------------------------
1 | package ws
2 |
3 | import (
4 | hermes "github.com/realTristan/hermes"
5 | "github.com/realTristan/hermes/cloud/socket/handlers"
6 | utils "github.com/realTristan/hermes/cloud/socket/utils"
7 | )
8 |
9 | // Map of functions that can be called from the client
10 | var Functions = map[string]func(*utils.Params, *hermes.Cache) []byte{
11 | "cache.length": handlers.Length,
12 | "cache.clean": handlers.Clean,
13 | "cache.set": handlers.Set,
14 | "cache.delete": handlers.Delete,
15 | "cache.get": handlers.Get,
16 | "cache.get.all": handlers.GetAll,
17 | "cache.keys": handlers.Keys,
18 | "cache.info": handlers.Info,
19 | "cache.info.testing": handlers.InfoForTesting,
20 | "cache.exists": handlers.Exists,
21 | "ft.init": handlers.FTInit,
22 | "ft.init.json": handlers.FTInitJson,
23 | "ft.clean": handlers.FTClean,
24 | "ft.search": handlers.Search,
25 | "ft.search.oneword": handlers.SearchOneWord,
26 | "ft.search.values": handlers.SearchValues,
27 | "ft.search.withkey": handlers.SearchWithKey,
28 | "ft.maxbytes.set": handlers.FTSetMaxBytes,
29 | "ft.maxsize.set": handlers.FTSetMaxSize,
30 | "ft.storage": handlers.FTStorage,
31 | "ft.storage.size": handlers.FTStorageSize,
32 | "ft.storage.length": handlers.FTStorageLength,
33 | "ft.isinitialized": handlers.FTIsInitialized,
34 | "ft.indices.sequence": handlers.FTSequenceIndices,
35 | }
36 |
--------------------------------------------------------------------------------
/cloud/socket/handlers/clean.go:
--------------------------------------------------------------------------------
1 | package handlers
2 |
3 | import (
4 | hermes "github.com/realTristan/hermes"
5 | utils "github.com/realTristan/hermes/cloud/socket/utils"
6 | )
7 |
8 | // Clean is a handler function that returns a fiber context handler function for cleaning the cache.
9 | // Parameters:
10 | // - _ (*utils.Params): A pointer to a utils.Params struct (unused).
11 | // - c (*hermes.Cache): A pointer to a hermes.Cache struct.
12 | //
13 | // Returns:
14 | // - []byte: A JSON-encoded byte slice containing a success message or an error message if the cleaning fails.
15 | func Clean(_ *utils.Params, c *hermes.Cache) []byte {
16 | c.Clean()
17 | return utils.Success("null")
18 | }
19 |
20 | // FTClean is a handler function that returns a fiber context handler function for cleaning the full-text storage.
21 | // Parameters:
22 | // - _ (*utils.Params): A pointer to a utils.Params struct (unused).
23 | // - c (*hermes.Cache): A pointer to a hermes.Cache struct.
24 | //
25 | // Returns:
26 | // - []byte: A JSON-encoded byte slice containing a success message or an error message if the cleaning fails.
27 | func FTClean(_ *utils.Params, c *hermes.Cache) []byte {
28 | if err := c.FTClean(); err != nil {
29 | return utils.Error(err)
30 | }
31 | return utils.Success("null")
32 | }
33 |
--------------------------------------------------------------------------------
/cloud/socket/handlers/delete.go:
--------------------------------------------------------------------------------
1 | package handlers
2 |
3 | import (
4 | hermes "github.com/realTristan/hermes"
5 | utils "github.com/realTristan/hermes/cloud/socket/utils"
6 | )
7 |
8 | // Delete is a handler function that returns a fiber context handler function for deleting a key from the cache.
9 | // Parameters:
10 | // - p (*utils.Params): A pointer to a utils.Params struct.
11 | // - c (*hermes.Cache): A pointer to a hermes.Cache struct.
12 | //
13 | // Returns:
14 | // - []byte: A JSON-encoded byte slice containing a success message or an error message if the key is not provided or the deletion fails.
15 | func Delete(p *utils.Params, c *hermes.Cache) []byte {
16 | // Get the key from the query
17 | var (
18 | key string
19 | err error
20 | )
21 | if key, err = utils.GetKeyParam(p); err != nil {
22 | return utils.Error("key not provided")
23 | }
24 |
25 | // Delete the key from the cache
26 | c.Delete(key)
27 | return utils.Success("null")
28 | }
29 |
--------------------------------------------------------------------------------
/cloud/socket/handlers/exists.go:
--------------------------------------------------------------------------------
1 | package handlers
2 |
3 | import (
4 | hermes "github.com/realTristan/hermes"
5 | utils "github.com/realTristan/hermes/cloud/socket/utils"
6 | )
7 |
8 | // Exists is a handler function that returns a fiber context handler function for checking if a key exists in the cache.
9 | // Parameters:
10 | // - p (*utils.Params): A pointer to a utils.Params struct.
11 | // - c (*hermes.Cache): A pointer to a hermes.Cache struct.
12 | //
13 | // Returns:
14 | // - []byte: A JSON-encoded byte slice containing a boolean value indicating whether the key exists in the cache or an error message if the key is not provided.
15 | func Exists(p *utils.Params, c *hermes.Cache) []byte {
16 | // Get the key from the query
17 | var (
18 | key string
19 | err error
20 | )
21 | if key, err = utils.GetKeyParam(p); err != nil {
22 | return utils.Error("key not provided")
23 | }
24 |
25 | // Return whether the key exists
26 | return utils.Success(c.Exists(key))
27 | }
28 |
--------------------------------------------------------------------------------
/cloud/socket/handlers/fulltext.go:
--------------------------------------------------------------------------------
1 | package handlers
2 |
3 | import (
4 | "encoding/json"
5 |
6 | hermes "github.com/realTristan/hermes"
7 | utils "github.com/realTristan/hermes/cloud/socket/utils"
8 | )
9 |
10 | // FTIsInitialized is a handler function that returns a fiber context handler function for checking if the full-text storage is initialized.
11 | // Parameters:
12 | // - _ (*utils.Params): A pointer to a utils.Params struct (unused).
13 | // - c (*hermes.Cache): A pointer to a hermes.Cache struct.
14 | //
15 | // Returns:
16 | // - []byte: A JSON-encoded byte slice containing a boolean value indicating whether the full-text storage is initialized.
17 | func FTIsInitialized(_ *utils.Params, c *hermes.Cache) []byte {
18 | return utils.Success(c.FTIsInitialized())
19 | }
20 |
21 | // FTSetMaxBytes is a handler function that returns a fiber context handler function for setting the maximum number of bytes for the full-text storage.
22 | // Parameters:
23 | // - p (*utils.Params): A pointer to a utils.Params struct.
24 | // - c (*hermes.Cache): A pointer to a hermes.Cache struct.
25 | //
26 | // Returns:
27 | // - []byte: A JSON-encoded byte slice containing a success message or an error message if the value is invalid or the setting fails.
28 | func FTSetMaxBytes(p *utils.Params, c *hermes.Cache) []byte {
29 | // Get the value from the query
30 | var value int
31 | if err := utils.GetMaxBytesParam(p, &value); err != nil {
32 | return utils.Error(err)
33 | }
34 |
35 | // Set the max bytes
36 | if err := c.FTSetMaxBytes(value); err != nil {
37 | return utils.Error(err)
38 | }
39 | return utils.Success("null")
40 | }
41 |
42 | // FTSetMaxSize is a handler function that returns a fiber context handler function for setting the maximum length for the full-text storage.
43 | // Parameters:
44 | // - p (*utils.Params): A pointer to a utils.Params struct.
45 | // - c (*hermes.Cache): A pointer to a hermes.Cache struct.
46 | //
47 | // Returns:
48 | // - []byte: A JSON-encoded byte slice containing a success message or an error message if the value is invalid or the setting fails.
49 | func FTSetMaxSize(p *utils.Params, c *hermes.Cache) []byte {
50 | // Get the value from the query
51 | var value int
52 | if err := utils.GetMaxSizeParam(p, &value); err != nil {
53 | return utils.Error(err)
54 | }
55 |
56 | // Set the max length
57 | if err := c.FTSetMaxSize(value); err != nil {
58 | return utils.Error(err)
59 | }
60 | return utils.Success("null")
61 | }
62 |
63 | // FTStorage is a handler function that returns a fiber context handler function for retrieving the full-text storage.
64 | // Parameters:
65 | // - _ (*utils.Params): A pointer to a utils.Params struct (unused).
66 | // - c (*hermes.Cache): A pointer to a hermes.Cache struct.
67 | //
68 | // Returns:
69 | // - []byte: A JSON-encoded byte slice containing the full-text storage or an error message if the retrieval fails.
70 | func FTStorage(_ *utils.Params, c *hermes.Cache) []byte {
71 | if data, err := c.FTStorage(); err != nil {
72 | return utils.Error(err)
73 | } else if data, err := json.Marshal(data); err != nil {
74 | return utils.Error(err)
75 | } else {
76 | return data
77 | }
78 | }
79 |
80 | // FTStorageLength is a handler function that returns a fiber context handler function for retrieving the length of the full-text storage.
81 | // Parameters:
82 | // - _ (*utils.Params): A pointer to a utils.Params struct (unused).
83 | // - c (*hermes.Cache): A pointer to a hermes.Cache struct.
84 | //
85 | // Returns:
86 | // - []byte: A JSON-encoded byte slice containing the length of the full-text storage or an error message if the retrieval fails.
87 | func FTStorageLength(_ *utils.Params, c *hermes.Cache) []byte {
88 | if length, err := c.FTStorageLength(); err != nil {
89 | return utils.Error(err)
90 | } else {
91 | return utils.Success(length)
92 | }
93 | }
94 |
95 | // FTStorageSize is a handler function that returns a fiber context handler function for retrieving the size of the full-text storage.
96 | // Parameters:
97 | // - _ (*utils.Params): A pointer to a utils.Params struct (unused).
98 | // - c (*hermes.Cache): A pointer to a hermes.Cache struct.
99 | //
100 | // Returns:
101 | // - []byte: A JSON-encoded byte slice containing the size of the full-text storage or an error message if the retrieval fails.
102 | func FTStorageSize(_ *utils.Params, c *hermes.Cache) []byte {
103 | if size, err := c.FTStorageSize(); err != nil {
104 | return utils.Error(err)
105 | } else {
106 | return utils.Success(size)
107 | }
108 | }
109 |
--------------------------------------------------------------------------------
/cloud/socket/handlers/get.go:
--------------------------------------------------------------------------------
1 | package handlers
2 |
3 | import (
4 | "encoding/json"
5 |
6 | hermes "github.com/realTristan/hermes"
7 | utils "github.com/realTristan/hermes/cloud/socket/utils"
8 | )
9 |
10 | // Get is a handler function that returns a fiber context handler function for retrieving a key from the cache.
11 | // Parameters:
12 | // - p (*utils.Params): A pointer to a utils.Params struct.
13 | // - c (*hermes.Cache): A pointer to a hermes.Cache struct.
14 | //
15 | // Returns:
16 | // - []byte: A JSON-encoded byte slice containing the value of the key or an error message if the key is not provided or the retrieval fails.
17 | func Get(p *utils.Params, c *hermes.Cache) []byte {
18 | // Get the key from the query
19 | var (
20 | key string
21 | err error
22 | )
23 | if key, err = utils.GetKeyParam(p); err != nil {
24 | return utils.Error("key not provided")
25 | }
26 |
27 | // Get the value from the cache
28 | if data, err := json.Marshal(c.Get(key)); err != nil {
29 | return utils.Error(err)
30 | } else {
31 | return data
32 | }
33 | }
34 |
35 | // GetAll is a handler function that returns a fiber context handler function for retrieving all data from the cache.
36 | // Parameters:
37 | // - _ (*utils.Params): A pointer to a utils.Params struct (unused).
38 | // - c (*hermes.Cache): A pointer to a hermes.Cache struct.
39 | //
40 | // Returns:
41 | // - []byte: A JSON-encoded byte slice containing all data from the cache or an error message if the retrieval fails.
42 | func GetAll(_ *utils.Params, c *hermes.Cache) []byte {
43 | return nil
44 | }
45 |
--------------------------------------------------------------------------------
/cloud/socket/handlers/indices.go:
--------------------------------------------------------------------------------
1 | package handlers
2 |
3 | import (
4 | hermes "github.com/realTristan/hermes"
5 | utils "github.com/realTristan/hermes/cloud/socket/utils"
6 | )
7 |
8 | // FTSequenceIndices is a handler function that returns a fiber context handler function for sequencing the full-text storage indices.
9 | // Parameters:
10 | // - _ (*utils.Params): A pointer to a utils.Params struct (unused).
11 | // - c (*hermes.Cache): A pointer to a hermes.Cache struct.
12 | //
13 | // Returns:
14 | // - []byte: A JSON-encoded byte slice containing a success message or an error message if the sequencing fails.
15 | func FTSequenceIndices(_ *utils.Params, c *hermes.Cache) []byte {
16 | c.FTSequenceIndices()
17 | return utils.Success("null")
18 | }
19 |
--------------------------------------------------------------------------------
/cloud/socket/handlers/info.go:
--------------------------------------------------------------------------------
1 | package handlers
2 |
3 | import (
4 | hermes "github.com/realTristan/hermes"
5 | utils "github.com/realTristan/hermes/cloud/socket/utils"
6 | )
7 |
8 | // Info is a function that returns information about the cache.
9 | //
10 | // Parameters:
11 | // - _ : A pointer to a utils.Params struct representing the parameters of the request. This parameter is ignored.
12 | // - c: A pointer to a hermes.Cache struct representing the cache to get information from.
13 | //
14 | // Returns:
15 | // - A byte slice representing the information about the cache.
16 | func Info(_ *utils.Params, c *hermes.Cache) []byte {
17 | if info, err := c.Info(); err != nil {
18 | return utils.Error(err)
19 | } else {
20 | return utils.Success(info)
21 | }
22 | }
23 |
24 | // InfoForTesting is a function that returns information about the cache for testing purposes.
25 | //
26 | // Parameters:
27 | // - _ : A pointer to a utils.Params struct representing the parameters of the request. This parameter is ignored.
28 | // - c: A pointer to a hermes.Cache struct representing the cache to get information from.
29 | //
30 | // Returns:
31 | // - A byte slice representing the information about the cache for testing purposes.
32 | func InfoForTesting(_ *utils.Params, c *hermes.Cache) []byte {
33 | if info, err := c.InfoForTesting(); err != nil {
34 | return utils.Error(err)
35 | } else {
36 | return utils.Success(info)
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/cloud/socket/handlers/init.go:
--------------------------------------------------------------------------------
1 | package handlers
2 |
3 | import (
4 | hermes "github.com/realTristan/hermes"
5 | utils "github.com/realTristan/hermes/cloud/socket/utils"
6 | )
7 |
8 | // FTInit is a handler function that returns a fiber context handler function for initializing the full-text search cache.
9 | // Parameters:
10 | // - p (*utils.Params): A pointer to a utils.Params struct.
11 | // - c (*hermes.Cache): A pointer to a hermes.Cache struct.
12 | //
13 | // Returns:
14 | // - []byte: A JSON-encoded byte slice containing a success message or an error message if the initialization fails.
15 | func FTInit(p *utils.Params, c *hermes.Cache) []byte {
16 | var (
17 | maxSize int
18 | maxBytes int
19 | minWordLength int
20 | )
21 |
22 | // Get the max length parameter
23 | if err := utils.GetMaxSizeParam(p, &maxSize); err != nil {
24 | return utils.Error(err)
25 | }
26 |
27 | // Get the max bytes parameter
28 | if err := utils.GetMaxBytesParam(p, &maxBytes); err != nil {
29 | return utils.Error(err)
30 | }
31 |
32 | // Get the min word length parameter
33 | if err := utils.GetMinWordLengthParam(p, &minWordLength); err != nil {
34 | return utils.Error(err)
35 | }
36 |
37 | // Initialize the full-text cache
38 | if err := c.FTInit(maxSize, maxBytes, minWordLength); err != nil {
39 | return utils.Error(err)
40 | }
41 | return utils.Success("null")
42 | }
43 |
44 | // FTInitJson is a handler function that returns a fiber context handler function for initializing the full-text search cache with a JSON object.
45 | // Parameters:
46 | // - p (*utils.Params): A pointer to a utils.Params struct.
47 | // - c (*hermes.Cache): A pointer to a hermes.Cache struct.
48 | //
49 | // Returns:
50 | // - []byte: A JSON-encoded byte slice containing a success message or an error message if the initialization fails.
51 | func FTInitJson(p *utils.Params, c *hermes.Cache) []byte {
52 | var (
53 | maxSize int
54 | maxBytes int
55 | minWordLength int
56 | json map[string]map[string]any
57 | )
58 |
59 | // Get the max length from the query
60 | if err := utils.GetMaxSizeParam(p, &maxSize); err != nil {
61 | return utils.Error(err)
62 | }
63 |
64 | // Get the max bytes from the query
65 | if err := utils.GetMaxBytesParam(p, &maxBytes); err != nil {
66 | return utils.Error(err)
67 | }
68 |
69 | // Get the min word length from the query
70 | if err := utils.GetMinWordLengthParam(p, &minWordLength); err != nil {
71 | return utils.Error(err)
72 | }
73 |
74 | // Get the JSON from the query
75 | if err := utils.GetJSONParam(p, &json); err != nil {
76 | return utils.Error(err)
77 | }
78 |
79 | // Initialize the full-text cache
80 | if err := c.FTInitWithMap(json, maxSize, maxBytes, minWordLength); err != nil {
81 | return utils.Error(err)
82 | }
83 |
84 | // Return success message
85 | return utils.Success("null")
86 | }
87 |
--------------------------------------------------------------------------------
/cloud/socket/handlers/keys.go:
--------------------------------------------------------------------------------
1 | package handlers
2 |
3 | import (
4 | "encoding/json"
5 |
6 | hermes "github.com/realTristan/hermes"
7 | utils "github.com/realTristan/hermes/cloud/socket/utils"
8 | )
9 |
10 | // Keys is a handler function that returns a fiber context handler function for retrieving all keys from the cache.
11 | // Parameters:
12 | // - _ (*utils.Params): A pointer to a utils.Params struct (unused).
13 | // - c (*hermes.Cache): A pointer to a hermes.Cache struct.
14 | //
15 | // Returns:
16 | // - []byte: A JSON-encoded byte slice containing all keys from the cache or an error message if the retrieval fails.
17 | func Keys(_ *utils.Params, c *hermes.Cache) []byte {
18 | if keys, err := json.Marshal(c.Keys()); err != nil {
19 | return utils.Error(err)
20 | } else {
21 | return keys
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/cloud/socket/handlers/length.go:
--------------------------------------------------------------------------------
1 | package handlers
2 |
3 | import (
4 | hermes "github.com/realTristan/hermes"
5 | utils "github.com/realTristan/hermes/cloud/socket/utils"
6 | )
7 |
8 | // Length is a handler function that returns a fiber context handler function for retrieving the length of the cache.
9 | // Parameters:
10 | // - _ (*utils.Params): A pointer to a utils.Params struct (unused).
11 | // - c (*hermes.Cache): A pointer to a hermes.Cache struct.
12 | //
13 | // Returns:
14 | // - []byte: A JSON-encoded byte slice containing the length of the cache.
15 | func Length(_ *utils.Params, c *hermes.Cache) []byte {
16 | return utils.Success(c.Length())
17 | }
18 |
--------------------------------------------------------------------------------
/cloud/socket/handlers/search.go:
--------------------------------------------------------------------------------
1 | package handlers
2 |
3 | import (
4 | "encoding/json"
5 |
6 | hermes "github.com/realTristan/hermes"
7 | utils "github.com/realTristan/hermes/cloud/socket/utils"
8 | )
9 |
10 | // Search is a handler function that returns a fiber context handler function for searching the cache for a query.
11 | // Parameters:
12 | // - p (*utils.Params): A pointer to a utils.Params struct.
13 | // - c (*hermes.Cache): A pointer to a hermes.Cache struct.
14 | //
15 | // Returns:
16 | // - []byte: A JSON-encoded byte slice containing the search results or an error message if the search fails.
17 | func Search(p *utils.Params, c *hermes.Cache) []byte {
18 | var (
19 | strict bool
20 | query string
21 | limit int
22 | err error
23 | )
24 |
25 | // Get the query from the params
26 | if query, err = utils.GetQueryParam(p); err != nil {
27 | return utils.Error("query not provided")
28 | }
29 |
30 | // Get the limit from the params
31 | if err := utils.GetLimitParam(p, &limit); err != nil {
32 | return utils.Error(err)
33 | }
34 |
35 | // Get the strict from the params
36 | if err := utils.GetStrictParam(p, &strict); err != nil {
37 | return utils.Error(err)
38 | }
39 |
40 | // Search for the query
41 | if res, err := c.Search(hermes.SearchParams{
42 | Query: query,
43 | Limit: limit,
44 | Strict: strict,
45 | }); err != nil {
46 | return utils.Error(err)
47 | } else if data, err := json.Marshal(res); err != nil {
48 | return utils.Error(err)
49 | } else {
50 | return data
51 | }
52 | }
53 |
54 | // SearchOneWord is a handler function that returns a fiber context handler function for searching the cache for a single word query.
55 | // Parameters:
56 | // - p (*utils.Params): A pointer to a utils.Params struct.
57 | // - c (*hermes.Cache): A pointer to a hermes.Cache struct.
58 | //
59 | // Returns:
60 | // - []byte: A JSON-encoded byte slice containing the search results or an error message if the search fails.
61 | func SearchOneWord(p *utils.Params, c *hermes.Cache) []byte {
62 | var (
63 | strict bool
64 | query string
65 | err error
66 | limit int
67 | )
68 |
69 | // Get the query from the params
70 | if query, err = utils.GetQueryParam(p); err != nil {
71 | return utils.Error("invalid query")
72 | }
73 |
74 | // Get the limit from the params
75 | if err := utils.GetLimitParam(p, &limit); err != nil {
76 | return utils.Error(err)
77 | }
78 |
79 | // Get the strict from the params
80 | if err := utils.GetStrictParam(p, &strict); err != nil {
81 | return utils.Error(err)
82 | }
83 |
84 | // Search for the query
85 | if res, err := c.SearchOneWord(hermes.SearchParams{
86 | Query: query,
87 | Limit: limit,
88 | Strict: strict,
89 | }); err != nil {
90 | return utils.Error(err)
91 | } else {
92 | if data, err := json.Marshal(res); err != nil {
93 | return utils.Error(err)
94 | } else {
95 | return data
96 | }
97 | }
98 | }
99 |
100 | // SearchValues is a handler function that returns a fiber context handler function for searching the cache for a query in values.
101 | // Parameters:
102 | // - p (*utils.Params): A pointer to a utils.Params struct.
103 | // - c (*hermes.Cache): A pointer to a hermes.Cache struct.
104 | //
105 | // Returns:
106 | // - []byte: A JSON-encoded byte slice containing the search results or an error message if the search fails.
107 | func SearchValues(p *utils.Params, c *hermes.Cache) []byte {
108 | var (
109 | query string
110 | limit int
111 | err error
112 | schema map[string]bool
113 | )
114 |
115 | // Get the query from the params
116 | if query, err = utils.GetQueryParam(p); err != nil {
117 | return utils.Error("invalid query")
118 | }
119 |
120 | // Get the limit from the params
121 | if err := utils.GetLimitParam(p, &limit); err != nil {
122 | return utils.Error(err)
123 | }
124 |
125 | // Get the schema from the params
126 | if err := utils.GetSchemaParam(p, &schema); err != nil {
127 | return utils.Error(err)
128 | }
129 |
130 | // Search for the query
131 | if res, err := c.SearchValues(hermes.SearchParams{
132 | Query: query,
133 | Limit: limit,
134 | Schema: schema,
135 | }); err != nil {
136 | return utils.Error(err)
137 | } else {
138 | if data, err := json.Marshal(res); err != nil {
139 | return utils.Error(err)
140 | } else {
141 | return data
142 | }
143 | }
144 | }
145 |
146 | // SearchWithKey is a handler function that returns a fiber context handler function for searching the cache for a query with a specific key.
147 | // Parameters:
148 | // - p (*utils.Params): A pointer to a utils.Params struct.
149 | // - c (*hermes.Cache): A pointer to a hermes.Cache struct.
150 | //
151 | // Returns:
152 | // - []byte: A JSON-encoded byte slice containing the search results or an error message if the search fails.
153 | func SearchWithKey(p *utils.Params, c *hermes.Cache) []byte {
154 | var (
155 | key string
156 | query string
157 | err error
158 | limit int
159 | schema map[string]bool
160 | )
161 |
162 | // Get the query from the params
163 | if query, err = utils.GetQueryParam(p); err != nil {
164 | return utils.Error("invalid query")
165 | }
166 |
167 | // Get the key from the params
168 | if key, err = utils.GetKeyParam(p); err != nil {
169 | return utils.Error("invalid key")
170 | }
171 |
172 | // Get the limit from the params
173 | if err := utils.GetLimitParam(p, &limit); err != nil {
174 | return utils.Error(err)
175 | }
176 |
177 | // Get the schema from the params
178 | if err := utils.GetSchemaParam(p, &schema); err != nil {
179 | return utils.Error(err)
180 | }
181 |
182 | // Search for the query
183 | if res, err := c.SearchWithKey(hermes.SearchParams{
184 | Query: query,
185 | Key: key,
186 | Limit: limit,
187 | }); err != nil {
188 | return utils.Error(err)
189 | } else {
190 | if data, err := json.Marshal(res); err != nil {
191 | return utils.Error(err)
192 | } else {
193 | return data
194 | }
195 | }
196 | }
197 |
--------------------------------------------------------------------------------
/cloud/socket/handlers/set.go:
--------------------------------------------------------------------------------
1 | package handlers
2 |
3 | import (
4 | hermes "github.com/realTristan/hermes"
5 | utils "github.com/realTristan/hermes/cloud/socket/utils"
6 | )
7 |
8 | // Set is a handler function that returns a fiber context handler function for setting a value in the cache.
9 | // Parameters:
10 | // - p (*utils.Params): A pointer to a utils.Params struct.
11 | // - c (*hermes.Cache): A pointer to a hermes.Cache struct.
12 | //
13 | // Returns:
14 | // - []byte: A JSON-encoded byte slice containing a success message or an error message if the set operation fails.
15 | func Set(p *utils.Params, c *hermes.Cache) []byte {
16 | var (
17 | key string
18 | err error
19 | value map[string]any
20 | )
21 |
22 | // Get the key from the query
23 | if key, err = utils.GetKeyParam(p); err != nil {
24 | return utils.Error("invalid key")
25 | }
26 |
27 | // Get the value from the query
28 | if err := utils.GetValueParam(p, &value); err != nil {
29 | return utils.Error(err)
30 | }
31 |
32 | // Set the value in the cache
33 | if err := c.Set(key, value); err != nil {
34 | return utils.Error(err)
35 | }
36 | return utils.Success("null")
37 | }
38 |
--------------------------------------------------------------------------------
/cloud/socket/handlers/values.go:
--------------------------------------------------------------------------------
1 | package handlers
2 |
3 | import (
4 | "encoding/json"
5 |
6 | hermes "github.com/realTristan/hermes"
7 | utils "github.com/realTristan/hermes/cloud/socket/utils"
8 | )
9 |
10 | // Values is a handler function that returns a fiber context handler function for retrieving all values from the cache.
11 | // Parameters:
12 | // - _ (*utils.Params): A pointer to a utils.Params struct (unused).
13 | // - c (*hermes.Cache): A pointer to a hermes.Cache struct.
14 | //
15 | // Returns:
16 | // - []byte: A JSON-encoded byte slice containing all values from the cache or an error message if the retrieval fails.
17 | func Values(_ *utils.Params, c *hermes.Cache) []byte {
18 | if values, err := json.Marshal(c.Values()); err != nil {
19 | return utils.Error(err)
20 | } else {
21 | return values
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/cloud/socket/router.go:
--------------------------------------------------------------------------------
1 | package ws
2 |
3 | import (
4 | "log"
5 | "sync"
6 |
7 | "github.com/gofiber/fiber/v2"
8 | "github.com/gofiber/websocket/v2"
9 | hermes "github.com/realTristan/hermes"
10 | utils "github.com/realTristan/hermes/cloud/socket/utils"
11 | )
12 |
13 | // Set the router for the socket
14 | func SetRouter(app *fiber.App, cache *hermes.Cache) {
15 | // Init a new socket
16 | var socket *Socket = &Socket{
17 | active: false,
18 | mutex: &sync.Mutex{},
19 | }
20 |
21 | // Middleware
22 | app.Use("/ws", func(c *fiber.Ctx) error {
23 | // Check if the socket is active
24 | if socket.IsActive() {
25 | return fiber.ErrLocked
26 | }
27 |
28 | // Check if the request is via socket
29 | if websocket.IsWebSocketUpgrade(c) {
30 | // Allow Locals
31 | c.Locals("allowed", true)
32 |
33 | // Set the socket to active
34 | socket.SetActive()
35 |
36 | // Return the next handler
37 | return c.Next()
38 | }
39 |
40 | // Return an error
41 | return fiber.ErrUpgradeRequired
42 | })
43 |
44 | // Main websocket handler
45 | app.Get("/ws/hermes", websocket.New(func(c *websocket.Conn) {
46 | for {
47 | var (
48 | msg []byte
49 | err error
50 | )
51 |
52 | // Read the message
53 | if _, msg, err = c.ReadMessage(); err != nil {
54 | log.Println("read:", err)
55 | if IsCloseError(err) {
56 | socket.SetInactive()
57 | }
58 | break
59 | }
60 |
61 | // Get the data
62 | var p *utils.Params
63 | if p, err = utils.ParseParams(msg); err != nil {
64 | log.Println("parse:", err)
65 | break
66 | }
67 |
68 | // Get the function
69 | var function string
70 | if function, err = p.GetFunction(); err != nil {
71 | log.Println("function:", err)
72 | break
73 | }
74 |
75 | // Check if the function exists
76 | if fn, ok := Functions[function]; !ok {
77 | if c.WriteMessage(websocket.TextMessage, []byte("Function not found")) != nil {
78 | log.Println("write:", err)
79 | break
80 | }
81 | } else if c.WriteMessage(websocket.TextMessage, fn(p, cache)) != nil {
82 | log.Println("function:", err)
83 | break
84 | }
85 | }
86 | }))
87 | }
88 |
--------------------------------------------------------------------------------
/cloud/socket/socket.go:
--------------------------------------------------------------------------------
1 | package ws
2 |
3 | import (
4 | "sync"
5 | )
6 |
7 | // Socket struct for storing the socket state and fiber app
8 | type Socket struct {
9 | mutex *sync.Mutex
10 | active bool
11 | }
12 |
13 | // Set the socket to active
14 | func (s *Socket) SetActive() {
15 | s.mutex.Lock()
16 | defer s.mutex.Unlock()
17 | s.active = true
18 | }
19 |
20 | // Set the socket to inactive
21 | func (s *Socket) SetInactive() {
22 | s.mutex.Lock()
23 | defer s.mutex.Unlock()
24 | s.active = false
25 | }
26 |
27 | // Get the socket state
28 | func (s *Socket) IsActive() bool {
29 | s.mutex.Lock()
30 | defer s.mutex.Unlock()
31 | return s.active
32 | }
33 |
--------------------------------------------------------------------------------
/cloud/socket/utils/decode.go:
--------------------------------------------------------------------------------
1 | package utils
2 |
3 | import (
4 | "encoding/base64"
5 | "encoding/json"
6 | )
7 |
8 | // Decode is a generic function that decodes a base64-encoded string and unmarshals the resulting JSON into a provided value.
9 | // Parameters:
10 | // - s (string): The base64-encoded string to decode and unmarshal.
11 | // - v (*T): A pointer to a value of type T to unmarshal the JSON into.
12 | //
13 | // Returns:
14 | // - error: An error if the decoding or unmarshaling fails, or nil if successful.
15 | func Decode[T any](s string, v *T) error {
16 | if data, err := base64.StdEncoding.DecodeString(s); err != nil {
17 | return err
18 | } else if err := json.Unmarshal(data, v); err != nil {
19 | return err
20 | }
21 | return nil
22 | }
23 |
--------------------------------------------------------------------------------
/cloud/socket/utils/errors.go:
--------------------------------------------------------------------------------
1 | package utils
2 |
3 | import (
4 | "strings"
5 |
6 | "github.com/gofiber/websocket/v2"
7 | )
8 |
9 | // IsCloseError is a function that checks if an error is a close error by converting
10 | // the error to a string and checking if it contains the word "close".
11 | // Parameters:
12 | // - err (error): The error to check.
13 | //
14 | // Returns:
15 | // - bool: true if the error is a close error, false otherwise.
16 | func IsCloseError(err error) bool {
17 | return strings.Contains(err.Error(), "close")
18 | }
19 |
20 | // IsSocketCloseError is a function that checks if an error is a WebSocket close error.
21 | // Parameters:
22 | // - err (error): The error to check.
23 | //
24 | // Returns:
25 | // - bool: true if the error is a WebSocket close error, false otherwise.
26 | func IsSocketCloseError(err error) bool {
27 | return websocket.IsCloseError(
28 | err,
29 | websocket.CloseNormalClosure,
30 | websocket.CloseGoingAway,
31 | websocket.CloseAbnormalClosure,
32 | websocket.CloseNoStatusReceived,
33 | websocket.CloseInvalidFramePayloadData,
34 | websocket.ClosePolicyViolation,
35 | websocket.CloseMessageTooBig,
36 | websocket.CloseMandatoryExtension,
37 | websocket.CloseInternalServerErr,
38 | websocket.CloseServiceRestart,
39 | websocket.CloseTryAgainLater,
40 | websocket.CloseTLSHandshake,
41 | websocket.CloseMessage,
42 | websocket.CloseProtocolError,
43 | websocket.CloseUnsupportedData,
44 | )
45 | }
46 |
--------------------------------------------------------------------------------
/cloud/socket/utils/http.go:
--------------------------------------------------------------------------------
1 | package utils
2 |
3 | import (
4 | "fmt"
5 | )
6 |
7 | // Error is a function that returns an error message with the provided error.
8 | // Parameters:
9 | // - err (T): The error to include in the error message.
10 | //
11 | // Returns:
12 | // - []byte: A byte slice containing the error message with the provided error.
13 | func Error[T any](err T) []byte {
14 | return []byte(fmt.Sprintf(`{"success":false,"error":"%v"}`, err))
15 | }
16 |
17 | // Success is a function that returns a success message with the provided data.
18 | // Parameters:
19 | // - v (T): The data to include in the success message.
20 | //
21 | // Returns:
22 | // - []byte: A byte slice containing the success message with the provided data.
23 | func Success[T any](v T) []byte {
24 | return []byte(fmt.Sprintf(`{"success":true,"data":%v}`, v))
25 | }
26 |
--------------------------------------------------------------------------------
/cloud/wrappers/go/main.go:
--------------------------------------------------------------------------------
1 | package hermescloud
2 |
3 | func Get() {
4 | // send http request
5 | }
6 |
--------------------------------------------------------------------------------
/cloud/wrappers/npm/main.ts:
--------------------------------------------------------------------------------
1 |
2 | /*
3 | To do:
4 |
5 |
6 |
7 |
8 |
9 | */
10 |
11 | // Get data from the cache function
12 | export const Get = (key: string): any => {
13 | // GET
14 | return fetch(`${host}/get`)
15 | .then(res => res.json())
16 | .then(res => res);
17 | }
18 |
19 | // Set data in the cache function
20 | export const Set = (key: string, value: map[string]interface{}): any => {
21 | // POST
22 | let value = base64encode(jsonEncode(value))
23 | return fetch(`${host}/set?key=${key}&value=${value}`)
24 | .then(res => res.json())
25 | .then(res => res);
26 | }
27 |
28 | // Delete key from the cache
29 | export const Delete = (key: string): any => {
30 | // DELETE
31 | return fetch(`${host}/delete?key=${key}`)
32 | .then(res => res.json())
33 | .then(res => res);
34 | }
--------------------------------------------------------------------------------
/cloud/wrappers/python/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2023 Tristan
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/cloud/wrappers/python/dist/hermescloud-0.0.6-py3-none-any.whl:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/realTristan/hermes/ad2f3c2255628af7372056976456e7395e9a7cb3/cloud/wrappers/python/dist/hermescloud-0.0.6-py3-none-any.whl
--------------------------------------------------------------------------------
/cloud/wrappers/python/dist/hermescloud-0.0.6.tar.gz:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/realTristan/hermes/ad2f3c2255628af7372056976456e7395e9a7cb3/cloud/wrappers/python/dist/hermescloud-0.0.6.tar.gz
--------------------------------------------------------------------------------
/cloud/wrappers/python/pyproject.toml:
--------------------------------------------------------------------------------
1 | [build-system]
2 | requires = ["setuptools>=61.0"]
3 | build-backend = "setuptools.build_meta"
--------------------------------------------------------------------------------
/cloud/wrappers/python/setup.cfg:
--------------------------------------------------------------------------------
1 | [metadata]
2 | name = hermescloud
3 | version = 0.0.6
4 | author = Tristan Simpson
5 | author_email = heytristaann@gmail.com
6 | description = Extremely fast full-text-search algorithm and caching system
7 | long_description = file: README.md
8 | long_description_content_type = text/markdown
9 | url = https://github.com/realTristan/Hermes
10 | project_urls =
11 | Bug Tracker = https://github.com/realTristan/Hermes/issues
12 | classifiers =
13 | Programming Language :: Python :: 3
14 | License :: OSI Approved :: MIT License
15 | Operating System :: OS Independent
16 |
17 | [options]
18 | package_dir =
19 | = src
20 | packages = find:
21 | python_requires = >=3.6
22 |
23 | [options.packages.find]
24 | where = src
25 |
26 | # python3 -m pip install --upgrade build
27 | # python3 -m pip install --upgrade twine
28 | # python3 -m build
29 | # python3 -m twine upload dist/*
--------------------------------------------------------------------------------
/cloud/wrappers/python/src/hermescloud.egg-info/SOURCES.txt:
--------------------------------------------------------------------------------
1 | LICENSE
2 | README.md
3 | pyproject.toml
4 | setup.cfg
5 | src/hermescloud/__init__.py
6 | src/hermescloud/cache.py
7 | src/hermescloud/utils.py
8 | src/hermescloud.egg-info/PKG-INFO
9 | src/hermescloud.egg-info/SOURCES.txt
10 | src/hermescloud.egg-info/dependency_links.txt
11 | src/hermescloud.egg-info/top_level.txt
--------------------------------------------------------------------------------
/cloud/wrappers/python/src/hermescloud.egg-info/dependency_links.txt:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/cloud/wrappers/python/src/hermescloud.egg-info/top_level.txt:
--------------------------------------------------------------------------------
1 | hermescloud
2 |
--------------------------------------------------------------------------------
/cloud/wrappers/python/src/hermescloud/__init__.py:
--------------------------------------------------------------------------------
1 | from .cache import *
--------------------------------------------------------------------------------
/cloud/wrappers/python/src/hermescloud/__pycache__/__init__.cpython-310.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/realTristan/hermes/ad2f3c2255628af7372056976456e7395e9a7cb3/cloud/wrappers/python/src/hermescloud/__pycache__/__init__.cpython-310.pyc
--------------------------------------------------------------------------------
/cloud/wrappers/python/src/hermescloud/__pycache__/cache.cpython-310.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/realTristan/hermes/ad2f3c2255628af7372056976456e7395e9a7cb3/cloud/wrappers/python/src/hermescloud/__pycache__/cache.cpython-310.pyc
--------------------------------------------------------------------------------
/cloud/wrappers/python/src/hermescloud/__pycache__/hermes.cpython-310.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/realTristan/hermes/ad2f3c2255628af7372056976456e7395e9a7cb3/cloud/wrappers/python/src/hermescloud/__pycache__/hermes.cpython-310.pyc
--------------------------------------------------------------------------------
/cloud/wrappers/python/src/hermescloud/__pycache__/utils.cpython-310.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/realTristan/hermes/ad2f3c2255628af7372056976456e7395e9a7cb3/cloud/wrappers/python/src/hermescloud/__pycache__/utils.cpython-310.pyc
--------------------------------------------------------------------------------
/cloud/wrappers/python/src/hermescloud/utils.py:
--------------------------------------------------------------------------------
1 | import base64
2 |
3 | class Utils:
4 | # Convert a string to base64
5 | @staticmethod
6 | def tob64(value: str):
7 | return base64.b64encode(value.encode("utf-8")).decode("utf-8")
8 |
--------------------------------------------------------------------------------
/cloud/wrappers/python/src/test_base.py:
--------------------------------------------------------------------------------
1 | from hermescloud import Cache
2 | import time
3 |
4 | # Create a new cache instance
5 | cache = Cache("localhost:3000")
6 |
7 | def main():
8 | # Initialize the full-text search engine
9 | print(cache.ft_init(-1, -1))
10 |
11 | # Set a value
12 | cache.set("user_id", {
13 | "name": {
14 | "$hermes.full_text": True,
15 | "$hermes.value": "tristan"
16 | }
17 | })
18 |
19 | # Get a value
20 | print(cache.get("user_id"))
21 |
22 | # Track the start time
23 | start_time = time.time()
24 |
25 | # Search for a value
26 | print(cache.ft_search("tristan", False, 100, {
27 | "name": True
28 | }))
29 |
30 | # Print the duration (average: 0.0006s)
31 | print(f"Duration: {time.time() - start_time}s")
32 |
33 | # Delete a value
34 | print(cache.delete("user_id"))
35 |
36 | # Get a value
37 | print(cache.get("user_id"))
38 |
39 | # Run the main function
40 | if __name__ == "__main__":
41 | main()
--------------------------------------------------------------------------------
/cloud/wrappers/python/src/test_withjson.py:
--------------------------------------------------------------------------------
1 | import hermescloud, json, base64, time
2 |
3 | # base64 encode a value
4 | def base64_encode(value):
5 | return base64.b64encode(value.encode("utf-8")).decode("utf-8")
6 |
7 | # Create a new cache instance
8 | cache = hermescloud.Cache("localhost:3000")
9 |
10 | # Initialize the full-text search engine
11 | #print(cache.ft_init(-1, -1))
12 |
13 | # open the data/data_hash.json file
14 | def set_data():
15 | with open("data/data_hash.json", "r") as file:
16 | # load the data_hash.json file
17 | data = json.loads(file.read())
18 |
19 | # loop through the data
20 | for key in data:
21 | # set the key
22 | cache.set(key, data[key])
23 |
24 | # set the data
25 | #set_data()
26 |
27 | # Track the start time
28 | start_time = time.time()
29 |
30 | # Search for a value
31 | r = cache.ft_search("computer", False, 100, {
32 | "id": False,
33 | "components": False,
34 | "units": False,
35 | "description": True,
36 | "name": True,
37 | "pre_requisites": True,
38 | "title": True
39 | })
40 |
41 | # Print the duration
42 | print(time.time() - start_time)
43 |
44 | # print the results
45 | #print(r[0])
46 |
--------------------------------------------------------------------------------
/cloud/wrappers/rust/Cargo.lock:
--------------------------------------------------------------------------------
1 | # This file is automatically @generated by Cargo.
2 | # It is not intended for manual editing.
3 | version = 3
4 |
5 | [[package]]
6 | name = "hermes"
7 | version = "0.1.0"
8 |
--------------------------------------------------------------------------------
/cloud/wrappers/rust/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "hermes"
3 | version = "0.1.0"
4 | edition = "2021"
5 |
6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
7 |
8 | [dependencies]
9 |
--------------------------------------------------------------------------------
/cloud/wrappers/rust/notes.txt:
--------------------------------------------------------------------------------
1 | make a rust crate for using Hermes
--------------------------------------------------------------------------------
/cloud/wrappers/rust/src/main.rs:
--------------------------------------------------------------------------------
1 | fn main() {
2 | println!("Hello, world!");
3 | }
4 |
--------------------------------------------------------------------------------
/cloud/wrappers/rust/target/.rustc_info.json:
--------------------------------------------------------------------------------
1 | {"rustc_fingerprint":472984446891056861,"outputs":{"10376369925670944939":{"success":true,"status":"","code":0,"stdout":"___\nlib___.rlib\nlib___.dylib\nlib___.dylib\nlib___.a\nlib___.dylib\n/Users/tristan/.rustup/toolchains/stable-x86_64-apple-darwin\ndebug_assertions\npanic=\"unwind\"\nproc_macro\ntarget_arch=\"x86_64\"\ntarget_endian=\"little\"\ntarget_env=\"\"\ntarget_family=\"unix\"\ntarget_feature=\"fxsr\"\ntarget_feature=\"sse\"\ntarget_feature=\"sse2\"\ntarget_feature=\"sse3\"\ntarget_feature=\"ssse3\"\ntarget_has_atomic=\"128\"\ntarget_has_atomic=\"16\"\ntarget_has_atomic=\"32\"\ntarget_has_atomic=\"64\"\ntarget_has_atomic=\"8\"\ntarget_has_atomic=\"ptr\"\ntarget_os=\"macos\"\ntarget_pointer_width=\"64\"\ntarget_vendor=\"apple\"\nunix\n","stderr":""},"15697416045686424142":{"success":true,"status":"","code":0,"stdout":"___\nlib___.rlib\nlib___.dylib\nlib___.dylib\nlib___.a\nlib___.dylib\n","stderr":""},"4614504638168534921":{"success":true,"status":"","code":0,"stdout":"rustc 1.66.0 (69f9c33d7 2022-12-12)\nbinary: rustc\ncommit-hash: 69f9c33d71c871fc16ac445211281c6e7a340943\ncommit-date: 2022-12-12\nhost: x86_64-apple-darwin\nrelease: 1.66.0\nLLVM version: 15.0.2\n","stderr":""}},"successes":{}}
--------------------------------------------------------------------------------
/cloud/wrappers/rust/target/CACHEDIR.TAG:
--------------------------------------------------------------------------------
1 | Signature: 8a477f597d28d172789f06886806bc55
2 | # This file is a cache directory tag created by cargo.
3 | # For information about cache directory tags see https://bford.info/cachedir/
4 |
--------------------------------------------------------------------------------
/cloud/wrappers/rust/target/debug/.cargo-lock:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/realTristan/hermes/ad2f3c2255628af7372056976456e7395e9a7cb3/cloud/wrappers/rust/target/debug/.cargo-lock
--------------------------------------------------------------------------------
/cloud/wrappers/rust/target/debug/.fingerprint/hermes-515e19fc54440f23/dep-test-bin-hermes:
--------------------------------------------------------------------------------
1 | src/main.rs
--------------------------------------------------------------------------------
/cloud/wrappers/rust/target/debug/.fingerprint/hermes-515e19fc54440f23/invoked.timestamp:
--------------------------------------------------------------------------------
1 | This file has an mtime of when this was started.
--------------------------------------------------------------------------------
/cloud/wrappers/rust/target/debug/.fingerprint/hermes-515e19fc54440f23/test-bin-hermes:
--------------------------------------------------------------------------------
1 | 6fc47864a16005b5
--------------------------------------------------------------------------------
/cloud/wrappers/rust/target/debug/.fingerprint/hermes-515e19fc54440f23/test-bin-hermes.json:
--------------------------------------------------------------------------------
1 | {"rustc":5585755218383029683,"features":"[]","target":11031198021622862316,"profile":11506243869495082934,"path":1684066648322511884,"deps":[],"local":[{"CheckDepInfo":{"dep_info":"debug/.fingerprint/hermes-515e19fc54440f23/dep-test-bin-hermes"}}],"rustflags":[],"metadata":7797948686568424061,"config":2202906307356721367,"compile_kind":0}
--------------------------------------------------------------------------------
/cloud/wrappers/rust/target/debug/.fingerprint/hermes-d80b4823bd3ba10f/bin-hermes:
--------------------------------------------------------------------------------
1 | 580e12857d03a55e
--------------------------------------------------------------------------------
/cloud/wrappers/rust/target/debug/.fingerprint/hermes-d80b4823bd3ba10f/bin-hermes.json:
--------------------------------------------------------------------------------
1 | {"rustc":5585755218383029683,"features":"[]","target":11031198021622862316,"profile":17483045194147818835,"path":1684066648322511884,"deps":[],"local":[{"CheckDepInfo":{"dep_info":"debug/.fingerprint/hermes-d80b4823bd3ba10f/dep-bin-hermes"}}],"rustflags":[],"metadata":7797948686568424061,"config":2202906307356721367,"compile_kind":0}
--------------------------------------------------------------------------------
/cloud/wrappers/rust/target/debug/.fingerprint/hermes-d80b4823bd3ba10f/dep-bin-hermes:
--------------------------------------------------------------------------------
1 | src/main.rs
--------------------------------------------------------------------------------
/cloud/wrappers/rust/target/debug/.fingerprint/hermes-d80b4823bd3ba10f/invoked.timestamp:
--------------------------------------------------------------------------------
1 | This file has an mtime of when this was started.
--------------------------------------------------------------------------------
/cloud/wrappers/rust/target/debug/deps/hermes-515e19fc54440f23.d:
--------------------------------------------------------------------------------
1 | /Users/tristan/Desktop/Hermes/rust/target/debug/deps/hermes-515e19fc54440f23.rmeta: src/main.rs
2 |
3 | /Users/tristan/Desktop/Hermes/rust/target/debug/deps/hermes-515e19fc54440f23.d: src/main.rs
4 |
5 | src/main.rs:
6 |
--------------------------------------------------------------------------------
/cloud/wrappers/rust/target/debug/deps/hermes-d80b4823bd3ba10f.d:
--------------------------------------------------------------------------------
1 | /Users/tristan/Desktop/Hermes/rust/target/debug/deps/hermes-d80b4823bd3ba10f.rmeta: src/main.rs
2 |
3 | /Users/tristan/Desktop/Hermes/rust/target/debug/deps/hermes-d80b4823bd3ba10f.d: src/main.rs
4 |
5 | src/main.rs:
6 |
--------------------------------------------------------------------------------
/cloud/wrappers/rust/target/debug/deps/libhermes-515e19fc54440f23.rmeta:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/realTristan/hermes/ad2f3c2255628af7372056976456e7395e9a7cb3/cloud/wrappers/rust/target/debug/deps/libhermes-515e19fc54440f23.rmeta
--------------------------------------------------------------------------------
/cloud/wrappers/rust/target/debug/deps/libhermes-d80b4823bd3ba10f.rmeta:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/realTristan/hermes/ad2f3c2255628af7372056976456e7395e9a7cb3/cloud/wrappers/rust/target/debug/deps/libhermes-d80b4823bd3ba10f.rmeta
--------------------------------------------------------------------------------
/cloud/wrappers/rust/target/debug/incremental/hermes-1b6yi2bw8b899/s-gkiytxaoyx-n4pevb-2nsnbq1tpmfaw/dep-graph.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/realTristan/hermes/ad2f3c2255628af7372056976456e7395e9a7cb3/cloud/wrappers/rust/target/debug/incremental/hermes-1b6yi2bw8b899/s-gkiytxaoyx-n4pevb-2nsnbq1tpmfaw/dep-graph.bin
--------------------------------------------------------------------------------
/cloud/wrappers/rust/target/debug/incremental/hermes-1b6yi2bw8b899/s-gkiytxaoyx-n4pevb-2nsnbq1tpmfaw/query-cache.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/realTristan/hermes/ad2f3c2255628af7372056976456e7395e9a7cb3/cloud/wrappers/rust/target/debug/incremental/hermes-1b6yi2bw8b899/s-gkiytxaoyx-n4pevb-2nsnbq1tpmfaw/query-cache.bin
--------------------------------------------------------------------------------
/cloud/wrappers/rust/target/debug/incremental/hermes-1b6yi2bw8b899/s-gkiytxaoyx-n4pevb-2nsnbq1tpmfaw/work-products.bin:
--------------------------------------------------------------------------------
1 | RSIC 1.66.0 (69f9c33d7 2022-12-12)
--------------------------------------------------------------------------------
/cloud/wrappers/rust/target/debug/incremental/hermes-1b6yi2bw8b899/s-gkiytxaoyx-n4pevb.lock:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/realTristan/hermes/ad2f3c2255628af7372056976456e7395e9a7cb3/cloud/wrappers/rust/target/debug/incremental/hermes-1b6yi2bw8b899/s-gkiytxaoyx-n4pevb.lock
--------------------------------------------------------------------------------
/cloud/wrappers/rust/target/debug/incremental/hermes-1msokizf0w59t/s-gkiytxaoyx-61kh8a-385m7v9kh6g64/dep-graph.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/realTristan/hermes/ad2f3c2255628af7372056976456e7395e9a7cb3/cloud/wrappers/rust/target/debug/incremental/hermes-1msokizf0w59t/s-gkiytxaoyx-61kh8a-385m7v9kh6g64/dep-graph.bin
--------------------------------------------------------------------------------
/cloud/wrappers/rust/target/debug/incremental/hermes-1msokizf0w59t/s-gkiytxaoyx-61kh8a-385m7v9kh6g64/query-cache.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/realTristan/hermes/ad2f3c2255628af7372056976456e7395e9a7cb3/cloud/wrappers/rust/target/debug/incremental/hermes-1msokizf0w59t/s-gkiytxaoyx-61kh8a-385m7v9kh6g64/query-cache.bin
--------------------------------------------------------------------------------
/cloud/wrappers/rust/target/debug/incremental/hermes-1msokizf0w59t/s-gkiytxaoyx-61kh8a-385m7v9kh6g64/work-products.bin:
--------------------------------------------------------------------------------
1 | RSIC 1.66.0 (69f9c33d7 2022-12-12)
--------------------------------------------------------------------------------
/cloud/wrappers/rust/target/debug/incremental/hermes-1msokizf0w59t/s-gkiytxaoyx-61kh8a.lock:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/realTristan/hermes/ad2f3c2255628af7372056976456e7395e9a7cb3/cloud/wrappers/rust/target/debug/incremental/hermes-1msokizf0w59t/s-gkiytxaoyx-61kh8a.lock
--------------------------------------------------------------------------------
/compression/gzip/gzip.go:
--------------------------------------------------------------------------------
1 | package gzip
2 |
3 | import (
4 | "bytes"
5 | "compress/gzip"
6 | "io"
7 | )
8 |
9 | // Compress is a function that compresses a string using gzip compression.
10 | //
11 | // Parameters:
12 | // - v: A string representing the value to compress.
13 | //
14 | // Returns:
15 | // - A byte slice representing the compressed value.
16 | // - An error if there was an error compressing the value.
17 | //
18 | // Example usage:
19 | //
20 | // compressed, err := Compress("value") // compressed == []byte{...}, err == nil
21 | func Compress(v []byte) ([]byte, error) {
22 | var (
23 | b *bytes.Buffer = new(bytes.Buffer)
24 | gz *gzip.Writer = gzip.NewWriter(b)
25 | )
26 | if _, err := gz.Write(v); err != nil {
27 | return nil, err
28 | }
29 | if err := gz.Close(); err != nil {
30 | return nil, err
31 | }
32 | return b.Bytes(), nil
33 | }
34 |
35 | // Decompress is a function that decompresses a byte slice using gzip decompression.
36 | //
37 | // Parameters:
38 | // - v: A byte slice representing the compressed value to decompress.
39 | //
40 | // Returns:
41 | // - A string representing the decompressed value.
42 | // - An error if there was an error decompressing the value.
43 | //
44 | // Example usage:
45 | //
46 | // decompressed, err := Decompress([]byte{...}) // decompressed == "value", err == nil
47 | func Decompress(v []byte) (string, error) {
48 | var b *bytes.Buffer = bytes.NewBuffer(v)
49 | if gz, err := gzip.NewReader(b); err != nil {
50 | return "", err
51 | } else if s, err := io.ReadAll(gz); err != nil {
52 | return "", err
53 | } else {
54 | return string(s), nil
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/compression/zlib/zlib.go:
--------------------------------------------------------------------------------
1 | package zlib
2 |
3 | import (
4 | "bytes"
5 | "compress/zlib"
6 | "io"
7 | )
8 |
9 | // Compress is a function that compresses a string using gzip compression.
10 | //
11 | // Parameters:
12 | // - v: A string representing the value to compress.
13 | //
14 | // Returns:
15 | // - A byte slice representing the compressed value.
16 | // - An error if there was an error compressing the value.
17 | //
18 | // Example usage:
19 | //
20 | // compressed, err := Compress("value") // compressed == []byte{...}, err == nil
21 | func Compress(v []byte) ([]byte, error) {
22 | var (
23 | b *bytes.Buffer = new(bytes.Buffer)
24 | gz *zlib.Writer = zlib.NewWriter(b)
25 | )
26 | if _, err := gz.Write(v); err != nil {
27 | return nil, err
28 | }
29 | if err := gz.Close(); err != nil {
30 | return nil, err
31 | }
32 | return b.Bytes(), nil
33 | }
34 |
35 | // Decompress is a function that decompresses a byte slice using gzip decompression.
36 | //
37 | // Parameters:
38 | // - v: A byte slice representing the compressed value to decompress.
39 | //
40 | // Returns:
41 | // - A string representing the decompressed value.
42 | // - An error if there was an error decompressing the value.
43 | //
44 | // Example usage:
45 | //
46 | // decompressed, err := Decompress([]byte{...}) // decompressed == "value", err == nil
47 | func Decompress(v []byte) (string, error) {
48 | var b *bytes.Buffer = bytes.NewBuffer(v)
49 | if gz, err := zlib.NewReader(b); err != nil {
50 | return "", err
51 | } else if s, err := io.ReadAll(gz); err != nil {
52 | return "", err
53 | } else {
54 | return string(s), nil
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/delete.go:
--------------------------------------------------------------------------------
1 | package hermes
2 |
3 | // Delete is a method of the Cache struct that removes a key from the cache.
4 | // If the full-text index is initialized, it is also removed from there.
5 | // This method is thread-safe.
6 | //
7 | // Parameters:
8 | // - key: A string representing the key to remove from the cache.
9 | //
10 | // Returns:
11 | // - None
12 | func (c *Cache) Delete(key string) {
13 | c.mutex.Lock()
14 | defer c.mutex.Unlock()
15 | c.delete(key)
16 | }
17 |
18 | // delete is a method of the Cache struct that removes a key from the cache.
19 | // If the full-text index is initialized, it is also removed from there.
20 | // This method is not thread-safe and should only be called from an exported function.
21 | //
22 | // Parameters:
23 | // - key: A string representing the key to remove from the cache.
24 | //
25 | // Returns:
26 | // - None
27 | func (c *Cache) delete(key string) {
28 | // Delete the key from the FT cache
29 | if c.ft != nil {
30 | c.ft.delete(key)
31 | }
32 |
33 | // Delete the key from the cache
34 | delete(c.data, key)
35 | }
36 |
37 | // delete is a method of the FullText struct that removes a key from the full-text storage.
38 | // This function is not thread-safe and should only be called from an exported function.
39 | //
40 | // Parameters:
41 | // - key: A string representing the key to remove from the full-text storage.
42 | //
43 | // Returns:
44 | // - None
45 | func (ft *FullText) delete(key string) {
46 | // Remove the key from the ft.storage
47 | for word, data := range ft.storage {
48 | // Check if the data is []int or int
49 | if _, ok := data.(int); ok {
50 | delete(ft.storage, word)
51 | continue
52 | }
53 |
54 | // If the data is []int, loop through the slice
55 | if keys, ok := data.([]int); !ok {
56 | for i := 0; i < len(keys); i++ {
57 | if key != ft.indices[keys[i]] {
58 | continue
59 | }
60 |
61 | // Remove the key from the ft.storage slice
62 | keys = append(keys[:i], keys[i+1:]...)
63 | ft.storage[word] = keys
64 | break
65 | }
66 |
67 | // If keys is empty, remove it from the storage
68 | if len(keys) == 0 {
69 | delete(ft.storage, word)
70 | } else if len(keys) == 1 {
71 | ft.storage[word] = keys[0]
72 | }
73 | }
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/docker-compose.yml:
--------------------------------------------------------------------------------
1 | version: "3"
2 | services:
3 | backend:
4 | build: .
5 | ports:
6 | - "3000:3000"
--------------------------------------------------------------------------------
/examples/basic/basic.go:
--------------------------------------------------------------------------------
1 | // ////////////////////////////////////////////////////////////////////////////
2 | //
3 | // Run Command: go run .
4 | //
5 | // Host URL: http://localhost:8000/courses?q=computer&limit=100&strict=false
6 | //
7 | // ////////////////////////////////////////////////////////////////////////////
8 | package main
9 |
10 | import (
11 | "fmt"
12 | "time"
13 |
14 | hermes "github.com/realTristan/hermes"
15 | )
16 |
17 | // Initialize the cache
18 | var cache *hermes.Cache = hermes.InitCache()
19 |
20 | func main() {
21 | // Initialize the full-text cache
22 | cache.FTInitWithJson("../../testing/data/data_hash.json", -1, -1, 3)
23 |
24 | // Search for a word in the cache
25 | var startTime time.Time = time.Now()
26 | var res, _ = cache.Search(hermes.SearchParams{
27 | Query: "computer",
28 | Limit: 100,
29 | Strict: false,
30 | })
31 | var duration time.Duration = time.Since(startTime)
32 | fmt.Println("Search took", duration)
33 | fmt.Println("Search results:", len(res))
34 | }
35 |
--------------------------------------------------------------------------------
/examples/router/router.go:
--------------------------------------------------------------------------------
1 | // /////////////////////////////////////////////////////////////////////////////
2 | //
3 | // Run Command: go run .
4 | //
5 | // Host URL: http://localhost:8000/courses?q=computer&limit=100&strict=false
6 | //
7 | // /////////////////////////////////////////////////////////////////////////////
8 | package main
9 |
10 | import (
11 | "encoding/json"
12 | "fmt"
13 | "net/http"
14 | "strconv"
15 | "time"
16 |
17 | hermes "github.com/realTristan/hermes"
18 | )
19 |
20 | // Global full text variable
21 | var cache *hermes.Cache
22 |
23 | // Main function
24 | func main() {
25 | cache = hermes.InitCache()
26 | cache.FTInitWithJson("../../testing/data/data_hash.json", -1, -1, 3)
27 |
28 | // Print host
29 | fmt.Println(" >> Listening on: http://localhost:8000/")
30 |
31 | // Listen and serve on port 8000
32 | http.HandleFunc("/courses", Handler)
33 | http.ListenAndServe(":8000", nil)
34 | }
35 |
36 | // Handle the incoming http request
37 | func Handler(w http.ResponseWriter, r *http.Request) {
38 | w.Header().Set("Access-Control-Allow-Origin", "*")
39 |
40 | // Get the query parameter
41 | var query string = "CS"
42 | if _query := r.URL.Query().Get("q"); _query != "" {
43 | query = _query
44 | }
45 |
46 | // Get the limit parameter
47 | var limit int = 100
48 | if _limit := r.URL.Query().Get("limit"); _limit != "" {
49 | limit, _ = strconv.Atoi(_limit)
50 | }
51 |
52 | // Get the strict parameter
53 | var strict bool = false
54 | if _strict := r.URL.Query().Get("strict"); _strict != "" {
55 | strict, _ = strconv.ParseBool(_strict)
56 | }
57 |
58 | // Track the start time
59 | var start time.Time = time.Now()
60 |
61 | // Search for a word in the cache
62 | // Make sure the show which keys you do want to search through,
63 | // and which ones you don't
64 | var res, _ = cache.Search(hermes.SearchParams{
65 | Query: query,
66 | Limit: limit,
67 | Strict: strict,
68 | })
69 |
70 | // Print the duration
71 | fmt.Printf("\nFound %v results in %v", len(res), time.Since(start))
72 |
73 | // Write the courses to the json response
74 | var response, _ = json.Marshal(res)
75 | w.Write(response)
76 | }
77 |
--------------------------------------------------------------------------------
/exists.go:
--------------------------------------------------------------------------------
1 | package hermes
2 |
3 | // Exists is a method of the Cache struct that checks if a key exists in the cache.
4 | // This method is thread-safe.
5 | //
6 | // Parameters:
7 | // - key: A string representing the key to check for existence in the cache.
8 | //
9 | // Returns:
10 | // - A boolean value indicating whether the key exists in the cache or not.
11 | func (c *Cache) Exists(key string) bool {
12 | c.mutex.RLock()
13 | defer c.mutex.RUnlock()
14 | return c.exists(key)
15 | }
16 |
17 | // exists is a method of the Cache struct that checks if a key exists in the cache.
18 | // This method is not thread-safe and should only be called from an exported function.
19 | //
20 | // Parameters:
21 | // - key: A string representing the key to check for existence in the cache.
22 | //
23 | // Returns:
24 | // - A boolean value indicating whether the key exists in the cache or not.
25 | func (c *Cache) exists(key string) bool {
26 | _, ok := c.data[key]
27 | return ok
28 | }
29 |
--------------------------------------------------------------------------------
/get.go:
--------------------------------------------------------------------------------
1 | package hermes
2 |
3 | // Get is a method of the Cache struct that retrieves the value associated with the given key from the cache.
4 | // This method is thread-safe.
5 | //
6 | // Parameters:
7 | // - key: A string representing the key to retrieve the value for.
8 | //
9 | // Returns:
10 | // - A map[string]any representing the value associated with the given key in the cache.
11 | func (c *Cache) Get(key string) map[string]any {
12 | c.mutex.RLock()
13 | defer c.mutex.RUnlock()
14 | return c.get(key)
15 | }
16 |
17 | // get is a method of the Cache struct that retrieves the value associated with the given key from the cache.
18 | // This method is not thread-safe and should only be called from an exported function.
19 | //
20 | // Parameters:
21 | // - key: A string representing the key to retrieve the value for.
22 | //
23 | // Returns:
24 | // - A map[string]any representing the value associated with the given key in the cache.
25 | func (c *Cache) get(key string) map[string]any {
26 | return c.data[key]
27 | }
28 |
--------------------------------------------------------------------------------
/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/realTristan/hermes
2 |
3 | go 1.20
4 |
5 | require (
6 | github.com/gofiber/fiber/v2 v2.45.0
7 | github.com/gofiber/websocket/v2 v2.2.0
8 | )
9 |
10 | require (
11 | github.com/andybalholm/brotli v1.0.5 // indirect
12 | github.com/fasthttp/websocket v1.5.3 // indirect
13 | github.com/google/uuid v1.3.0 // indirect
14 | github.com/klauspost/compress v1.16.5 // indirect
15 | github.com/mattn/go-colorable v0.1.13 // indirect
16 | github.com/mattn/go-isatty v0.0.18 // indirect
17 | github.com/mattn/go-runewidth v0.0.14 // indirect
18 | github.com/philhofer/fwd v1.1.2 // indirect
19 | github.com/rivo/uniseg v0.2.0 // indirect
20 | github.com/savsgio/dictpool v0.0.0-20221023140959-7bf2e61cea94 // indirect
21 | github.com/savsgio/gotils v0.0.0-20230208104028-c358bd845dee // indirect
22 | github.com/tinylib/msgp v1.1.8 // indirect
23 | github.com/valyala/bytebufferpool v1.0.0 // indirect
24 | github.com/valyala/fasthttp v1.47.0 // indirect
25 | github.com/valyala/tcplisten v1.0.0 // indirect
26 | golang.org/x/sys v0.8.0 // indirect
27 | )
28 |
--------------------------------------------------------------------------------
/go.work:
--------------------------------------------------------------------------------
1 | go 1.20
2 |
3 | use (
4 | ./
5 | ./cloud/app/server
6 | )
7 |
--------------------------------------------------------------------------------
/go.work.sum:
--------------------------------------------------------------------------------
1 | golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU=
2 | golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
3 | golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
4 |
--------------------------------------------------------------------------------
/indices.go:
--------------------------------------------------------------------------------
1 | package hermes
2 |
3 | // When you delete a number of keys from the cache, the index remains
4 | // the same. Over time, this number will grow to be very large, and will
5 | // cause the cache to use a lot of memory. This function resets the indices
6 | // to be sequential, starting from 0.
7 | // This function is thread-safe.
8 | func (c *Cache) FTSequenceIndices() {
9 | c.mutex.Lock()
10 | defer c.mutex.Unlock()
11 | c.ft.sequenceIndices()
12 | }
13 |
14 | // When you delete a number of keys from the cache, the index remains
15 | // the same. Over time, this number will grow to be very large, and will
16 | // cause the cache to use a lot of memory. This function resets the indices
17 | // to be sequential, starting from 0.
18 | // This function is not thread-safe, and should only be called from
19 | // an exported function.
20 | func (ft *FullText) sequenceIndices() {
21 | // Store the temp variables
22 | var (
23 | tempIndices map[int]string = make(map[int]string)
24 | tempindex int = 0
25 | tempKeys map[string]int = make(map[string]int)
26 | )
27 |
28 | // Fill the temp indices by iterating over the current
29 | // indices and adding them to the tempIndices map
30 | for _, value := range ft.indices {
31 | tempIndices[tempindex] = value
32 | tempindex++
33 | }
34 |
35 | // Fill the temp keys with the opposites of ft.indices
36 | for key, value := range tempIndices {
37 | tempKeys[value] = key
38 | }
39 |
40 | // Iterate over the ft storage
41 | for word, data := range ft.storage {
42 | // Check if the data is []int or int
43 | if v, ok := data.(int); ok {
44 | ft.storage[word] = tempKeys[ft.indices[v]]
45 | continue
46 | }
47 |
48 | // If the data is []int, loop through the slice
49 | if keys, ok := data.([]int); !ok {
50 | for i := 0; i < len(keys); i++ {
51 | var index int = keys[i]
52 |
53 | // Get the key from the old indices
54 | var key string = ft.indices[index]
55 |
56 | // Set the new index
57 | ft.storage[word].([]int)[i] = tempKeys[key]
58 | }
59 | }
60 | }
61 |
62 | // Set the old variables to the new variables
63 | ft.indices = tempIndices
64 | ft.index = tempindex
65 | }
66 |
--------------------------------------------------------------------------------
/info.go:
--------------------------------------------------------------------------------
1 | package hermes
2 |
3 | import (
4 | "errors"
5 |
6 | utils "github.com/realTristan/hermes/utils"
7 | )
8 |
9 | // Info is a method of the Cache struct that returns a map with the cache and full-text info.
10 | // This method is thread-safe.
11 | // An error is returned if the full-text index is not initialized.
12 | //
13 | // Returns:
14 | // - A map[string]any representing the cache and full-text info.
15 | // - An error if the full-text index is not initialized.
16 | func (c *Cache) Info() (map[string]any, error) {
17 | c.mutex.RLock()
18 | defer c.mutex.RUnlock()
19 | return c.info()
20 | }
21 |
22 | // info is a method of the Cache struct that returns a map with the cache and full-text info.
23 | // This method is not thread-safe, and should only be called from an exported function.
24 | // An error is returned if the full-text index is not initialized.
25 | //
26 | // Returns:
27 | // - A map[string]any representing the cache and full-text info.
28 | // - An error if the full-text index is not initialized.
29 | func (c *Cache) info() (map[string]any, error) {
30 | var info map[string]any = map[string]any{
31 | "keys": len(c.data),
32 | }
33 |
34 | // Check if the cache full-text has been initialized
35 | if c.ft == nil {
36 | return info, errors.New("full-text is not initialized")
37 | }
38 |
39 | // Add the full-text info to the map
40 | if size, err := utils.Size(c.ft.storage); err != nil {
41 | return info, err
42 | } else {
43 | // Add the full-text info to the map
44 | info["full-text"] = map[string]any{
45 | "keys": len(c.ft.storage),
46 | "index": c.ft.index,
47 | "size": size,
48 | }
49 | }
50 |
51 | // Return the info map
52 | return info, nil
53 | }
54 |
55 | // InfoForTesting is a method of the Cache struct that returns a map with the cache and full-text info for testing purposes.
56 | // This method is thread-safe.
57 | // An error is returned if the full-text index is not initialized.
58 | //
59 | // Returns:
60 | // - A map[string]any representing the cache and full-text info for testing purposes.
61 | // - An error if the full-text index is not initialized.
62 | func (c *Cache) InfoForTesting() (map[string]any, error) {
63 | c.mutex.RLock()
64 | defer c.mutex.RUnlock()
65 | return c.infoForTesting()
66 | }
67 |
68 | // infoForTesting is a method of the Cache struct that returns a map with the cache and full-text info for testing purposes.
69 | // This method is not thread-safe, and should only be called from an exported function.
70 | // An error is returned if the full-text index is not initialized.
71 | //
72 | // Returns:
73 | // - A map[string]any representing the cache and full-text info for testing purposes.
74 | // - An error if the full-text index is not initialized.
75 | func (c *Cache) infoForTesting() (map[string]any, error) {
76 | var info map[string]any = map[string]any{
77 | "keys": len(c.data),
78 | "data": c.data,
79 | }
80 |
81 | // Check if the cache full-text has been initialized
82 | if c.ft == nil {
83 | return info, errors.New("full-text is not initialized")
84 | }
85 |
86 | // Add the full-text info to the map
87 | if size, err := utils.Size(c.ft.storage); err != nil {
88 | return info, err
89 | } else {
90 | info["full-text"] = map[string]any{
91 | "keys": len(c.ft.storage),
92 | "index": c.ft.index,
93 | "size": size,
94 | "storage": c.ft.storage,
95 | "indices": c.ft.indices,
96 | }
97 | }
98 |
99 | // Return the info map
100 | return info, nil
101 | }
102 |
--------------------------------------------------------------------------------
/keys.go:
--------------------------------------------------------------------------------
1 | package hermes
2 |
3 | // Keys is a method of the Cache struct that returns all the keys in the cache.
4 | // This function is thread-safe.
5 | //
6 | // Returns:
7 | // - A slice of strings containing all the keys in the cache.
8 | func (c *Cache) Keys() []string {
9 | c.mutex.RLock()
10 | defer c.mutex.RUnlock()
11 | return c.keys()
12 | }
13 |
14 | // keys is a method of the Cache struct that returns all the keys in the cache.
15 | // This function is not thread-safe, and should only be called from an exported function.
16 | //
17 | // Returns:
18 | // - A slice of strings containing all the keys in the cache.
19 | func (c *Cache) keys() []string {
20 | keys := make([]string, 0, len(c.data))
21 | for key := range c.data {
22 | keys = append(keys, key)
23 | }
24 | return keys
25 | }
26 |
--------------------------------------------------------------------------------
/length.go:
--------------------------------------------------------------------------------
1 | package hermes
2 |
3 | // Length is a method of the Cache struct that returns the number of items stored in the cache.
4 | // This function is thread-safe.
5 | //
6 | // Returns:
7 | // - An integer representing the number of items stored in the cache.
8 | func (c *Cache) Length() int {
9 | c.mutex.RLock()
10 | defer c.mutex.RUnlock()
11 | return c.length()
12 | }
13 |
14 | // length is a method of the Cache struct that returns the number of items stored in the cache.
15 | // This function is not thread-safe, and should only be called from an exported function.
16 | //
17 | // Returns:
18 | // - An integer representing the number of items stored in the cache.
19 | func (c *Cache) length() int {
20 | return len(c.data)
21 | }
22 |
--------------------------------------------------------------------------------
/nocache/examples/basic/basic.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | "time"
6 |
7 | hermes "github.com/realTristan/hermes/nocache"
8 | )
9 |
10 | // Main function
11 | func main() {
12 | // Define variables
13 | var (
14 | // Initialize the full text
15 | ft, _ = hermes.InitWithJson("../../../testing/data/data_array.json", 3)
16 |
17 | // Track the start time
18 | start time.Time = time.Now()
19 |
20 | // Search for a word in the cache
21 | // @params: query, limit, strict
22 | res, _ = ft.Search(hermes.SearchParams{
23 | Query: "computer",
24 | Limit: 100,
25 | Strict: false,
26 | })
27 | )
28 |
29 | // Print the duration
30 | fmt.Printf("\nFound %v results in %v", len(res), time.Since(start))
31 |
32 | // Search in values with key
33 | var (
34 | // Track the start time
35 | start2 time.Time = time.Now()
36 |
37 | // Search for a word in the cache
38 | res2, _ = ft.SearchWithKey("CS", "title", 100)
39 | )
40 |
41 | // Print the duration
42 | fmt.Printf("\nFound %v results in %v", len(res2), time.Since(start2))
43 | }
44 |
--------------------------------------------------------------------------------
/nocache/examples/router/router.go:
--------------------------------------------------------------------------------
1 | // /////////////////////////////////////////////////////////////////////////////
2 | //
3 | // Run Command: go run .
4 | //
5 | // Host URL: http://localhost:8000/courses?q=computer&limit=100&strict=false
6 | //
7 | // /////////////////////////////////////////////////////////////////////////////
8 | package main
9 |
10 | import (
11 | "encoding/json"
12 | "fmt"
13 | "net/http"
14 | "strconv"
15 | "time"
16 |
17 | hermes "github.com/realTristan/hermes/nocache"
18 | )
19 |
20 | // Global full text variable
21 | var ft *hermes.FullText
22 |
23 | // Main function
24 | func main() {
25 | ft, _ = hermes.InitWithJson("../../../testing/data/data_array.json", 3)
26 |
27 | // Print host
28 | fmt.Println(" >> Listening on: http://localhost:8000/")
29 |
30 | // Listen and serve on port 8000
31 | http.HandleFunc("/courses", Handler)
32 | http.ListenAndServe(":8000", nil)
33 | }
34 |
35 | // Handle the incoming http request
36 | func Handler(w http.ResponseWriter, r *http.Request) {
37 | w.Header().Set("Access-Control-Allow-Origin", "*")
38 |
39 | // Get the query parameter
40 | var query string = "CS"
41 | if _query := r.URL.Query().Get("q"); _query != "" {
42 | query = _query
43 | }
44 |
45 | // Get the limit parameter
46 | var limit int = 100
47 | if _limit := r.URL.Query().Get("limit"); _limit != "" {
48 | limit, _ = strconv.Atoi(_limit)
49 | }
50 |
51 | // Get the strict parameter
52 | var strict bool = false
53 | if _strict := r.URL.Query().Get("strict"); _strict != "" {
54 | strict, _ = strconv.ParseBool(_strict)
55 | }
56 |
57 | // Track the start time
58 | var start time.Time = time.Now()
59 |
60 | // Search for a word in the cache
61 | // Make sure the show which keys you do want to search through,
62 | // and which ones you don't
63 | var res, _ = ft.Search(hermes.SearchParams{
64 | Query: query,
65 | Limit: limit,
66 | Strict: strict,
67 | })
68 |
69 | // Print the duration
70 | fmt.Printf("\nFound %v results in %v", len(res), time.Since(start))
71 |
72 | // Write the courses to the json response
73 | var response, _ = json.Marshal(res)
74 | w.Write(response)
75 | }
76 |
--------------------------------------------------------------------------------
/nocache/fulltext.go:
--------------------------------------------------------------------------------
1 | package nocache
2 |
3 | import (
4 | "sync"
5 | )
6 |
7 | /*
8 | FullText is a struct that represents a full text search cache. It has the following fields:
9 | - mutex (*sync.RWMutex): a pointer to a read-write mutex used to synchronize access to the cache
10 | - storage (map[string]any): a map where the keys are words and the values are arrays of integers representing the indices of the data items that contain the word
11 | - words ([]string): a slice of strings representing all the unique words in the cache
12 | - data ([]map[string]any): a slice of maps representing the data items in the cache, where the keys are the names of the fields and the values are the field values
13 | */
14 | type FullText struct {
15 | mutex *sync.RWMutex
16 | storage map[string]any
17 | words []string
18 | data []map[string]any
19 | }
20 |
--------------------------------------------------------------------------------
/nocache/init.go:
--------------------------------------------------------------------------------
1 | package nocache
2 |
3 | import (
4 | "sync"
5 |
6 | utils "github.com/realTristan/hermes/utils"
7 | )
8 |
9 | // Initialize the full-text cache with the provided data.
10 | // This function is thread safe.
11 | func InitWithMapSlice(data []map[string]any, minWordLength int) (*FullText, error) {
12 | var ft *FullText = &FullText{
13 | mutex: &sync.RWMutex{},
14 | storage: make(map[string]any),
15 | words: []string{},
16 | data: []map[string]any{},
17 | }
18 |
19 | // Load the cache data
20 | if err := ft.insert(data, minWordLength); err != nil {
21 | return nil, err
22 | }
23 |
24 | // Set the data
25 | ft.data = data
26 |
27 | // Return the full-text variable
28 | return ft, nil
29 | }
30 |
31 | // Initialize the full-text cache with the provided json file.
32 | // This function is thread safe.
33 | func InitWithJson(file string, minWordLength int) (*FullText, error) {
34 | // Read the json data
35 | if data, err := utils.ReadJson[[]map[string]any](file); err != nil {
36 | return nil, err
37 | } else {
38 | return InitWithMapSlice(data, minWordLength)
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/nocache/insert.go:
--------------------------------------------------------------------------------
1 | package nocache
2 |
3 | import (
4 | "strings"
5 |
6 | utils "github.com/realTristan/hermes/utils"
7 | )
8 |
9 | // Insert data into the full-text cache.
10 | // This function is not thread-safe, and should only be called from
11 | // an exported function.
12 | func (ft *FullText) insert(data []map[string]any, minWordLength int) error {
13 | // Loop through the data
14 | for i, item := range data {
15 | // Loop through the map
16 | for key, value := range item {
17 | // Get the string value
18 | var (
19 | strvNormal string
20 | strv string
21 | )
22 | if _strv := WFTGetValueFromMap(value); len(_strv) > 0 {
23 | strv = _strv
24 | strvNormal = _strv
25 | } else {
26 | continue
27 | }
28 |
29 | // Clean the value
30 | strv = strings.TrimSpace(strv)
31 | strv = utils.RemoveDoubleSpaces(strv)
32 | strv = strings.ToLower(strv)
33 |
34 | // Loop through the words
35 | for _, word := range strings.Split(strv, " ") {
36 | if len(word) == 0 {
37 | continue
38 | }
39 |
40 | // Trim the word
41 | word = utils.TrimNonAlphaNum(word)
42 | var words []string = utils.SplitByAlphaNum(word)
43 |
44 | // Loop through the words
45 | for j := 0; j < len(words); j++ {
46 | if len(words[j]) < minWordLength {
47 | continue
48 | }
49 | if temp, ok := ft.storage[words[j]]; !ok {
50 | ft.storage[words[j]] = []int{i}
51 | ft.words = append(ft.words, words[j])
52 | } else if indices, ok := temp.([]int); !ok {
53 | ft.storage[words[j]] = []int{temp.(int), i}
54 | } else {
55 | if utils.SliceContains(indices, i) {
56 | continue
57 | }
58 | ft.storage[words[j]] = append(indices, i)
59 | }
60 | }
61 | }
62 |
63 | // Iterate over the temp storage and set the values with len 1 to int
64 | for k, v := range ft.storage {
65 | if v, ok := v.([]int); ok && len(v) == 1 {
66 | ft.storage[k] = v[0]
67 | }
68 | }
69 |
70 | // Set the value
71 | data[i][key] = strvNormal
72 | }
73 | }
74 | return nil
75 | }
76 |
--------------------------------------------------------------------------------
/nocache/search.go:
--------------------------------------------------------------------------------
1 | package nocache
2 |
3 | import (
4 | "errors"
5 | "strings"
6 | )
7 |
8 | // Search searches for all occurrences of the given query string in the FullText object's data.
9 | // The search is done by splitting the query into separate words and looking for each of them in the data.
10 | // The search result is limited to the specified number of entries.
11 | // Parameters:
12 | // - sp (SearchParams): A SearchParams struct containing the search parameters.
13 | //
14 | // Returns:
15 | // - []map[string]any: A slice of maps where each map represents a data record that matches the given query.
16 | // The keys of the map correspond to the column names of the data that were searched and returned in the result.
17 | // - error: An error if the query or limit is invalid.
18 | func (ft *FullText) Search(sp SearchParams) ([]map[string]any, error) {
19 | switch {
20 | case len(sp.Query) == 0:
21 | return []map[string]any{}, errors.New("invalid query")
22 | case sp.Limit < 1:
23 | return []map[string]any{}, errors.New("invalid limit")
24 | }
25 |
26 | // Convert the query to lowercase
27 | sp.Query = strings.ToLower(sp.Query)
28 |
29 | // Lock the mutex
30 | ft.mutex.RLock()
31 | defer ft.mutex.RUnlock()
32 |
33 | // Perform the search
34 | return ft.search(sp), nil
35 | }
36 |
37 | // search searches for all occurrences of the given query string in the FullText object's data.
38 | // The search is done by splitting the query into separate words and looking for each of them in the data.
39 | // The search result is limited to the specified number of entries.
40 | // Parameters:
41 | // - sp (SearchParams): A SearchParams struct containing the search parameters.
42 | //
43 | // Returns:
44 | // - []map[string]any: A slice of maps where each map represents a data record that matches the given query.
45 | // The keys of the map correspond to the column names of the data that were searched and returned in the result.
46 | // - error: An error if the query or limit is invalid.
47 | func (ft *FullText) search(sp SearchParams) []map[string]any {
48 | // Split the query into separate words
49 | var words []string = strings.Split(strings.TrimSpace(sp.Query), " ")
50 | switch {
51 | // If the words array is empty
52 | case len(words) == 0:
53 | return []map[string]any{}
54 | // Get the search result of the first word
55 | case len(words) == 1:
56 | sp.Query = words[0]
57 | return ft.searchOneWord(sp)
58 | }
59 |
60 | // Check if the query is in the cache
61 | if _, ok := ft.storage[words[0]]; !ok {
62 | return []map[string]any{}
63 | }
64 |
65 | // Define variables
66 | var result []map[string]any = []map[string]any{}
67 |
68 | // Variables for storing the smallest words array
69 | var (
70 | smallest int = 0 //nolint:ineffassign
71 | smallestIndex int = 0
72 | )
73 |
74 | // Check if the query is in the cache
75 | if v, ok := ft.storage[words[0]]; !ok {
76 | return []map[string]any{}
77 | } else {
78 | if index, ok := v.(int); ok {
79 | return []map[string]any{
80 | ft.data[index],
81 | }
82 | }
83 | smallest = len(v.([]int))
84 | }
85 |
86 | // Find the smallest words array
87 | // Don't include the first or last words from the query
88 | for i := 1; i < len(words)-1; i++ {
89 | if v, ok := ft.storage[words[i]]; ok {
90 | if v, ok := v.(int); ok {
91 | return []map[string]any{
92 | ft.data[v],
93 | }
94 | }
95 | if l := len(v.([]int)); l < smallest {
96 | smallest = l
97 | smallestIndex = i
98 | }
99 | }
100 | }
101 |
102 | // Loop through the indices
103 | var indices []int = ft.storage[words[smallestIndex]].([]int)
104 | for i := 0; i < len(indices); i++ {
105 | for _, value := range ft.data[indices[i]] {
106 | if v, ok := value.(string); !ok {
107 | continue
108 | } else if strings.Contains(strings.ToLower(v), sp.Query) {
109 | result = append(result, ft.data[indices[i]])
110 | }
111 | }
112 | }
113 |
114 | // Return the result
115 | return result
116 | }
117 |
--------------------------------------------------------------------------------
/nocache/searchoneword.go:
--------------------------------------------------------------------------------
1 | package nocache
2 |
3 | import (
4 | "errors"
5 | "strings"
6 |
7 | utils "github.com/realTristan/hermes/utils"
8 | )
9 |
10 | // SearchOneWord searches for a single query within the data using a full-text search approach.
11 | // The search result is limited to the specified number of entries, and can optionally be filtered to only include exact matches.
12 | // Parameters:
13 | // - query (string): The search query to use. This string will be searched for as a single word in any value associated with any key in each entry of the data.
14 | // - limit (int): The maximum number of search results to return. If the number of matching results exceeds this limit, the excess results will be ignored.
15 | // - strict (bool): If true, only exact matches will be returned. If false, partial matches will also be returned.
16 | //
17 | // Returns:
18 | // - []map[string]any: An array of maps representing the search results. Each map contains key-value pairs
19 | // from the entry in the data that matched the search query. If no results are found, an empty array is returned.
20 | // - error: An error object. If no error occurs, this will be nil.
21 | //
22 | // Note: The search is case-insensitive.
23 | func (ft *FullText) SearchOneWord(sp SearchParams) ([]map[string]any, error) {
24 | switch {
25 | case len(sp.Query) == 0:
26 | return []map[string]any{}, errors.New("invalid query")
27 | case sp.Limit < 1:
28 | return []map[string]any{}, errors.New("invalid limit")
29 | }
30 |
31 | // Set the query to lowercase
32 | sp.Query = strings.ToLower(sp.Query)
33 |
34 | // Lock the mutex
35 | ft.mutex.RLock()
36 | defer ft.mutex.RUnlock()
37 |
38 | // Search the data
39 | return ft.searchOneWord(sp), nil
40 | }
41 |
42 | // searchOneWord searches for a single query within the data using a full-text search approach.
43 | // The search result is limited to the specified number of entries, and can optionally be filtered to only include exact matches.
44 | // Parameters:
45 | // - query (string): The search query to use. This string will be searched for as a single word in any value associated with any key in each entry of the data.
46 | // - limit (int): The maximum number of search results to return. If the number of matching results exceeds this limit, the excess results will be ignored.
47 | // - strict (bool): If true, only exact matches will be returned. If false, partial matches will also be returned.
48 | //
49 | // Returns:
50 | // - []map[string]any: An array of maps representing the search results. Each map contains key-value pairs
51 | // from the entry in the data that matched the search query. If no results are found, an empty array is returned.
52 | //
53 | // Note: The search is case-insensitive.
54 | func (ft *FullText) searchOneWord(sp SearchParams) []map[string]any {
55 | // Define the result variable
56 | var result []map[string]any = []map[string]any{}
57 |
58 | // If the user wants a strict search, just return the result
59 | // straight from the cache
60 | if sp.Strict {
61 | return ft.searchOneWordStrict(result, sp)
62 | }
63 |
64 | // true for already checked
65 | var alreadyAdded map[int]int = map[int]int{}
66 |
67 | // Loop through the cache keys
68 | for i := 0; i < len(ft.words); i++ {
69 | switch {
70 | case len(result) >= sp.Limit:
71 | return result
72 | case !utils.Contains(ft.words[i], sp.Query):
73 | continue
74 | }
75 |
76 | // Loop through the cache indices
77 | if v, ok := ft.storage[ft.words[i]].(int); ok {
78 | if _, ok := alreadyAdded[v]; ok {
79 | continue
80 | }
81 | result = append(result, ft.data[v])
82 | alreadyAdded[v] = 0
83 | continue
84 | }
85 |
86 | var indices []int = ft.storage[ft.words[i]].([]int)
87 | for j := 0; j < len(indices); j++ {
88 | if _, ok := alreadyAdded[indices[j]]; ok {
89 | continue
90 | }
91 | result = append(result, ft.data[indices[j]])
92 | alreadyAdded[indices[j]] = 0
93 | }
94 | }
95 |
96 | // Return the result
97 | return result
98 | }
99 |
100 | // searchOneWordStrict is a method of the FullText struct that searches for a single word in the full-text cache and returns the results.
101 | //
102 | // Parameters:
103 | // - result: A slice of map[string]any representing the current search results.
104 | // - query: A string representing the word to search for.
105 | // - limit: An integer representing the maximum number of results to return.
106 | //
107 | // Returns:
108 | // - A slice of map[string]any representing the search results.
109 | func (ft *FullText) searchOneWordStrict(result []map[string]any, sp SearchParams) []map[string]any {
110 | // Check if the query is in the cache
111 | if _, ok := ft.storage[sp.Query]; !ok {
112 | return result
113 | }
114 |
115 | // Check if the cache value is an integer
116 | if v, ok := ft.storage[sp.Query].(int); ok {
117 | return []map[string]any{ft.data[v]}
118 | }
119 |
120 | // Loop through the indices
121 | var indices []int = ft.storage[sp.Query].([]int)
122 | for i := 0; i < len(indices); i++ {
123 | if len(result) >= sp.Limit {
124 | return result
125 | }
126 | result = append(result, ft.data[indices[i]])
127 | }
128 |
129 | // Return the result
130 | return result
131 | }
132 |
--------------------------------------------------------------------------------
/nocache/searchparams.go:
--------------------------------------------------------------------------------
1 | package nocache
2 |
3 | // SearchParams is a struct that contains the search parameters for the Cache search methods.
4 | type SearchParams struct {
5 | // The search query
6 | Query string
7 | // The limit of search results to return
8 | Limit int
9 | // A boolean to indicate whether the search should be strict or not
10 | Strict bool
11 | // A map containing the schema to search for
12 | Schema map[string]bool
13 | // Key to search in
14 | Key string
15 | }
16 |
--------------------------------------------------------------------------------
/nocache/searchvalues.go:
--------------------------------------------------------------------------------
1 | package nocache
2 |
3 | import (
4 | "errors"
5 | "strings"
6 | )
7 |
8 | // SearchValues searches for all occurrences of the given query string in the FullText object's data.
9 | // The search is done by splitting the query into separate words and looking for each of them in the data.
10 | // The search result is limited to the specified number of entries, and can optionally be filtered to only
11 | // include keys that match a given schema.
12 | // Parameters:
13 | // - sp (SearchParams): A SearchParams struct containing the search parameters.
14 | //
15 | // Returns:
16 | // - []map[string]any: An array of maps representing the search results. Each map contains key-value pairs
17 | // from the entry in the data that matched the search query. If no results are found, an empty array is returned.
18 | // - error: An error object. If no error occurs, this will be nil.
19 | //
20 | // Note: The search is case-insensitive.
21 | func (ft *FullText) SearchValues(sp SearchParams) ([]map[string]any, error) {
22 | switch {
23 | case len(sp.Query) == 0:
24 | return []map[string]any{}, errors.New("invalid query")
25 | case sp.Limit < 1:
26 | return []map[string]any{}, errors.New("invalid limit")
27 | }
28 |
29 | // Set the query to lowercase
30 | sp.Query = strings.ToLower(sp.Query)
31 |
32 | // Lock the mutex
33 | ft.mutex.RLock()
34 | defer ft.mutex.RUnlock()
35 |
36 | // Search the data
37 | return ft.searchValues(sp), nil
38 | }
39 |
40 | // searchValues searches for all occurrences of the given query string in the FullText object's data.
41 | // The search is done by looking for the query as a substring in any value associated with any key in each entry of the data.
42 | // The search result is limited to the specified number of entries, and can optionally be filtered to only include keys that match a given schema.
43 | // Parameters:
44 | // - sp (SearchParams): A SearchParams struct containing the search parameters.
45 | //
46 | // Returns:
47 | // - []map[string]any: An array of maps representing the search results. Each map contains key-value pairs
48 | // from the entry in the data that matched the search query. If no results are found, an empty array is returned.
49 | //
50 | // Note: The search is case-insensitive.
51 | func (ft *FullText) searchValues(sp SearchParams) []map[string]any {
52 | // Define variables
53 | var result []map[string]any = []map[string]any{}
54 |
55 | // Iterate over the query result
56 | for i := 0; i < len(ft.data); i++ {
57 | // Iterate over the keys and values for the data
58 | for key, value := range ft.data[i] {
59 | if v, ok := value.(string); !ok {
60 | continue
61 | } else {
62 | switch {
63 | case len(result) >= sp.Limit:
64 | return result
65 | case !sp.Schema[key]:
66 | continue
67 | case strings.Contains(strings.ToLower(v), sp.Query):
68 | result = append(result, ft.data[i])
69 | }
70 | }
71 | }
72 | }
73 |
74 | // Return the result
75 | return result
76 | }
77 |
--------------------------------------------------------------------------------
/nocache/searchwithkey.go:
--------------------------------------------------------------------------------
1 | package nocache
2 |
3 | import (
4 | "errors"
5 | "strings"
6 | )
7 |
8 | // SearchWithKey searches for all records containing the given query in the specified key column with a limit of results to return.
9 | // The search result is limited to the specified number of entries.
10 | // Parameters:
11 | // - query (string): The search query to use. This string will be searched for as a substring in the data value associated with the given key.
12 | // - key (string): The name of the key in the data whose data value should be searched.
13 | // - limit (int): The maximum number of search results to return. If the number of matching results exceeds this limit, the excess results will be ignored.
14 | //
15 | // Returns:
16 | // - []map[string]any: A slice of maps where each map represents a data record that matches the given query.
17 | // The keys of the map correspond to the column names of the data that were searched and returned in the result.
18 | // - error: An error if the query, key or limit is invalid.
19 | func (ft *FullText) SearchWithKey(query string, key string, limit int) ([]map[string]any, error) {
20 | switch {
21 | case len(key) == 0:
22 | return []map[string]any{}, errors.New("invalid key")
23 | case len(query) == 0:
24 | return []map[string]any{}, errors.New("invalid query")
25 | case limit < 1:
26 | return []map[string]any{}, errors.New("invalid limit")
27 | }
28 |
29 | // Set the query to lowercase
30 | query = strings.ToLower(query)
31 |
32 | // Lock the mutex
33 | ft.mutex.RLock()
34 | defer ft.mutex.RUnlock()
35 |
36 | // Search for the query
37 | return ft.searchWithKey(query, key, limit), nil
38 | }
39 |
40 | // searchWithKey searches for all occurrences of the given query string in the FullText object's data associated with the specified key.
41 | // The search is done by looking for the query as a substring in the data value associated with the given key.
42 | // The search result is limited to the specified number of entries.
43 | // Parameters:
44 | // - query (string): The search query to use. This string will be searched for as a substring in the data value associated with the given key.
45 | // - key (string): The name of the key in the data whose data value should be searched.
46 | // - limit (int): The maximum number of search results to return. If the number of matching results exceeds this limit, the excess results will be ignored.
47 | //
48 | // Returns:
49 | // - []map[string]any: An array of maps representing the search results. Each map contains key-value pairs
50 | // from the entry in the data that matched the search query. If no results are found, an empty array is returned.
51 | // - error: An error object. If no error occurs, this will be nil.
52 | //
53 | // Note: The search is case-insensitive.
54 | func (ft *FullText) searchWithKey(query string, key string, limit int) []map[string]any {
55 | // Define variables
56 | var result []map[string]any = []map[string]any{}
57 |
58 | // Iterate over the query result
59 | for i := 0; i < len(ft.data); i++ {
60 | if v, ok := ft.data[i][key].(string); !ok {
61 | continue
62 | } else {
63 | switch {
64 | case len(result) >= limit:
65 | return result
66 | case strings.Contains(strings.ToLower(v), query):
67 | result = append(result, ft.data[i])
68 | }
69 | }
70 | }
71 |
72 | // Return the result
73 | return result
74 | }
75 |
--------------------------------------------------------------------------------
/nocache/withft.go:
--------------------------------------------------------------------------------
1 | package nocache
2 |
3 | // Get the full-text value from a map
4 | func WFTGetValueFromMap(value any) string {
5 | if _, ok := value.(map[string]any); !ok {
6 | return ""
7 | }
8 |
9 | // Verify that the map has the correct length
10 | var v map[string]any = value.(map[string]any)
11 | if len(v) != 2 {
12 | return ""
13 | }
14 |
15 | // Verify that the map has the correct keys
16 | if ft, ok := v["$hermes.full_text"]; ok {
17 | if ft, ok := ft.(bool); ok && ft {
18 | if v, ok := v["$hermes.value"]; ok {
19 | if v, ok := v.(string); ok {
20 | return v
21 | }
22 | }
23 | }
24 | }
25 | return ""
26 | }
27 |
--------------------------------------------------------------------------------
/search.go:
--------------------------------------------------------------------------------
1 | package hermes
2 |
3 | import (
4 | "errors"
5 | "strings"
6 | )
7 |
8 | // Search is a method of the Cache struct that searches for a query by splitting the query into separate words and returning the search results.
9 | // Parameters:
10 | // - c (c *Cache): A pointer to the Cache struct
11 | // - sp (SearchParams): A SearchParams struct containing the search parameters.
12 | //
13 | // Returns:
14 | // - []map[string]any: A slice of maps containing the search results.
15 | // - error: An error if the query is invalid or if the smallest words array is not found in the cache.
16 | func (c *Cache) Search(sp SearchParams) ([]map[string]any, error) {
17 | // If the query is empty, return an error
18 | if len(sp.Query) == 0 {
19 | return []map[string]any{}, errors.New("invalid query")
20 | }
21 |
22 | // If no limit is provided, set it to 10
23 | if sp.Limit == 0 {
24 | sp.Limit = 10
25 | }
26 |
27 | // Lock the mutex
28 | c.mutex.RLock()
29 | defer c.mutex.RUnlock()
30 |
31 | // Check if the FT index is initialized
32 | if c.ft == nil {
33 | return []map[string]any{}, errors.New("full-text not initialized")
34 | }
35 |
36 | // Set the query to lowercase
37 | sp.Query = strings.ToLower(sp.Query)
38 |
39 | // Search for the query
40 | return c.search(sp), nil
41 | }
42 |
43 | // search is a method of the Cache struct that searches for a query by splitting the query into separate words and returning the search results.
44 | // Parameters:
45 | // - c (c *Cache): A pointer to the Cache struct
46 | // - sp (SearchParams): A SearchParams struct containing the search parameters.
47 | //
48 | // Returns:
49 | // - []map[string]any: A slice of maps containing the search results.
50 | func (c *Cache) search(sp SearchParams) []map[string]any {
51 | // Split the query into separate words
52 | var words []string = strings.Split(strings.TrimSpace(sp.Query), " ")
53 | switch {
54 | // If the words array is empty
55 | case len(words) == 0:
56 | return []map[string]any{}
57 | // Get the search result of the first word
58 | case len(words) == 1:
59 | sp.Query = words[0]
60 | return c.searchOneWord(sp)
61 | }
62 |
63 | // Define variables
64 | var result []map[string]any = []map[string]any{}
65 |
66 | // Variables for storing the smallest words array
67 | // var smallestData []int = []int{}
68 | var (
69 | smallestIndex int = 0
70 | smallest int = 0
71 | )
72 |
73 | // Check if the query is in the cache
74 | if indices, ok := c.ft.storage[words[0]]; !ok {
75 | return []map[string]any{}
76 | } else {
77 | /*for {
78 | if v, ok := indices.(string); ok {
79 | indices = c.ft.storage[v]
80 | } else {
81 | break
82 | }
83 | }*/
84 | if temp, ok := indices.(int); ok {
85 | return []map[string]any{
86 | c.data[c.ft.indices[temp]],
87 | }
88 | }
89 | // smallestData = indices.([]int)
90 | smallest = len(indices.([]int))
91 | }
92 |
93 | // Find the smallest words array
94 | // Don't include the first or last words from the query
95 | for i := 1; i < len(words)-1; i++ {
96 | if indices, ok := c.ft.storage[words[i]]; ok {
97 | /*for {
98 | if v, ok := indices.(string); ok {
99 | indices = c.ft.storage[v]
100 | } else {
101 | break
102 | }
103 | }*/
104 | if index, ok := indices.(int); ok {
105 | return []map[string]any{
106 | c.data[c.ft.indices[index]],
107 | }
108 | }
109 | /*if l := len(indices.([]int)); l < len(smallestData) {
110 | smallestData = indices.([]int)
111 | }*/
112 | if l := len(indices.([]int)); l < smallest {
113 | smallest = l
114 | smallestIndex = i
115 | }
116 | }
117 | }
118 |
119 | // Loop through the indices
120 | /*for i := 0; i < len(smallestData); i++ {
121 | for _, value := range c.data[c.ft.indices[smallestData[i]]] {
122 | // Check if the value contains the query
123 | if v, ok := value.(string); ok {
124 | if strings.Contains(strings.ToLower(v), sp.Query) {
125 | result = append(result, c.data[c.ft.indices[smallestData[i]]])
126 | }
127 | }
128 | }
129 | }*/
130 | var keys []int = c.ft.storage[words[smallestIndex]].([]int)
131 | for i := 0; i < len(keys); i++ {
132 | for _, value := range c.data[c.ft.indices[keys[i]]] {
133 | // Check if the value contains the query
134 | if v, ok := value.(string); ok {
135 | if strings.Contains(strings.ToLower(v), sp.Query) {
136 | result = append(result, c.data[c.ft.indices[keys[i]]])
137 | }
138 | }
139 | }
140 | }
141 |
142 | // Return the result
143 | return result
144 | }
145 |
--------------------------------------------------------------------------------
/searchoneword.go:
--------------------------------------------------------------------------------
1 | package hermes
2 |
3 | import (
4 | "errors"
5 | "strings"
6 |
7 | utils "github.com/realTristan/hermes/utils"
8 | )
9 |
10 | // SearchOneWord searches for a single word in the FullText struct's data and returns a list of maps containing the search results.
11 | // Parameters:
12 | // - c (c *Cache): A pointer to the Cache struct
13 | // - sp (SearchParams): A SearchParams struct containing the search parameters.
14 | //
15 | // Returns:
16 | // - []map[string]any: A slice of maps where each map represents a data record that matches the given query.
17 | // The keys of the map correspond to the column names of the data that were searched and returned in the result.
18 | // - error: An error if the query or limit is invalid or if the full-text is not initialized.
19 | func (c Cache) SearchOneWord(sp SearchParams) ([]map[string]any, error) {
20 | // If the query is empty, return an error
21 | if len(sp.Query) == 0 {
22 | return []map[string]any{}, errors.New("invalid query")
23 | }
24 |
25 | // If no limit is provided, set it to 10
26 | if sp.Limit == 0 {
27 | sp.Limit = 10
28 | }
29 |
30 | // Lock the mutex
31 | c.mutex.RLock()
32 | defer c.mutex.RUnlock()
33 |
34 | // Check if the full-text is initialized
35 | if c.ft == nil {
36 | return []map[string]any{}, errors.New("full-text is not initialized")
37 | }
38 |
39 | // Search the data
40 | return c.searchOneWord(sp), nil
41 | }
42 |
43 | // searchOneWord searches for a single word in the FullText struct's data and returns a list of maps containing the search results.
44 | // Parameters:
45 | // - c (c *Cache): A pointer to the Cache struct
46 | // - sp (SearchParams): A SearchParams struct containing the search parameters.
47 | //
48 | // Returns:
49 | // - []map[string]any: A slice of maps where each map represents a data record that matches the given query.
50 | // The keys of the map correspond to the column names of the data that were searched and returned in the result.
51 | func (c *Cache) searchOneWord(sp SearchParams) []map[string]any {
52 | // Set the query to lowercase
53 | sp.Query = strings.ToLower(sp.Query)
54 |
55 | // Define variables
56 | var result []map[string]any = []map[string]any{}
57 |
58 | // If the user wants a strict search, just return the result
59 | // straight from the cache
60 | if sp.Strict {
61 | return c.searchOneWordStrict(result, sp)
62 | }
63 |
64 | // Define a map to store the indices that have already been added
65 | var alreadyAdded map[int]int = map[int]int{}
66 |
67 | // Loop through the cache keys
68 | for k, v := range c.ft.storage {
69 | switch {
70 | case len(result) >= sp.Limit:
71 | return result
72 | case !utils.Contains(k, sp.Query):
73 | continue
74 | }
75 |
76 | // Loop through the cache indices
77 | if index, ok := v.(int); ok {
78 | if _, ok := alreadyAdded[index]; ok {
79 | continue
80 | }
81 | result = append(result, c.data[c.ft.indices[index]])
82 | alreadyAdded[index] = 0
83 | continue
84 | }
85 |
86 | var indices []int = v.([]int)
87 | for j := 0; j < len(indices); j++ {
88 | if _, ok := alreadyAdded[indices[j]]; ok {
89 | continue
90 | }
91 |
92 | // Else, append the index to the result
93 | result = append(result, c.data[c.ft.indices[indices[j]]])
94 | alreadyAdded[indices[j]] = 0
95 | }
96 | }
97 |
98 | // Return the result
99 | return result
100 | }
101 |
102 | // searchOneWordStrict is a method of the Cache struct that searches for a single word in the cache and returns the results.
103 | // This function is not thread-safe.
104 | //
105 | // Parameters:
106 | // - c (c *Cache): A pointer to the Cache struct
107 | // - sp (SearchParams): A SearchParams struct containing the search parameters.
108 | //
109 | // Returns:
110 | // - A slice of map[string]any representing the search results.
111 | func (c *Cache) searchOneWordStrict(result []map[string]any, sp SearchParams) []map[string]any {
112 | // Check if the query is in the cache
113 | if _, ok := c.ft.storage[sp.Query]; !ok {
114 | return result
115 | }
116 |
117 | // If there's only one result
118 | if v, ok := c.ft.storage[sp.Query].(int); ok {
119 | return []map[string]any{c.data[c.ft.indices[v]]}
120 | }
121 |
122 | // Loop through the indices
123 | for i := 0; i < len(c.ft.storage[sp.Query].([]int)); i++ {
124 | if len(result) >= sp.Limit {
125 | return result
126 | }
127 | var (
128 | index int = c.ft.storage[sp.Query].([]int)[i]
129 | key string = c.ft.indices[index]
130 | )
131 | result = append(result, c.data[key])
132 | }
133 |
134 | // Return the result
135 | return result
136 | }
137 |
--------------------------------------------------------------------------------
/searchparams.go:
--------------------------------------------------------------------------------
1 | package hermes
2 |
3 | // SearchParams is a struct that contains the search parameters for the Cache search methods.
4 | type SearchParams struct {
5 | // The search query
6 | Query string
7 | // The limit of search results to return
8 | Limit int
9 | // A boolean to indicate whether the search should be strict or not
10 | Strict bool
11 | // A map containing the schema to search for
12 | Schema map[string]bool
13 | // Key to search in
14 | Key string
15 | }
16 |
--------------------------------------------------------------------------------
/searchvalues.go:
--------------------------------------------------------------------------------
1 | package hermes
2 |
3 | import (
4 | "errors"
5 | "strings"
6 | )
7 |
8 | // SearchValues searches for all records containing the given query in the specified schema with a limit of results to return.
9 | // Parameters:
10 | // - c (c *Cache): A pointer to the Cache struct
11 | // - sp (SearchParams): A SearchParams struct containing the search parameters.
12 | //
13 | // Returns:
14 | // - []map[string]any: A slice of maps where each map represents a data record that matches the given query.
15 | // The keys of the map correspond to the column names of the data that were searched and returned in the result.
16 | // - error: An error if the query or limit is invalid
17 | func (c *Cache) SearchValues(sp SearchParams) ([]map[string]any, error) {
18 | // If the query is empty, return an error
19 | if len(sp.Query) == 0 {
20 | return []map[string]any{}, errors.New("invalid query")
21 | }
22 |
23 | // If no limit is provided, set it to 10
24 | if sp.Limit == 0 {
25 | sp.Limit = 10
26 | }
27 |
28 | // If no schema is provided, set it to all columns
29 | if len(sp.Schema) == 0 {
30 | sp.Schema = make(map[string]bool)
31 | }
32 |
33 | // Set the query to lowercase
34 | sp.Query = strings.ToLower(sp.Query)
35 |
36 | // Lock the mutex
37 | c.mutex.RLock()
38 | defer c.mutex.RUnlock()
39 |
40 | // Search the data
41 | return c.searchValues(sp), nil
42 | }
43 |
44 | // searchValues searches for all records containing the given query in the specified schema with a limit of results to return.
45 | // Parameters:
46 | // - c (c *Cache): A pointer to the Cache struct
47 | // - sp (SearchParams): A SearchParams struct containing the search parameters.
48 | //
49 | // Returns:
50 | // - []map[string]any: A slice of maps where each map represents a data record that matches the given query.
51 | // The keys of the map correspond to the column names of the data that were searched and returned in the result.
52 | func (c *Cache) searchValues(sp SearchParams) []map[string]any {
53 | // Define variables
54 | var result []map[string]any = []map[string]any{}
55 |
56 | // Iterate over the query result
57 | for _, item := range c.data {
58 | // Iterate over the keys and values for the data for that index
59 | for key, value := range item {
60 | switch {
61 | case len(result) >= sp.Limit:
62 | return result
63 | case !sp.Schema[key]:
64 | continue
65 | }
66 |
67 | // Check if the value contains the query
68 | if v, ok := value.(string); ok {
69 | if strings.Contains(strings.ToLower(v), sp.Query) {
70 | result = append(result, item)
71 | }
72 | }
73 | }
74 | }
75 |
76 | // Return the result
77 | return result
78 | }
79 |
--------------------------------------------------------------------------------
/searchwithkey.go:
--------------------------------------------------------------------------------
1 | package hermes
2 |
3 | import (
4 | "errors"
5 | "strings"
6 | )
7 |
8 | // SearchWithKey searches for all records containing the given query in the specified key column with a limit of results to return.
9 | // Parameters:
10 | // - c (c *Cache): A pointer to the Cache struct
11 | // - sp (SearchParams): A SearchParams struct containing the search parameters.
12 | //
13 | // Returns:
14 | // - []map[string]any: A slice of maps containing the search results
15 | // - error: An error if the key, query or limit is invalid
16 | func (c *Cache) SearchWithKey(sp SearchParams) ([]map[string]any, error) {
17 | switch {
18 | case len(sp.Key) == 0:
19 | return []map[string]any{}, errors.New("invalid key")
20 | case len(sp.Query) == 0:
21 | return []map[string]any{}, errors.New("invalid query")
22 | }
23 |
24 | // Set the query to lowercase
25 | sp.Query = strings.ToLower(sp.Query)
26 |
27 | // Lock the mutex
28 | c.mutex.RLock()
29 | defer c.mutex.RUnlock()
30 |
31 | // Search the data
32 | return c.searchWithKey(sp), nil
33 | }
34 |
35 | // searchWithKey searches for all records containing the given query in the specified key column with a limit of results to return.
36 | // Parameters:
37 | // - c (c *Cache): A pointer to the Cache struct
38 | // - sp (SearchParams): A SearchParams struct containing the search parameters.
39 | //
40 | // Returns:
41 | // - []map[string]any: A slice of maps containing the search results
42 | func (c *Cache) searchWithKey(sp SearchParams) []map[string]any {
43 | // Define variables
44 | var result []map[string]any = []map[string]any{}
45 |
46 | // Iterate over the query result
47 | for _, item := range c.data {
48 | for _, v := range item {
49 | if len(result) >= sp.Limit {
50 | return result
51 | }
52 |
53 | // Check if the value contains the query
54 | if v, ok := v.(string); ok {
55 | if strings.Contains(strings.ToLower(v), sp.Query) {
56 | result = append(result, item)
57 | }
58 | }
59 | }
60 | }
61 |
62 | // Return the result
63 | return result
64 | }
65 |
--------------------------------------------------------------------------------
/set.go:
--------------------------------------------------------------------------------
1 | package hermes
2 |
3 | import "fmt"
4 |
5 | // Set is a method of the Cache struct that sets a value in the cache for the specified key.
6 | // This function is thread-safe.
7 | //
8 | // Parameters:
9 | // - key: A string representing the key to set the value for.
10 | // - value: A map[string]any representing the value to set.
11 | //
12 | // Returns:
13 | // - Error
14 | func (c *Cache) Set(key string, value map[string]any) error {
15 | c.mutex.Lock()
16 | defer c.mutex.Unlock()
17 | return c.set(key, value)
18 | }
19 |
20 | // set is a method of the Cache struct that sets a value in the cache for the specified key.
21 | // This function is not thread-safe, and should only be called from an exported function.
22 | // If fullText is true, set the value in the full-text cache as well.
23 | //
24 | // Parameters:
25 | // - key: A string representing the key to set the value for.
26 | // - value: A map[string]any representing the value to set.
27 | //
28 | // Returns:
29 | // - An error if the full-text cache key already exists. Otherwise, nil.
30 | func (c *Cache) set(key string, value map[string]any) error {
31 | if _, ok := c.data[key]; ok {
32 | return fmt.Errorf("full-text cache key already exists (%s). delete it before setting it another value", key)
33 | }
34 |
35 | // Update the value in the FT cache
36 | if c.ft != nil {
37 | if err := c.ftSet(key, value); err != nil {
38 | return err
39 | }
40 | }
41 |
42 | // Update the value in the cache
43 | c.data[key] = value
44 |
45 | // Return nil for no error
46 | return nil
47 | }
48 |
49 | // ftSet is a method of the Cache struct that sets a value in the full-text cache for the specified key.
50 | // This function is not thread-safe, and should only be called from an exported function.
51 | //
52 | // Parameters:
53 | // - key: A string representing the key to set the value for.
54 | // - value: A map[string]any representing the value to set.
55 | //
56 | // Returns:
57 | // - An error if the full-text storage limit or byte-size limit is reached. Otherwise, nil.
58 | func (c *Cache) ftSet(key string, value map[string]any) error {
59 | var ts *TempStorage = NewTempStorage(c.ft)
60 | for k, v := range value {
61 | if ftv := WFTGetValue(v); len(ftv) == 0 {
62 | continue
63 | } else {
64 | // Update the value
65 | value[k] = ftv
66 |
67 | // Insert the value in the temp storage
68 | if err := ts.insert(c.ft, key, ftv); err != nil {
69 | return err
70 | }
71 | }
72 | }
73 |
74 | // Iterate over the temp storage and set the values with len 1 to int
75 | ts.cleanSingleArrays()
76 |
77 | // Set the full-text cache to the temp map
78 | ts.updateFullText(c.ft)
79 |
80 | // Return nil for no errors
81 | return nil
82 | }
83 |
--------------------------------------------------------------------------------
/testing/api/request.py:
--------------------------------------------------------------------------------
1 | import requests, json, base64, time
2 |
3 | def base64_encode(value):
4 | return base64.b64encode(value.encode("utf-8")).decode("utf-8")
5 |
6 | def test_search():
7 | headers = {
8 | "Content-Type": "application/json"
9 | }
10 |
11 | # schema
12 | schema = base64_encode(json.dumps({
13 | "id": False,
14 | "components": False,
15 | "units": False,
16 | "description": True,
17 | "name": True,
18 | "pre_requisites": True,
19 | "title": True
20 | }))
21 |
22 | # search for a value
23 | url = "http://localhost:3000/ft/search"
24 | params = "?query=computer&strict=false&limit=100&schema=" + schema
25 |
26 | # make the request
27 | r = requests.get(url+params, headers=headers)
28 | print(r.text)
29 |
30 | def test_set_json(data):
31 | headers = {
32 | "Content-Type": "application/json"
33 | }
34 |
35 | # set the data
36 | for key in data:
37 | # set the key
38 | url = "http://localhost:3000/cache/set"
39 | params = "?key=" + key + "&value=" + base64_encode(json.dumps(data[key]))
40 |
41 | # make the request
42 | r = requests.post(url+params, headers=headers)
43 | #print(r.text)
44 |
45 | def test_set():
46 | headers = {
47 | "Content-Type": "application/json"
48 | }
49 |
50 | # set the data
51 | url = "http://localhost:3000/cache/set"
52 | params = "?key=testing&value=" + base64_encode(json.dumps({
53 | "name": "tristan"
54 | }))
55 |
56 | # make the request
57 | r = requests.post(url+params, headers=headers)
58 | print(r.text)
59 |
60 |
61 | def test_init_ft_json(data):
62 | headers = {
63 | "Content-Type": "application/json"
64 | }
65 |
66 | # url and params
67 | _json = base64_encode(json.dumps(data))
68 | url = "http://localhost:3000/ft/init/json"
69 | params = "?maxlength=-1&maxbytes=-1&json=" + _json
70 |
71 | # make the request
72 | r = requests.post(url+params, headers=headers)
73 |
74 | # print the response
75 | print(r.text)
76 |
77 |
78 | def test_init_ft():
79 | headers = {
80 | "Content-Type": "application/json"
81 | }
82 |
83 | # url and params
84 | url = "http://localhost:3000/ft/init"
85 | params = "?maxlength=-1&maxbytes=-1"
86 |
87 | # make the request
88 | r = requests.post(url+params, headers=headers)
89 |
90 | # print the response
91 | print(r.text)
92 |
93 |
94 | def test_get():
95 | headers = {
96 | "Content-Type": "application/json"
97 | }
98 |
99 | # get b4a3261059ea6f1b48eb8039e720e0b48d087583
100 | url = "http://localhost:3000/cache/get"
101 | params = "?key=b4a3261059ea6f1b48eb8039e720e0b48d087583"
102 |
103 | # make the request
104 | r = requests.get(url+params, headers=headers)
105 | print(r.text)
106 |
107 | def test_cache_info():
108 | headers = {
109 | "Content-Type": "application/json"
110 | }
111 |
112 | # get b4a3261059ea6f1b48eb8039e720e0b48d087583
113 | url = "http://localhost:3000/cache/info/testing"
114 |
115 | # make the request
116 | r = requests.get(url, headers=headers)
117 | print(r.text)
118 |
119 |
120 | if __name__ == "__main__":
121 | # read the json file from the data folder
122 | with open("data/data_hash.json", "r") as file:
123 | # load the json file
124 | data = json.loads(file.read())
125 | # test_init_ft_json(data)
126 | test_init_ft()
127 | # test_cache_info()
128 | # test_get()
129 | # test_set_json(data)
130 | # test_set()
131 | # test_cache_info()
132 |
133 | st = time.time()
134 | test_search()
135 | print(time.time() - st)
--------------------------------------------------------------------------------
/testing/api/router.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "github.com/gofiber/fiber/v2"
5 | hermes "github.com/realTristan/hermes"
6 | api "github.com/realTristan/hermes/cloud/api"
7 | )
8 |
9 | func main() {
10 | app := fiber.New()
11 | cache := hermes.InitCache()
12 | api.SetRoutes(app, cache)
13 | app.Listen(":3000")
14 | }
15 |
--------------------------------------------------------------------------------
/testing/cache/cache.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 |
6 | Hermes "github.com/realTristan/hermes"
7 | )
8 |
9 | func main() {
10 | var cache *Hermes.Cache = Hermes.InitCache()
11 | cache.FTInit(3, -1, -1)
12 |
13 | // Test Set, Get, Delete, Clean, Length, Values, Keys, and Exists
14 | var data = map[string]any{
15 | "name": cache.WithFT("Tristan"),
16 | "age": 17,
17 | }
18 |
19 | // Set data
20 | cache.Set("user_id1", data)
21 | cache.Set("user_id1", data)
22 | cache.Set("user_id2", data)
23 | cache.Set("user_id3", data)
24 |
25 | // Get data
26 | fmt.Println(cache.Get("user_id1"))
27 | fmt.Println(cache.Get("user_id2"))
28 | fmt.Println(cache.Get("user_id3"))
29 |
30 | // Exists
31 | fmt.Println(cache.Exists("user_id1"))
32 | fmt.Println(cache.Exists("user_id2"))
33 |
34 | // Length
35 | fmt.Println(cache.Length())
36 |
37 | // Values
38 | fmt.Println(cache.Values())
39 |
40 | // Keys
41 | fmt.Println(cache.Keys())
42 |
43 | // Delete data
44 | cache.Delete("user_id1")
45 |
46 | // Exists
47 | fmt.Println(cache.Exists("user_id1"))
48 | fmt.Println(cache.Exists("user_id2"))
49 |
50 | // Get data
51 | fmt.Println(cache.Get("user_id1"))
52 | fmt.Println(cache.Get("user_id2"))
53 | fmt.Println(cache.Get("user_id3"))
54 |
55 | // Clean data
56 | cache.Clean()
57 |
58 | // Get data
59 | fmt.Println(cache.Get("user_id1"))
60 | fmt.Println(cache.Get("user_id2"))
61 | fmt.Println(cache.Get("user_id3"))
62 |
63 | // Length
64 | fmt.Println(cache.Length())
65 |
66 | // Values
67 | fmt.Println(cache.Values())
68 |
69 | // Keys
70 | fmt.Println(cache.Keys())
71 | }
72 |
--------------------------------------------------------------------------------
/testing/compression/compression.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | "strings"
6 | "time"
7 |
8 | gzip "github.com/realTristan/hermes/compression/gzip"
9 | "github.com/realTristan/hermes/compression/zlib"
10 | utils "github.com/realTristan/hermes/utils"
11 | )
12 |
13 | func main() {
14 | var v string = strings.Repeat("computer", 100)
15 | TestGzip(v)
16 | TestZlib(v)
17 | }
18 |
19 | // Test the zlib compression and decompression functions.
20 | func TestZlib(v string) {
21 | fmt.Println("zlib")
22 | var (
23 | b []byte
24 | err error
25 | st time.Time = time.Now()
26 | )
27 | if b, err = zlib.Compress([]byte(v)); err != nil {
28 | panic(err)
29 | }
30 | fmt.Println(time.Since(st))
31 | st = time.Now()
32 | if v, err = zlib.Decompress(b); err != nil {
33 | panic(err)
34 | }
35 | fmt.Println(time.Since(st))
36 | fmt.Println(utils.Size(v))
37 | fmt.Println(utils.Size(b))
38 | }
39 |
40 | // Test the gzip compression and decompression functions.
41 | func TestGzip(v string) {
42 | fmt.Println("gzip")
43 | var (
44 | b []byte
45 | err error
46 | st time.Time = time.Now()
47 | )
48 | if b, err = gzip.Compress([]byte(v)); err != nil {
49 | panic(err)
50 | }
51 | fmt.Println(time.Since(st))
52 | st = time.Now()
53 | if v, err = gzip.Decompress(b); err != nil {
54 | panic(err)
55 | }
56 | fmt.Println(time.Since(st))
57 | fmt.Println(utils.Size(v))
58 | fmt.Println(utils.Size(b))
59 | }
60 |
--------------------------------------------------------------------------------
/testing/data/data_stats.py:
--------------------------------------------------------------------------------
1 | import json, sys
2 |
3 | # // Store the amount of words in the data set
4 | total_words: int = 0
5 |
6 | # // Load the json data
7 | data = json.load(open("data.json"))
8 |
9 | # // Iterate over the json itmes
10 | for item in data:
11 | for k, v in item.items():
12 | for word in v.strip().split():
13 | if word.isalnum():
14 | total_words += 1
15 |
16 | # // Print the results
17 | print(f"Total words: {total_words}")
18 | print(f"Total keys: {len(data)}")
19 | print(f"Data size: {sys.getsizeof(data)} bytes")
20 |
--------------------------------------------------------------------------------
/testing/fulltext/clean.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 |
6 | hermes "github.com/realTristan/hermes"
7 | )
8 |
9 | func clean() {
10 | var cache *hermes.Cache = hermes.InitCache()
11 |
12 | // Test CleanFT()
13 | var data = map[string]any{
14 | "name": "tristan",
15 | "age": 17,
16 | }
17 |
18 | // Set data
19 | cache.Set("user_id1", data)
20 | cache.Set("user_id1", data)
21 | cache.Set("user_id2", data)
22 |
23 | // Initialize the FT cache
24 | cache.FTInit(-1, -1, 3)
25 |
26 | // Search for a word in the cache
27 | var result, _ = cache.SearchOneWord(hermes.SearchParams{
28 | Query: "tristan",
29 | Limit: 100,
30 | Strict: false,
31 | })
32 | fmt.Println(result)
33 |
34 | // Clean
35 | cache.FTClean()
36 |
37 | // Search for a word in the cache
38 | result, _ = cache.SearchOneWord(hermes.SearchParams{
39 | Query: "tristan",
40 | Limit: 100,
41 | Strict: false,
42 | })
43 | fmt.Println(result)
44 | }
45 |
--------------------------------------------------------------------------------
/testing/fulltext/delete.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 |
6 | hermes "github.com/realTristan/hermes"
7 | )
8 |
9 | func delete() {
10 | var cache *hermes.Cache = hermes.InitCache()
11 |
12 | // Initialize the FT cache
13 | cache.FTInit(-1, -1, 3)
14 |
15 | // Test Delete()
16 | var data = map[string]any{
17 | "name": "tristan",
18 | "age": 17,
19 | }
20 |
21 | // print cache info
22 | fmt.Println(cache.InfoForTesting())
23 |
24 | // Set data
25 | cache.Set("user_id1", data)
26 | cache.Set("user_id1", data)
27 | cache.Set("user_id2", data)
28 |
29 | // print cache info
30 | fmt.Println(cache.InfoForTesting())
31 |
32 | // Delete data
33 | cache.Delete("user_id1")
34 | cache.Delete("user_id1")
35 |
36 | // Get data
37 | fmt.Println(cache.Get("user_id1"))
38 | fmt.Println(cache.Get("user_id2"))
39 |
40 | // Exists
41 | fmt.Println(cache.Exists("user_id1"))
42 | fmt.Println(cache.Exists("user_id2"))
43 |
44 | // Length
45 | fmt.Println(cache.Length())
46 |
47 | // Values
48 | fmt.Println(cache.Values())
49 |
50 | // Keys
51 | fmt.Println(cache.Keys())
52 |
53 | // Print the cache info
54 | fmt.Println(cache.InfoForTesting())
55 | }
56 |
--------------------------------------------------------------------------------
/testing/fulltext/ft.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 |
6 | hermes "github.com/realTristan/hermes"
7 | )
8 |
9 | func ft() {
10 | var cache *hermes.Cache = hermes.InitCache()
11 |
12 | // Initialize the FT cache
13 | cache.FTInit(-1, -1, 3)
14 |
15 | // print cache info
16 | fmt.Println(cache.InfoForTesting())
17 | }
18 |
--------------------------------------------------------------------------------
/testing/fulltext/insert_key_merge.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 |
6 | hermes "github.com/realTristan/hermes"
7 | )
8 |
9 | func insert_key_merge() {
10 | var cache *hermes.Cache = hermes.InitCache()
11 |
12 | // Initialize the FT cache
13 | cache.FTInit(-1, -1, 3)
14 | cache.Set("user_id1", map[string]any{
15 | "name": cache.WithFT("tristan"),
16 | "age": 17,
17 | })
18 | cache.Set("user_id2", map[string]any{
19 | "name": cache.WithFT("tris is cool"),
20 | "age": 17,
21 | })
22 |
23 | // Search for tris
24 | var result, _ = cache.Search(hermes.SearchParams{
25 | Query: "tris is",
26 | Limit: 100,
27 | Strict: false,
28 | })
29 | fmt.Println(result)
30 |
31 | // print cache info
32 | fmt.Println(cache.InfoForTesting())
33 | }
34 |
--------------------------------------------------------------------------------
/testing/fulltext/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | func main() {
4 | insert_key_merge()
5 | //delete()
6 | //clean()
7 | //ft()
8 | //search()
9 | //set()
10 | }
11 |
--------------------------------------------------------------------------------
/testing/fulltext/search.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 |
6 | hermes "github.com/realTristan/hermes"
7 | )
8 |
9 | func search() {
10 | var cache *hermes.Cache = hermes.InitCache()
11 |
12 | // Initialize the FT cache
13 | cache.FTInit(-1, -1, 3)
14 |
15 | // print cache info
16 | fmt.Println(cache.InfoForTesting())
17 | }
18 |
--------------------------------------------------------------------------------
/testing/fulltext/set.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 |
6 | hermes "github.com/realTristan/hermes"
7 | )
8 |
9 | func set() {
10 | var cache *hermes.Cache = hermes.InitCache()
11 |
12 | // Initialize the FT cache
13 | cache.FTInit(-1, -1, 3)
14 |
15 | // print cache info
16 | fmt.Println(cache.InfoForTesting())
17 | }
18 |
--------------------------------------------------------------------------------
/testing/nocache/speed/speed.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "encoding/json"
5 | "fmt"
6 | "os"
7 | "strings"
8 | "time"
9 |
10 | hermes "github.com/realTristan/hermes/nocache"
11 | )
12 |
13 | // Results: hermes is about 40x faster than a basic search
14 | func main() {
15 | BasicSearch()
16 | hermesSearch()
17 | }
18 |
19 | // Basic Search
20 | func BasicSearch() {
21 | // read the json data
22 | if data, err := readJson("../../../testing/data/data_array.json"); err != nil {
23 | panic(err)
24 | } else {
25 | var average int64 = 0
26 | for i := 0; i < 100; i++ {
27 | var startTime = time.Now()
28 | // Iterate over the data array
29 | for _, val := range data {
30 | // Iterate over the map
31 | for k, v := range val {
32 | var data map[string]interface{}
33 | if v, ok := v.(map[string]interface{}); !ok {
34 | continue
35 | } else {
36 | data = v
37 | }
38 | // Get the value
39 | var value string = data["$hermes.value"].(string)
40 |
41 | // Check if the value contains the search term
42 | if strings.Contains(strings.ToLower(value), strings.ToLower("computer")) {
43 | var _ = k
44 | }
45 | }
46 | }
47 | average += time.Since(startTime).Nanoseconds()
48 | }
49 | var averageNanos float64 = float64(average) / 100
50 | var averageMillis float64 = averageNanos / 1000000
51 | fmt.Println("\nBasic: Average time is: ", averageNanos, "ns or", averageMillis, "ms")
52 | }
53 | }
54 |
55 | // hermes Search
56 | func hermesSearch() {
57 | // Initialize the cache
58 | var cache, err = hermes.InitWithJson("../../../data/data_array.json", 3)
59 | if err != nil {
60 | panic(err)
61 | }
62 |
63 | var average int64 = 0
64 | for i := 0; i < 100; i++ {
65 | // Track the start time
66 | var start time.Time = time.Now()
67 |
68 | // Search for a word in the cache
69 | cache.Search(hermes.SearchParams{
70 | Query: "computer",
71 | Limit: 100,
72 | Strict: false,
73 | })
74 |
75 | // Print the duration
76 | average += time.Since(start).Nanoseconds()
77 | }
78 |
79 | var averageNanos float32 = float32(average) / 100
80 | var averageMillis float32 = averageNanos / 1000000
81 | fmt.Println("\nhermes: Average time is: ", averageNanos, "ns or", averageMillis, "ms")
82 | }
83 |
84 | // Read a json file
85 | func readJson(file string) ([]map[string]interface{}, error) {
86 | var v []map[string]interface{} = []map[string]interface{}{}
87 |
88 | // Read the json data
89 | if data, err := os.ReadFile(file); err != nil {
90 | return nil, err
91 | } else {
92 | if err := json.Unmarshal(data, &v); err != nil {
93 | return nil, err
94 | }
95 | }
96 | return v, nil
97 | }
98 |
--------------------------------------------------------------------------------
/testing/socket/router.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "github.com/gofiber/fiber/v2"
5 | hermes "github.com/realTristan/hermes"
6 | Socket "github.com/realTristan/hermes/cloud/socket"
7 | )
8 |
9 | func main() {
10 | // Cache and fiber app
11 | cache := hermes.InitCache()
12 | app := fiber.New()
13 |
14 | // Set the router
15 | Socket.SetRouter(app, cache)
16 |
17 | // Listen on port 3000
18 | app.Listen(":3000")
19 | }
20 |
--------------------------------------------------------------------------------
/testing/speed/func_speeds/fnspeeds.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | "time"
6 |
7 | hermes "github.com/realTristan/hermes"
8 | )
9 |
10 | func main() {
11 | // Cache
12 | var cache *hermes.Cache = hermes.InitCache()
13 |
14 | /* Initialize the FT cache
15 | if err := cache.InitFTWithJson("../../data/data_hash.json", maxWords, maxSizeBytes, schema); err != nil {
16 | fmt.Println(err)
17 | }
18 | */
19 | // cache.Info()
20 |
21 | // Initialize the FT cache
22 | if err := cache.FTInit(-1, -1, 3); err != nil {
23 | fmt.Println(err)
24 | }
25 |
26 | // The data for the user_id and user_id2 key
27 | var data = map[string]any{
28 | "name": cache.WithFT("tristan1"),
29 | "age": 17,
30 | }
31 | var data2 = map[string]any{
32 | "name": map[string]any{
33 | "$hermes.full_text": true,
34 | "value": "tristan2",
35 | },
36 | "age": 17,
37 | }
38 |
39 | // Set the value in the cache
40 | duration("Set", func() {
41 | if err := cache.Set("user_id", data); err != nil {
42 | fmt.Println(err)
43 | }
44 |
45 | if err := cache.Set("user_id2", data2); err != nil {
46 | fmt.Println(err)
47 | }
48 | })
49 |
50 | // Get the user_id value
51 | duration("Get", func() {
52 | var user = cache.Get("user_id")
53 | fmt.Println(user)
54 | })
55 |
56 | // Search for a word in the cache
57 | duration("Search", func() {
58 | var result, _ = cache.SearchOneWord(hermes.SearchParams{
59 | Query: "tristan",
60 | Limit: 100,
61 | Strict: false,
62 | })
63 | fmt.Println(result)
64 | })
65 |
66 | // Print all the cache info
67 | //cache.Info()
68 |
69 | /* Reset the FT cache
70 | if err := cache.ResetFT(maxWords, maxSizeBytes, schema); err != nil {
71 | fmt.Println(err)
72 | }*/
73 |
74 | // Delete the user_id key
75 | duration("Delete", func() {
76 | cache.Delete("user_id")
77 | })
78 |
79 | // Search for a word in the cache
80 | duration("Search", func() {
81 | var result, _ = cache.SearchOneWord(hermes.SearchParams{
82 | Query: "tristan",
83 | Limit: 100,
84 | Strict: false,
85 | })
86 | fmt.Println(result)
87 | })
88 |
89 | // Print all the cache info
90 | //cache.Info()
91 | }
92 |
93 | // Track the duration of a function
94 | func duration(key string, f func()) {
95 | var start time.Time = time.Now()
96 | f()
97 | fmt.Printf("\nExecution Duration for %s: %s\n", key, time.Since(start))
98 | }
99 |
--------------------------------------------------------------------------------
/testing/speed/search_speeds/searchspeeds.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "encoding/json"
5 | "fmt"
6 | "os"
7 | "strings"
8 | "time"
9 |
10 | hermes "github.com/realTristan/hermes"
11 | )
12 |
13 | // Results: hermes is about 40x faster than a basic search
14 | func main() {
15 | BasicSearch()
16 | hermesSearch()
17 | }
18 |
19 | // Basic Search
20 | func BasicSearch() {
21 | // read the json data
22 | if data, err := readJson("../../data/data_hash.json"); err != nil {
23 | panic(err)
24 | } else {
25 | var average int64 = 0
26 | for i := 0; i < 100; i++ {
27 | var startTime = time.Now()
28 | // print the data
29 | for _, val := range data {
30 | for k, v := range val {
31 | var data map[string]any
32 | if v, ok := v.(map[string]any); !ok {
33 | continue
34 | } else {
35 | data = v
36 | }
37 | // Get the value
38 | var value string = data["$hermes.value"].(string)
39 |
40 | if strings.Contains(strings.ToLower(value), strings.ToLower("computer")) {
41 | var _ = k
42 | }
43 | }
44 | }
45 | average += time.Since(startTime).Nanoseconds()
46 | }
47 | var (
48 | averageNanos float64 = float64(average) / 100
49 | averageMillis float64 = averageNanos / 1000000
50 | )
51 | fmt.Println("Basic: Average time is: ", averageNanos, "ns or", averageMillis, "ms")
52 | }
53 | }
54 |
55 | // hermes Search
56 | func hermesSearch() {
57 | // Initialize the cache
58 | var cache *hermes.Cache = hermes.InitCache()
59 |
60 | // Initialize the FT cache with a json file
61 | cache.FTInitWithJson("../../data/data_hash.json", -1, -1, 3)
62 | var (
63 | average int64 = 0
64 | total int = 0
65 | )
66 | for i := 0; i < 100; i++ {
67 | // Track the start time
68 | var (
69 | start time.Time = time.Now()
70 |
71 | // Search for a word in the cache
72 | res, _ = cache.Search(hermes.SearchParams{
73 | Query: "computer science",
74 | Limit: 100,
75 | Strict: false,
76 | Schema: map[string]bool{
77 | "name": true,
78 | "description": true,
79 | "title": true,
80 | },
81 | })
82 | )
83 |
84 | // Print the duration
85 | average += time.Since(start).Nanoseconds()
86 | total += len(res)
87 | }
88 | var (
89 | averageNanos float64 = float64(average) / 100
90 | averageMillis float64 = averageNanos / 1000000
91 | )
92 | fmt.Println("hermes: Average time is: ", averageNanos, "ns or", averageMillis, "ms")
93 | fmt.Println("hermes: Results: ", total)
94 | }
95 |
96 | // Read a json file
97 | func readJson(file string) (map[string]map[string]any, error) {
98 | var v map[string]map[string]any = map[string]map[string]any{}
99 |
100 | // Read the json data
101 | if data, err := os.ReadFile(file); err != nil {
102 | return nil, err
103 | } else if err := json.Unmarshal(data, &v); err != nil {
104 | return nil, err
105 | }
106 | return v, nil
107 | }
108 |
--------------------------------------------------------------------------------
/utils/buffer.go:
--------------------------------------------------------------------------------
1 | package utils
2 |
3 | import (
4 | "bytes"
5 | "encoding/gob"
6 | )
7 |
8 | // Size is a function that calculates the real size in memory of a given value by encoding it using the gob package and returning the length of the resulting byte buffer.
9 | // Parameters:
10 | // - v (any): The value to calculate the size of.
11 | //
12 | // Returns:
13 | // - int: The size of the value in memory.
14 | // - error: An error if the encoding fails, or nil if successful.
15 | func Size(v any) (int, error) {
16 | var b *bytes.Buffer = new(bytes.Buffer)
17 | if err := gob.NewEncoder(b).Encode(v); err != nil {
18 | return 0, err
19 | }
20 | return b.Len(), nil
21 | }
22 |
--------------------------------------------------------------------------------
/utils/json.go:
--------------------------------------------------------------------------------
1 | package utils
2 |
3 | import (
4 | "encoding/json"
5 | "os"
6 | )
7 |
8 | // ReadJson is a generic function that reads a JSON file and unmarshals its contents into a provided value of type T.
9 | // Parameters:
10 | // - file (string): The path to the JSON file to read.
11 | //
12 | // Returns:
13 | // - T: The unmarshalled value of type T.
14 | // - error: An error if the file cannot be read or the unmarshalling fails, or nil if successful.
15 | func ReadJson[T any](file string) (T, error) {
16 | var v T
17 |
18 | // Read the json data
19 | if data, err := os.ReadFile(file); err != nil {
20 | return *new(T), err
21 | } else if err := json.Unmarshal(data, &v); err != nil {
22 | return *new(T), err
23 | }
24 | return v, nil
25 | }
26 |
--------------------------------------------------------------------------------
/utils/slices.go:
--------------------------------------------------------------------------------
1 | package utils
2 |
3 | // SliceContains is a generic function that checks if a given value is present in a given slice of comparable values.
4 | // Parameters:
5 | // - array ([]T): The slice to search for the value.
6 | // - value (T): The value to search for in the slice.
7 | //
8 | // Returns:
9 | // - bool: true if the value is present in the slice, false otherwise.
10 | func SliceContains[T comparable](array []T, value T) bool {
11 | for i := 0; i < len(array); i++ {
12 | if array[i] == value {
13 | return true
14 | }
15 | }
16 | return false
17 | }
18 |
--------------------------------------------------------------------------------
/utils/strings.go:
--------------------------------------------------------------------------------
1 | package utils
2 |
3 | import (
4 | "strings"
5 | )
6 |
7 | // IsAlphaNumChar is a function that checks if a given byte is an alphanumeric character.
8 | // Parameters:
9 | // - c (byte): The byte to check.
10 | //
11 | // Returns:
12 | // - bool: true if the byte is an alphanumeric character, false otherwise.
13 | func IsAlphaNumChar(c byte) bool {
14 | return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z')
15 | }
16 |
17 | // IsAlphaNum is a function that checks if a given string consists entirely of alphanumeric characters.
18 | // Parameters:
19 | // - s (string): The string to check.
20 | //
21 | // Returns:
22 | // - bool: true if the string consists entirely of alphanumeric characters, false otherwise.
23 | func IsAlphaNum(s string) bool {
24 | for _, c := range s {
25 | if !IsAlphaNumChar(byte(c)) {
26 | return false
27 | }
28 | }
29 | return true
30 | }
31 |
32 | // TrimNonAlphaNum is a function that removes non-alphanumeric characters from the beginning and end of a given string.
33 | // Parameters:
34 | // - s (string): The string to trim.
35 | //
36 | // Returns:
37 | // - string: The trimmed string.
38 | func TrimNonAlphaNum(s string) string {
39 | if len(s) == 0 {
40 | return s
41 | }
42 | for !IsAlphaNumChar(s[0]) {
43 | if len(s) < 2 {
44 | return ""
45 | }
46 | s = s[1:]
47 | }
48 | for !IsAlphaNumChar(s[len(s)-1]) {
49 | if len(s) < 2 {
50 | return ""
51 | }
52 | s = s[:len(s)-1]
53 | }
54 | return s
55 | }
56 |
57 | // SplitByAlphaNum is a function that splits a given string into a slice of substrings by alphanumeric characters.
58 | // Parameters:
59 | // - s (string): The string to split.
60 | //
61 | // Returns:
62 | // - []string: A slice of substrings split by alphanumeric characters.
63 | func SplitByAlphaNum(s string) []string {
64 | var (
65 | word string = ""
66 | words []string = make([]string, 0)
67 | )
68 | for i := 0; i < len(s); i++ {
69 | if s[i] == '-' || s[i] == '.' || IsAlphaNumChar(s[i]) {
70 | word += string(s[i])
71 | } else {
72 | if len(word) > 0 {
73 | words = append(words, word)
74 | word = ""
75 | }
76 | }
77 | }
78 | if len(word) > 0 {
79 | words = append(words, word)
80 | }
81 | return words
82 | }
83 |
84 | // RemoveDoubleSpaces is a function that removes double spaces from a given string and returns the modified string.
85 | // Parameters:
86 | // - s (string): The string to remove double spaces from.
87 | //
88 | // Returns:
89 | // - string: The modified string with double spaces removed.
90 | func RemoveDoubleSpaces(s string) string {
91 | for strings.Contains(s, " ") {
92 | s = strings.Replace(s, " ", " ", -1)
93 | }
94 | return s
95 | }
96 |
97 | // Contains is a function that checks if a given string contains another string as a substring.
98 | // Parameters:
99 | // - s1 (string): The string to search for the substring.
100 | // - s2 (string): The substring to search for in the string.
101 | //
102 | // Returns:
103 | // - bool: true if the substring is found in the string, false otherwise.
104 | func Contains(s1 string, s2 string) bool {
105 | var (
106 | s1Len int = len(s1)
107 | s2Len int = len(s2)
108 | )
109 | switch {
110 | case s1Len == s2Len:
111 | return s1 == s2
112 | case s1Len < s2Len:
113 | return false
114 | }
115 | for i := 0; i < s1Len-s2Len; i++ {
116 | if s1[i] == s2[0] {
117 | if s1[i:i+s2Len] == s2 {
118 | return true
119 | }
120 | }
121 | }
122 | return false
123 | }
124 |
--------------------------------------------------------------------------------
/values.go:
--------------------------------------------------------------------------------
1 | package hermes
2 |
3 | // Values is a method of the Cache struct that gets all the values in the cache.
4 | // This function is thread-safe.
5 | //
6 | // Returns:
7 | // - A slice of map[string]any representing all the values in the cache.
8 | func (c *Cache) Values() []map[string]any {
9 | c.mutex.RLock()
10 | defer c.mutex.RUnlock()
11 | return c.values()
12 | }
13 |
14 | // values is a method of the Cache struct that returns all the values in the cache.
15 | // This function is not thread-safe, and should only be called from an exported function.
16 | //
17 | // Returns:
18 | // - A slice of map[string]any representing all the values in the cache.
19 | func (c *Cache) values() []map[string]any {
20 | values := make([]map[string]any, 0, len(c.data))
21 | for _, value := range c.data {
22 | values = append(values, value)
23 | }
24 | return values
25 | }
26 |
--------------------------------------------------------------------------------
/website/.editorconfig:
--------------------------------------------------------------------------------
1 | # Editor configuration, see https://editorconfig.org
2 | root = true
3 |
4 | [*]
5 | charset = utf-8
6 | indent_style = space
7 | indent_size = 2
8 | insert_final_newline = true
9 | trim_trailing_whitespace = true
10 |
11 | [*.ts]
12 | quote_type = single
13 |
14 | [*.md]
15 | max_line_length = off
16 | trim_trailing_whitespace = false
17 |
--------------------------------------------------------------------------------
/website/.gitignore:
--------------------------------------------------------------------------------
1 | # See http://help.github.com/ignore-files/ for more about ignoring files.
2 |
3 | # Compiled output
4 | /dist
5 | /tmp
6 | /out-tsc
7 | /bazel-out
8 |
9 | # Node
10 | /node_modules
11 | npm-debug.log
12 | yarn-error.log
13 |
14 | # IDEs and editors
15 | .idea/
16 | .project
17 | .classpath
18 | .c9/
19 | *.launch
20 | .settings/
21 | *.sublime-workspace
22 |
23 | # Visual Studio Code
24 | .vscode/*
25 | !.vscode/settings.json
26 | !.vscode/tasks.json
27 | !.vscode/launch.json
28 | !.vscode/extensions.json
29 | .history/*
30 |
31 | # Miscellaneous
32 | /.angular/cache
33 | .sass-cache/
34 | /connect.lock
35 | /coverage
36 | /libpeerconnection.log
37 | testem.log
38 | /typings
39 |
40 | # System files
41 | .DS_Store
42 | Thumbs.db
43 |
--------------------------------------------------------------------------------
/website/.vscode/extensions.json:
--------------------------------------------------------------------------------
1 | {
2 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=827846
3 | "recommendations": ["angular.ng-template"]
4 | }
5 |
--------------------------------------------------------------------------------
/website/.vscode/launch.json:
--------------------------------------------------------------------------------
1 | {
2 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
3 | "version": "0.2.0",
4 | "configurations": [
5 | {
6 | "name": "ng serve",
7 | "type": "chrome",
8 | "request": "launch",
9 | "preLaunchTask": "npm: start",
10 | "url": "http://localhost:4200/"
11 | },
12 | {
13 | "name": "ng test",
14 | "type": "chrome",
15 | "request": "launch",
16 | "preLaunchTask": "npm: test",
17 | "url": "http://localhost:9876/debug.html"
18 | }
19 | ]
20 | }
21 |
--------------------------------------------------------------------------------
/website/.vscode/tasks.json:
--------------------------------------------------------------------------------
1 | {
2 | // For more information, visit: https://go.microsoft.com/fwlink/?LinkId=733558
3 | "version": "2.0.0",
4 | "tasks": [
5 | {
6 | "type": "npm",
7 | "script": "start",
8 | "isBackground": true,
9 | "problemMatcher": {
10 | "owner": "typescript",
11 | "pattern": "$tsc",
12 | "background": {
13 | "activeOnStart": true,
14 | "beginsPattern": {
15 | "regexp": "(.*?)"
16 | },
17 | "endsPattern": {
18 | "regexp": "bundle generation complete"
19 | }
20 | }
21 | }
22 | },
23 | {
24 | "type": "npm",
25 | "script": "test",
26 | "isBackground": true,
27 | "problemMatcher": {
28 | "owner": "typescript",
29 | "pattern": "$tsc",
30 | "background": {
31 | "activeOnStart": true,
32 | "beginsPattern": {
33 | "regexp": "(.*?)"
34 | },
35 | "endsPattern": {
36 | "regexp": "bundle generation complete"
37 | }
38 | }
39 | }
40 | }
41 | ]
42 | }
43 |
--------------------------------------------------------------------------------
/website/README.md:
--------------------------------------------------------------------------------
1 | # Hermes
2 |
3 | ## Documentation
4 | - API
5 | - Request, Response, etc.
6 |
7 |
8 | - Sockets
9 | - Database (in progress)
10 | - Hermes Go
11 | - Hermes Python, Rust, etc.
12 | - Compression/Decompression
--------------------------------------------------------------------------------
/website/angular.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "./node_modules/@angular/cli/lib/config/schema.json",
3 | "version": 1,
4 | "newProjectRoot": "projects",
5 | "projects": {
6 | "hermescloud": {
7 | "projectType": "application",
8 | "schematics": {},
9 | "root": "",
10 | "sourceRoot": "src",
11 | "prefix": "app",
12 | "architect": {
13 | "build": {
14 | "builder": "@angular-devkit/build-angular:browser",
15 | "options": {
16 | "outputPath": "dist/hermescloud",
17 | "index": "src/index.html",
18 | "main": "src/main.ts",
19 | "polyfills": [
20 | "zone.js"
21 | ],
22 | "tsConfig": "tsconfig.app.json",
23 | "assets": [
24 | "src/favicon.ico",
25 | "src/assets"
26 | ],
27 | "styles": [
28 | "src/styles.css"
29 | ],
30 | "scripts": []
31 | },
32 | "configurations": {
33 | "production": {
34 | "budgets": [
35 | {
36 | "type": "initial",
37 | "maximumWarning": "500kb",
38 | "maximumError": "1mb"
39 | },
40 | {
41 | "type": "anyComponentStyle",
42 | "maximumWarning": "2kb",
43 | "maximumError": "4kb"
44 | }
45 | ],
46 | "outputHashing": "all"
47 | },
48 | "development": {
49 | "buildOptimizer": false,
50 | "optimization": false,
51 | "vendorChunk": true,
52 | "extractLicenses": false,
53 | "sourceMap": true,
54 | "namedChunks": true
55 | }
56 | },
57 | "defaultConfiguration": "production"
58 | },
59 | "serve": {
60 | "builder": "@angular-devkit/build-angular:dev-server",
61 | "configurations": {
62 | "production": {
63 | "browserTarget": "hermescloud:build:production"
64 | },
65 | "development": {
66 | "browserTarget": "hermescloud:build:development"
67 | }
68 | },
69 | "defaultConfiguration": "development"
70 | },
71 | "extract-i18n": {
72 | "builder": "@angular-devkit/build-angular:extract-i18n",
73 | "options": {
74 | "browserTarget": "hermescloud:build"
75 | }
76 | },
77 | "test": {
78 | "builder": "@angular-devkit/build-angular:karma",
79 | "options": {
80 | "polyfills": [
81 | "zone.js",
82 | "zone.js/testing"
83 | ],
84 | "tsConfig": "tsconfig.spec.json",
85 | "assets": [
86 | "src/favicon.ico",
87 | "src/assets"
88 | ],
89 | "styles": [
90 | "src/styles.css"
91 | ],
92 | "scripts": []
93 | }
94 | }
95 | }
96 | }
97 | }
98 | }
99 |
--------------------------------------------------------------------------------
/website/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "hermescloud",
3 | "version": "0.0.0",
4 | "scripts": {
5 | "ng": "ng",
6 | "start": "ng serve",
7 | "build": "ng build",
8 | "watch": "ng build --watch --configuration development",
9 | "test": "ng test"
10 | },
11 | "private": true,
12 | "dependencies": {
13 | "@angular/animations": "^16.0.0",
14 | "@angular/common": "^16.0.0",
15 | "@angular/compiler": "^16.0.0",
16 | "@angular/core": "^16.0.0",
17 | "@angular/forms": "^16.0.0",
18 | "@angular/platform-browser": "^16.0.0",
19 | "@angular/platform-browser-dynamic": "^16.0.0",
20 | "@angular/router": "^16.0.0",
21 | "rxjs": "~7.8.0",
22 | "tslib": "^2.3.0",
23 | "zone.js": "~0.13.0"
24 | },
25 | "devDependencies": {
26 | "@angular-devkit/build-angular": "^16.0.2",
27 | "@angular/cli": "~16.0.2",
28 | "@angular/compiler-cli": "^16.0.0",
29 | "@types/jasmine": "~4.3.0",
30 | "angular-cli-ghpages": "^1.0.6",
31 | "autoprefixer": "^10.4.14",
32 | "jasmine-core": "~4.6.0",
33 | "karma": "~6.4.0",
34 | "karma-chrome-launcher": "~3.2.0",
35 | "karma-coverage": "~2.2.0",
36 | "karma-jasmine": "~5.1.0",
37 | "karma-jasmine-html-reporter": "~2.0.0",
38 | "postcss": "^8.4.23",
39 | "tailwindcss": "^3.3.2",
40 | "typescript": "~5.0.2"
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/website/src/app/app-routing.module.ts:
--------------------------------------------------------------------------------
1 | import { NgModule } from '@angular/core';
2 | import { RouterModule, Routes } from '@angular/router';
3 |
4 | const routes: Routes = [];
5 |
6 | @NgModule({
7 | imports: [RouterModule.forRoot(routes)],
8 | exports: [RouterModule]
9 | })
10 | export class AppRoutingModule { }
11 |
--------------------------------------------------------------------------------
/website/src/app/app.component.css:
--------------------------------------------------------------------------------
1 | :host {
2 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
3 | font-size: 14px;
4 | color: #333;
5 | box-sizing: border-box;
6 | -webkit-font-smoothing: antialiased;
7 | -moz-osx-font-smoothing: grayscale;
8 | }
9 |
10 | h1,
11 | h2,
12 | h3,
13 | h4,
14 | h5,
15 | h6 {
16 | margin: 8px 0;
17 | }
18 |
19 | p {
20 | margin: 0;
21 | }
--------------------------------------------------------------------------------
/website/src/app/app.component.html:
--------------------------------------------------------------------------------
1 |
2 |
The speed you need. Execute 12 | lightning fast full-text searches with just a few lines of code. Easily access 13 | your data with caching. Both Socket and API implementations are already 14 | provided. Start now with Go. 15 |
16 |Speed made easy. Import Hermes 30 | directly into your Go project without any worry of a network connection. 31 |
32 |Connect with confidence. Install the Hermes 45 | Cloud CLI and immediately begin developing in other languages. Hermes Cloud takes advantage of 46 | sockets to deliver powerful performance. Start now with Python.
47 |Transparency leads to success. Worried about 66 | bugs or malicious intent? Then check out our github, everything is all open-sourced! Leave a 67 | star to support the project and share it with your friends! Click the laptop or here to visit.
69 |10 | func main() {{'{'}} 11 | // Initialize the cache 12 | cache := hermes.InitCache() 13 | cache.FTInit(10, -1, 3) 14 | 15 | // Set a value in the cache with the key "user_id" 16 | cache.Set("user_id", map[string]any {{'{'}} 17 | "name": cache.WithFT("tristan"), 18 | "age": 17, 19 | {{'}'}}) 20 | 21 | // Search for the word "tristan" 22 | result, err := cache.Search(hermes.SearchParams{{'{'}} 23 | Query: "tristan", 24 | Limit: 100, 25 | Strict: false, 26 | {{'}'}}) 27 | fmt.Println(result, err) 28 | {{'}'}} 29 |30 |
12 | {{text}}
13 |
14 |