Skip to content

Commit

Permalink
fixed amount of returning entities when using expand, fixed incorrect…
Browse files Browse the repository at this point in the history
… $count=true for certain requests
  • Loading branch information
tebben committed Feb 28, 2018
1 parent 0cd5e3f commit 9fc9764
Show file tree
Hide file tree
Showing 2 changed files with 99 additions and 102 deletions.
179 changes: 93 additions & 86 deletions database/postgis/querybuilder.go
Original file line number Diff line number Diff line change
Expand Up @@ -229,37 +229,7 @@ func (qb *QueryBuilder) createJoin(e1 entities.Entity, e2 entities.Entity, id in
}
}

if !isExpand {
nqo = &odata.QueryOptions{}
nqo.Select = &godata.GoDataSelectQuery{SelectItems: []*godata.SelectItem{{Segments: []*godata.Token{{Value: "id"}}}}}
if id != nil {
var err error
nqo.Filter, err = godata.ParseFilterString(fmt.Sprintf("id eq %v", id))
if err != nil {
fmt.Printf("\n\n ERROR %v \n\n", err)
}
}

join := getJoin(qb.tables, et2, e1.GetEntityType(), asPrefix)
lowerJoin := strings.ToLower(join)
filterPrefix := "WHERE"
if strings.Contains(lowerJoin, "where") {
filterPrefix = "AND"
}

filter := qb.getFilterQueryString(et2, nqo, filterPrefix)

joinString = fmt.Sprintf("%s"+
"INNER JOIN LATERAL ("+
"SELECT %s FROM %s %s "+
"%s) "+
"AS %s on true ", joinString,
qb.getSelect(e2, nqo, nil, true, true, false, false, ""),
qb.tables[et2],
join,
filter,
tableMappings[et2])
} else {
if isExpand {
join := getJoin(qb.tables, et2, e1.GetEntityType(), asPrefix)
lowerJoin := strings.ToLower(join)
filterPrefix := "WHERE"
Expand Down Expand Up @@ -315,6 +285,41 @@ func (qb *QueryBuilder) createJoin(e1 entities.Entity, e2 entities.Entity, id in
return joinString
}

func (qb *QueryBuilder) createSelectByRelationString(e1 entities.Entity, e2 entities.Entity, id interface{}, qpi *QueryParseInfo) string {
et2 := e2.GetEntityType()

nqo := &odata.QueryOptions{}
nqo.Select = &godata.GoDataSelectQuery{SelectItems: []*godata.SelectItem{{Segments: []*godata.Token{{Value: "id"}}}}}
if id != nil {
var err error
nqo.Filter, err = godata.ParseFilterString(fmt.Sprintf("id eq %v", id))
if err != nil {
fmt.Printf("\n\n ERROR %v \n\n", err)
}
}

join := getJoin(qb.tables, et2, e1.GetEntityType(), "")
lowerJoin := strings.ToLower(join)
filterPrefix := "WHERE"
if strings.Contains(lowerJoin, "where") {
filterPrefix = "AND"
}

filter := qb.getFilterQueryString(et2, nqo, filterPrefix)

joinString := fmt.Sprintf(
"(SELECT %s "+
"FROM %s %s "+
"%s) "+
"IS NOT NULL",
qb.getSelect(e2, nqo, nil, true, true, false, false, ""),
qb.tables[et2],
join,
filter)

return joinString
}

func isExpandGenerated(sq *godata.GoDataSelectQuery) bool {
generatedExpand := false
if sq != nil && sq.SelectItems != nil && len(sq.SelectItems) > 0 {
Expand Down Expand Up @@ -847,7 +852,6 @@ func findFirstCouplingParseNode(pn *godata.ParseNode) *godata.ParseNode {
// Returns an empty string if ODATA Query Count is set to false.
// example: Datastreams(1)/Thing = CreateCountQuery(&entities.Thing, &entities.Datastream, 1, nil)
func (qb *QueryBuilder) CreateCountQuery(e1 entities.Entity, e2 entities.Entity, id interface{}, queryOptions *odata.QueryOptions) string {

if queryOptions.Count == nil {
return ""
}
Expand All @@ -856,32 +860,7 @@ func (qb *QueryBuilder) CreateCountQuery(e1 entities.Entity, e2 entities.Entity,
defer gostLog.DebugWithElapsedTime(logger, time.Now(), "constructing count query")
}

var qo *odata.QueryOptions

if queryOptions != nil {
qo = &odata.QueryOptions{}
*qo = *queryOptions
qb.sortQueryOptions(qo)
}

et1 := e1.GetEntityType()
et2 := e1.GetEntityType()
if e2 != nil { // 2nd entity is given, this means get e1 by e2
et2 = e2.GetEntityType()
}

queryString := fmt.Sprintf("SELECT COUNT(*) FROM %s %s", qb.tables[et1], qb.createJoin(e1, e2, id, false, false, nil, nil, ""))
if id != nil {
queryString = fmt.Sprintf("%s WHERE %s.%s = %v", queryString, tableMappings[et2], asMappings[et2][idField], id)
}

if qo != nil && qo.Filter != nil {
if id != nil {
queryString = fmt.Sprintf("%s AND %s", queryString, qb.getFilterQueryString(et1, qo, ""))
} else {
queryString = fmt.Sprintf("%s %s", queryString, qb.getFilterQueryString(et1, qo, "WHERE"))
}
}
queryString, _ := qb.getQueryString(e1, e2, id, queryOptions, true)

return queryString
}
Expand All @@ -896,6 +875,12 @@ func (qb *QueryBuilder) CreateQuery(e1 entities.Entity, e2 entities.Entity, id i
defer gostLog.DebugWithElapsedTime(logger, time.Now(), "constructing select query")
}

queryString, qpi := qb.getQueryString(e1, e2, id, queryOptions, false)

return queryString, qpi
}

func (qb *QueryBuilder) getQueryString(e1 entities.Entity, e2 entities.Entity, id interface{}, queryOptions *odata.QueryOptions, isCount bool) (string, *QueryParseInfo) {
var qo *odata.QueryOptions
qo = nil

Expand All @@ -906,10 +891,6 @@ func (qb *QueryBuilder) CreateQuery(e1 entities.Entity, e2 entities.Entity, id i
}

et1 := e1.GetEntityType()
et2 := e1.GetEntityType()
if e2 != nil { // 2nd entity is given, this means get e1 by e2
et2 = e2.GetEntityType()
}

eo := &godata.ExpandItem{}
if qo != nil {
Expand All @@ -932,47 +913,73 @@ func (qb *QueryBuilder) CreateQuery(e1 entities.Entity, e2 entities.Entity, id i
}
}

queryString := qb.getMainQueryString(e1, e2, et1, et2, id, qo, qpi)

limit := ""
if qo != nil && qo.Top != nil && int(*qo.Top) != -1 {
limit = fmt.Sprintf("LIMIT %v", qb.getLimit(qo, 1))
firstSelect := fmt.Sprintf("COUNT(DISTINCT %s)", qb.addAsPrefix(qpi, fmt.Sprintf("%s.%s", tableMappings[et1], asMappings[et1][idField])))
if !isCount {
firstSelect = qb.getSelect(e1, qo, qpi, true, true, true, false, "")
}
//queryString = fmt.Sprintf("%s ORDER BY %s )", queryString, qb.getOrderBy(et1, qo))
queryString = fmt.Sprintf("%s) AS %s %s ORDER BY %s %s OFFSET %s",
queryString,
qb.addAsPrefix(qpi, tableMappings[et1]),
qb.createJoin(e1, e2, id, false, false, qo, qpi, ""),
fmt.Sprintf("%s_%s", qpi.AsPrefix, qb.getOrderBy(et1, qo, true)),
limit,
qb.getOffset(qo),
)

return queryString, qpi
}

func (qb *QueryBuilder) getMainQueryString(e1 entities.Entity, e2 entities.Entity, et1 entities.EntityType, et2 entities.EntityType, id interface{}, qo *odata.QueryOptions, qpi *QueryParseInfo) string {
queryString := fmt.Sprintf("SELECT %s FROM (SELECT %s FROM %s",
qb.getSelect(e1, qo, qpi, true, true, true, false, ""),
firstSelect,
qb.getSelect(e1, qo, nil, true, true, false, true, ""),
qb.tables[et1],
)

where := ""

if id != nil && e2 == nil {
queryString = fmt.Sprintf("%s WHERE %s = %v", queryString, selectMappings[et2][idField], id)
where = fmt.Sprintf("%s WHERE %s = %v", where, selectMappings[et1][idField], id)
}

if qo != nil && qo.Filter != nil {
if id != nil {
if e2 == nil {
queryString = fmt.Sprintf("%s AND %s", queryString, qb.getFilterQueryString(et1, qo, ""))
where = fmt.Sprintf("%s AND %s", where, qb.getFilterQueryString(et1, qo, ""))
} else {
queryString = fmt.Sprintf("%s %s", queryString, qb.getFilterQueryString(et1, qo, "WHERE"))
where = fmt.Sprintf("%s %s", where, qb.getFilterQueryString(et1, qo, "WHERE"))
}
} else {
queryString = fmt.Sprintf("%s %s", queryString, qb.getFilterQueryString(et1, qo, "WHERE"))
where = fmt.Sprintf("%s %s", where, qb.getFilterQueryString(et1, qo, "WHERE"))
}
}

return queryString
queryString = fmt.Sprintf("%s %s", queryString, strings.TrimSpace(where))

// get entity by other entity
if e2 != nil {
selectBy := qb.createSelectByRelationString(e1, e2, id, qpi)
prefix := "WHERE"
if where != "" {
prefix = "AND"
}

queryString = fmt.Sprintf("%s %s %s", queryString, prefix, selectBy)
}

if isCount {
queryString = fmt.Sprintf("%s ORDER BY %s) AS %s",
queryString,
qb.getOrderBy(et1, qo, true),
qb.addAsPrefix(qpi, tableMappings[et1]),
)
} else {
limit := ""
if qo != nil && qo.Top != nil && int(*qo.Top) != -1 {
limit = fmt.Sprintf("LIMIT %v", qb.getLimit(qo, 1))
}

queryString = fmt.Sprintf("%s ORDER BY %s %s OFFSET %s) AS %s",
queryString,
qb.getOrderBy(et1, qo, true),
limit,
qb.getOffset(qo),
qb.addAsPrefix(qpi, tableMappings[et1]),
)
}

queryString = fmt.Sprintf("%s %s",
queryString,
qb.createJoin(e1, e2, id, false, false, qo, qpi, ""),
)

return queryString, qpi
}
22 changes: 6 additions & 16 deletions database/postgis/querybuilder_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -113,29 +113,19 @@ func TestGetOrderByWithNilOptions(t *testing.T) {
assert.True(t, res == "datastream.id DESC")
}

func TestCreateJoin(t *testing.T) {
// arrange
qb := CreateQueryBuilder("v1.0", 1)
thing := &entities.Thing{}
location := &entities.Location{}

join := qb.createJoin(thing, location, 1, false, false, nil, nil, "")
assert.True(t, join == "INNER JOIN LATERAL (SELECT location.id AS location_id FROM v1.0.location INNER JOIN v1.0.thing_to_location ON thing_to_location.location_id = location.id AND thing_to_location.thing_id = thing.id WHERE location.id = 1) AS location on true ")
}

func TestCreateJoinWithExpand(t *testing.T) {
// arrange
qb := CreateQueryBuilder("v1.0", 1)
thing := &entities.Thing{}
location := &entities.Location{}

join := qb.createJoin(thing, location, 1, true, false, nil, nil, "")
assert.True(t, join == "LEFT JOIN LATERAL (SELECT location.id AS location_id, location.name AS location_name, location.description AS location_description, location.encodingtype AS location_encodingtype, public.ST_AsGeoJSON(location.location) AS location_location FROM v1.0.location INNER JOIN v1.0.thing_to_location ON thing_to_location.location_id = location.id AND thing_to_location.thing_id = thing.id ORDER BY location.id DESC LIMIT 1 OFFSET 0) AS location on true ")
assert.Equal(t, "LEFT JOIN LATERAL (SELECT location.id AS location_id, location.name AS location_name, location.description AS location_description, location.encodingtype AS location_encodingtype, public.ST_AsGeoJSON(location.location) AS location_location FROM v1.0.location INNER JOIN v1.0.thing_to_location ON thing_to_location.location_id = location.id AND thing_to_location.thing_id = thing.id ORDER BY location.id DESC LIMIT 1 OFFSET 0) AS location on true ", join, join)
}
func TestCreateCountQuery(t *testing.T) {
// arrange
qb := CreateQueryBuilder("v1.0", 1)
expected := "SELECT COUNT(*) FROM v1.0.datastream INNER JOIN LATERAL (SELECT thing.id AS thing_id FROM v1.0.thing WHERE thing.id = datastream.thing_id AND thing.id = 1) AS thing on true WHERE thing.thing_id = 1 AND datastream.name = 'Milk' AND Price < 2.55"
expected := "SELECT COUNT(DISTINCT A_datastream.datastream_id) FROM (SELECT datastream.thing_id AS datastream_thing_id, datastream.observedproperty_id AS datastream_observedproperty_id, datastream.sensor_id AS datastream_sensor_id, datastream.id AS datastream_id, datastream.name AS datastream_name, datastream.description AS datastream_description, datastream.unitofmeasurement AS datastream_unitofmeasurement, datastream.observationtype AS datastream_observationtype, public.ST_AsGeoJSON(datastream.observedarea) AS datastream_observedarea, datastream.phenomenontime AS datastream_phenomenontime, datastream.resulttime AS datastream_resulttime FROM v1.0.datastream WHERE datastream.name = 'Milk' AND Price < 2.55 AND (SELECT thing.id AS thing_id FROM v1.0.thing WHERE thing.id = datastream.thing_id AND thing.id = 1) IS NOT NULL ORDER BY datastream_id DESC) AS A_datastream "
qo := &odata.QueryOptions{}
cs, _ := godata.ParseCountString("true")
qo.Count = cs
Expand Down Expand Up @@ -197,7 +187,7 @@ func TestOdataLogicalOperatorToPostgreSQL(t *testing.T) {
func TestCreateCountQueryWithoutId(t *testing.T) {
// arrange
qb := CreateQueryBuilder("v1.0", 1)
expected := "SELECT COUNT(*) FROM v1.0.datastream INNER JOIN LATERAL (SELECT thing.id AS thing_id FROM v1.0.thing WHERE thing.id = datastream.thing_id ) AS thing on true WHERE datastream.name = 'Milk' AND Price < 2.55"
expected := "SELECT COUNT(DISTINCT A_datastream.datastream_id) FROM (SELECT datastream.thing_id AS datastream_thing_id, datastream.observedproperty_id AS datastream_observedproperty_id, datastream.sensor_id AS datastream_sensor_id, datastream.id AS datastream_id, datastream.name AS datastream_name, datastream.description AS datastream_description, datastream.unitofmeasurement AS datastream_unitofmeasurement, datastream.observationtype AS datastream_observationtype, public.ST_AsGeoJSON(datastream.observedarea) AS datastream_observedarea, datastream.phenomenontime AS datastream_phenomenontime, datastream.resulttime AS datastream_resulttime FROM v1.0.datastream WHERE datastream.name = 'Milk' AND Price < 2.55 AND (SELECT thing.id AS thing_id FROM v1.0.thing WHERE thing.id = datastream.thing_id ) IS NOT NULL ORDER BY datastream_id DESC) AS A_datastream "
qo := &odata.QueryOptions{}
cs, _ := godata.ParseCountString("true")
qo.Count = cs
Expand All @@ -209,20 +199,20 @@ func TestCreateCountQueryWithoutId(t *testing.T) {

// assert
assert.NotNil(t, res)
assert.True(t, expected == res)
assert.Equal(t, expected, res)
}

func TestCreateQuery(t *testing.T) {
// arrange
qb := CreateQueryBuilder("v1.0", 1)
expected := "SELECT A_datastream.datastream_id AS A_datastream_id, A_datastream.datastream_name AS A_datastream_name, A_datastream.datastream_description AS A_datastream_description, A_datastream.datastream_unitofmeasurement AS A_datastream_unitofmeasurement, A_datastream.datastream_observationtype AS A_datastream_observationtype, A_datastream.datastream_observedarea AS A_datastream_observedarea, A_datastream.datastream_phenomenontime AS A_datastream_phenomenontime, A_datastream.datastream_resulttime AS A_datastream_resulttime FROM (SELECT datastream.thing_id AS datastream_thing_id, datastream.observedproperty_id AS datastream_observedproperty_id, datastream.sensor_id AS datastream_sensor_id, datastream.id AS datastream_id, datastream.name AS datastream_name, datastream.description AS datastream_description, datastream.unitofmeasurement AS datastream_unitofmeasurement, datastream.observationtype AS datastream_observationtype, public.ST_AsGeoJSON(datastream.observedarea) AS datastream_observedarea, datastream.phenomenontime AS datastream_phenomenontime, datastream.resulttime AS datastream_resulttime FROM v1.0.datastream) AS A_datastream INNER JOIN LATERAL (SELECT thing.id AS thing_id FROM v1.0.thing WHERE thing.id = A_datastream.datastream_thing_id AND thing.id = 0) AS thing on true ORDER BY A_datastream_id DESC OFFSET 0"
expected := "SELECT A_datastream.datastream_id AS A_datastream_id, A_datastream.datastream_name AS A_datastream_name, A_datastream.datastream_description AS A_datastream_description, A_datastream.datastream_unitofmeasurement AS A_datastream_unitofmeasurement, A_datastream.datastream_observationtype AS A_datastream_observationtype, A_datastream.datastream_observedarea AS A_datastream_observedarea, A_datastream.datastream_phenomenontime AS A_datastream_phenomenontime, A_datastream.datastream_resulttime AS A_datastream_resulttime FROM (SELECT datastream.thing_id AS datastream_thing_id, datastream.observedproperty_id AS datastream_observedproperty_id, datastream.sensor_id AS datastream_sensor_id, datastream.id AS datastream_id, datastream.name AS datastream_name, datastream.description AS datastream_description, datastream.unitofmeasurement AS datastream_unitofmeasurement, datastream.observationtype AS datastream_observationtype, public.ST_AsGeoJSON(datastream.observedarea) AS datastream_observedarea, datastream.phenomenontime AS datastream_phenomenontime, datastream.resulttime AS datastream_resulttime FROM v1.0.datastream WHERE (SELECT thing.id AS thing_id FROM v1.0.thing WHERE thing.id = datastream.thing_id AND thing.id = 0) IS NOT NULL ORDER BY datastream_id DESC OFFSET 0) AS A_datastream "

// act
query, _ := qb.CreateQuery(&entities.Datastream{}, &entities.Thing{}, 0, nil)

// assert
assert.NotNil(t, query)
assert.True(t, expected == query)
assert.True(t, expected == query, query)
}

func TestConstructQueryParseInfo(t *testing.T) {
Expand Down

0 comments on commit 9fc9764

Please sign in to comment.