orientechnologies / orientdb

OrientDB is the most versatile DBMS supporting Graph, Document, Reactive, Full-Text and Geospatial models in one Multi-Model product. OrientDB can run distributed (Multi-Master), supports SQL, ACID Transactions, Full-Text indexing and Reactive Queries.
https://orientdb.dev
Apache License 2.0
4.74k stars 870 forks source link

Live query: "delete" notification does not contain embedded fields #9614

Closed denis-itskovich closed 3 years ago

denis-itskovich commented 3 years ago

OrientDB Version: 3.1.11

Java Version: JDK11

OS: Windows, Linux

Expected behavior

on delete notification should provide the entire deleted object, including embedded field "embedded"

Actual behavior (works fine in 3.0.x, broken in 3.1.x)

on delete notification provides deleted object, without embedded field (embedded: null)

Steps to reproduce

Schema: ClassA: {name: String} ClassB: {embedded: ClassA} Test case:

  1. live query: select from ClassB
  2. Add object: ClassB: {embedded: {name: test, @class: "ClassA"}, @class: "ClassB"}
  3. Delete this object

Test case is available here: LiveQueryEmbeddedFieldTest.java

public class LiveQueryEmbeddedFieldTest {
    private final static String dbName = "testDb_" + LiveQueryEmbeddedFieldTest.class.getSimpleName();
    private OrientDB dbClient;
    private Supplier<ODatabaseSession> sessionProvider;

    @BeforeEach
    public void setUp() {
        dbClient = new OrientDB("embedded:" + dbName, "root", "root", OrientDBConfig.defaultConfig());
        dbClient.createIfNotExists(dbName, ODatabaseType.MEMORY);
        sessionProvider = () -> dbClient.open(dbName, "admin", "admin");
    }

    class Subscriber extends AbstractLiveQueryResultListener implements AutoCloseable {
        private final OLiveQueryMonitor monitor;

        Subscriber(String query) {
            try (ODatabaseDocument session = sessionProvider.get()) {
                this.monitor = session.live(query, this);
            }
        }

        @Override
        public void close() {
            monitor.unSubscribe();
        }
    }

    @Test
    public void testLiveQueryWithEmbeddedField() throws InterruptedException {
        try (ODatabaseDocument session = sessionProvider.get()) {
            var classA = session.createClass("ClassA");
            classA.createProperty("name", OType.STRING);
            session.createClass("ClassB").createProperty("embedded", OType.EMBEDDED, classA);
        }

        List<String> createdObjects = new ArrayList<>();
        List<String> deletedObjects = new ArrayList<>();

        try (Subscriber subscriber = new Subscriber("select from ClassB") {
            @Override
            public void onCreate(ODatabaseDocument database, OResult data) {
                createdObjects.add(data.toJSON());
            }

            @Override
            public void onDelete(ODatabaseDocument database, OResult data) {
                deletedObjects.add(data.toJSON());
            }
        }) {
            ORID rowId = null;
            try (ODatabaseSession session = sessionProvider.get()) {
                var doc = new ODocument("ClassB")
                        .field("embedded", new ODocument("ClassA").field("name", "Object1"));
                rowId = session.save(doc).getIdentity();
            }

            Thread.sleep(1000);
            Assertions.assertEquals(1, createdObjects.size());
            Assertions.assertTrue(createdObjects.get(0).contains("\"embedded\": {\"name\": \"Object1\", \"@class\": \"ClassA\"}"));

            try (ODatabaseSession session = sessionProvider.get()) {
                session.delete(rowId);
            }

            Thread.sleep(1000);
            Assertions.assertEquals(1, deletedObjects.size());
            Assertions.assertTrue(deletedObjects.get(0).contains("\"embedded\": {\"name\": \"Object1\", \"@class\": \"ClassA\"}"));
        }
    }
}
public class AbstractLiveQueryResultListener implements OLiveQueryResultListener {
    @Override
    public void onCreate(ODatabaseDocument database, OResult data) {

    }

    @Override
    public void onUpdate(ODatabaseDocument database, OResult before, OResult after) {

    }

    @Override
    public void onDelete(ODatabaseDocument database, OResult data) {

    }

    @Override
    public void onError(ODatabaseDocument database, OException exception) {

    }

    @Override
    public void onEnd(ODatabaseDocument database) {

    }
}
luigidellaquila commented 3 years ago

Hi @denis-itskovich

It seems to be a bug, I'll check it as soon as I have a moment

Thanks

Luigi

denis-itskovich commented 3 years ago

Any progress with this issue? This is a serious regression compared to 3.0.x, which prevents us migrating to 3.1.x.

luigidellaquila commented 3 years ago

HI @denis-itskovich

I'm testing a fix right now, I'll update you very soon

Thanks

Luigi

luigidellaquila commented 3 years ago

Hi @denis-itskovich

I just pushed a fix that solves this problem. The fix will be released with v 3.1.13 and 3.2.1

Now the embedded object is returned correctly, but your test is not guaranteed to pass, as the JSON of the embedded document could not be exactly the same (eg. temporary RID, order of fields and so on)

Thanks

Luigi