-
Notifications
You must be signed in to change notification settings - Fork 139
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Feature request: Plot graph using graphviz #48
Comments
I had something similar in mind 👍 I thought about having something like Format could be An Initially I wanted to produce a JSON version, because it might be easier to pipe through other programs, including D3.js (which the project you shared uses). But I'm still trying to figure out the best JSON schema. There's a few spec out there but unfortunately nothing really standard:
Making a prototype in DOT could be a first step. There's even a Go package to generate DOT graphs. I'm just wondering if DOT is really appropriate when you have so many nodes. But it could be useful for a small subset of your ZK. |
Cool, let me know if you need help testing! JSON would be very nice indeed, as it would allow to feed the output in something that renders mustache templates. I'm specifically thinking of mustache-go here, which I already use to render static content, e.g. for documentation. This would also allow to just create a dot or svg mustache template probably without further code. EDIT: I'm playing around with the
|
Sure, I'll keep you posted on this issue.
I also want to add a I need to refactor some stuff in the models to have links more readily available. I expect that it could make the index retrieval slower for large ZKs, as it will need SQL joins to retrieve the links. Maybe adding an option |
Good point, though I'd assume the ZKs would have to be really big it to be an issue performance-wise. Fully agree on the |
I have a 30k notes ZK for testing and will see if it's an issue with it. If not, we can default with links and optionally add an option to disable links. |
I've been working on the JSON output for I added a new $ zk list --format '{ "title": {{json title}}, "tags": {{json tags}} }' (The Handlebars template parser trips on With the new $ zk list --format '{ "title": {{json title}}, "tags": {{json tags}} }' --delimiter , --header [ --footer ] I built upon this to add two new formats
|
Very useful, thank you! |
No indeed. I actually added this today, but I'm not 100% sure I want to take this direction with Technically you should be able to post-process It's not a cop out though, I still see the benefit of having the links but I'm exploring the {
"nodes": [
{ "id": 1, "name": "A" },
{ "id": 2, "name": "B" }
],
"links": [
{ "source": 1, "target": 2 }
]
} I will just add some extra metadata in the objects. Do you have any idea what might be needed there? JGF is more specified, but at the same time I didn't see concrete applications using it. |
Just a thought: Maybe it would be wise to adhere to the
in addition to the output you already implemented above with the ExampleI'll add a full example, of what I personally would like to do. Take it with a grain of salt, this might be subjective, I'm just trying to give some ideas. Assume the following notes: Note 1
Note 2
Note 3
Note 4
Running Expected output with `--format json`[
{
"path": "testnote3.md",
"title": "testnote3",
"link": "[testnote3](testnote3)",
"lead": "# testnote3",
"body": "# testnote3\n\nThe third note.",
"snippets": [
"# testnote3\n\nThe third note."
],
"rawContent": "edited/removed to keep example short",
"wordCount": 18,
"tags": [
"testnote3"
],
"metadata": {
"created": "2021-06-20",
"language": "en",
"tags": [
"testnote3"
],
"title": "testnote3",
"visibility": "private"
},
"created": "2021-06-20T14:14:45.031440861Z",
"modified": "2021-06-20T14:14:55.598864042Z",
"checksum": "5ae38fe0ff556bc37df7b9bfcce7893a0d090a6258c27f85dfaa441a23717fc1",
"links": [ ]
},
{
"path": "testnote4.md",
"title": "testnote4",
"link": "[testnote4](testnote4)",
"lead": "# testnote4",
"body": "# testnote4\n\nHaving four notes is enough for an example.",
"snippets": [
"# testnote4\n\nHaving four notes is enough for an example."
],
"rawContent": "edited/removed to keep example short",
"wordCount": 23,
"tags": [
"testnote4"
],
"metadata": {
"created": "2021-06-20",
"language": "en",
"tags": [
"testnote4"
],
"title": "testnote4",
"visibility": "private"
},
"created": "2021-06-20T14:15:02.689644096Z",
"modified": "2021-06-20T14:15:44.49852715Z",
"checksum": "70dd52ebab26ca37f483c0a36277bd9121ebe4a5bcb8aad9ad5335915a904e91",
"links": [ ]
},
{
"path": "testnote1.md",
"title": "testnote1",
"link": "[testnote1](testnote1)",
"lead": "# testnote1",
"body": "# testnote1\n\nThis is the first test note, [[testnote2]] is related to this.",
"snippets": [
"# testnote1\n\nThis is the first test note, [[testnote2]] is related to this."
],
"rawContent": "edited/removed to keep example short",
"wordCount": 26,
"tags": [
"testnote1"
],
"metadata": {
"created": "2021-06-20",
"language": "en",
"tags": [
"testnote1"
],
"title": "testnote1",
"visibility": "private"
},
"created": "2021-06-20T14:13:19.435952767Z",
"modified": "2021-06-20T14:13:39.439388055Z",
"checksum": "b216e9e1192701e4f9582a6964cbc8df93250977fd0d34ee410edaa557ecbc1f",
"links": [ "testnote2" ]
},
{
"path": "testnote2.md",
"title": "testnote2",
"link": "[testnote2](testnote2)",
"lead": "# testnote2",
"body": "# testnote2\n\nHere is a second test note. [[testnote3]] is related and [[testnote4]] aswell.",
"snippets": [
"# testnote2\n\nHere is a second test note. [[testnote3]] is related and [[testnote4]] aswell."
],
"rawContent": "edited/removed to keep example short",
"wordCount": 27,
"tags": [
"testnote2"
],
"metadata": {
"created": "2021-06-20",
"language": "en",
"tags": [
"testnote2"
],
"title": "testnote2",
"visibility": "private"
},
"created": "2021-06-20T14:13:45.750153716Z",
"modified": "2021-06-20T14:14:36.358996502Z",
"checksum": "f3e54b0c4702663bc4e9f81c71567b04d5d1a4127c83eb2cf3a194299dc9c939",
"links": [ "testnote3", "testnote4" ]
}
]
Using any mustache parser (here I'm using this simple go cli tool ) it would allow to render a graph easily. I have saved the above to a file digraph D {
{{#.}}
"{{title}}" [shape=box, label="{{title}}" ]
{{#links}}
"{{title}}" -> "{{.}}" [color="black", style=dashed]
{{/links}}
{{/.}}
} Running With a different template, any other graphing tool, e.g. TL;DRI would like to be able to do this:
And get the image above. Sorry for the long comment, I hope you can find anything useful in it. The only missing piece of the puzzle is adding |
Thanks for the food for thought, different use cases are always super helpful!
I fully agree, that's why I wanted to find a standard spec to represent graphs. D3's format is quite clean but you convinced me to use a custom format more semantically related to the note domain, although I will keep the overall structure of D3. {
"notes": [
{ "path": "a.md", "title": "A" },
{ "path": "b.md", "title": "B" }
],
"links": [
{ "source": "a.md", "target": "b.md" }
]
} If you look at JGF, Sigma or even DOT, they all follow the same structure:
Keeping this structure would make it trivial to convert to other formats with
I think interconnectedness is an important part of a healthy notebook and I want to have links and graphs as first-class citizens in
Also having a dedicated |
I see your point, having a better or more wide-used and common structure is definitely a plus!
This is mostly why I put the links in the nodes itself in my example. Assuming the link only has a "target", the source is always the note itself, which would allow to access any field in it as part of the template. E.g. I used {{#links}}
"{{title}}" -> "{{.}}" [color="black", style=dashed]
{{/links}} But I could have used any other field. Using the title is not very wise actually, as there could be multiple notes with the same title in a zettekasten that does not use the title as file name. I suppose the
I agree this could be an issue. I'll look into JGF and Sigma in more detail, I'm not very familiar with how to save graphs as json yet. That being said, there is always the option of using I personally would not mind having to use Thanks for looking into this by the way! |
@pinpox I noticed you forked the project. If you want to test it out until I add |
Hey, thanks for the patch! |
@pinpox You can try out the
You can use the Also I removed the Please share any cool script you come up with to generate the actual graphs 👍 |
Awesome! This makes it easy to generate graphs. Here is a working example: File
Then do:
You can also use Example using I'm working on an example to also show tags |
Nice, thanks! I think you should be able to adapt the code of the link you shared easily. Just need to expand the Using zk graph --format json -m "editor" | mustache graph.mustache | sfdp -Tpng > pic.png But a JS solution would be probably better for larger graphs, like in Obsidian: zk graph --format json | mustache graph.mustache | sfdp -Tpng > pic.png |
Definitely! I'll try to get a working example with |
Yes because each link might have different associated metadata. |
I got a first working example with d3: The json data is directly understood by the script, just run |
Nice, thanks! Chrome gave me a CORS error but I could test it easily running On neuron they have been working on graphs with D3 as well: srid/neuron#589 We probably need some fine-tuning for the attraction settings. Or maybe using a much smaller font and use zooming. |
Yes, the attraction simulation is not optimal. Specifically this example would be a better fit:
(Taken from this example: https://observablehq.com/@d3/disjoint-force-directed-graph?collection=@d3/d3-force ) I have tried modifying my code to use the example settings: const simulation = d3.forceSimulation(nodes)
.force("link", d3.forceLink(links).id(d => d.id))
.force("charge", d3.forceManyBody())
.force("x", d3.forceX())
.force("y", d3.forceY()); But can't get it to work. Seems like the different d3 versions don't work exactly the same way, but I havn't been able to fix it. Using the example above should result in the nodes been stretched apart into a circle when disconnected. I'm sure it's not that complicated, but my Javascript skills are not that good and I'm struggeling to figure out how to convert these observablehq.com notebooks to plain javascript/html files. There is another example here that also looks way better, but I have the same problem. |
I actually never worked with D3 before but I've seen it being used a lot for this use case. If you're able to make the font size much smaller that could be a good workaround. |
Small update. Got the correct forces working. The labels are still messy and overlapping, but I think I'm on the right track now. The simulation now prevents disconnected nodes to "fly away", they all nicely arrange into a circle. I'll either add collision to the text labels too or use some placement algorithm, there seem to be a few options. Another, simpler, way might be just making the links longer or the text smaller. Here is the updated source EDIT: updated force parameters and text size. Better now. |
@mickael-menu Is https://github.com/mickael-menu/zk/releases/tag/v0.6.0 compatible with the command above? The |
Not yet, the graph feature is a significant change and I need more time to clean up the code. I merged |
@pinpox I merged the feature in You might need to reset your database if you used the old |
@mickael-menu Looks good! I build from master and the command seems to produce correct JSON. Maybe it would be a good idea to document an example of how to render a proper graph with it somewhere in the docs? |
That's a good idea. I didn't spend much time playing with this though, would you consider writing a Markdown page dedicated to the |
As a late addition to the reference of the non-working The Zettelkasten is a view on a Logseq Journal.$ cat graph.sh
#!/usr/bin/env bash
set -m
# https://unix.stackexchange.com/questions/50179/what-happens-when-you-delete-a-hard-link
rm -rf ./zk/*.md
# https://unix.stackexchange.com/questions/40634/in-ubuntu-is-there-a-way-to-virtually-merge-two-folders-without-unionfs-or-aufs
cp -anl "$PWD/journals/"* zk/
cp -anl "$PWD/pages/"* zk/
zk -W zk graph --format json > graph/notes.json
http -q graph & xdg-open http://localhost:8000 && fg
In the |
@almereyda I'm struggeling a bit to understand. Did you use the gists, your forked zetteltools repo or both?
@mickael-menu I'd like to include a full working example in the documentation with the above if possible. The forces in |
Sure, feel free to open a PR on the repo 👍 |
I have the same issue. With your gist it also seems not all the notes fit in the frame and I am not sure how to zoom. Did anyone figure out how to get the |
It would be nice to be able to visualise the connections between notes as a graph. I have seen this functionality on other applications implementing that implement a Zettelkasten-style notebook and find it very helpful. Here is an example of how that could look.
I propose a command, e.g.
zk graph
that, that just outputs the source for a graph as a graphviz graph, allowing to pipe it to a file or render it usingdot
.Here is another example of a very useful visualisation, haven't been able to run it on my
zk
notes folder though.Maybe there already is a solution for this I'm not aware of, if so please let me know!
The text was updated successfully, but these errors were encountered: