Skip to content

feat(server): GuessSchemaFields which guess schema field data from asset to schema fields #1461

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 24 commits into from
May 30, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
201 changes: 201 additions & 0 deletions server/e2e/gql_field_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,36 @@ func updateField(e *httpexpect.Expect, mID, fID, title, desc, key string, multip
return res.Path("$.data.updateField.field.id").Raw().(string), res
}

func guessSchemaFields(e *httpexpect.Expect, assetId, modelId string) *httpexpect.Value {
requestBody := GraphQLRequest{
Query: `query GuessSchemaFields($input: GuessSchemaFieldsInput!) {
guessSchemaFields(input: $input) {
total_count
fields {
name
key
type
}
}
}`,
Variables: map[string]any{
"input": map[string]any{
"assetId": assetId,
"modelId": modelId,
},
},
}

return e.POST("/api/graphql").
WithHeader("Origin", "https://example.com").
WithHeader("X-Reearth-Debug-User", uId1.String()).
WithHeader("Content-Type", "application/json").
WithJSON(requestBody).
Expect().
Status(http.StatusOK).
JSON()
}

type fIds struct {
textFId string
textAreaFId string
Expand Down Expand Up @@ -627,3 +657,174 @@ func TestClearFieldDefaultValue(t *testing.T) {

assert.Equal(t, []any{nil, nil, nil}, dv)
}

func TestGuessSchemaFields(t *testing.T) {
e := StartServer(t, &app.Config{}, true, baseSeederUser)
pId, _ := createProject(e, wId.String(), "test", "test", "test-schema-guess")
mId, _ := createModel(e, pId, "test", "test", "test-schema-guess")

// region Json input
jsonContent := `[{"name": "Item 1", "count": 42, "active": true, "tags": ["tag1", "tag2"]}]`
assetId := uploadAsset(e, pId, "./sample.json", jsonContent).Object().Value("id").String().Raw()

res := guessSchemaFields(e, assetId, mId)

res.Path("$.data.guessSchemaFields.total_count").Number().IsEqual(4)

res.Path("$.data.guessSchemaFields.fields").
Array().
IsEqual([]map[string]any{
{
"key": "name",
"type": "text",
"name": "name",
},
{
"key": "count",
"type": "integer",
"name": "count",
},
{
"key": "active",
"type": "bool",
"name": "active",
},
{
"key": "tags",
"type": "text",
"name": "tags",
},
})
// endregion

// region GeoJson input
geojsonContent := `{
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"properties": {
"name": "Point Example",
"category": "landmark",
"elevation": 100.5,
"length": 15.2
},
"geometry": {
"type": "Point",
"coordinates": [125.6, 10.1]
}
},
{
"type": "Feature",
"properties": {
"name": "Line Example",
"category": "route"
},
"geometry": {
"type": "LineString",
"coordinates": [
[102.0, 0.0],
[103.0, 1.0],
[104.0, 0.0],
[105.0, 1.0]
]
}
}
]
}`

geoJsonAssetId := uploadAsset(e, pId, "./sample.geojson", geojsonContent).Object().Value("id").String().Raw()

res = guessSchemaFields(e, geoJsonAssetId, mId)

res.Path("$.data.guessSchemaFields.total_count").Number().IsEqual(5)

res.Path("$.data.guessSchemaFields.fields").
Array().
IsEqual([]map[string]any{
{
"key": "geometry",
"type": "geometryObject",
"name": "geometry",
},
{
"key": "name",
"type": "text",
"name": "name",
},
{
"key": "category",
"type": "text",
"name": "category",
},
{
"key": "elevation",
"type": "number",
"name": "elevation",
},
{
"key": "length",
"type": "number",
"name": "length",
},
})
// endregion

// Test with existing schema
// Create a new model with predefined fields
existingModelId, _ := createModel(e, pId, "existing", "existing model", "existing-model")

// Create some fields in the model
createField(e, existingModelId, "name", "name field", "name",
false, false, true, true, "Text",
map[string]any{
"text": map[string]any{},
})

createField(e, existingModelId, "category", "category field", "category",
false, false, false, false, "Select",
map[string]any{
"select": map[string]any{
"defaultValue": nil,
"values": []any{"landmark", "route", "area"},
},
})

// Upload JSON with both existing and new fields
mixedContent := `[
{
"name": "Test Item",
"category": "landmark",
"rating": 4.5,
"tags": ["new", "test"],
"active": true
}
]`

mixedAssetId := uploadAsset(e, pId, "./mixed.json", mixedContent).Object().Value("id").String().Raw()

// Call GuessSchemaFields query for the mixed content
res = guessSchemaFields(e, mixedAssetId, existingModelId)

res.Path("$.data.guessSchemaFields.total_count").Number().IsEqual(3)
res.Path("$.data.guessSchemaFields.fields").
Array().
IsEqual([]map[string]any{
{
"key": "rating",
"type": "number",
"name": "rating",
},
{
"key": "tags",
"type": "text",
"name": "tags",
},
{
"key": "active",
"type": "bool",
"name": "active",
},
})

}
3 changes: 3 additions & 0 deletions server/i18n/en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,13 +37,16 @@ internal: ""
invalid URL: ""
invalid alias: ""
invalid base URL: ""
invalid content type: ""
invalid content type for schema conversion: ""
invalid cursor: ""
invalid default values: ""
invalid document: ""
invalid email address: ""
invalid field: ""
invalid file: ""
invalid input: ""
invalid json schema: ""
invalid key: ""
invalid lang: ""
invalid object: ""
Expand Down
5 changes: 4 additions & 1 deletion server/i18n/ja.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ comment already exist in this thread: コメントは既にこのスレッドに
comment does not exist in this thread: コメントはこのスレッドに存在しません。
comment not found: コメントが見つかりませんでした。
createdBy is required: createdByは必須です。
data type mismatch: データ型の不一致
data type mismatch: データ型が一致しません。
duplicated item: アイテムが重複しています。
duplicated key: キーが重複しています。
duplicated value: 値が重複しています。
Expand All @@ -37,13 +37,16 @@ internal: 内部
invalid URL: 無効なURLです。
invalid alias: 無効なエイリアスです。
invalid base URL: 無効なベースURLです。
invalid content type: 無効なコンテンツタイプです。
invalid content type for schema conversion: スキーマ変換のための無効なコンテンツタイプです。
invalid cursor: 無効なカーソルです。
invalid default values: 無効なデフォルト値です。
invalid document: 無効なドキュメントです。
invalid email address: 無効なEmailアドレスです。
invalid field: 無効なフィールドです。
invalid file: 無効なファイルです。
invalid input: 無効な入力です。
invalid json schema: 無効なJSONスキーマです。
invalid key: 無効なキーです。
invalid lang: 無効な言語です。
invalid object: 無効なオブジェクトです。
Expand Down
Loading
Loading