Skip to content

Commit

Permalink
Support GeoPoint value in Datastore v1beta3
Browse files Browse the repository at this point in the history
  • Loading branch information
Ajay Kannan committed Oct 19, 2015
1 parent af6ab8f commit f6eeddd
Show file tree
Hide file tree
Showing 12 changed files with 384 additions and 39 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import static com.google.gcloud.datastore.DateTimeValue.of;
import static com.google.gcloud.datastore.DoubleValue.of;
import static com.google.gcloud.datastore.EntityValue.of;
import static com.google.gcloud.datastore.GeoPointValue.of;
import static com.google.gcloud.datastore.KeyValue.of;
import static com.google.gcloud.datastore.ListValue.of;
import static com.google.gcloud.datastore.LongValue.of;
Expand Down Expand Up @@ -159,6 +160,11 @@ public B set(String name, DateTime value) {
return self();
}

public B set(String name, GeoPoint value) {
properties.put(name, of(value));
return self();
}

public B set(String name, Key value) {
properties.put(name, of(value));
return self();
Expand Down Expand Up @@ -320,6 +326,17 @@ public DateTime getDateTime(String name) {
return ((Value<DateTime>) getValue(name)).get();
}

/**
* Returns the property value as a GeoPoint.
*
* @throws DatastoreException if not such property.
* @throws ClassCastException if value is not a GeoPoint.
*/
@SuppressWarnings("unchecked")
public GeoPoint getGeoPoint(String name) {
return ((Value<GeoPoint>) getValue(name)).get();
}

/**
* Returns the property value as a Key.
*
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
/*
* Copyright 2015 Google Inc. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.google.gcloud.datastore;

import static com.google.common.base.Preconditions.checkArgument;

import com.google.protobuf.InvalidProtocolBufferException;

import java.util.Objects;

/**
* A Google Cloud Datastore GeoPoint (represented by latitude and longitude in degrees).
* This class is immutable.
*
* @see <a href="https://cloud.google.com/datastore/docs/concepts/entities">Google Cloud Datastore
* Entities, Properties, and Keys</a>
*/
public final class GeoPoint extends Serializable<com.google.type.LatLng> {

private static final long serialVersionUID = 9077060962655752073L;

private final transient double latitude;
private final transient double longitude;

GeoPoint(double latitude, double longitude) {
checkArgument(
latitude >= -90.0 && latitude <= 90.0, "latitude must be in the range [-90, 90] degrees");
checkArgument(
longitude >= -180.0 && longitude <= 180.0,
"latitude must be in the range [-180, 180] degrees");
this.latitude = latitude;
this.longitude = longitude;
}

@Override
public String toString() {
return Double.toString(latitude) + ", " + Double.toString(longitude);
}

@Override
public int hashCode() {
return Objects.hash(latitude, longitude);
}

@Override
public boolean equals(Object obj) {
return obj == this
|| (obj instanceof GeoPoint && new Double(this.latitude).equals(((GeoPoint) obj).latitude))
&& new Double(this.longitude).equals(((GeoPoint) obj).longitude);
}

public static GeoPoint of(double latitude, double longitude) {
return new GeoPoint(latitude, longitude);
}

@Override
protected com.google.type.LatLng toPb() {
return com.google.type.LatLng.newBuilder()
.setLatitude(latitude)
.setLongitude(longitude)
.build();
}

@Override
protected Object fromPb(byte[] bytesPb) throws InvalidProtocolBufferException {
com.google.type.LatLng parsedLatLng = com.google.type.LatLng.parseFrom(bytesPb);
return new GeoPoint(parsedLatLng.getLatitude(), parsedLatLng.getLongitude());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
/*
* Copyright 2015 Google Inc. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.google.gcloud.datastore;

import static com.google.datastore.v1beta3.Value.GEO_POINT_VALUE_FIELD_NUMBER;

public final class GeoPointValue extends Value<GeoPoint> {

private static final long serialVersionUID = -5810614280642405898L;

static final BaseMarshaller<GeoPoint, GeoPointValue, Builder> MARSHALLER =
new BaseMarshaller<GeoPoint, GeoPointValue, Builder>() {

private static final long serialVersionUID = -3550567536035178649L;

@Override
public int getProtoFieldId() {
return GEO_POINT_VALUE_FIELD_NUMBER;
}

@Override
public Builder newBuilder(GeoPoint value) {
return builder(value);
}

@Override
protected GeoPoint getValue(com.google.datastore.v1beta3.Value from) {
return new GeoPoint(
from.getGeoPointValue().getLatitude(), from.getGeoPointValue().getLongitude());
}

@Override
protected void setValue(GeoPointValue from, com.google.datastore.v1beta3.Value.Builder to) {
to.setGeoPointValue(from.get().toPb());
}
};

public static final class Builder extends Value.BaseBuilder<GeoPoint, GeoPointValue, Builder> {

private Builder() {
super(ValueType.GEO_POINT);
}

@Override
public GeoPointValue build() {
return new GeoPointValue(this);
}
}

public GeoPointValue(GeoPoint value) {
this(builder(value));
}

private GeoPointValue(Builder builder) {
super(builder);
}

@Override
public Builder toBuilder() {
return new Builder().mergeFrom(this);
}

public static GeoPointValue of(GeoPoint value) {
return new GeoPointValue(value);
}

public static Builder builder(GeoPoint value) {
return new Builder().set(value);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,11 @@ public Builder<V> setBinding(String name, DateTime... value) {
namedBindings.put(name, toBinding(DateTimeValue.MARSHALLER, Arrays.asList(value)));
return this;
}

public Builder<V> setBinding(String name, GeoPoint... value) {
namedBindings.put(name, toBinding(GeoPointValue.MARSHALLER, Arrays.asList(value)));
return this;
}

public Builder<V> setBinding(String name, Key... value) {
namedBindings.put(name, toBinding(KeyValue.MARSHALLER, Arrays.asList(value)));
Expand Down Expand Up @@ -258,6 +263,11 @@ public Builder<V> addBinding(DateTime... value) {
return this;
}

public Builder<V> addBinding(GeoPoint... value) {
positionalBindings.add(toBinding(GeoPointValue.MARSHALLER, Arrays.asList(value)));
return this;
}

public Builder<V> addBinding(Key... value) {
positionalBindings.add(toBinding(KeyValue.MARSHALLER, Arrays.asList(value)));
return this;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import static com.google.gcloud.datastore.BooleanValue.of;
import static com.google.gcloud.datastore.DateTimeValue.of;
import static com.google.gcloud.datastore.DoubleValue.of;
import static com.google.gcloud.datastore.GeoPointValue.of;
import static com.google.gcloud.datastore.KeyValue.of;
import static com.google.gcloud.datastore.LongValue.of;
import static com.google.gcloud.datastore.StringValue.of;
Expand Down Expand Up @@ -420,6 +421,10 @@ public static PropertyFilter eq(String property, DateTime value) {
return new PropertyFilter(property, Operator.EQUAL, of(value));
}

public static PropertyFilter eq(String property, GeoPoint value) {
return new PropertyFilter(property, Operator.EQUAL, of(value));
}

public static PropertyFilter eq(String property, Key value) {
return new PropertyFilter(property, Operator.EQUAL, of(value));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,12 +78,12 @@ public enum ValueType {
/**
* Represents a raw/unparsed value.
*/
RAW_VALUE(RawValue.MARSHALLER);
RAW_VALUE(RawValue.MARSHALLER),

/**
* TODO(ajaykannan): add GEO_POINT_VALUE
* Will represent a geolocation value in latitude/longitude
* Represents a {@link GeoPoint} value
*/
GEO_POINT(GeoPointValue.MARSHALLER);

private static final ImmutableMap<Integer, ValueType> DESCRIPTOR_TO_TYPE_MAP;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ public class BaseEntityTest {

private static final Blob BLOB = Blob.copyFrom(new byte[]{1, 2});
private static final DateTime DATE_TIME = DateTime.now();
private static final GeoPoint GEO_POINT = new GeoPoint(30.5, -40.5);
private static final Key KEY = Key.builder("ds1", "k1", "n1").build();
private static final Entity ENTITY = Entity.builder(KEY).set("name", "foo").build();
private static final IncompleteKey INCOMPLETE_KEY = IncompleteKey.builder("ds1", "k1").build();
Expand Down Expand Up @@ -62,9 +63,9 @@ public void setUp() {
builder = new Builder();
builder.set("blob", BLOB).set("boolean", true).set("dateTime", DATE_TIME);
builder.set("double", 1.25).set("key", KEY).set("string", "hello world");
builder.set("long", 125).setNull("null").set("entity", ENTITY);
builder.set("long", 125).setNull("null").set("entity", ENTITY).set("geoPoint", GEO_POINT);
builder.set("partialEntity", PARTIAL_ENTITY).set("stringValue", StringValue.of("bla"));
builder.set("list1", NullValue.of(), StringValue.of("foo"));
builder.set("list1", NullValue.of(), StringValue.of("foo"), GeoPointValue.of(GEO_POINT));
builder.set("list2", ImmutableList.of(LongValue.of(10), DoubleValue.of(2)));
builder.set("list3", Collections.singletonList(BooleanValue.of(true)));
}
Expand Down Expand Up @@ -149,6 +150,12 @@ public void testGetDateTime() throws Exception {
assertEquals(dateTime, entity.getDateTime("dateTime"));
}

@Test
public void testGetGeoPoint() throws Exception {
BaseEntity<Key> entity = builder.build();
assertEquals(GEO_POINT, entity.getGeoPoint("geoPoint"));
}

@Test
public void testGetKey() throws Exception {
BaseEntity<Key> entity = builder.build();
Expand All @@ -171,9 +178,10 @@ public void testGetEntity() throws Exception {
public void testGetList() throws Exception {
BaseEntity<Key> entity = builder.build();
List<? extends Value<?>> list = entity.getList("list1");
assertEquals(2, list.size());
assertEquals(3, list.size());
assertEquals(NullValue.of(), list.get(0));
assertEquals("foo", list.get(1).get());
assertEquals(GEO_POINT, list.get(2).get());
list = entity.getList("list2");
assertEquals(2, list.size());
assertEquals(Long.valueOf(10), list.get(0).get());
Expand All @@ -196,9 +204,10 @@ public void testGetBlob() throws Exception {

@Test
public void testNames() throws Exception {
Set<String> names = ImmutableSet.<String>builder()
.add("string", "stringValue", "boolean", "double", "long", "list1", "list2", "list3")
.add("entity", "partialEntity", "null", "dateTime", "blob", "key")
Set<String> names =
ImmutableSet.<String>builder()
.add("string", "stringValue", "boolean", "double", "long", "list1", "list2", "list3")
.add("entity", "partialEntity", "null", "dateTime", "geoPoint", "blob", "key")
.build();
BaseEntity<Key> entity = builder.build();
assertEquals(names, entity.names());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ public class DatastoreTest {
.build();
private static final ListValue LIST_VALUE2 = ListValue.of(Collections.singletonList(KEY_VALUE));
private static final DateTimeValue DATE_TIME_VALUE = new DateTimeValue(DateTime.now());
private static final GeoPointValue GEO_POINT_VALUE = new GeoPointValue(new GeoPoint(30.5, 40.5));
private static final FullEntity<IncompleteKey> PARTIAL_ENTITY1 =
FullEntity.builder(INCOMPLETE_KEY2).set("str", STR_VALUE).set("bool", BOOL_VALUE)
.set("list", LIST_VALUE1).build();
Expand All @@ -83,13 +84,15 @@ public class DatastoreTest {
private static final FullEntity<IncompleteKey> PARTIAL_ENTITY3 =
FullEntity.builder(PARTIAL_ENTITY1).key(IncompleteKey.builder(PROJECT_ID, KIND3).build())
.build();
private static final Entity ENTITY1 = Entity.builder(KEY1)
.set("str", STR_VALUE)
.set("date", DATE_TIME_VALUE)
.set("bool", BOOL_VALUE)
.set("partial1", EntityValue.of(PARTIAL_ENTITY1))
.set("list", LIST_VALUE2)
.build();
private static final Entity ENTITY1 =
Entity.builder(KEY1)
.set("str", STR_VALUE)
.set("date", DATE_TIME_VALUE)
.set("geoPoint", GEO_POINT_VALUE)
.set("bool", BOOL_VALUE)
.set("partial1", EntityValue.of(PARTIAL_ENTITY1))
.set("list", LIST_VALUE2)
.build();
private static final Entity ENTITY2 = Entity.builder(ENTITY1).key(KEY2).remove("str")
.set("name", "Dan").setNull("null").set("age", 20).build();
private static final Entity ENTITY3 = Entity.builder(ENTITY1).key(KEY3).remove("str")
Expand Down Expand Up @@ -504,9 +507,11 @@ public void testGet() {
assertEquals(LIST_VALUE2, value3);
DateTimeValue value4 = entity.getValue("date");
assertEquals(DATE_TIME_VALUE, value4);
FullEntity<IncompleteKey> value5 = entity.getEntity("partial1");
assertEquals(PARTIAL_ENTITY1, value5);
assertEquals(5, entity.names().size());
GeoPointValue value5 = entity.getValue("geoPoint");
assertEquals(GEO_POINT_VALUE, value5);
FullEntity<IncompleteKey> value6 = entity.getEntity("partial1");
assertEquals(PARTIAL_ENTITY1, value6);
assertEquals(6, entity.names().size());
assertFalse(entity.contains("bla"));
}

Expand All @@ -528,7 +533,8 @@ public void testGetArray() {
assertEquals(PARTIAL_ENTITY2, partial1);
assertEquals(ENTITY2, partial2);
assertEquals(ValueType.BOOLEAN, entity3.getValue("bool").type());
assertEquals(6, entity3.names().size());
assertEquals(GEO_POINT_VALUE, entity3.getValue("geoPoint"));
assertEquals(7, entity3.names().size());
assertFalse(entity3.contains("bla"));
try {
entity3.getString("str");
Expand Down
Loading

0 comments on commit f6eeddd

Please sign in to comment.