fluree / db

Fluree database library
https://fluree.github.io/db/
Other
330 stars 21 forks source link

using insert/where/delete with values (predicates) which have no value causing unwanted cardinality changes [bug] #799

Closed frydj closed 1 month ago

frydj commented 1 month ago

See example below:

create the ledger with an entity "ex:betty"

Transaction:

{
  "ledger": "test123",
  "insert": [
    {
      "@id": "ex:betty",
      "@type": "ex:Yeti",
      "schema:name": "Betty"
    }
  ]
}

Response:

{
  "ledger": "test123",
  "commit": "fluree:file://test123/commit/b3c723c64791f14b0afb9d79761f3d4478891e23733d23d4349dee293bdb632d.json",
  "t": 1,
  "tx-id": "007d3dce61f6233656eb70fb1c5bac6691333f01842a68c46943d7c054252abc"
}

query for betty

Query:

{
  "from": "test123",
  "select": {
    "ex:betty": ["*"]
  }
}

Response:

[
  {
    "@type": "ex:Yeti",
    "schema:name": "Betty",
    "@id": "ex:betty"
  }
]

use insert/where/delete to update some facts about betty

Transaction:

{
  "ledger": "test123",
  "insert": [
    {
      "@id": "ex:betty",
      "schema:name": "Letty",
      "schema:description": "This is the description"
    }
  ],
  "where": {
    "@id": "ex:betty",
    "schema:name": "?name",
    "schema:description": "?description"
  },
  "delete": {
    "@id": "ex:betty",
    "schema:name": "?name",
    "schema:description": "?description"
  }
}

Response:

{
  "ledger": "test123",
  "commit": "fluree:file://test123/commit/3a93e89a4ad6be6ffee4220cb2758df499f2939cd7259c802d53fd70fce7a61d.json",
  "t": 2,
  "tx-id": "9a8fa79e6776a99a18193614fcc28697325bf719ba48d6d6747813a9d95a2fd2"
}

query for betty again

Query:

{
  "from": "test123",
  "select": {
    "ex:betty": ["*"]
  }
}

Response:

[
  {
    "@type": "ex:Yeti",
    "schema:description": "This is the description",
    "schema:name": [
      "Betty", // unexpected multi-cardinal value?
      "Letty"
    ],
    "@id": "ex:betty"
  }
]

update some existing facts again

Transaction:

{
  "ledger": "test123",
  "insert": [
    {
      "@id": "ex:betty",
      "schema:name": "Setty",
      "schema:description": "This is NOT the description"
    }
  ],
  "where": {
    "@id": "ex:betty",
    "schema:name": "?name",
    "schema:description": "?description"
  },
  "delete": {
    "@id": "ex:betty",
    "schema:name": "?name",
    "schema:description": "?description"
  }
}

Response:

{
  "ledger": "test123",
  "commit": "fluree:file://test123/commit/2311efeee5896c1da6e2dc7dcb4ae8472040b3fe9f88e56898698bf4eb0c8dd5.json",
  "t": 3,
  "tx-id": "c530c6456512e0e95ead10a19ce1fcadfda10018ee24ee524395646cae8e8e8a"
}

query again

Query:

{
  "from": "test123",
  "select": {
    "ex:betty": ["*"]
  }
}

Response:

[
  {
    // this looks fine
    "@type": "ex:Yeti",
    "schema:description": "This is NOT the description",
    "schema:name": "Setty",
    "@id": "ex:betty"
  }
]

insert/where/delete again on a field which doesn't have values yet

Transaction:

{
  "ledger": "test123",
  "insert": [
    {
      "@id": "ex:betty",
      "schema:name": "Retty",
      "schema:description": "this is a new description.",
      "schema:age": 20
    }
  ],
  "where": {
    "@id": "ex:betty",
    "schema:name": "?name",
    "schema:description": "?description",
    "schema:age": "?age"
  },
  "delete": {
    "@id": "ex:betty",
    "schema:name": "?name",
    "schema:description": "?description",
    "schema:age": "?age"
  }
}

Response

{
  "ledger": "test123",
  "commit": "fluree:file://test123/commit/97bf769b3ed571397ccbdd28cf66d5555df79998818f4ebc694657cfdf6a33a9.json",
  "t": 4,
  "tx-id": "38e71f6a5e01a58fe2b45365197c9b6d27c656da9562469b346083df759b2f49"
}

query again for betty values

Query:

{
  "from": "test123",
  "select": {
    "ex:betty": ["*"]
  }
}

Response:

[
  {
    "@type": "ex:Yeti",
    "schema:age": 20,
    "schema:description": [ // unexpected multi-cardinal value
      "This is NOT the description",
      "this is a new description."
    ],
    "schema:name": ["Retty", "Setty"], // unexpected multi-cardinal value
    "@id": "ex:betty"
  }
]

it appears that when doing insert/where/delete where you include values which don't exist yet causes this unwanted insertion of multiple values (?)

frydj commented 1 month ago

This was with a docker build of fluree-server at commit 1bcae4ed161bd9b5f43a22f6d4f7e69a3a2737e0

frydj commented 1 month ago

The issue here is that the where clause won't be applied as part of the where clause is trying to assert based on values which aren't present, so it gets dropped (as I understand). So in order to do insert/where/delete when you aren't sure if there's a value on a predicate, you should use optional, as in this example:

{
  "ledger": "test123",
  "insert": [
    {
      "@id": "ex:betty",
      "schema:name": "Letty",
      "schema:description": "This is the description"
    }
  ],
  "where": [
    {
      "@id": "ex:betty",
      "schema:name": "?name"
    },
    [
      "optional",
      {
        "schema:description": "?description"
      }
    ]
  ],
  "delete": [
    {
      "@id": "ex:betty",
      "schema:name": "?name",
      "schema:description": "?description"
    }
  ]
}
frydj commented 1 month ago

You can also do something along the lines of:

"where": { "@id": "ex:betty", "?prop": "?val" },
"delete": { "@id": "ex:betty", "?prop": "?val" },
"insert": { ... whatever you want }

This would just be a way of deleting any facts on the subject, and inserting new ones. So this example isn't really a neat "edit" as much as a rip & replace of the facts on a subject.