Syncleus / Ferma

An ORM / OGM for the TinkerPop graph stack.

Home Page:http://syncleus.com/Ferma

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Strange behavior of class casting.

oleghailenko opened this issue · comments

Hello guys.
I have found some unexpected behaviour of using framed graph.
Let's assume we have 2 classes A and B. Each of them have the same property name.
In the graph are two objects: object A with name = a, and object B with name = b.
But if I am trying to traverse over graph to grab object with name = b and get object of kind A using method next... I am getting object A.
Here is code:

package ferma;

import com.syncleus.ferma.AbstractVertexFrame;
import com.syncleus.ferma.DelegatingFramedGraph;
import com.syncleus.ferma.FramedGraph;
import com.syncleus.ferma.annotations.Property;
import org.apache.tinkerpop.gremlin.structure.Graph;
import org.apache.tinkerpop.gremlin.tinkergraph.structure.TinkerGraph;
import org.junit.Assert;
import org.junit.Test;

import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;

public class RowTest {

    public static abstract class Person extends AbstractVertexFrame {

        @Property("name")
        public abstract String getName();

        @Property("name")
        public abstract void setName(String name);

    }

    public static abstract class Animal extends AbstractVertexFrame {

        @Property("name")
        public abstract String getName();

        @Property("name")
        public abstract void setName(String name);

        @Property("kind")
        public abstract String getKind();

        @Property("kind")
        public abstract void setKind(String kind);

    }


    @Test
    public void name() {
        Set<Class<? extends AbstractVertexFrame>> types = new HashSet<>(Arrays.asList(
            Person.class,
            Animal.class
        ));

        Graph graph = TinkerGraph.open();
        FramedGraph fg = new DelegatingFramedGraph<>(graph, true, types);

        Person person = fg.addFramedVertex(Person.class);
        String jeff = "Jeff";
        person.setName(jeff);

        Animal animal = fg.addFramedVertex(Animal.class);
        animal.setKind("dog");
        String lessi = "lessi";
        animal.setName(lessi);

        Person personLessi = fg.traverse(input -> input.V().has("name", lessi)).next(Person.class);

        Assert.assertEquals(personLessi.getName(), lessi);

        System.out.println(personLessi.getClass());
        System.out.println(personLessi);

    }
}

// Result:
//    class ferma.RowTest$Person$ByteBuddy$0BUlWIhG
//    {
//    "id": 3,
//    "elementClass": "vertex",
//    "ferma_type": "ferma.RowTest$Animal",
//    "kind": "dog",
//    "name": "lessi"
//    }

Is it expected behavior? I did not found any information about this in the documentation.

I am using ferma version 3.2.1

This is normal behavior and expected (for several reasons). When you do a next call it will cast the object into that type regardless of what type it is in the graph, however if it is a subtype int he graph it will instantiate as the subtype.

To do what you want to do you have to invoke the typeresolver's hasType method which will filter only by type (it takes the class you want to filter by).

@oleghailenko Here is one of our unit tests that shows the proper way to do what your trying to do

 @Test
    public void testHasTypeParentFromPackage() {
        final Graph godGraph = TinkerGraph.open();
        final FramedGraph framedGraph = new DelegatingFramedGraph(godGraph, TEST_MODEL_PACKAGE);

        //add a single node to the graph, a programmer.
        framedGraph.addFramedVertex(Programmer.class);

        //make sure the newly added node is actually a programmer
        final Person programmer = framedGraph.traverse(input -> framedGraph.getTypeResolver().hasType(input.V(), Person.class)).next(Person.class);
        Assert.assertTrue(programmer instanceof Programmer);

        //change the type resolution to person
        programmer.setTypeResolution(Person.class);

        //make sure the newly added node is not actually a programmer
        final Person person = framedGraph.traverse(input -> input.V()).next(Person.class);
        Assert.assertFalse(person instanceof Programmer);
    }

@oleghailenko closing this but feel free to continue to comment or reopen.