feat: implement relation management system with controllers, services, repositories, and corresponding API documentation.
Build and Release / release (push) Successful in 1m47s
Build and Release / release (push) Successful in 1m47s
This commit is contained in:
+4
-1
@@ -162,6 +162,7 @@ func (s *FiberServer) SetupServer(
|
|||||||
statisticService := services.NewStatisticService(statisticRepo)
|
statisticService := services.NewStatisticService(statisticRepo)
|
||||||
battleReplayService := services.NewBattleReplayService(battleReplayRepo)
|
battleReplayService := services.NewBattleReplayService(battleReplayRepo)
|
||||||
goongService := services.NewGoongService(redis)
|
goongService := services.NewGoongService(redis)
|
||||||
|
relationService := services.NewRelationService(wikiRepo, entityRepo, geometryRepo)
|
||||||
|
|
||||||
// controller setup
|
// controller setup
|
||||||
authController := controllers.NewAuthController(authService, oauth)
|
authController := controllers.NewAuthController(authService, oauth)
|
||||||
@@ -181,6 +182,7 @@ func (s *FiberServer) SetupServer(
|
|||||||
statisticController := controllers.NewStatisticController(statisticService)
|
statisticController := controllers.NewStatisticController(statisticService)
|
||||||
battleReplayController := controllers.NewBattleReplayController(battleReplayService)
|
battleReplayController := controllers.NewBattleReplayController(battleReplayService)
|
||||||
goongController := controllers.NewGoongController(goongService)
|
goongController := controllers.NewGoongController(goongService)
|
||||||
|
relationController := controllers.NewRelationController(relationService)
|
||||||
|
|
||||||
// route setup
|
// route setup
|
||||||
routes.AuthRoutes(s.App, authController, userRepo)
|
routes.AuthRoutes(s.App, authController, userRepo)
|
||||||
@@ -193,11 +195,12 @@ func (s *FiberServer) SetupServer(
|
|||||||
routes.EntityRoutes(s.App, entityController)
|
routes.EntityRoutes(s.App, entityController)
|
||||||
routes.GeometryRoutes(s.App, geometryController)
|
routes.GeometryRoutes(s.App, geometryController)
|
||||||
routes.WikiRoutes(s.App, wikiController)
|
routes.WikiRoutes(s.App, wikiController)
|
||||||
routes.RelationRoutes(s.App, wikiController, entityController)
|
routes.RelationRoutes(s.App, wikiController, entityController, relationController)
|
||||||
routes.ProjectRoutes(s.App, projectController, commitController, userRepo)
|
routes.ProjectRoutes(s.App, projectController, commitController, userRepo)
|
||||||
routes.SubmissionRoutes(s.App, submissionController, userRepo)
|
routes.SubmissionRoutes(s.App, submissionController, userRepo)
|
||||||
routes.ChatbotRoutes(s.App, chatbotController, userRepo)
|
routes.ChatbotRoutes(s.App, chatbotController, userRepo)
|
||||||
routes.StatisticRoutes(s.App, statisticController, userRepo)
|
routes.StatisticRoutes(s.App, statisticController, userRepo)
|
||||||
|
|
||||||
routes.BattleReplayRoutes(s.App, battleReplayController)
|
routes.BattleReplayRoutes(s.App, battleReplayController)
|
||||||
routes.GoongRoutes(s.App, goongController)
|
routes.GoongRoutes(s.App, goongController)
|
||||||
routes.NotFoundRoute(s.App)
|
routes.NotFoundRoute(s.App)
|
||||||
|
|||||||
@@ -248,3 +248,9 @@ SELECT
|
|||||||
)::uuid[] AS replay_ids
|
)::uuid[] AS replay_ids
|
||||||
FROM geometries
|
FROM geometries
|
||||||
WHERE geometries.bound_with = $1 AND geometries.is_deleted = false;
|
WHERE geometries.bound_with = $1 AND geometries.is_deleted = false;
|
||||||
|
|
||||||
|
-- name: GetGeometryIDsByEntityIDs :many
|
||||||
|
SELECT entity_id, geometry_id
|
||||||
|
FROM entity_geometries
|
||||||
|
WHERE entity_id = ANY($1::uuid[]);
|
||||||
|
|
||||||
|
|||||||
@@ -132,3 +132,9 @@ ORDER BY created_at DESC;
|
|||||||
SELECT entity_id, wiki_id
|
SELECT entity_id, wiki_id
|
||||||
FROM entity_wikis
|
FROM entity_wikis
|
||||||
WHERE entity_id = ANY($1::uuid[]);
|
WHERE entity_id = ANY($1::uuid[]);
|
||||||
|
|
||||||
|
-- name: GetEntityIDsByWikiIDs :many
|
||||||
|
SELECT wiki_id, entity_id
|
||||||
|
FROM entity_wikis
|
||||||
|
WHERE wiki_id = ANY($1::uuid[]);
|
||||||
|
|
||||||
|
|||||||
+69
-1
@@ -895,6 +895,13 @@ const docTemplate = `{
|
|||||||
"name": "has_bound",
|
"name": "has_bound",
|
||||||
"in": "query"
|
"in": "query"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"maximum": 100,
|
||||||
|
"minimum": 1,
|
||||||
|
"type": "integer",
|
||||||
|
"name": "limit",
|
||||||
|
"in": "query"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"maximum": 90,
|
"maximum": 90,
|
||||||
"minimum": -90,
|
"minimum": -90,
|
||||||
@@ -2933,6 +2940,66 @@ const docTemplate = `{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"/relations": {
|
||||||
|
"get": {
|
||||||
|
"description": "Get relations by type (wiki-entity, entity-wiki, geometry-entity, entity-geometry) and list of IDs",
|
||||||
|
"consumes": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"produces": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"Relations"
|
||||||
|
],
|
||||||
|
"summary": "Get generalized batch relations",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"minItems": 1,
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"collectionFormat": "csv",
|
||||||
|
"name": "ids",
|
||||||
|
"in": "query",
|
||||||
|
"required": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"enum": [
|
||||||
|
"wiki-entity",
|
||||||
|
"entity-wiki",
|
||||||
|
"geometry-entity",
|
||||||
|
"entity-geometry"
|
||||||
|
],
|
||||||
|
"type": "string",
|
||||||
|
"name": "type",
|
||||||
|
"in": "query",
|
||||||
|
"required": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "OK",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/history-api_internal_dtos_response.CommonResponse"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"400": {
|
||||||
|
"description": "Bad Request",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/history-api_internal_dtos_response.CommonResponse"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"500": {
|
||||||
|
"description": "Internal Server Error",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/history-api_internal_dtos_response.CommonResponse"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"/relations/entities-by-geometries": {
|
"/relations/entities-by-geometries": {
|
||||||
"get": {
|
"get": {
|
||||||
"description": "Get entities grouped by geometry IDs",
|
"description": "Get entities grouped by geometry IDs",
|
||||||
@@ -4900,7 +4967,8 @@ const docTemplate = `{
|
|||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
"question": {
|
"question": {
|
||||||
"type": "string"
|
"type": "string",
|
||||||
|
"maxLength": 500
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
+69
-1
@@ -888,6 +888,13 @@
|
|||||||
"name": "has_bound",
|
"name": "has_bound",
|
||||||
"in": "query"
|
"in": "query"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"maximum": 100,
|
||||||
|
"minimum": 1,
|
||||||
|
"type": "integer",
|
||||||
|
"name": "limit",
|
||||||
|
"in": "query"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"maximum": 90,
|
"maximum": 90,
|
||||||
"minimum": -90,
|
"minimum": -90,
|
||||||
@@ -2926,6 +2933,66 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"/relations": {
|
||||||
|
"get": {
|
||||||
|
"description": "Get relations by type (wiki-entity, entity-wiki, geometry-entity, entity-geometry) and list of IDs",
|
||||||
|
"consumes": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"produces": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"Relations"
|
||||||
|
],
|
||||||
|
"summary": "Get generalized batch relations",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"minItems": 1,
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"collectionFormat": "csv",
|
||||||
|
"name": "ids",
|
||||||
|
"in": "query",
|
||||||
|
"required": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"enum": [
|
||||||
|
"wiki-entity",
|
||||||
|
"entity-wiki",
|
||||||
|
"geometry-entity",
|
||||||
|
"entity-geometry"
|
||||||
|
],
|
||||||
|
"type": "string",
|
||||||
|
"name": "type",
|
||||||
|
"in": "query",
|
||||||
|
"required": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "OK",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/history-api_internal_dtos_response.CommonResponse"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"400": {
|
||||||
|
"description": "Bad Request",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/history-api_internal_dtos_response.CommonResponse"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"500": {
|
||||||
|
"description": "Internal Server Error",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/history-api_internal_dtos_response.CommonResponse"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"/relations/entities-by-geometries": {
|
"/relations/entities-by-geometries": {
|
||||||
"get": {
|
"get": {
|
||||||
"description": "Get entities grouped by geometry IDs",
|
"description": "Get entities grouped by geometry IDs",
|
||||||
@@ -4893,7 +4960,8 @@
|
|||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
"question": {
|
"question": {
|
||||||
"type": "string"
|
"type": "string",
|
||||||
|
"maxLength": 500
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -82,6 +82,7 @@ definitions:
|
|||||||
project_id:
|
project_id:
|
||||||
type: string
|
type: string
|
||||||
question:
|
question:
|
||||||
|
maxLength: 500
|
||||||
type: string
|
type: string
|
||||||
required:
|
required:
|
||||||
- question
|
- question
|
||||||
@@ -1327,6 +1328,11 @@ paths:
|
|||||||
- in: query
|
- in: query
|
||||||
name: has_bound
|
name: has_bound
|
||||||
type: boolean
|
type: boolean
|
||||||
|
- in: query
|
||||||
|
maximum: 100
|
||||||
|
minimum: 1
|
||||||
|
name: limit
|
||||||
|
type: integer
|
||||||
- in: query
|
- in: query
|
||||||
maximum: 90
|
maximum: 90
|
||||||
minimum: -90
|
minimum: -90
|
||||||
@@ -2655,6 +2661,48 @@ paths:
|
|||||||
summary: Get raster tile metadata
|
summary: Get raster tile metadata
|
||||||
tags:
|
tags:
|
||||||
- Tile
|
- Tile
|
||||||
|
/relations:
|
||||||
|
get:
|
||||||
|
consumes:
|
||||||
|
- application/json
|
||||||
|
description: Get relations by type (wiki-entity, entity-wiki, geometry-entity,
|
||||||
|
entity-geometry) and list of IDs
|
||||||
|
parameters:
|
||||||
|
- collectionFormat: csv
|
||||||
|
in: query
|
||||||
|
items:
|
||||||
|
type: string
|
||||||
|
minItems: 1
|
||||||
|
name: ids
|
||||||
|
required: true
|
||||||
|
type: array
|
||||||
|
- enum:
|
||||||
|
- wiki-entity
|
||||||
|
- entity-wiki
|
||||||
|
- geometry-entity
|
||||||
|
- entity-geometry
|
||||||
|
in: query
|
||||||
|
name: type
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
|
produces:
|
||||||
|
- application/json
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: OK
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/history-api_internal_dtos_response.CommonResponse'
|
||||||
|
"400":
|
||||||
|
description: Bad Request
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/history-api_internal_dtos_response.CommonResponse'
|
||||||
|
"500":
|
||||||
|
description: Internal Server Error
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/history-api_internal_dtos_response.CommonResponse'
|
||||||
|
summary: Get generalized batch relations
|
||||||
|
tags:
|
||||||
|
- Relations
|
||||||
/relations/entities-by-geometries:
|
/relations/entities-by-geometries:
|
||||||
get:
|
get:
|
||||||
consumes:
|
consumes:
|
||||||
|
|||||||
@@ -0,0 +1,57 @@
|
|||||||
|
package controllers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"history-api/internal/dtos/request"
|
||||||
|
"history-api/internal/dtos/response"
|
||||||
|
"history-api/internal/services"
|
||||||
|
"history-api/pkg/validator"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/gofiber/fiber/v3"
|
||||||
|
)
|
||||||
|
|
||||||
|
type RelationController struct {
|
||||||
|
service services.RelationService
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewRelationController(svc services.RelationService) *RelationController {
|
||||||
|
return &RelationController{service: svc}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetRelations handles fetching relationships dynamically.
|
||||||
|
// @Summary Get generalized batch relations
|
||||||
|
// @Description Get relations by type (wiki-entity, entity-wiki, geometry-entity, entity-geometry) and list of IDs
|
||||||
|
// @Tags Relations
|
||||||
|
// @Accept json
|
||||||
|
// @Produce json
|
||||||
|
// @Param query query request.GetRelationsDto true "Query Parameters"
|
||||||
|
// @Success 200 {object} response.CommonResponse
|
||||||
|
// @Failure 400 {object} response.CommonResponse
|
||||||
|
// @Failure 500 {object} response.CommonResponse
|
||||||
|
// @Router /relations [get]
|
||||||
|
func (h *RelationController) GetRelations(c fiber.Ctx) error {
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
dto := &request.GetRelationsDto{}
|
||||||
|
if err := validator.ValidateQueryDto(c, dto); err != nil {
|
||||||
|
return c.Status(fiber.StatusBadRequest).JSON(response.CommonResponse{
|
||||||
|
Status: false,
|
||||||
|
Errors: err,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := h.service.GetRelations(ctx, dto)
|
||||||
|
if err != nil {
|
||||||
|
return c.Status(err.Code).JSON(response.CommonResponse{
|
||||||
|
Status: false,
|
||||||
|
Message: err.Message,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.Status(fiber.StatusOK).JSON(response.CommonResponse{
|
||||||
|
Status: true,
|
||||||
|
Data: res,
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -11,3 +11,18 @@ type GetEntitiesByGeometryIDsDto struct {
|
|||||||
type GetWikiContentsPreviewDto struct {
|
type GetWikiContentsPreviewDto struct {
|
||||||
IDs []string `json:"ids" query:"ids" validate:"required,min=1,dive,uuid"`
|
IDs []string `json:"ids" query:"ids" validate:"required,min=1,dive,uuid"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type GetEntitiesByWikiIDsDto struct {
|
||||||
|
WikiIDs []string `json:"wiki_ids" query:"wiki_ids" validate:"required,min=1,dive,uuid"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type GetGeometriesByEntityIDsDto struct {
|
||||||
|
EntityIDs []string `json:"entity_ids" query:"entity_ids" validate:"required,min=1,dive,uuid"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type GetRelationsDto struct {
|
||||||
|
Type string `json:"type" query:"type" validate:"required,oneof=wiki-entity entity-wiki geometry-entity entity-geometry"`
|
||||||
|
IDs []string `json:"ids" query:"ids" validate:"required,min=1,dive,uuid"`
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -538,6 +538,37 @@ func (q *Queries) GetGeometryById(ctx context.Context, id pgtype.UUID) (GetGeome
|
|||||||
return i, err
|
return i, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const getGeometryIDsByEntityIDs = `-- name: GetGeometryIDsByEntityIDs :many
|
||||||
|
SELECT entity_id, geometry_id
|
||||||
|
FROM entity_geometries
|
||||||
|
WHERE entity_id = ANY($1::uuid[])
|
||||||
|
`
|
||||||
|
|
||||||
|
type GetGeometryIDsByEntityIDsRow struct {
|
||||||
|
EntityID pgtype.UUID `json:"entity_id"`
|
||||||
|
GeometryID pgtype.UUID `json:"geometry_id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *Queries) GetGeometryIDsByEntityIDs(ctx context.Context, dollar_1 []pgtype.UUID) ([]GetGeometryIDsByEntityIDsRow, error) {
|
||||||
|
rows, err := q.db.Query(ctx, getGeometryIDsByEntityIDs, dollar_1)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
items := []GetGeometryIDsByEntityIDsRow{}
|
||||||
|
for rows.Next() {
|
||||||
|
var i GetGeometryIDsByEntityIDsRow
|
||||||
|
if err := rows.Scan(&i.EntityID, &i.GeometryID); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
items = append(items, i)
|
||||||
|
}
|
||||||
|
if err := rows.Err(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return items, nil
|
||||||
|
}
|
||||||
|
|
||||||
const searchGeometries = `-- name: SearchGeometries :many
|
const searchGeometries = `-- name: SearchGeometries :many
|
||||||
SELECT
|
SELECT
|
||||||
g.id, g.geo_type, g.draw_geometry, g.bound_with, g.time_start, g.time_end, g.project_id,
|
g.id, g.geo_type, g.draw_geometry, g.bound_with, g.time_start, g.time_end, g.project_id,
|
||||||
|
|||||||
@@ -188,6 +188,37 @@ func (q *Queries) DeleteWikisByIDs(ctx context.Context, dollar_1 []pgtype.UUID)
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const getEntityIDsByWikiIDs = `-- name: GetEntityIDsByWikiIDs :many
|
||||||
|
SELECT wiki_id, entity_id
|
||||||
|
FROM entity_wikis
|
||||||
|
WHERE wiki_id = ANY($1::uuid[])
|
||||||
|
`
|
||||||
|
|
||||||
|
type GetEntityIDsByWikiIDsRow struct {
|
||||||
|
WikiID pgtype.UUID `json:"wiki_id"`
|
||||||
|
EntityID pgtype.UUID `json:"entity_id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *Queries) GetEntityIDsByWikiIDs(ctx context.Context, dollar_1 []pgtype.UUID) ([]GetEntityIDsByWikiIDsRow, error) {
|
||||||
|
rows, err := q.db.Query(ctx, getEntityIDsByWikiIDs, dollar_1)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
items := []GetEntityIDsByWikiIDsRow{}
|
||||||
|
for rows.Next() {
|
||||||
|
var i GetEntityIDsByWikiIDsRow
|
||||||
|
if err := rows.Scan(&i.WikiID, &i.EntityID); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
items = append(items, i)
|
||||||
|
}
|
||||||
|
if err := rows.Err(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return items, nil
|
||||||
|
}
|
||||||
|
|
||||||
const getWikiById = `-- name: GetWikiById :one
|
const getWikiById = `-- name: GetWikiById :one
|
||||||
SELECT id, project_id, title, slug, is_deleted, created_at, updated_at
|
SELECT id, project_id, title, slug, is_deleted, created_at, updated_at
|
||||||
FROM wikis
|
FROM wikis
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ type EntityRepository interface {
|
|||||||
Delete(ctx context.Context, id pgtype.UUID) error
|
Delete(ctx context.Context, id pgtype.UUID) error
|
||||||
DeleteByIDs(ctx context.Context, ids []pgtype.UUID) error
|
DeleteByIDs(ctx context.Context, ids []pgtype.UUID) error
|
||||||
GetByProjectID(ctx context.Context, projectID pgtype.UUID) ([]*models.EntityEntity, error)
|
GetByProjectID(ctx context.Context, projectID pgtype.UUID) ([]*models.EntityEntity, error)
|
||||||
|
GetEntityIDsByWikiIDs(ctx context.Context, wikiIDs []string) (map[string][]string, error)
|
||||||
GetEntityIDsByGeometryIDs(ctx context.Context, geometryIDs []string) (map[string][]string, error)
|
GetEntityIDsByGeometryIDs(ctx context.Context, geometryIDs []string) (map[string][]string, error)
|
||||||
WithTx(tx pgx.Tx) EntityRepository
|
WithTx(tx pgx.Tx) EntityRepository
|
||||||
}
|
}
|
||||||
@@ -473,3 +474,63 @@ func (r *entityRepository) GetEntityIDsByGeometryIDs(ctx context.Context, geomet
|
|||||||
|
|
||||||
return result, nil
|
return result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *entityRepository) GetEntityIDsByWikiIDs(ctx context.Context, wikiIDs []string) (map[string][]string, error) {
|
||||||
|
if len(wikiIDs) == 0 {
|
||||||
|
return make(map[string][]string), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
keys := make([]string, len(wikiIDs))
|
||||||
|
for i, id := range wikiIDs {
|
||||||
|
keys[i] = cache.Key("entity_wikis:wiki", id)
|
||||||
|
}
|
||||||
|
|
||||||
|
raws := r.c.MGet(ctx, keys...)
|
||||||
|
result := make(map[string][]string, len(wikiIDs))
|
||||||
|
missingWikiIDs := make([]string, 0, len(wikiIDs))
|
||||||
|
missingPgIDs := make([]pgtype.UUID, 0, len(wikiIDs))
|
||||||
|
|
||||||
|
for i, b := range raws {
|
||||||
|
if len(b) > 0 {
|
||||||
|
var entityIDs []string
|
||||||
|
if err := json.Unmarshal(b, &entityIDs); err == nil {
|
||||||
|
result[wikiIDs[i]] = entityIDs
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
missingWikiIDs = append(missingWikiIDs, wikiIDs[i])
|
||||||
|
pgID, err := convert.StringToUUID(wikiIDs[i])
|
||||||
|
if err == nil {
|
||||||
|
missingPgIDs = append(missingPgIDs, pgID)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(missingPgIDs) > 0 {
|
||||||
|
rows, err := r.q.GetEntityIDsByWikiIDs(ctx, missingPgIDs)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
dbMap := make(map[string][]string, len(missingWikiIDs))
|
||||||
|
for _, id := range missingWikiIDs {
|
||||||
|
dbMap[id] = []string{}
|
||||||
|
}
|
||||||
|
for _, row := range rows {
|
||||||
|
wID := convert.UUIDToString(row.WikiID)
|
||||||
|
eID := convert.UUIDToString(row.EntityID)
|
||||||
|
dbMap[wID] = append(dbMap[wID], eID)
|
||||||
|
}
|
||||||
|
|
||||||
|
missingToCache := make(map[string]any, len(dbMap))
|
||||||
|
for wID, eIDs := range dbMap {
|
||||||
|
result[wID] = eIDs
|
||||||
|
missingToCache[cache.Key("entity_wikis:wiki", wID)] = eIDs
|
||||||
|
}
|
||||||
|
if len(missingToCache) > 0 {
|
||||||
|
_ = r.c.MSet(ctx, missingToCache, constants.NormalCacheDuration)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ type GeometryRepository interface {
|
|||||||
BulkDeleteEntityGeometriesByGeometryID(ctx context.Context, geometryID pgtype.UUID) error
|
BulkDeleteEntityGeometriesByGeometryID(ctx context.Context, geometryID pgtype.UUID) error
|
||||||
DeleteEntityGeometry(ctx context.Context, entityID pgtype.UUID, geometryID pgtype.UUID) error
|
DeleteEntityGeometry(ctx context.Context, entityID pgtype.UUID, geometryID pgtype.UUID) error
|
||||||
DeleteEntityGeometriesByProjectID(ctx context.Context, projectID pgtype.UUID) error
|
DeleteEntityGeometriesByProjectID(ctx context.Context, projectID pgtype.UUID) error
|
||||||
|
GetGeometryIDsByEntityIDs(ctx context.Context, entityIDs []string) (map[string][]string, error)
|
||||||
WithTx(tx pgx.Tx) GeometryRepository
|
WithTx(tx pgx.Tx) GeometryRepository
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -565,3 +566,63 @@ func (r *geometryRepository) SearchByEntityName(ctx context.Context, params sqlc
|
|||||||
|
|
||||||
return geometries, nil
|
return geometries, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *geometryRepository) GetGeometryIDsByEntityIDs(ctx context.Context, entityIDs []string) (map[string][]string, error) {
|
||||||
|
if len(entityIDs) == 0 {
|
||||||
|
return make(map[string][]string), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
keys := make([]string, len(entityIDs))
|
||||||
|
for i, id := range entityIDs {
|
||||||
|
keys[i] = cache.Key("entity_geometries:entity", id)
|
||||||
|
}
|
||||||
|
|
||||||
|
raws := r.c.MGet(ctx, keys...)
|
||||||
|
result := make(map[string][]string, len(entityIDs))
|
||||||
|
missingEntityIDs := make([]string, 0, len(entityIDs))
|
||||||
|
missingPgIDs := make([]pgtype.UUID, 0, len(entityIDs))
|
||||||
|
|
||||||
|
for i, b := range raws {
|
||||||
|
if len(b) > 0 {
|
||||||
|
var geometryIDs []string
|
||||||
|
if err := json.Unmarshal(b, &geometryIDs); err == nil {
|
||||||
|
result[entityIDs[i]] = geometryIDs
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
missingEntityIDs = append(missingEntityIDs, entityIDs[i])
|
||||||
|
pgID, err := convert.StringToUUID(entityIDs[i])
|
||||||
|
if err == nil {
|
||||||
|
missingPgIDs = append(missingPgIDs, pgID)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(missingPgIDs) > 0 {
|
||||||
|
rows, err := r.q.GetGeometryIDsByEntityIDs(ctx, missingPgIDs)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
dbMap := make(map[string][]string, len(missingEntityIDs))
|
||||||
|
for _, id := range missingEntityIDs {
|
||||||
|
dbMap[id] = []string{}
|
||||||
|
}
|
||||||
|
for _, row := range rows {
|
||||||
|
eID := convert.UUIDToString(row.EntityID)
|
||||||
|
gID := convert.UUIDToString(row.GeometryID)
|
||||||
|
dbMap[eID] = append(dbMap[eID], gID)
|
||||||
|
}
|
||||||
|
|
||||||
|
missingToCache := make(map[string]any, len(dbMap))
|
||||||
|
for eID, gIDs := range dbMap {
|
||||||
|
result[eID] = gIDs
|
||||||
|
missingToCache[cache.Key("entity_geometries:entity", eID)] = gIDs
|
||||||
|
}
|
||||||
|
if len(missingToCache) > 0 {
|
||||||
|
_ = r.c.MSet(ctx, missingToCache, constants.NormalCacheDuration)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,9 +6,16 @@ import (
|
|||||||
"github.com/gofiber/fiber/v3"
|
"github.com/gofiber/fiber/v3"
|
||||||
)
|
)
|
||||||
|
|
||||||
func RelationRoutes(router fiber.Router, wikiController *controllers.WikiController, entityController *controllers.EntityController) {
|
func RelationRoutes(
|
||||||
|
router fiber.Router,
|
||||||
|
wikiController *controllers.WikiController,
|
||||||
|
entityController *controllers.EntityController,
|
||||||
|
relationController *controllers.RelationController,
|
||||||
|
) {
|
||||||
relation := router.Group("/relations")
|
relation := router.Group("/relations")
|
||||||
|
relation.Get("", relationController.GetRelations)
|
||||||
relation.Get("/wikis-by-entities", wikiController.GetWikisByEntityIDs)
|
relation.Get("/wikis-by-entities", wikiController.GetWikisByEntityIDs)
|
||||||
relation.Get("/entities-by-geometries", entityController.GetEntitiesByGeometryIDs)
|
relation.Get("/entities-by-geometries", entityController.GetEntitiesByGeometryIDs)
|
||||||
relation.Get("/wiki-contents/preview", wikiController.GetWikiContentsPreviewByIDs)
|
relation.Get("/wiki-contents/preview", wikiController.GetWikiContentsPreviewByIDs)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,188 +0,0 @@
|
|||||||
package services
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"errors"
|
|
||||||
"history-api/internal/dtos/request"
|
|
||||||
"history-api/internal/dtos/response"
|
|
||||||
"history-api/internal/gen/sqlc"
|
|
||||||
"history-api/internal/models"
|
|
||||||
"history-api/internal/repositories"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/jackc/pgx/v5"
|
|
||||||
"github.com/jackc/pgx/v5/pgtype"
|
|
||||||
)
|
|
||||||
|
|
||||||
// mockGeometryRepository implements repositories.GeometryRepository for unit testing
|
|
||||||
type mockGeometryRepository struct {
|
|
||||||
mockGeometries []*models.GeometryEntity
|
|
||||||
mockErr error
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *mockGeometryRepository) GetByID(ctx context.Context, id pgtype.UUID) (*models.GeometryEntity, error) {
|
|
||||||
if m.mockErr != nil {
|
|
||||||
return nil, m.mockErr
|
|
||||||
}
|
|
||||||
if len(m.mockGeometries) > 0 {
|
|
||||||
return m.mockGeometries[0], nil
|
|
||||||
}
|
|
||||||
return nil, errors.New("not found")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *mockGeometryRepository) GetByIDs(ctx context.Context, ids []string) ([]*models.GeometryEntity, error) {
|
|
||||||
return m.mockGeometries, m.mockErr
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *mockGeometryRepository) Search(ctx context.Context, params sqlc.SearchGeometriesParams) ([]*models.GeometryEntity, error) {
|
|
||||||
return m.mockGeometries, m.mockErr
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *mockGeometryRepository) SearchByEntityName(ctx context.Context, params sqlc.SearchGeometriesByEntityNameParams) ([]*models.EntityGeometriesSearchEntity, error) {
|
|
||||||
return nil, m.mockErr
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *mockGeometryRepository) Create(ctx context.Context, params sqlc.CreateGeometryParams) (*models.GeometryEntity, error) {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *mockGeometryRepository) Update(ctx context.Context, params sqlc.UpdateGeometryParams) (*models.GeometryEntity, error) {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *mockGeometryRepository) Delete(ctx context.Context, id pgtype.UUID) error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *mockGeometryRepository) CreateEntityGeometries(ctx context.Context, params sqlc.CreateEntityGeometriesParams) error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *mockGeometryRepository) BulkDeleteEntityGeometriesByEntityId(ctx context.Context, entityId pgtype.UUID) error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *mockGeometryRepository) GetByProjectID(ctx context.Context, projectID pgtype.UUID) ([]*models.GeometryEntity, error) {
|
|
||||||
return m.mockGeometries, m.mockErr
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *mockGeometryRepository) GetGeometriesByBoundWith(ctx context.Context, boundWith pgtype.UUID) ([]*models.GeometryEntity, error) {
|
|
||||||
return m.mockGeometries, m.mockErr
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *mockGeometryRepository) DeleteByIDs(ctx context.Context, ids []pgtype.UUID) error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *mockGeometryRepository) BulkDeleteEntityGeometriesByGeometryID(ctx context.Context, geometryID pgtype.UUID) error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *mockGeometryRepository) DeleteEntityGeometry(ctx context.Context, entityID pgtype.UUID, geometryID pgtype.UUID) error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *mockGeometryRepository) DeleteEntityGeometriesByProjectID(ctx context.Context, projectID pgtype.UUID) error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *mockGeometryRepository) WithTx(tx pgx.Tx) repositories.GeometryRepository {
|
|
||||||
return m
|
|
||||||
}
|
|
||||||
|
|
||||||
// Helper to float pointer
|
|
||||||
func floatPtr(f float64) *float64 {
|
|
||||||
return &f
|
|
||||||
}
|
|
||||||
|
|
||||||
// Helper to int pointer
|
|
||||||
func intPtr(i int32) *int32 {
|
|
||||||
return &i
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestGetGeometryByID_InvalidID(t *testing.T) {
|
|
||||||
repo := &mockGeometryRepository{}
|
|
||||||
svc := NewGeometryService(repo)
|
|
||||||
|
|
||||||
_, fErr := svc.GetGeometryByID(context.Background(), "invalid-uuid-format")
|
|
||||||
if fErr == nil {
|
|
||||||
t.Fatal("Expected error for invalid UUID format, got nil")
|
|
||||||
}
|
|
||||||
if fErr.Code != 400 {
|
|
||||||
t.Errorf("Expected status 400, got %d", fErr.Code)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSearchGeometries_NoBoundingBox(t *testing.T) {
|
|
||||||
repo := &mockGeometryRepository{}
|
|
||||||
svc := NewGeometryService(repo)
|
|
||||||
|
|
||||||
req := &request.SearchGeometryDto{
|
|
||||||
MinLng: nil, // Missing bounding box parameters
|
|
||||||
}
|
|
||||||
|
|
||||||
_, fErr := svc.SearchGeometries(context.Background(), req)
|
|
||||||
if fErr == nil {
|
|
||||||
t.Fatal("Expected error for missing bounding box, got nil")
|
|
||||||
}
|
|
||||||
if fErr.Code != 400 {
|
|
||||||
t.Errorf("Expected status 400, got %d", fErr.Code)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSearchGeometries_InvalidBoundingBox(t *testing.T) {
|
|
||||||
repo := &mockGeometryRepository{}
|
|
||||||
svc := NewGeometryService(repo)
|
|
||||||
|
|
||||||
req := &request.SearchGeometryDto{
|
|
||||||
MinLng: floatPtr(105.0),
|
|
||||||
MinLat: floatPtr(21.0),
|
|
||||||
MaxLng: floatPtr(100.0), // MaxLng < MinLng (Invalid)
|
|
||||||
MaxLat: floatPtr(22.0),
|
|
||||||
}
|
|
||||||
|
|
||||||
_, fErr := svc.SearchGeometries(context.Background(), req)
|
|
||||||
if fErr == nil {
|
|
||||||
t.Fatal("Expected error for invalid bounding box coordinates, got nil")
|
|
||||||
}
|
|
||||||
if fErr.Code != 400 {
|
|
||||||
t.Errorf("Expected status 400, got %d", fErr.Code)
|
|
||||||
}
|
|
||||||
if fErr.Message != "Invalid bounding box coordinates" {
|
|
||||||
t.Errorf("Expected error message 'Invalid bounding box coordinates', got '%s'", fErr.Message)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSearchGeometries_Valid(t *testing.T) {
|
|
||||||
mockGeometries := []*models.GeometryEntity{
|
|
||||||
{
|
|
||||||
ID: "87570494-0cfb-4e14-8789-7cfc0245037d",
|
|
||||||
GeoType: 3, // Polygon
|
|
||||||
Bbox: &response.Bbox{
|
|
||||||
MinLng: 102.0, MinLat: 8.0, MaxLng: 109.0, MaxLat: 23.0,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
repo := &mockGeometryRepository{
|
|
||||||
mockGeometries: mockGeometries,
|
|
||||||
}
|
|
||||||
svc := NewGeometryService(repo)
|
|
||||||
|
|
||||||
req := &request.SearchGeometryDto{
|
|
||||||
MinLng: floatPtr(100.0),
|
|
||||||
MinLat: floatPtr(5.0),
|
|
||||||
MaxLng: floatPtr(110.0),
|
|
||||||
MaxLat: floatPtr(25.0),
|
|
||||||
}
|
|
||||||
|
|
||||||
res, fErr := svc.SearchGeometries(context.Background(), req)
|
|
||||||
if fErr != nil {
|
|
||||||
t.Fatalf("Expected no error, got %v", fErr)
|
|
||||||
}
|
|
||||||
if len(res) != 1 {
|
|
||||||
t.Fatalf("Expected 1 geometry response, got %d", len(res))
|
|
||||||
}
|
|
||||||
if res[0].ID != mockGeometries[0].ID {
|
|
||||||
t.Errorf("Expected ID %s, got %s", mockGeometries[0].ID, res[0].ID)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,240 @@
|
|||||||
|
package services
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"history-api/internal/dtos/request"
|
||||||
|
"history-api/internal/dtos/response"
|
||||||
|
"history-api/internal/models"
|
||||||
|
"history-api/internal/repositories"
|
||||||
|
|
||||||
|
"github.com/gofiber/fiber/v3"
|
||||||
|
)
|
||||||
|
|
||||||
|
type RelationService interface {
|
||||||
|
GetRelations(ctx context.Context, req *request.GetRelationsDto) (interface{}, *fiber.Error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type relationService struct {
|
||||||
|
wikiRepo repositories.WikiRepository
|
||||||
|
entityRepo repositories.EntityRepository
|
||||||
|
geometryRepo repositories.GeometryRepository
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewRelationService(
|
||||||
|
wikiRepo repositories.WikiRepository,
|
||||||
|
entityRepo repositories.EntityRepository,
|
||||||
|
geometryRepo repositories.GeometryRepository,
|
||||||
|
) RelationService {
|
||||||
|
return &relationService{
|
||||||
|
wikiRepo: wikiRepo,
|
||||||
|
entityRepo: entityRepo,
|
||||||
|
geometryRepo: geometryRepo,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *relationService) GetRelations(ctx context.Context, req *request.GetRelationsDto) (interface{}, *fiber.Error) {
|
||||||
|
switch req.Type {
|
||||||
|
case "wiki-entity":
|
||||||
|
return s.getEntitiesByWikiIDs(ctx, req.IDs)
|
||||||
|
case "entity-wiki":
|
||||||
|
return s.getWikisByEntityIDs(ctx, req.IDs)
|
||||||
|
case "geometry-entity":
|
||||||
|
return s.getEntitiesByGeometryIDs(ctx, req.IDs)
|
||||||
|
case "entity-geometry":
|
||||||
|
return s.getGeometriesByEntityIDs(ctx, req.IDs)
|
||||||
|
default:
|
||||||
|
return nil, fiber.NewError(fiber.StatusBadRequest, "Unsupported relation type")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *relationService) getEntitiesByWikiIDs(ctx context.Context, wikiIDs []string) (map[string][]*response.EntityResponse, *fiber.Error) {
|
||||||
|
mapping, err := s.entityRepo.GetEntityIDsByWikiIDs(ctx, wikiIDs)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to fetch entity IDs by wiki IDs")
|
||||||
|
}
|
||||||
|
|
||||||
|
totalEntityIDs := 0
|
||||||
|
for _, eIDs := range mapping {
|
||||||
|
totalEntityIDs += len(eIDs)
|
||||||
|
}
|
||||||
|
|
||||||
|
entityIDMap := make(map[string]struct{}, totalEntityIDs)
|
||||||
|
allEntityIDs := make([]string, 0, totalEntityIDs)
|
||||||
|
for _, eIDs := range mapping {
|
||||||
|
for _, eID := range eIDs {
|
||||||
|
if _, ok := entityIDMap[eID]; !ok {
|
||||||
|
entityIDMap[eID] = struct{}{}
|
||||||
|
allEntityIDs = append(allEntityIDs, eID)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
entities, err := s.entityRepo.GetByIDs(ctx, allEntityIDs)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to fetch entities")
|
||||||
|
}
|
||||||
|
|
||||||
|
entitiesByID := make(map[string]*models.EntityEntity, len(entities))
|
||||||
|
for _, e := range entities {
|
||||||
|
entitiesByID[e.ID] = e
|
||||||
|
}
|
||||||
|
|
||||||
|
result := make(map[string][]*response.EntityResponse, len(wikiIDs))
|
||||||
|
for _, idStr := range wikiIDs {
|
||||||
|
eIDs, exists := mapping[idStr]
|
||||||
|
result[idStr] = make([]*response.EntityResponse, 0, len(eIDs))
|
||||||
|
if exists {
|
||||||
|
for _, eID := range eIDs {
|
||||||
|
if e, found := entitiesByID[eID]; found {
|
||||||
|
result[idStr] = append(result[idStr], e.ToResponse())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *relationService) getWikisByEntityIDs(ctx context.Context, entityIDs []string) (map[string][]*response.WikiResponse, *fiber.Error) {
|
||||||
|
mapping, err := s.wikiRepo.GetWikiIDsByEntityIDs(ctx, entityIDs)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to fetch wiki IDs by entity IDs")
|
||||||
|
}
|
||||||
|
|
||||||
|
totalWikiIDs := 0
|
||||||
|
for _, wIDs := range mapping {
|
||||||
|
totalWikiIDs += len(wIDs)
|
||||||
|
}
|
||||||
|
|
||||||
|
wikiIDMap := make(map[string]struct{}, totalWikiIDs)
|
||||||
|
allWikiIDs := make([]string, 0, totalWikiIDs)
|
||||||
|
for _, wIDs := range mapping {
|
||||||
|
for _, wID := range wIDs {
|
||||||
|
if _, ok := wikiIDMap[wID]; !ok {
|
||||||
|
wikiIDMap[wID] = struct{}{}
|
||||||
|
allWikiIDs = append(allWikiIDs, wID)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
wikis, err := s.wikiRepo.GetByIDs(ctx, allWikiIDs)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to fetch wikis")
|
||||||
|
}
|
||||||
|
|
||||||
|
wikisByID := make(map[string]*models.WikiEntity, len(wikis))
|
||||||
|
for _, w := range wikis {
|
||||||
|
wikisByID[w.ID] = w
|
||||||
|
}
|
||||||
|
|
||||||
|
result := make(map[string][]*response.WikiResponse, len(entityIDs))
|
||||||
|
for _, idStr := range entityIDs {
|
||||||
|
wIDs, exists := mapping[idStr]
|
||||||
|
result[idStr] = make([]*response.WikiResponse, 0, len(wIDs))
|
||||||
|
if exists {
|
||||||
|
for _, wID := range wIDs {
|
||||||
|
if w, found := wikisByID[wID]; found {
|
||||||
|
result[idStr] = append(result[idStr], w.ToResponse())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *relationService) getEntitiesByGeometryIDs(ctx context.Context, geometryIDs []string) (map[string][]*response.EntityResponse, *fiber.Error) {
|
||||||
|
mapping, err := s.entityRepo.GetEntityIDsByGeometryIDs(ctx, geometryIDs)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to fetch entity IDs by geometry IDs")
|
||||||
|
}
|
||||||
|
|
||||||
|
totalEntityIDs := 0
|
||||||
|
for _, eIDs := range mapping {
|
||||||
|
totalEntityIDs += len(eIDs)
|
||||||
|
}
|
||||||
|
|
||||||
|
entityIDMap := make(map[string]struct{}, totalEntityIDs)
|
||||||
|
allEntityIDs := make([]string, 0, totalEntityIDs)
|
||||||
|
for _, eIDs := range mapping {
|
||||||
|
for _, eID := range eIDs {
|
||||||
|
if _, ok := entityIDMap[eID]; !ok {
|
||||||
|
entityIDMap[eID] = struct{}{}
|
||||||
|
allEntityIDs = append(allEntityIDs, eID)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
entities, err := s.entityRepo.GetByIDs(ctx, allEntityIDs)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to fetch entities")
|
||||||
|
}
|
||||||
|
|
||||||
|
entitiesByID := make(map[string]*models.EntityEntity, len(entities))
|
||||||
|
for _, e := range entities {
|
||||||
|
entitiesByID[e.ID] = e
|
||||||
|
}
|
||||||
|
|
||||||
|
result := make(map[string][]*response.EntityResponse, len(geometryIDs))
|
||||||
|
for _, idStr := range geometryIDs {
|
||||||
|
eIDs, exists := mapping[idStr]
|
||||||
|
result[idStr] = make([]*response.EntityResponse, 0, len(eIDs))
|
||||||
|
if exists {
|
||||||
|
for _, eID := range eIDs {
|
||||||
|
if e, found := entitiesByID[eID]; found {
|
||||||
|
result[idStr] = append(result[idStr], e.ToResponse())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *relationService) getGeometriesByEntityIDs(ctx context.Context, entityIDs []string) (map[string][]*response.GeometryResponse, *fiber.Error) {
|
||||||
|
mapping, err := s.geometryRepo.GetGeometryIDsByEntityIDs(ctx, entityIDs)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to fetch geometry IDs by entity IDs")
|
||||||
|
}
|
||||||
|
|
||||||
|
totalGeometryIDs := 0
|
||||||
|
for _, gIDs := range mapping {
|
||||||
|
totalGeometryIDs += len(gIDs)
|
||||||
|
}
|
||||||
|
|
||||||
|
geometryIDMap := make(map[string]struct{}, totalGeometryIDs)
|
||||||
|
allGeometryIDs := make([]string, 0, totalGeometryIDs)
|
||||||
|
for _, gIDs := range mapping {
|
||||||
|
for _, gID := range gIDs {
|
||||||
|
if _, ok := geometryIDMap[gID]; !ok {
|
||||||
|
geometryIDMap[gID] = struct{}{}
|
||||||
|
allGeometryIDs = append(allGeometryIDs, gID)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
geometries, err := s.geometryRepo.GetByIDs(ctx, allGeometryIDs)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to fetch geometries")
|
||||||
|
}
|
||||||
|
|
||||||
|
geometriesByID := make(map[string]*models.GeometryEntity, len(geometries))
|
||||||
|
for _, g := range geometries {
|
||||||
|
geometriesByID[g.ID] = g
|
||||||
|
}
|
||||||
|
|
||||||
|
result := make(map[string][]*response.GeometryResponse, len(entityIDs))
|
||||||
|
for _, idStr := range entityIDs {
|
||||||
|
gIDs, exists := mapping[idStr]
|
||||||
|
result[idStr] = make([]*response.GeometryResponse, 0, len(gIDs))
|
||||||
|
if exists {
|
||||||
|
for _, gID := range gIDs {
|
||||||
|
if g, found := geometriesByID[gID]; found {
|
||||||
|
result[idStr] = append(result[idStr], g.ToResponse())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user