diff --git a/master/internal/api_experiment.go b/master/internal/api_experiment.go index 8fd3ec2456c..960239c9407 100644 --- a/master/internal/api_experiment.go +++ b/master/internal/api_experiment.go @@ -2495,7 +2495,8 @@ func sortExperiments(sortString *string, experimentQuery *bun.SelectQuery) error sortDirection := sortByMap[paramDetail[1]] switch { case strings.HasPrefix(paramDetail[0], "hp."): - hps := strings.ReplaceAll(strings.TrimPrefix(paramDetail[0], "hp."), ".", "'->'") + param := strings.ReplaceAll(paramDetail[0], "'", "") + hps := strings.ReplaceAll(strings.TrimPrefix(param, "hp."), ".", "'->'") experimentQuery.OrderExpr( fmt.Sprintf("e.config->'hyperparameters'->'%s' %s", hps, sortDirection)) case strings.Contains(paramDetail[0], "."): diff --git a/master/internal/api_experiment_intg_test.go b/master/internal/api_experiment_intg_test.go index caa0f78c9b2..48fa6962a2d 100644 --- a/master/internal/api_experiment_intg_test.go +++ b/master/internal/api_experiment_intg_test.go @@ -813,6 +813,35 @@ func TestSearchExperiments(t *testing.T) { require.Equal(t, int32(5), resp.Experiments[2].BestTrial.Restarts) } +func TestSearchExperimentsMalformed(t *testing.T) { + api, curUser, ctx := setupAPITest(t, nil) + _, projectIDInt := createProjectAndWorkspace(ctx, t, api) + projectID := int32(projectIDInt) + + // No trial doesn't cause errors. + exp := createTestExpWithProjectID(t, api, curUser, projectIDInt) + + req := &apiv1.SearchExperimentsRequest{ + ProjectId: &projectID, + Sort: ptrs.Ptr("wow.it's.mean=asc"), + } + resp, err := api.SearchExperiments(ctx, req) + require.NoError(t, err) + require.Len(t, resp.Experiments, 1) + require.Nil(t, resp.Experiments[0].BestTrial) + require.Equal(t, int32(exp.ID), resp.Experiments[0].Experiment.Id) + + req = &apiv1.SearchExperimentsRequest{ + ProjectId: &projectID, + Sort: ptrs.Ptr("hp.tests'=asc"), + } + resp, err = api.SearchExperiments(ctx, req) + require.NoError(t, err) + require.Len(t, resp.Experiments, 1) + require.Nil(t, resp.Experiments[0].BestTrial) + require.Equal(t, int32(exp.ID), resp.Experiments[0].Experiment.Id) +} + // Test that endpoints don't puke when running against old experiments. func TestLegacyExperiments(t *testing.T) { err := etc.SetRootPath("../static/srv")