applctv / gcp-scala-datastore

Scala wrapper to Google Cloud Datastore operations
15 stars 16 forks source link

Support for collections of values in any given field #23

Open stevebakh opened 6 years ago

stevebakh commented 6 years ago

Google Datastore allows for storing a collection of values against a single property in an entity. This is supported in the Google client library, and also in the Spotify async client library, but I don't believe it's supported by this project, from what I can see.

It would be neat to be able to define a traversable type property in my entity case class, and have ReflectionHelper.instanceToDatastoreEntity correctly convert the value.

e.g.

case class Photo(id: Long, name: String, tags: List[String], ...)

Example in the Spotify library: https://github.com/spotify/async-datastore-client/blob/master/src/main/java/com/spotify/asyncdatastoreclient/Value.java#L124-L136

If there's already a way of doing this using your library, please let me know.

Kind regards, Steven

stevebakh commented 6 years ago

I took a very quick stab at implementing this feature. Wrote a couple of failing tests and got them passing, but working only for lists of strings. I would spend more time making this generic, but I've now had to switch to a different library as further development revealed that an entity builder would fit my requirements better than using case classes.

import scala.collection.JavaConverters._
builder.set(name, value.map(v => StringValue.of(v)).toList.asJava)

...

case ListClassName => entity.getList[StringValue](fieldName).asScala.map(_.get).toList

Thanks for all your work on the library so far. If I need this functionality in the near future, I'll likely pick this up again, but if not, hopefully there's enough here for somebody to see what needs to be done.

If it helps anybody, the following tests were written in the ReflectionHelperSpec file.

  "Convert instance with traversable type to datastore entity" in {
    import scala.collection.JavaConverters._
    val key = Key(CloudKey.newBuilder("test", "TestClass4", "test").build())
    val expected = List("foo", "bar", "baz")
    val entity = helper.instanceToDatastoreEntity[TestClass4](key, TestClass4(1, expected), classOf[TestClass4])
    entity.getNames.size() shouldEqual 1
    entity.getList[StringValue]("tags").asScala.map(_.get()) shouldEqual expected
  }

  "Convert datastore entity to a class with a traversable type" in  {
    val instance = TestClass4(1, List("foo", "bar", "baz"))
    val key = Key(CloudKey.newBuilder("test", "TestClass4", "test").setId(instance.id).build())
    val clazz = classOf[TestClass4]
    val entity = helper.instanceToDatastoreEntity(key, instance, clazz)
    val res = helper.datastoreEntityToInstance[TestClass4](entity, clazz)
    res shouldEqual instance
  }

...

case class TestClass4(id: Long, tags: List[String])