diff --git a/server/e2e/gql_nlslayer_test.go b/server/e2e/gql_nlslayer_test.go index 1232d5b93e..13b9f21021 100644 --- a/server/e2e/gql_nlslayer_test.go +++ b/server/e2e/gql_nlslayer_test.go @@ -780,19 +780,23 @@ func addCustomProperties( return requestBody, res } -func TestCustomProperties(t *testing.T) { - mr, err := miniredis.Run() - if err != nil { - t.Fatal(err) +func customProperties(t *testing.T, isUseRedis bool) { + redisAddress := "" + if isUseRedis { + mr, err := miniredis.Run() + if err != nil { + t.Fatal(err) + } + defer mr.Close() + redisAddress = mr.Addr() } - defer mr.Close() e := StartServer(t, &config.Config{ Origins: []string{"https://example.com"}, AuthSrv: config.AuthSrvConfig{ Disabled: true, }, - RedisHost: mr.Addr(), + RedisHost: redisAddress, }, true, baseSeeder) pId := createProject(e) @@ -862,3 +866,11 @@ func TestCustomProperties(t *testing.T) { Value("customPropertySchema").Object(). Value("extrudedHeight").Equal(10) } + +func TestCustomProperties(t *testing.T) { + customProperties(t, false) +} + +func TestCustomPropertiesWithRedis(t *testing.T) { + customProperties(t, true) +} diff --git a/server/internal/infrastructure/mongo/mongodoc/nlslayer.go b/server/internal/infrastructure/mongo/mongodoc/nlslayer.go index 7276e2481b..4cb44dc7ea 100644 --- a/server/internal/infrastructure/mongo/mongodoc/nlslayer.go +++ b/server/internal/infrastructure/mongo/mongodoc/nlslayer.go @@ -2,6 +2,7 @@ package mongodoc import ( "errors" + "log" "github.com/reearth/reearth/server/pkg/id" "github.com/reearth/reearth/server/pkg/nlslayer" @@ -536,6 +537,10 @@ func NewNLSLayerFeature(f nlslayer.Feature) NLSLayerFeatureDocument { } func NewNLSLayerGeometry(g nlslayer.Geometry) map[string]any { + if gMapFromRedis, ok := g.(map[string]any); ok { + return NewNLSLayerGeometryFromRedisMap(gMapFromRedis) + } + gMap := make(map[string]any) switch g := g.(type) { case *nlslayer.Point: @@ -560,3 +565,61 @@ func NewNLSLayerGeometry(g nlslayer.Geometry) map[string]any { } return gMap } + +func NewNLSLayerGeometryFromRedisMap(redisMap map[string]any) map[string]any { + typeFields := []string{ + "PointTypeField", + "LineStringTypeField", + "PolygonTypeField", + "MultiPolygonTypeField", + "GeometryCollectionTypeField", + } + + var geometryType string + for _, field := range typeFields { + if typeVal, ok := redisMap[field]; ok { + geometryType = typeVal.(string) + break + } + } + + if geometryType == "" { + log.Println("geometry type is missing") + return nil + } + + gMap := make(map[string]any) + gMap["type"] = geometryType + + if geometryType == "GeometryCollection" { + rawGeometries, ok := redisMap["GeometriesField"].([]any) + if !ok { + log.Println("invalid geometry collection data format") + return nil + } + + gmapSlice := make([]map[string]any, 0, len(rawGeometries)) + for _, rawGeometry := range rawGeometries { + geometry, ok := rawGeometry.(map[string]any) + if !ok { + log.Println("invalid geometry data format in collection") + continue + } + + transformedGeometry := NewNLSLayerGeometryFromRedisMap(geometry) + if transformedGeometry != nil { + gmapSlice = append(gmapSlice, transformedGeometry) + } + } + gMap["geometries"] = gmapSlice + } else { + coords, ok := redisMap["CoordinatesField"] + if !ok { + log.Println("coordinates field is missing") + return nil + } + gMap["coordinates"] = coords + } + + return gMap +} diff --git a/server/internal/infrastructure/mongo/mongodoc/nlslayer_test.go b/server/internal/infrastructure/mongo/mongodoc/nlslayer_test.go index 759776da44..afd3b24891 100644 --- a/server/internal/infrastructure/mongo/mongodoc/nlslayer_test.go +++ b/server/internal/infrastructure/mongo/mongodoc/nlslayer_test.go @@ -406,6 +406,95 @@ func TestNewNLSLayerGeometry(t *testing.T) { args: nlslayer.Geometry(nil), want: map[string]any{}, }, + { + name: "New point from redis map", + args: map[string]any{ + "PointTypeField": "Point", + "CoordinatesField": []any{1, 2}, + }, + want: map[string]any{ + "type": "Point", + "coordinates": []any{1, 2}, + }, + }, + { + name: "New line string from redis map", + args: map[string]any{ + "LineStringTypeField": "LineString", + "CoordinatesField": [][]any{{1, 2}, {3, 4}}, + }, + want: map[string]any{ + "type": "LineString", + "coordinates": [][]any{{1, 2}, {3, 4}}, + }, + }, + { + name: "New polygon from redis map", + args: map[string]any{ + "PolygonTypeField": "Polygon", + "CoordinatesField": [][][]any{{{1, 2}, {3, 4}, {5, 6}, {1, 2}}}, + }, + want: map[string]any{ + "type": "Polygon", + "coordinates": [][][]any{{{1, 2}, {3, 4}, {5, 6}, {1, 2}}}, + }, + }, + { + name: "New multi polygon from redis map", + args: map[string]any{ + "MultiPolygonTypeField": "MultiPolygon", + "CoordinatesField": [][][][]any{{{{1, 2}, {3, 4}, {5, 6}, {1, 2}}}}, + }, + want: map[string]any{ + "type": "MultiPolygon", + "coordinates": [][][][]any{{{{1, 2}, {3, 4}, {5, 6}, {1, 2}}}}, + }, + }, + { + name: "New geometry collection from redis map", + args: map[string]any{ + "GeometryCollectionTypeField": "GeometryCollection", + "GeometriesField": []any{ + map[string]any{ + "PointTypeField": "Point", + "CoordinatesField": []any{1, 2}, + }, + map[string]any{ + "LineStringTypeField": "LineString", + "CoordinatesField": [][]any{{1, 2}, {3, 4}}, + }, + map[string]any{ + "PolygonTypeField": "Polygon", + "CoordinatesField": [][][]any{{{1, 2}, {3, 4}, {5, 6}, {1, 2}}}, + }, + map[string]any{ + "MultiPolygonTypeField": "MultiPolygon", + "CoordinatesField": [][][][]any{{{{1, 2}, {3, 4}, {5, 6}, {1, 2}}}}, + }, + }, + }, + want: map[string]any{ + "type": "GeometryCollection", + "geometries": []map[string]any{ + { + "type": "Point", + "coordinates": []any{1, 2}, + }, + { + "type": "LineString", + "coordinates": [][]any{{1, 2}, {3, 4}}, + }, + { + "type": "Polygon", + "coordinates": [][][]any{{{1, 2}, {3, 4}, {5, 6}, {1, 2}}}, + }, + { + "type": "MultiPolygon", + "coordinates": [][][][]any{{{{1, 2}, {3, 4}, {5, 6}, {1, 2}}}}, + }, + }, + }, + }, } for _, tt := range tests { diff --git a/server/internal/usecase/interactor/nlslayer.go b/server/internal/usecase/interactor/nlslayer.go index 3b4969161c..d5cc534b3e 100644 --- a/server/internal/usecase/interactor/nlslayer.go +++ b/server/internal/usecase/interactor/nlslayer.go @@ -616,11 +616,21 @@ func (i *NLSLayer) AddCustomProperties(ctx context.Context, inp interfaces.AddCu } }() - layer, err := i.nlslayerRepo.FindByID(ctx, inp.LayerID) + var layer nlslayer.NLSLayer + layerSimple, err := getFromCache[*nlslayer.NLSLayerSimple](ctx, i.redis, nlslayer.NLSLayerCacheKey(inp.LayerID)) if err != nil { return nil, err } + if layerSimple == nil { + layer, err = i.nlslayerRepo.FindByID(ctx, inp.LayerID) + if err != nil { + return nil, err + } + } else { + layer = layerSimple + } + if layer.Sketch() == nil { featureCollection := nlslayer.NewFeatureCollection( "FeatureCollection", @@ -643,6 +653,12 @@ func (i *NLSLayer) AddCustomProperties(ctx context.Context, inp interfaces.AddCu } tx.Commit() + + err = setToCache[nlslayer.NLSLayer](ctx, i.redis, nlslayer.NLSLayerCacheKey(layer.ID()), layer) + if err != nil { + return nil, err + } + return layer, nil } @@ -659,11 +675,21 @@ func (i *NLSLayer) AddGeoJSONFeature(ctx context.Context, inp interfaces.AddNLSL } }() - layer, err := i.nlslayerRepo.FindByID(ctx, inp.LayerID) + var layer nlslayer.NLSLayer + layerSimple, err := getFromCache[*nlslayer.NLSLayerSimple](ctx, i.redis, nlslayer.NLSLayerCacheKey(inp.LayerID)) if err != nil { return nlslayer.Feature{}, err } + if layerSimple == nil { + layer, err = i.nlslayerRepo.FindByID(ctx, inp.LayerID) + if err != nil { + return nlslayer.Feature{}, err + } + } else { + layer = layerSimple + } + geometry, err := nlslayer.NewGeometryFromMap(inp.Geometry) if err != nil { return nlslayer.Feature{}, err @@ -704,6 +730,12 @@ func (i *NLSLayer) AddGeoJSONFeature(ctx context.Context, inp interfaces.AddNLSL } tx.Commit() + + err = setToCache[nlslayer.NLSLayer](ctx, i.redis, nlslayer.NLSLayerCacheKey(layer.ID()), layer) + if err != nil { + return nlslayer.Feature{}, err + } + return *feature, nil } @@ -720,11 +752,21 @@ func (i *NLSLayer) UpdateGeoJSONFeature(ctx context.Context, inp interfaces.Upda } }() - layer, err := i.nlslayerRepo.FindByID(ctx, inp.LayerID) + var layer nlslayer.NLSLayer + layerSimple, err := getFromCache[*nlslayer.NLSLayerSimple](ctx, i.redis, nlslayer.NLSLayerCacheKey(inp.LayerID)) if err != nil { return nlslayer.Feature{}, err } + if layerSimple == nil { + layer, err = i.nlslayerRepo.FindByID(ctx, inp.LayerID) + if err != nil { + return nlslayer.Feature{}, err + } + } else { + layer = layerSimple + } + if layer.Sketch() == nil || layer.Sketch().FeatureCollection() == nil || layer.Sketch().FeatureCollection().Features() == nil || len(layer.Sketch().FeatureCollection().Features()) == 0 { return nlslayer.Feature{}, interfaces.ErrFeatureNotFound } @@ -756,6 +798,12 @@ func (i *NLSLayer) UpdateGeoJSONFeature(ctx context.Context, inp interfaces.Upda } tx.Commit() + + err = setToCache[nlslayer.NLSLayer](ctx, i.redis, nlslayer.NLSLayerCacheKey(layer.ID()), layer) + if err != nil { + return nlslayer.Feature{}, err + } + return updatedFeature, nil } @@ -772,11 +820,21 @@ func (i *NLSLayer) DeleteGeoJSONFeature(ctx context.Context, inp interfaces.Dele } }() - layer, err := i.nlslayerRepo.FindByID(ctx, inp.LayerID) + var layer nlslayer.NLSLayer + layerSimple, err := getFromCache[*nlslayer.NLSLayerSimple](ctx, i.redis, nlslayer.NLSLayerCacheKey(inp.LayerID)) if err != nil { return id.FeatureID{}, err } + if layerSimple == nil { + layer, err = i.nlslayerRepo.FindByID(ctx, inp.LayerID) + if err != nil { + return id.FeatureID{}, err + } + } else { + layer = layerSimple + } + if layer.Sketch() == nil || layer.Sketch().FeatureCollection() == nil || layer.Sketch().FeatureCollection().Features() == nil || len(layer.Sketch().FeatureCollection().Features()) == 0 { return id.FeatureID{}, interfaces.ErrFeatureNotFound } @@ -792,5 +850,11 @@ func (i *NLSLayer) DeleteGeoJSONFeature(ctx context.Context, inp interfaces.Dele } tx.Commit() + + err = deleteFromCache(ctx, i.redis, nlslayer.NLSLayerCacheKey(layer.ID())) + if err != nil { + return id.FeatureID{}, err + } + return inp.FeatureID, nil }