Skip to content

Ranges support #44

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

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
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
59 changes: 58 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ Currently the plugin supports array, hstore and json fields as well as some quer
* [JSON](#json)
* [Criterias](#criterias)
* [Has field value](#has-field-value)
* [Ranges](#ranges)
* [Authors](#authors)
* [Release Notes](#release-notes)

Expand Down Expand Up @@ -488,7 +489,6 @@ instance.save()

As you can see the plugin converts to Json automatically the attributes and the lists in the map type.


#### Criterias

The plugin provides some criterias to query json fields. You can check the official [Postgresql Json functions and operators](http://www.postgresql.org/docs/9.3/static/functions-json.html) in case you need additional ones.
Expand All @@ -512,6 +512,63 @@ The previous criteria will return all the rows that have a `name` attribute in t



### Ranges


PostgreSQL has native support for range types you can check the [http://www.postgresql.org/docs/9.2/static/rangetypes.html](documentation)

Grails doesn't allow to map either groovy.lang.IntRange or groovy.lang.ObjectRange to columns even if you use a custom UserType. For this reason we
provide for a custom range wrappers depending on the kind of range you want to use.

The custom range wrappers are:

- net.kaleidos.hibernate.postgresql.range.IntegerRange (int4range)
- net.kaleidos.hibernate.postgresql.range.LongRange (int8range)
- net.kaleidos.hibernate.postgresql.range.DateRange (daterange)
- net.kaleidos.hibernate.postgresql.range.TimestampRange (tsrange)

To use a range inside your domain objects you must declare a property with one of the range wrappers and use the UserType like this:

```groovy
package test.range

import net.kaleidos.hibernate.postgresql.range.IntegerRange
import net.kaleidos.hibernate.usertype.RangeType

class TestIntegerRange {
IntegerRange integerRange

static mapping = {
integerRange type:RangeType, params: ["type": IntegerRange]
}

static constraints = {
integerRange nullable: true
}
}
```

#### Using Ranges

Now you can use the object as any entity:

```groovy
def instance = new TestIntegerRange(integerRange: new IntegerRange(1, 100))
instance.save()
```


```
=# select * from test_integer_range;

id | version | integer_range
----+---------+-------------------------------------------------------------------------------------------------------------
1 | 0 | [1, 101)
```

For integer, long and date ranges Postgres stores the range in the cannonical form "[A,B)" so the upper bound is not strict.


Authors
-------

Expand Down
2 changes: 1 addition & 1 deletion grails-app/conf/DataSource.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ hibernate {
environments {
development {
dataSource {
dbCreate = "" // one of '', 'create', 'create-drop','update'
dbCreate = "create-drop" // one of '', 'create', 'create-drop','update'
driverClassName = "org.postgresql.Driver"
dialect = "net.kaleidos.hibernate.PostgresqlExtensionsDialect"
url = "jdbc:postgresql://localhost:5432/pg_extensions"
Expand Down
16 changes: 16 additions & 0 deletions grails-app/domain/test/range/TestDateRange.groovy
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package test.range

import net.kaleidos.hibernate.postgresql.range.DateRange
import net.kaleidos.hibernate.usertype.RangeType

class TestDateRange {
DateRange dateRange

static mapping = {
dateRange type: RangeType, params: ["type": DateRange]
}

static constraints = {
dateRange nullable: true
}
}
16 changes: 16 additions & 0 deletions grails-app/domain/test/range/TestIntegerRange.groovy
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package test.range

import net.kaleidos.hibernate.postgresql.range.IntegerRange
import net.kaleidos.hibernate.usertype.RangeType

class TestIntegerRange {
IntegerRange integerRange

static mapping = {
integerRange type: RangeType, params: ["type": IntegerRange]
}

static constraints = {
integerRange nullable: true
}
}
16 changes: 16 additions & 0 deletions grails-app/domain/test/range/TestLongRange.groovy
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package test.range

import net.kaleidos.hibernate.postgresql.range.LongRange
import net.kaleidos.hibernate.usertype.RangeType

class TestLongRange {
LongRange longRange

static mapping = {
longRange type:RangeType, params: ["type": LongRange]
}

static constraints = {
longRange nullable: true
}
}
16 changes: 16 additions & 0 deletions grails-app/domain/test/range/TestTimestampRange.groovy
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package test.range

import net.kaleidos.hibernate.postgresql.range.TimestampRange
import net.kaleidos.hibernate.usertype.RangeType

class TestTimestampRange {
TimestampRange timestampRange

static mapping = {
timestampRange type:RangeType, params: ["type": TimestampRange]
}

static constraints = {
timestampRange nullable: true
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package net.kaleidos.hibernate.postgresql.range

class DateRange {
ObjectRange range

public DateRange(Date from, Date to) {
range = new ObjectRange(from, to)
}

public Date getFrom() {
return range?.from
}

public Date getTo() {
return range?.to
}

public Range toRange() {
return range
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package net.kaleidos.hibernate.postgresql.range

class IntegerRange {
IntRange range

public IntegerRange(Integer from, Integer to) {
range = new IntRange(from, to)
}

public Integer getFrom() {
return range?.from
}

public Integer getTo() {
return range?.to
}

public Range toRange() {
return range
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package net.kaleidos.hibernate.postgresql.range

class LongRange {
ObjectRange range

public LongRange(Long from, Long to) {
range = new ObjectRange(from, to)
}

public Long getFrom() {
return range?.from
}

public Long getTo() {
return range?.to
}

public Range toRange() {
return range
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package net.kaleidos.hibernate.postgresql.range

class TimestampRange {
ObjectRange range

public TimestampRange(Date from, Date to) {
range = new ObjectRange(from, to)
}

public Date getFrom() {
return range?.from
}

public Date getTo() {
return range?.to
}

public Range toRange() {
return range
}
}
63 changes: 63 additions & 0 deletions src/groovy/net/kaleidos/hibernate/utils/PgRangeUtils.groovy
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package net.kaleidos.hibernate.utils

import net.kaleidos.hibernate.postgresql.range.DateRange
import net.kaleidos.hibernate.postgresql.range.IntegerRange
import net.kaleidos.hibernate.postgresql.range.LongRange
import net.kaleidos.hibernate.postgresql.range.TimestampRange

public class PgRangeUtils {
private static final String DATE_FORMAT = "yyyy-MM-dd"
private static final String TS_FORMAT = "yyyy-MM-dd hh:mm:ss.SS"

public static IntegerRange parseIntegerRange(String pgResult) {
String parentRemoved = pgResult.substring(1, pgResult.length() - 1)
String[] splitted = parentRemoved.split(",")
return new IntegerRange(new Integer(splitted[0]), new Integer(splitted[1]) - 1)
}

public static LongRange parseLongRange(String pgResult) {
String parentRemoved = pgResult.substring(1, pgResult.length() - 1)
String[] splitted = parentRemoved.split(",")
return new LongRange(new Long(splitted[0]), new Long(splitted[1]) - 1)
}

public static DateRange parseDateRange(String pgResult) {
String parentRemoved = pgResult.substring(1, pgResult.length() - 1)
String[] splitted = parentRemoved.split(",")
return new DateRange(Date.parse(DATE_FORMAT, splitted[0]), Date.parse(DATE_FORMAT, splitted[1] - 1))
}

public static TimestampRange parseTimestampRange(String pgResult) {
String parentRemoved = pgResult.substring(1, pgResult.length() - 1)
String[] splitted = parentRemoved.split(",")
return new TimestampRange(Date.parse(TS_FORMAT, splitted[0]), Date.parse(TS_FORMAT, splitted[1]))
}

public static String format(IntegerRange range) {
if (!range) {
return null
}
return "[" + range.getFrom() + ", " + range.getTo() + "]"
}

public static String format(LongRange range) {
if (!range) {
return null
}
return "[" + range.getFrom() + ", " + range.getTo() + "]"
}

public static String format(DateRange range) {
if (!range) {
return null
}
return "[" + range.getFrom().format(DATE_FORMAT) + ", " + range.getTo().format(DATE_FORMAT) + "]"
}

public static String format(TimestampRange range) {
if (!range) {
return null
}
return "[" + range.getFrom().format(TS_FORMAT) + ", " + range.getTo().format(TS_FORMAT) + "]"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import net.kaleidos.hibernate.usertype.ArrayType;
import net.kaleidos.hibernate.usertype.HstoreType;
import net.kaleidos.hibernate.usertype.JsonMapType;
import net.kaleidos.hibernate.usertype.RangeType;
import org.hibernate.dialect.Dialect;
import org.hibernate.dialect.PostgreSQL81Dialect;
import org.hibernate.id.PersistentIdentifierGenerator;
Expand All @@ -29,6 +30,11 @@ public PostgresqlExtensionsDialect() {
registerColumnType(ArrayType.FLOAT_ARRAY, "float[]");
registerColumnType(HstoreType.SQLTYPE, "hstore");
registerColumnType(JsonMapType.SQLTYPE, "json");
registerColumnType(RangeType.INTEGER_RANGE, "int4range");
registerColumnType(RangeType.LONG_RANGE, "int8range");
registerColumnType(RangeType.TIMESTAMP_RANGE, "tsrange");
//registerColumnType(RangeType.TIMESTAMP_TZ_RANGE, "tstzrange");
registerColumnType(RangeType.DATE_RANGE, "daterange");
}

/**
Expand Down
Loading