orientechnologies / orientdb-gremlin

TinkerPop3 Graph Structure Implementation for OrientDB
Apache License 2.0
93 stars 32 forks source link

Creating vertices from case class with nested case class fields #82

Closed ecausarano closed 8 years ago

ecausarano commented 8 years ago

Hi,

I'm trying to persist a case class containing several other case classes. TinkerGraph tests work alright whereas when switching to OrientGraph causes a

com.orientechnologies.orient.core.exception.ODatabaseException: Error on deserialization of Serializable ... Caused by: java.lang.ClassNotFoundException: ...

Seems that in org.apache.tinkerpop.gremlin.orientdb.OrientElement#property(java.lang.Object...) the property is passed on as-is to the ODocument without mapping it first to another ODocument.

mpollmeier commented 8 years ago

you don't provide much context so I can only guess, but let me guess that you're trying to persist one of your own classes via a remote connection to orientdb? that wouldn't work because orient doesn't know your classes

ecausarano commented 8 years ago

Yup, that's the situation, eventually the ODocument sent off for saving contains an ODocumentEntry with the raw instance of my domain class. I think it should be also wrapped into an ODocument.

Would it be a big deal to wrap embedded classes in the orientdb-gremlin driver?

mpollmeier commented 8 years ago

maybe i wasn't clear enough - you cannot send your own classes via a remote connection to orientdb. you need to transform them into a vertex first. gremlin-scala e.g. has a macro that does that job for you: https://github.com/mpollmeier/gremlin-scala/#mapping-vertices-fromto-case-classes

ecausarano commented 8 years ago

Hi,

sorry for not being mistaken, but I was under the impression that this would work:

import org.apache.tinkerpop.gremlin.orientdb.OrientGraphFactory
import org.scalatest.{Matchers, WordSpec}
import gremlin.scala._
import org.apache.tinkerpop.gremlin.tinkergraph.structure.TinkerGraph

class SerializeNestedSpec extends WordSpec with Matchers {
  "Classes with Nested members" must {
    "be correctly serialized" in new Fixture {
      val foo = Foo("a string")
      val bar = Bar("another string", foo)

      val vertex = graph + bar

      val bar1 = vertex.toCC[Bar]
      bar1.should(equal(bar))
    }
  }

  case class Foo(string: String)
  case class Bar(string: String, foo: Foo)
  trait Fixture {
//    val graph = TinkerGraph.open.asScala
    val graph = new OrientGraphFactory(s"memory:test-${math.random}").getNoTx().asScala
  }
}

The problem is that the macro doesn't recur. If you step into the addVertex it'll eventually call org.apache.tinkerpop.gremlin.orientdb.OrientElement#property(java.lang.Object...) with a Foo object in the properties. The TinkerGraph implementation doesn't mind, but Orient does as it expects nested documents to be wrapped in ODocuments, not ODocumentEntries.

I don't know if it would be a good approach but the Orient driver (in com.orientechnologies.orient.core.record.impl.ODocument#field(java.lang.String, java.lang.Object, which is eventually called) supports nested fields using . interpolation: fromCC could flatten the foo instance fiends in bar as a foo.string, while toCC would de-interpolate the fields and rebuild the objects accordingly. I think this could also help the Neo4J backend, since it doesn't support nested documents at all!

jCalamari commented 8 years ago

This is general issue with scala classes/types. Funny part is, this works for memory storage, whereas remote doesn't.

ecausarano commented 8 years ago

I guess it's because when running "in memory" the JVM knows how to serialize the CCs because they're in the classpath.

mpollmeier commented 8 years ago

exactly - the in memory instance knows all your classes, but in production you're unlikely to run it this way. and yeah, quite possible that the macro doesn't support nesting.