Skip to content

Commit 7480ffb

Browse files
committed
- mvp of working Manticore migration
1 parent 8e8e790 commit 7480ffb

File tree

12 files changed

+252
-128
lines changed

12 files changed

+252
-128
lines changed

Dockerfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
FROM golang:1.21.3
1+
FROM golang:1.23.7
22

33
WORKDIR /app
44

docker-compose.yml

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,17 +6,15 @@ services:
66
manticore:
77
image: manticoresearch/manticore:7.4.6
88
environment:
9-
EXTRA: 1
10-
# Will attempt to index 'gamenode' on startup
11-
CREATE_PLAIN_TABLES: gamenode
9+
CREATE_PLAIN_TABLES: games:0 3 * * *;users:0 * * * *;games;users
1210

1311
restart: no
1412
ports:
1513
# Note: ManticoreSearch as minimum built-in security,
1614
# so it's not recommended to expose it to the host in production.
1715
# SQL port, use it with care.
18-
- "127.0.0.1:9306:9306"
19-
- "127.0.0.1:9308:9308"
16+
- "9306:9306"
17+
- "9308:9308"
2018

2119
ulimits:
2220
nproc: 65535
@@ -31,8 +29,12 @@ services:
3129
- manticore:/var/lib/manticore
3230
- ./manticore.conf:/etc/manticoresearch/manticore.conf
3331

34-
network_mode: host
32+
networks:
33+
- game_node_app
3534

35+
networks:
36+
game_node_app:
37+
external: true
3638

3739
volumes:
3840
manticore:

main.go

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,32 @@ func main() {
7777
_, _ = w.Write(responseBytes)
7878
return
7979
})
80+
r.Post("/search/games/autocomplete", func(w http.ResponseWriter, r *http.Request) {
81+
defer r.Body.Close()
82+
reqDtoBytes, _ := io.ReadAll(r.Body)
83+
84+
var reqDto games.GameAutocompleteRequestDto
85+
err := json.Unmarshal(reqDtoBytes, &reqDto)
86+
if err != nil {
87+
slog.Error("Error while responding to request: ", "err", err)
88+
w.WriteHeader(http.StatusBadRequest)
89+
w.Write([]byte(err.Error()))
90+
return
91+
}
92+
93+
response, err := games.Autocomplete(&reqDto)
94+
w.Header().Set("Content-Type", "application/json")
95+
if err != nil {
96+
slog.Error("Error while responding to request: ", "err", err)
97+
w.WriteHeader(http.StatusBadRequest)
98+
w.Write([]byte(err.Error()))
99+
return
100+
}
101+
responseBytes, _ := json.Marshal(response)
102+
w.WriteHeader(http.StatusOK)
103+
_, _ = w.Write(responseBytes)
104+
return
105+
})
80106
r.Post("/search/users", func(w http.ResponseWriter, r *http.Request) {
81107
defer r.Body.Close()
82108
reqDtoBytes, _ := io.ReadAll(r.Body)

manticore.conf

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,6 @@ source games_source {
8282
sql_attr_bigint = status
8383
sql_attr_bigint = num_views
8484
sql_attr_bigint = num_likes
85-
8685
}
8786

8887
source users_source {
@@ -103,21 +102,22 @@ source users_source {
103102
sql_attr_timestamp = created_at
104103
sql_attr_timestamp = updated_at
105104
sql_attr_string = user_id
106-
107-
108105
}
109106

110107
table games {
111108
dict = keywords
112109
type = plain
110+
min_infix_len = 3
111+
index_exact_words = 1
112+
expand_keywords = 1
113113
source = games_source
114114
path = /var/lib/manticore/games
115115
}
116116

117117
table users {
118118
dict = keywords
119119
type = plain
120+
min_infix_len = 3
120121
source = users_source
121122
path = /var/lib/manticore/users
122-
min_infix_len = 3
123123
}

schema/schema.go

Lines changed: 0 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -2,55 +2,6 @@ package schema
22

33
type AnyMap map[string]interface{}
44

5-
type UserManticoreSearchSource struct {
6-
UserId string `json:"user_id"`
7-
Username string `json:"username"`
8-
}
9-
10-
type UserManticoreResponseHit struct {
11-
ID string `json:"_id"`
12-
Score int `json:"_score"`
13-
Source UserManticoreSearchSource `json:"_source"`
14-
}
15-
16-
type UserManticoreSearchHits struct {
17-
MaxScore *float64 `json:"max_score,omitempty"`
18-
Total *int `json:"total,omitempty"`
19-
TotalRelation *string `json:"total_relation,omitempty"`
20-
Hits []UserManticoreResponseHit `json:"hits,omitempty"`
21-
}
22-
23-
type UserManticoreSearchResponse struct {
24-
Took *int `json:"took,omitempty"`
25-
TimedOut *bool `json:"timed_out,omitempty"`
26-
Aggregations map[string]interface{} `json:"aggregations,omitempty"`
27-
Hits *UserManticoreSearchHits `json:"hits,omitempty"`
28-
Profile *map[string]interface{} `json:"profile,omitempty"`
29-
Warning map[string]interface{} `json:"warning,omitempty"`
30-
}
31-
32-
type UserDto struct {
33-
UserId string `json:"userId"`
34-
Username string `json:"username"`
35-
}
36-
37-
type UserSearchResponseData struct {
38-
Took *int32 `json:"took,omitempty"`
39-
Items *[]UserDto `json:"items,omitempty"`
40-
Profile map[string]interface{} `json:"profile,omitempty"`
41-
}
42-
43-
type UserSearchResponseDto struct {
44-
Data *UserSearchResponseData `json:"data"`
45-
Pagination *PaginationInfo `json:"pagination"`
46-
}
47-
48-
type UserSearchRequestDto struct {
49-
Query string `json:"query"`
50-
Limit *int32 `json:"limit"`
51-
Page *int32 `json:"page"`
52-
}
53-
545
const DefaultLimit int32 = 20
556

567
type PaginationInfo struct {

search/games/autocomplete.go

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
package games
2+
3+
import (
4+
"fmt"
5+
)
6+
7+
// Autocomplete games autocomplete handler
8+
//
9+
// @Summary Autocomplete games names using Manticore engine
10+
// @Description Returns a parsed search response from the Manticore engine
11+
// @Tags search
12+
// @Accept json
13+
// @Produce json
14+
// @Param query body games.GameAutocompleteRequestDto true ""
15+
// @Success 200 {object} games.GameAutocompleteResponseDto
16+
// @Router /search/games/autocomplete [post]
17+
func Autocomplete(dto *GameAutocompleteRequestDto) (*GameAutocompleteResponseDto, error) {
18+
autocompleteQuery := fmt.Sprintf("^\"%s*\"", dto.Query)
19+
20+
limit := int32(5)
21+
22+
searchRequest := GameSearchRequestDto{
23+
Query: autocompleteQuery,
24+
Limit: &limit,
25+
}
26+
27+
searchResponseDto, err := Search(&searchRequest)
28+
if err != nil {
29+
return nil, err
30+
}
31+
32+
items := searchResponseDto.Data.Items
33+
34+
response := GameAutocompleteResponseDto{Data: make([]string, 0), Total: 0}
35+
36+
if items != nil && len(*items) > 0 {
37+
for _, game := range *items {
38+
response.Data = append(response.Data, game.Name)
39+
}
40+
41+
response.Total = uint(len(*items))
42+
}
43+
44+
return &response, nil
45+
}

search/games/parser.go

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,7 @@ package games
33
import (
44
"game-node-search/schema"
55
Manticoresearch "github.com/manticoresoftware/manticoresearch-go"
6-
"github.com/mitchellh/mapstructure"
76
"log/slog"
8-
"strconv"
97
"time"
108
)
119

@@ -15,6 +13,12 @@ func buildPaginationInfo(mr *Manticoresearch.SearchResponse, limit *int32, curre
1513
i := schema.DefaultLimit
1614
limitToUse = &i
1715
}
16+
17+
if currentPage == nil {
18+
p := int32(1)
19+
currentPage = &p
20+
}
21+
1822
var paginationInfo = schema.PaginationInfo{
1923
TotalItems: 0,
2024
TotalPages: 0,
@@ -47,18 +51,24 @@ func buildResponseData(mr *Manticoresearch.SearchResponse) (*ResponseData, error
4751
}
4852

4953
if mr.Hits != nil && mr.Hits.Hits != nil && len(mr.Hits.Hits) > 0 {
54+
5055
for _, hit := range mr.Hits.Hits {
51-
var convertedHit ManticoreResponseHit
52-
err := mapstructure.Decode(hit, convertedHit)
56+
hitJson, err := json.Marshal(hit)
5357
if err != nil {
54-
slog.Error("Error while decoding results to return type.", "err", err)
58+
slog.Error("Error while decoding results to JSON.", "err", err)
5559
return nil, err
5660
}
5761

58-
hitIdNumber, _ := strconv.ParseUint(convertedHit.ID, 10, 64)
62+
var convertedHit ManticoreResponseHit
63+
64+
err = json.Unmarshal(hitJson, &convertedHit)
65+
if err != nil {
66+
slog.Error("Error while decoding results from JSON.", "err", err)
67+
return nil, err
68+
}
5969

6070
searchGame := SearchGame{
61-
ID: hitIdNumber,
71+
ID: convertedHit.ID,
6272
Name: convertedHit.Source.Name,
6373
Slug: convertedHit.Source.Slug,
6474
Summary: convertedHit.Source.Summary,
@@ -87,5 +97,9 @@ func buildResponseData(mr *Manticoresearch.SearchResponse) (*ResponseData, error
8797

8898
data.Items = &searchGames
8999

100+
if searchGames == nil {
101+
data.Items = &[]SearchGame{}
102+
}
103+
90104
return &data, nil
91105
}

search/games/schema.go

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ type ManticoreResponseHitSource struct {
3131
}
3232

3333
type ManticoreResponseHit struct {
34-
ID string `json:"_id"`
34+
ID uint64 `json:"_id"`
3535
Score int `json:"_score"`
3636
// To avoid re-mapping here, we only map the final SearchGame which is used in GameSearchResponseHit
3737
// This is basically SearchGame as returned by ManticoreSearch, in snake_case.
@@ -88,13 +88,22 @@ type GameSearchResponseDto struct {
8888
}
8989

9090
type GameSearchRequestDto struct {
91-
Query string `json:"query"`
92-
Category *[]int `json:"category,omitempty"`
93-
Status *[]int `json:"status,omitempty"`
94-
Genres *[]string `json:"genres"`
95-
Themes *[]string `json:"themes"`
96-
Platforms *[]string `json:"platforms"`
97-
Limit *int32 `json:"limit,omitempty"`
98-
Page *int32 `json:"page,omitempty"`
99-
Profile *bool `json:"profile,omitempty"`
91+
Query string `json:"query"`
92+
Category []int `json:"category,omitempty"`
93+
Status []int `json:"status,omitempty"`
94+
Genres []string `json:"genres,omitempty"`
95+
Themes []string `json:"themes,omitempty"`
96+
Platforms []string `json:"platforms,omitempty"`
97+
Limit *int32 `json:"limit,omitempty"`
98+
Page *int32 `json:"page,omitempty"`
99+
Profile *bool `json:"profile,omitempty"`
100+
}
101+
102+
type GameAutocompleteRequestDto struct {
103+
Query string `json:"query"`
104+
}
105+
106+
type GameAutocompleteResponseDto struct {
107+
Total uint `json:"total"`
108+
Data []string `json:"data"`
100109
}

0 commit comments

Comments
 (0)