make sure tokens support modifiers during query
make sure we can use multiple AND clauses in query.
This commit is contained in:
parent
d0f2080d09
commit
a5eba794b5
|
@ -313,9 +313,10 @@ func ProcessSearchParameter(searchCodeWithModifier string, searchParamTypeLookup
|
|||
searchParameter.Type = SearchParameterType(searchParamTypeStr)
|
||||
}
|
||||
|
||||
//if this is a token search parameter with a modifier, we need to throw an error
|
||||
if searchParameter.Type == SearchParameterTypeToken && len(searchParameter.Modifier) > 0 {
|
||||
return searchParameter, fmt.Errorf("token search parameter %s cannot have a modifier", searchParameter.Name)
|
||||
//only a limited set of token modifiers are allowed. Otherwise we need to throw an error
|
||||
allowedTokenModifiers := []string{"not"}
|
||||
if searchParameter.Type == SearchParameterTypeToken && len(searchParameter.Modifier) > 0 && !lo.Contains(allowedTokenModifiers, searchParameter.Modifier) {
|
||||
return searchParameter, fmt.Errorf("token search parameter %s does not support this modifier: %s", searchParameter.Name, searchParameter.Modifier)
|
||||
}
|
||||
|
||||
return searchParameter, nil
|
||||
|
@ -332,8 +333,8 @@ func ProcessSearchParameter(searchCodeWithModifier string, searchParamTypeLookup
|
|||
//
|
||||
// For example, searchParamCodeValueOrValuesWithPrefix may be:
|
||||
//
|
||||
// "code": "29463-7,3141-9,27113001"
|
||||
// "code": ["le29463-7", "gt3141-9", "27113001"]
|
||||
// "code": "29463-7,3141-9,27113001" = OR
|
||||
// "code": ["le29463-7", "gt3141-9", "27113001"] = AND
|
||||
func ProcessSearchParameterValueIntoOperatorTree(searchParameter SearchParameter, searchParamCodeValueOrValuesWithPrefix interface{}) (SearchParameterValueOperatorTree, error) {
|
||||
|
||||
searchParamCodeValuesWithPrefix := []string{}
|
||||
|
@ -344,6 +345,11 @@ func ProcessSearchParameterValueIntoOperatorTree(searchParameter SearchParameter
|
|||
case []string:
|
||||
searchParamCodeValuesWithPrefix = v
|
||||
break
|
||||
case []interface{}:
|
||||
for _, searchParamCodeValue := range v {
|
||||
searchParamCodeValuesWithPrefix = append(searchParamCodeValuesWithPrefix, fmt.Sprintf("%v", searchParamCodeValue))
|
||||
}
|
||||
break
|
||||
default:
|
||||
return nil, fmt.Errorf("invalid search parameter value type %T, must be a string or a list of strings (%s=%v)", v, searchParameter.Name, searchParamCodeValueOrValuesWithPrefix)
|
||||
}
|
||||
|
@ -574,7 +580,11 @@ func SearchCodeToWhereClause(searchParam SearchParameter, searchParamValue Searc
|
|||
//setup the clause
|
||||
clause := []string{}
|
||||
if searchParamValue.Value.(string) != "" {
|
||||
clause = append(clause, fmt.Sprintf("%sJson.value ->> '$.code' = @%s", searchParam.Name, NamedParameterWithSuffix(searchParam.Name, namedParameterSuffix)))
|
||||
if searchParam.Modifier == "" {
|
||||
clause = append(clause, fmt.Sprintf("%sJson.value ->> '$.code' = @%s", searchParam.Name, NamedParameterWithSuffix(searchParam.Name, namedParameterSuffix)))
|
||||
} else if searchParam.Modifier == "not" {
|
||||
clause = append(clause, fmt.Sprintf("%sJson.value ->> '$.code' <> @%s", searchParam.Name, NamedParameterWithSuffix(searchParam.Name, namedParameterSuffix)))
|
||||
}
|
||||
}
|
||||
|
||||
//append the code and/or system clauses (if required)
|
||||
|
|
|
@ -149,6 +149,80 @@ func (suite *RepositorySqlTestSuite) TestQueryResources_SQL_WithMultipleWhereCon
|
|||
})
|
||||
}
|
||||
|
||||
func (suite *RepositorySqlTestSuite) TestQueryResources_SQL_WithTokenWithNotModifier() {
|
||||
//setup
|
||||
sqliteRepo := suite.TestRepository.(*GormRepository)
|
||||
sqliteRepo.GormClient = sqliteRepo.GormClient.Session(&gorm.Session{DryRun: true})
|
||||
|
||||
//test
|
||||
authContext := context.WithValue(context.Background(), pkg.ContextKeyTypeAuthUsername, "test_username")
|
||||
|
||||
sqlQuery, err := sqliteRepo.sqlQueryResources(authContext, models.QueryResource{
|
||||
Select: []string{},
|
||||
Where: map[string]interface{}{
|
||||
"code:not": "test_code",
|
||||
},
|
||||
From: "Observation",
|
||||
})
|
||||
require.NoError(suite.T(), err)
|
||||
var results []map[string]interface{}
|
||||
statement := sqlQuery.Find(&results).Statement
|
||||
sqlString := statement.SQL.String()
|
||||
sqlParams := statement.Vars
|
||||
|
||||
//assert
|
||||
require.NoError(suite.T(), err)
|
||||
require.Equal(suite.T(),
|
||||
strings.Join([]string{
|
||||
"SELECT fhir.*",
|
||||
"FROM fhir_observation as fhir, json_each(fhir.code) as codeJson",
|
||||
"WHERE ((codeJson.value ->> '$.code' <> ?)) AND (user_id = ?)",
|
||||
"GROUP BY `fhir`.`id`",
|
||||
"ORDER BY fhir.sort_date DESC",
|
||||
}, " "),
|
||||
sqlString)
|
||||
require.Equal(suite.T(), sqlParams, []interface{}{
|
||||
"test_code", "00000000-0000-0000-0000-000000000000",
|
||||
})
|
||||
}
|
||||
|
||||
func (suite *RepositorySqlTestSuite) TestQueryResources_SQL_WithTokenMultipleValuesWithNotModifier() {
|
||||
//setup
|
||||
sqliteRepo := suite.TestRepository.(*GormRepository)
|
||||
sqliteRepo.GormClient = sqliteRepo.GormClient.Session(&gorm.Session{DryRun: true})
|
||||
|
||||
//test
|
||||
authContext := context.WithValue(context.Background(), pkg.ContextKeyTypeAuthUsername, "test_username")
|
||||
|
||||
sqlQuery, err := sqliteRepo.sqlQueryResources(authContext, models.QueryResource{
|
||||
Select: []string{},
|
||||
Where: map[string]interface{}{
|
||||
"code:not": []string{"test_code", "test_code2"},
|
||||
},
|
||||
From: "Observation",
|
||||
})
|
||||
require.NoError(suite.T(), err)
|
||||
var results []map[string]interface{}
|
||||
statement := sqlQuery.Find(&results).Statement
|
||||
sqlString := statement.SQL.String()
|
||||
sqlParams := statement.Vars
|
||||
|
||||
//assert
|
||||
require.NoError(suite.T(), err)
|
||||
require.Equal(suite.T(),
|
||||
strings.Join([]string{
|
||||
"SELECT fhir.*",
|
||||
"FROM fhir_observation as fhir, json_each(fhir.code) as codeJson",
|
||||
"WHERE ((codeJson.value ->> '$.code' <> ?)) AND ((codeJson.value ->> '$.code' <> ?)) AND (user_id = ?)",
|
||||
"GROUP BY `fhir`.`id`",
|
||||
"ORDER BY fhir.sort_date DESC",
|
||||
}, " "),
|
||||
sqlString)
|
||||
require.Equal(suite.T(), sqlParams, []interface{}{
|
||||
"test_code", "test_code2", "00000000-0000-0000-0000-000000000000",
|
||||
})
|
||||
}
|
||||
|
||||
func (suite *RepositorySqlTestSuite) TestQueryResources_SQL_WithPrimitiveOrderByAggregation() {
|
||||
//setup
|
||||
sqliteRepo := suite.TestRepository.(*GormRepository)
|
||||
|
|
|
@ -38,7 +38,9 @@ func TestProcessSearchParameter(t *testing.T) {
|
|||
{"url:below", map[string]string{"url": "string"}, SearchParameter{Type: "string", Name: "url", Modifier: "below"}, false},
|
||||
{"url:above", map[string]string{"url": "string"}, SearchParameter{Type: "string", Name: "url", Modifier: "above"}, false},
|
||||
|
||||
{"display:text", map[string]string{"display": "token"}, SearchParameter{}, true},
|
||||
{"display", map[string]string{"display": "token"}, SearchParameter{Type: "token", Name: "display", Modifier: ""}, false},
|
||||
{"display:not", map[string]string{"display": "token"}, SearchParameter{Type: "token", Name: "display", Modifier: "not"}, false},
|
||||
{"display:unsupported", map[string]string{"display": "token"}, SearchParameter{}, true},
|
||||
}
|
||||
|
||||
//test && assert
|
||||
|
@ -161,7 +163,9 @@ func TestSearchCodeToWhereClause(t *testing.T) {
|
|||
|
||||
{SearchParameter{Type: "token", Name: "code", Modifier: ""}, SearchParameterValue{Value: "ha125", Prefix: "", SecondaryValues: map[string]interface{}{"codeSystem": "http://acme.org/conditions/codes"}}, "0_0", "(codeJson.value ->> '$.code' = @code_0_0 AND codeJson.value ->> '$.system' = @codeSystem_0_0)", map[string]interface{}{"code_0_0": "ha125", "codeSystem_0_0": "http://acme.org/conditions/codes"}, false},
|
||||
{SearchParameter{Type: "token", Name: "code", Modifier: ""}, SearchParameterValue{Value: "ha125", Prefix: "", SecondaryValues: map[string]interface{}{}}, "0_0", "(codeJson.value ->> '$.code' = @code_0_0)", map[string]interface{}{"code_0_0": "ha125"}, false},
|
||||
{SearchParameter{Type: "token", Name: "identifier", Modifier: "otype"}, SearchParameterValue{Value: "MR|446053", Prefix: "", SecondaryValues: map[string]interface{}{"identifierSystem": "http://terminology.hl7.org/CodeSystem/v2-0203"}}, "0_0", "(identifierJson.value ->> '$.code' = @identifier_0_0 AND identifierJson.value ->> '$.system' = @identifierSystem_0_0)", map[string]interface{}{"identifier_0_0": "MR|446053", "identifierSystem_0_0": "http://terminology.hl7.org/CodeSystem/v2-0203"}, false},
|
||||
{SearchParameter{Type: "token", Name: "identifier", Modifier: ""}, SearchParameterValue{Value: "MR|446053", Prefix: "", SecondaryValues: map[string]interface{}{"identifierSystem": "http://terminology.hl7.org/CodeSystem/v2-0203"}}, "0_0", "(identifierJson.value ->> '$.code' = @identifier_0_0 AND identifierJson.value ->> '$.system' = @identifierSystem_0_0)", map[string]interface{}{"identifier_0_0": "MR|446053", "identifierSystem_0_0": "http://terminology.hl7.org/CodeSystem/v2-0203"}, false},
|
||||
{SearchParameter{Type: "token", Name: "gender", Modifier: ""}, SearchParameterValue{Value: "male", Prefix: "", SecondaryValues: map[string]interface{}{"genderSystem": "http://terminology.hl7.org/CodeSystem/v2-0203"}}, "0_0", "(genderJson.value ->> '$.code' = @gender_0_0 AND genderJson.value ->> '$.system' = @genderSystem_0_0)", map[string]interface{}{"gender_0_0": "male", "genderSystem_0_0": "http://terminology.hl7.org/CodeSystem/v2-0203"}, false},
|
||||
{SearchParameter{Type: "token", Name: "gender", Modifier: "not"}, SearchParameterValue{Value: "male", Prefix: "", SecondaryValues: map[string]interface{}{"genderSystem": "http://terminology.hl7.org/CodeSystem/v2-0203"}}, "0_0", "(genderJson.value ->> '$.code' <> @gender_0_0 AND genderJson.value ->> '$.system' = @genderSystem_0_0)", map[string]interface{}{"gender_0_0": "male", "genderSystem_0_0": "http://terminology.hl7.org/CodeSystem/v2-0203"}, false},
|
||||
|
||||
{SearchParameter{Type: "keyword", Name: "id", Modifier: ""}, SearchParameterValue{Value: "1234", Prefix: "", SecondaryValues: map[string]interface{}{}}, "0_0", "(id = @id_0_0)", map[string]interface{}{"id_0_0": "1234"}, false},
|
||||
}
|
||||
|
|
|
@ -19,6 +19,7 @@ type DatabaseRepository interface {
|
|||
GetCurrentUser(ctx context.Context) (*models.User, error)
|
||||
DeleteCurrentUser(ctx context.Context) error
|
||||
|
||||
//get a count of every resource type
|
||||
GetSummary(ctx context.Context) (*models.Summary, error)
|
||||
|
||||
GetResourceByResourceTypeAndId(context.Context, string, string) (*models.ResourceBase, error)
|
||||
|
|
Loading…
Reference in New Issue