A case for user defined ID’s for a block

Currently when adding a block to a document via the API, the API generates a new block ID for me. I don’t have any info about the underlying architecture, so this request is operating on some assumptions.

Assumption 1: Links made from one block to another block using the @linking feature likely use the block id of the source block and is embedded in the content run of the destination block.

is this correct?

If it is, I would Iike to propose that the developer can create his own block ID at the time of adding the block. That way in the future, any links to these blocks will use a fixed ID. I have experience from dev on other tools that show the ability to o control the block ID has a few key long-term advantages.

First, Obsidian allows the user to either use a system generated ID or specify their own. Roam, while in the UI does not allow you to define the ID, you can do so programmatically.

The advantage is as you import data, you can define a specific ID for use with that block. Any linking to that block then uses the ID of the imported block. If in the future that imported block needs to be regenerated, or moved to another database (space, graph, not sure what you call it), the link relationship remains in place.

So in summary: if the linking is backed on block ID, give developers the ability to set the block id. This will simplify maintaining and updating imported data, and also provide reliability of linking to a known block.

Of course, these means the developer will have to handle conflicts. So one thing I would do is test for the existence of a block id before adding it to the graph. If it exists, I would then do an update on the block id.

Thanks for your feedback, and what you write does make sense.
One of the challenges Craft has vs Obsidian is the sync + offline first nature. I.e. an extension generating a blockId with “extension_123” and the same extensions running on a different device generating the same ID would result in a conflict which could only be detected once both clients sync - resulting in a conflict which results in data loss.
What we could do is enable temporary IDs when creating the blocks in memory, so you could create your links easily in your extension code - and than upon insertion to the database, we would switch those to randomised one. Would this approach cover your needs?

Can I ask, how are block references internally handled? So I have block 1, and from block 2 I use @ and find block 1 and now add it into the text run (Transclusion).

Is the text run embedding the id of block 1?

Your scenario is a good one. I wasn’t thinking so much about interop, rather ongoing updates to blocks imported into craft. Imagine I have block 1, and it was inserted by my plugin. Later I update block 1, or accidentally delete block 1 and then need to reimport it. The data changed, but what hat data represents is still the same thing. An example might be importing a customer contact. I updated something about the customer, but the customer ID has not changed. Thus any block references I have made to that customer I want to maintain.

By the way, I can imagine this might not be possible, since your underlying DB may not support that idea. Obsidian using text files is a different thing since there is no underlying DB. Roam is interesting, block references are just in the text run ((id of block)) and if you delete the referenced block, the block reference is still there in the text run. But if you add another block using ((id of block)) roam will render it.

So this might be a stretch, but wanted to propose the scenario earlier in your design work.

We have a connection table internally for managing/updating block links. This is both good for performance and other purposes (i.e. there is a transclusion-like ability for Block Links). So technically as we expose to the API, it would be possible that you :

  1. Want to replace ID1 with ID2 (update block)
  2. To do you query all block links which point to ID1
  3. Update in the table them to ID2
  4. Delete ID1

Upon deleting a block we will remove all links associated to it (to avoid broken UX), but this ordering would make what you want to do possible.

We are also looking at creating links to “external data to craft”. I.e. there would be a “ExternalObject” table, which has a description of the external object (i.e. tweet:id). You could thank create bi-directional links to blocks as well - making it possible to quickly look up all blocks which reference a given tweet. We already use this in our daily note functionality (i.e. a daily note is a doc which is linked to “date:2021-12-07” object).

Balint,
Thank you for the description. This model will work well. Key is to maintain relationship between blocks during changes.

Also I am super happy to hear you are thinking about performance related to these block relationships. This is where other tools break down. As users make more and more links, the database is not optimized for it and the UX takes a hit.

Szivélyes üdvözlet