ahyatt / ekg Goto Github PK
View Code? Open in Web Editor NEWThe emacs knowledge graph, app for notes and structured data.
License: GNU General Public License v3.0
The emacs knowledge graph, app for notes and structured data.
License: GNU General Public License v3.0
Currently, the MELPA recipe doesn't install ekg-org-roam.el
; only ekg.el
. That makes sense if you intend to add a separate MELPA recipe, but maybe not otherwise?
Hi @ahyatt ,
Currently, after deletion of resource URL (I changed my mind to remove it), there's no ID for the ekg note any more. As such, transclusion of such notes is no longer possible.
Do we want to regenerate a new id before save? Or, do we want to allow multiple ids when ekg note is captured, e.g. an essential id (always exists), and optional ids like URL?
Zheng
In my opinion, package `ekg' is not opinionated, rather, more human friendly, and personally I can't see it as substitute for other
applications, I see it as unique program.
So I propose changing your introduction: "The ekg module is a simple but opinionated note taking application. It is a substitute for such other emacs applications such as org-roam or denote. ekg stands for emacs knowledge graph."
There are a few core ideas driving the design of ekg. The first is
that a title and a tag are the same thing.
If I may say, I deal with many tags, tag I consider a class of
attributes or properties to an object.
I have elementary objects, they have their types, subtypes, types can
be actionable or not, there may be additional properties, there are
relations, etc. All those are properties in some sense.
Tags are properties across everything and are in itself important. I
use notion of tag types as well:
1 Default
2 Skill
3 Topic
4 Language
5 Action
6 Place
7 Sales
8 Computer
And notion that user should be able to enter tag types.
What we like in databases is use of intersection. It implies for me
that the more various sets of properties are there, the better we may
pin point to various destinations.
Practically it means we can better find the note we are looking for.
We can easier find relationships between objects.
Tags as separate properties from object name are useful to create more
intersections. Eliminating tags eliminate usefulness.
Back to searching, I have mentioned intersections. That is basic
principle by which various database based searches work. There are
different tables, different columns, types, classes defined in the
databases.
When searching we can then design functions such as:
How I have understood your idea is that you wish to say that searching
through tags and names of objects is helpful. Sure it is.
But it is not the only way of making intersections, there is number
3. and number 4. and X different ways for intersections.
If we think of merging various properties into "one" to provide better
search results, than I can recommend the full text search, one
reference is here:
Hyperscope full text search with PostgreSQL:
https://hyperscope.link/3/6/7/6/8/Hyperscope-full-text-search-with-PostgreSQL-36768.html
As then, I can update tokens to include names of the tags, to include
the name of object, description, text, language, country, related
currency, names of related people, etc.
I can't say that excluding tags alone is beneficial, but I can say
that including tags is beneficial in various search functions.
I have given you example in PostgreSQL, and I think full text search
in SQLite does not exist. Even building on it is less productive for
future, as it is single user database.
Build products that are multi-user and collaboration based.
This isn’t unique to ekg, other tools such as Logseq also consider
tags to be equivalent to pages of the same name, although this
functionality is limited since tags can only be just one word.
In my work I have table tags
Table "public.tags"
┌───────────────────┬──────────────────────────┬───────────┬──────────┬───────────────────────────────────────┐
│ Column │ Type │ Collation │ Nullable │ Default │
├───────────────────┼──────────────────────────┼───────────┼──────────┼───────────────────────────────────────┤
│ tags_id │ integer │ │ not null │ nextval('tags_tags_id_seq'::regclass) │
│ tags_datecreated │ timestamp with time zone │ │ not null │ CURRENT_TIMESTAMP │
│ tags_datemodified │ timestamp with time zone │ │ │ │
│ tags_usercreated │ text │ │ not null │ CURRENT_USER │
│ tags_usermodified │ text │ │ not null │ CURRENT_USER │
│ tags_name │ text │ │ not null │ │
│ tags_description │ text │ │ │ │
│ tags_languages │ integer │ │ not null │ 1 │
│ tags_tag1 │ integer │ │ │ │
│ tags_tag2 │ integer │ │ │ │
│ tags_tag3 │ integer │ │ │ │
│ tags_tagtypes │ integer │ │ not null │ 1 │
│ tags_hidden │ boolean │ │ not null │ false │
│ tags_people │ integer │ │ │ │
│ tags_rank │ integer │ │ not null │ 0 │
└───────────────────┴──────────────────────────┴───────────┴──────────┴───────────────────────────────────────┘
You may notice that tag has its own properties. It has its name, and
because tag is addressed by its unique ID, the name can have
spaces. It may be long. It can contain any chars. It can have its
description. It may be in different language.
It may be tagged by three other tags. Why is that useful? Tag once any
object, and computer will update it with any additonal tags. If I tag
something with "Video" computer will tag it with "Media" and "Video",
as maybe that is what user wants. But I do not keep to add relevant
synonyms or relevant tags, I can add just one of them. If that feature
becomes very useful, I would simply provide tagging of tags.
Because tags are addressed by ID as integer and not by name, I can
rename tags on the object without losing the tag from the object. All
objects then appear with the renamed tag!
Then there are tag types. Very useful.
Maybe there is elementary object, like note, speaking about "Technical
School", but that they provide skill of "Optician" is not derived from
the name alone. That is one example among many why tags are useful.
It is however useful concatenating tags with name and searching among
them, that is one of intersections that are helpful to human.
In org-roam, a tag is just a tag, so you can have a note called
“emacs” and a tag called “emacs”, but these are not related.
Okay
ekg takes the idea a step further: there are (mostly) no titles,
only tags. So, instead of writing text in a note called “emacs”,
just write a note and tag it with “emacs”. There is no “title”, only
tags.
In my opinion that design is backwards, it does not help.
If it is searching by name, or splitting name into words to search by
those words, then it is not "tag".
Tag is a property separate from object. It may have different name
than anything in the object. I can have "USD" for US dollar as
tag.
Tags are properties of the object that have no special group or class.
It is useful to have tags in existence which are not related to any
object. This is for human to have easier work. What if you have tags
for currencies such as "USD", "EUR", "GBP", and you wish to tag object
with it, but in that moment you did not have "GBP" ready, so instead
of writing it every single time, you can prepare the tag list for user
to select it easily.
Elementary Objects:
https://www.dougengelbart.org/content/view/110/460/#2a1a
My elementary objects have "Currency" property directly attached, and
whatever is more important it becomes directly attached to the
object. Tags are there as dettached properties that may belong to any
object.
Separate functions can be made to automatically relate objects to
tags. Such as making tags out of the name. As that seems to be that
what you designed.
Example is with the object named "NonGNU Emacs Lisp Package Archive"
that may be automatically tagged by tags such as "Emacs" and
"Archive".
If you write another note about emacs, also tag it “emacs”, and
maybe something else too. Or tag it something more involved, like an
idea: “emacs’s power derives from putting all data in buffers, and
making all commands deal with buffers.” That’s a perfectly fine tag,
and if you notice a connecting idea, you can tag it with this as
well.
That is right and good. My tags may also be of arbitrary
length.
Though tags are useful because they are more simplistic ideas, not
complex.
Their usefulness is derived from their combination, not from their
quality.
Their meaning shall be elementary meaning, not complex meaning.
Their purpose shall be generation of intersections.
A tag like "Emacs" is less useful, as it would find anything about
Emacs. Then "Emacs" combined with "ekg" is more useful. Isn't it?
Using name of object to generate tags is useful. But excluding tags as
such is not.
The advantage of this method is that it solves something that has
bothered me for a while about the recent suite of tools like
org-roam: backlinks are non-symmetrical. If you enter a note in your
org-roam daily about emacs, and link it to the emacs note, then when
you go to the emacs note, you have to explicitly enable the
backlinks buffer to see the daily entry where you first entered
it. Systems such as Logseq and the original Roam have backlinks
alongside normal content, but this doesn’t seem possible in emacs,
where a buffer of a file is expected show the file, and tricks with
overlays can’t solve the issue. Even if it could, I want a system in
which it doesn’t matter where you enter the data, it shows up in the
original place the same as everywhere else it is linked to, not as a
backlink, but just as part of the content. Having notes with no
title, only tags, makes this possible, because there is no longer a
difference between linking and writing in the context in, both are
denoted by tags.
In my opinion your solution is only one of many solutions. It is not
necessary to be so, it can be implemented in various ways.
As a consequence of this design, notes can be small, because to add
another note to a subject, you don’t need to append to an existing
note, you can create another note.
That is right, multiple notes shall be available for any possible
relation.
Additionally, ekg has another key difference: it uses sqlite instead
of the filesystem. When notes are small and do not have titles,
files don’t make a lot of sense anymore.
File system is one way of "tagging" and sorting of files. It has
directories, filenames, hierarchical structure and access by variety
of means.
It is up to user to "sort" his stuff, relate to each other. It is not
as flexible as database.
Additionally, the filesystem is limited. Even in org-roam, which
uses it, it needs to be augmented with sqlite anyway to enable fast
querying of tags and other operations. The sqlite-only approach also
means it is much easier to make certain kinds of changes, since they
only involve changing the database and not the text as well. In
general, text and data are separated as much as possible here, so
there’s no need or desire for the text to have to store data as
well, we leave that completely to the database.
In my opinion your introduction is difficult to comprehend. It is good
if you make video or screenshots.
Prefixed tags
Another concept, loosely applied in ekg is that of tags with
standard prefixes. By default, date tags are prefixed with
“date/”. This is a way to distinguish date tags from other kinds of
tags. Most tags shouldn’t need it, but it often is useful to have
prefixes to group tags in some way. For instance, perhaps all idea
tags should be prefixed with “idea/”. In my ekg repository I use in
my company, I have “person/” as a tag prefix for my coworker’s
username.
We speak here of tag types. Think of implementing it. If you implement
"Default" tag type and let user add any other tag type, that way you
will give useful system. Users can then decide that tag belongs to tag
type "Idea", or "Skill" or maybe "Time".
The benefit of this is that it’s now possible to narrow in on just
tags of a certain type if necessary.
We speak here basically of tags for tags. Or properties of the
tags. And I find it very good notion for future.
There are a few other types of prefixes commonly used for tags. One
is that titled resources have default tags that are prefixed with
“doc/”, followed by the name of the document. Removed tags are
prefixed with “trash/”, but these are normally invisible to the
user. There’s a section on these trash tags below which goes into
more detail.
I would not do it that way to allow human mistake, for human to add
string with slash, rather using tag types.
I have table tagging
, if tag is removed from object, it is removed
from table tagging, the table references tag with elementary
object or with people. But there is use for the tag to remain in
the database, as for future selection of tags.
In this case tag can be related to people object or document
object. There is hundred of other tables, but for those two tags are
most useful.
Table "public.tagging"
┌──────────────────────┬──────────────────────────┬───────────┬──────────┬───────────────────────────────────────────────────┐
│ Column │ Type │ Collation │ Nullable │ Default │
├──────────────────────┼──────────────────────────┼───────────┼──────────┼───────────────────────────────────────────────────┤
│ tagging_id │ integer │ │ not null │ nextval('peopletags_peopletags_id_seq'::regclass) │
│ tagging_datecreated │ timestamp with time zone │ │ not null │ CURRENT_TIMESTAMP │
│ tagging_datemodified │ timestamp with time zone │ │ │ │
│ tagging_usercreated │ text │ │ not null │ CURRENT_USER │
│ tagging_usermodified │ text │ │ not null │ CURRENT_USER │
│ tagging_tags │ integer │ │ not null │ │
│ tagging_people │ integer │ │ │ │
│ tagging_hyobjects │ integer │ │ │ │
│ tagging_description │ text │ │ │ │
└──────────────────────┴──────────────────────────┴───────────┴──────────┴───────────────────────────────────────────────────┘
My tagging is such that I can tag with existing tags or newly created
tags. Tag searching uses function completing-read-multiple
to allow
me finding multiple tags.
It looks like in addition to triples
, the package also depends on s
and kv
.
Should this be mentioned in installation instructions?
Hi @ahyatt ,
The transclude-* doesn't play quite well with text around. After ekg-edit-finalize
, the transclude text will move one char forward. (Please note the "t" of "test").
Seems like a bug?
Zheng
Edit: update img
When using the provided example configuration from the README:
(use-package ekg
:bind (([f11] . ekg-capture)))
Calling (ekg-capture-finialize)
from a an ekg capture buffer fails, requiring me to set an LLM provider:
Debugger entered--Lisp error: (error "LLM provider was nil. Please set the provider in ...")
signal(error ("LLM provider was nil. Please set the provider in ..."))
triples--with-transaction(#<sqlite db=0x55dce80570e8 name=/home/maxtrussell/.emacs.d/triples.db> #<subr F616e6f6e796d6f75732d6c616d626461_anonymous_lambda_59>)
ekg--save-note-in-buffer()
#<subr ekg-capture-finalize>()
apply(#<subr ekg-capture-finalize> nil)
#f(compiled-function (body &rest args) #<bytecode -0x80dd22094e3b867>)(#<subr ekg-capture-finalize>)
apply(#f(compiled-function (body &rest args) #<bytecode -0x80dd22094e3b867>) #<subr ekg-capture-finalize> nil)
ekg-capture-finalize()
eval-expression((ekg-capture-finalize) nil nil 127)
funcall-interactively(eval-expression (ekg-capture-finalize) nil nil 127)
command-execute(eval-expression)
Full error message:
triples--with-transaction: LLM provider was nil. Please set the provider in the application you are using
I would prefer not to use an LLM to manage my notes (sacrificing any associated features) . Is it possible to use ekg without an LLM provider?
Thanks in advance!
If I invoke ekg-capture-url after restarting emacs I can see it failing due to uninitialized ekg-db
variable. Shall the ekg-capture-url
be calling ekg-connect
before calling triples-get-subject
?
Under Emacs-Q, run ekg-clean-db
==> backtrace:
Debugger entered--Lisp error: (wrong-type-argument stringp nil)
string-trim-right(nil nil)
string-trim(nil)
(string= (string-trim (let* ((cl-x note)) (progn (or (let* ((cl-x cl-x)) (progn (and (memq (type-of cl-x) cl-struct-ekg-note-tags) t))) (signal 'wrong-type-argument (list 'ekg-note cl-x))) (aref cl-x 2)))) "*")
(or (not (ekg-has-live-tags-p id)) (string= (string-trim (let* ((cl-x note)) (progn (or (let* ((cl-x cl-x)) (progn (and (memq (type-of cl-x) cl-struct-ekg-note-tags) t))) (signal 'wrong-type-argument (list ... cl-x))) (aref cl-x 2)))) "*"))
(if (or (not (ekg-has-live-tags-p id)) (string= (string-trim (let* ((cl-x note)) (progn (or (let* ((cl-x cl-x)) (progn (and (memq (type-of cl-x) cl-struct-ekg-note-tags) t))) (signal 'wrong-type-argument (list 'ekg-note cl-x))) (aref cl-x 2)))) "*")) (progn (ekg-note-delete note)))
(let ((note (ekg-get-note-with-id id))) (if (or (not (ekg-has-live-tags-p id)) (string= (string-trim (let* ((cl-x note)) (progn (or (let* ((cl-x cl-x)) (progn (and (memq (type-of cl-x) cl-struct-ekg-note-tags) t))) (signal 'wrong-type-argument (list 'ekg-note cl-x))) (aref cl-x 2)))) "*")) (progn (ekg-note-delete note))))
(while (consp --cl-var--) (setq id (car --cl-var--)) (let ((note (ekg-get-note-with-id id))) (if (or (not (ekg-has-live-tags-p id)) (string= (string-trim (let* ((cl-x note)) (progn (or (let* ((cl-x cl-x)) (progn (and (memq (type-of cl-x) cl-struct-ekg-note-tags) t))) (signal 'wrong-type-argument (list 'ekg-note cl-x))) (aref cl-x 2)))) "*")) (progn (ekg-note-delete note)))) (setq --cl-var-- (cdr --cl-var--)))
(let* ((--cl-var-- (triples-subjects-of-type ekg-db 'text)) (id nil)) (while (consp --cl-var--) (setq id (car --cl-var--)) (let ((note (ekg-get-note-with-id id))) (if (or (not (ekg-has-live-tags-p id)) (string= (string-trim (let* ((cl-x note)) (progn (or (let* ((cl-x cl-x)) (progn (and (memq (type-of cl-x) cl-struct-ekg-note-tags) t))) (signal 'wrong-type-argument (list 'ekg-note cl-x))) (aref cl-x 2)))) "*")) (progn (ekg-note-delete note)))) (setq --cl-var-- (cdr --cl-var--))) nil)
ekg-clean-db()
funcall-interactively(ekg-clean-db)
command-execute(ekg-clean-db record)
execute-extended-command(nil "ekg-clean-db" nil)
funcall-interactively(execute-extended-command nil "ekg-clean-db" nil)
command-execute(execute-extended-command)
I am not sure if this will only happen to my buggy database.:-(
But it may relate to string-trim
string-trim-left
string-trim-right
that they dont accept nil
?
The criminal s-exps:
(string= (string-trim (ekg-note-text note))
"*")
Extracted from ekg-clean-db
:
(defun ekg-clean-db ()
"Clean all useless or malformed data from the database.
Some of this are tags which have no uses, which we consider
useless. This will always make a backup, regardless of backup
settings, and will not delete any backups, regardless of other
settings.
In general, this isn't necessary to run, but it may help if you
have a lot of tags that you no longer use, or feel like your
database is bigger than it should be.
Specifically, this does a few things:
1) Calls `ekg-remove-unused-tags' to remove all tags that no note is using.
2) Remove any notes that have no content or almost no content."
(interactive)
(ekg-connect)
(triples-backup ekg-db ekg-db-file most-positive-fixnum)
(ekg-remove-unused-tags)
(cl-loop for id in (triples-subjects-of-type ekg-db 'text) do
(let ((note (ekg-get-note-with-id id)))
(when (or (not (ekg-has-live-tags-p id))
(string= (string-trim (ekg-note-text note))
"*"))
(ekg-note-delete note)))))
Zheng
When I run M-x ekg-notes-tag, I get the error message:
ewoc-locate: Wrong type argument: ewoc, nil
This is on GNU Emacs 28.2 (build 2, x86_64-w64-mingw32) of 2022-09-13, using the latest ekg downloaded today with the use-package statment in the ekg README.
Running Ekg from main branch on my windows home laptop. Trying to export with ekg-logseq-export
generates following error:
Property logseq/last-export not found in schema
This means that the exports are not incremental. Although export seems to work fine.
Hello! Interesting project!
Have you considered adding a custom org link type for linking to ekg notes (or tag buffers)?
Joseph
Hello,
I'm giving ekg
a test. However, I'm getting an error when calling (ekg-org-roam-import)
:
Debugger entered--Lisp error: (void-function ekg-org-roam-import-tags-from-links)
ekg-org-roam-import-tags-from-links()
ekg-org-roam-import()
I had ekg up and running on a previous init.el file, and then decided to "start fresh". Clean ubuntu install (on wsl2) clean build of emacs 29.1. Trying to open ekg gives a "Searching for program: No such file or directory, sqlite3" error. Everything was installed via straight.el from the get go.
I'm able to open the triples.db file via "sqlite-mode-open-file", which is extra odd.
I'm not even sure where this is stemming from in the EKG package. Any thoughts?
I thought you'd be interested to know that, when I start up emacs, I see this in the async-native-compile-log
buffer
Compiling c:/users/scott/.emacs.d/straight/build/ekg/ekg.el...
In end of data:
ekg.el:860:12: Warning: the function `delete-line' is not known to be defined.
c:/users/scott/.emacs.d/straight/build/ekg/ekg.el: Error: Invalid function #
Compilation finished.
I haven't noticed that this causes a problem, though. This is on emacs 28.2.
Hi,
Wondered if to import from org-roam has to be in v2 format or if v1 is compaticle as well?
Cheers!
Hello @ahyatt ,
https://github.com/ahyatt/ekg/blob/7811c17453fc6f59f16bb5fa04af06ebee50e00e/ekg.el#L1239C20-L1239C52
I don't think we need to use `ekg--split-metadata-string' for the title, or the title containing "," will be split.
(defun ekg--metadata-update-title (val)
"Update the title field from the metadata VAL."
(setf (ekg-note-properties ekg-note)
(plist-put (ekg-note-properties ekg-note) :titled/title
(ekg--split-metadata-string val))))
Say, I have a note with title: "This is a test title, with a comma", after save I got this in db:
(Note: the title is split into 2 parts)
sqlite> select * from triples where subject is 33419178152;
subject predicate object properties
----------- -------------------------- ---------------------- ----------
33419178152 base/type tagged ()
33419178152 base/type text ()
33419178152 base/type time-tracked ()
33419178152 base/type titled ()
33419178152 tagged/tag "date/2023-10-22" (:index 0)
33419178152 tagged/tag "test" (:index 1)
33419178152 text/mode org-mode ()
33419178152 text/text "" ()
33419178152 time-tracked/creation-time 1697905670 ()
33419178152 time-tracked/modified-time 1697905779 ()
33419178152 titled/title "This is a test title" (:index 0)
33419178152 titled/title "with a comma" (:index 1)
sqlite>
Note that titled/title is split into 2 parts.
In this case, keep the original val is okay:
(defun ekg--metadata-update-title (val)
"Update the title field from the metadata VAL."
(setf (ekg-note-properties ekg-note)
(plist-put (ekg-note-properties ekg-note) :titled/title
;;; (ekg--split-metadata-string val) ; <-- comment out
(list val) ; <-- change to this
)))
What do you think?
Is it possible to export notes from database to other file formats, such as ord or md?
Got following error when trying to run ekg-capture-url
command after installation.
cl-no-applicable-method: No applicable method: emacsql, nil, [:select [object] :from triples :where (= predicate $s2) :and (= subject $s1)], "https://github.com/LGUG2Z/komorebi", base/type
Trying to debug it more, but any guidance is helpful. I do have org-roam-v2 working fine on this windows emacs. This is standalone windows emacs downloaded from https://www.gnu.org/software/emacs/download.html#nonfree
. Windows is one of my machine which I use at home and share with family members.
The ekg
package does not say about not working with windows, so I am hoping that it should work, especially since org-roam
v2 is working which also relies on sqlite
.
The installation compile-log does show some warnings though
Leaving directory ‘c:/Users/jayra/AppData/Roaming/.emacs.d/elpa/triples-0.2.6’
Compiling file c:/Users/jayra/AppData/Roaming/.emacs.d/elpa/triples-0.2.6/triples-backups-test.el at Sun Apr 9 20:02:11 2023
Entering directory ‘c:/Users/jayra/AppData/Roaming/.emacs.d/elpa/triples-0.2.6/’
Compiling file c:/Users/jayra/AppData/Roaming/.emacs.d/elpa/triples-0.2.6/triples-backups.el at Sun Apr 9 20:02:12 2023
Compiling file c:/Users/jayra/AppData/Roaming/.emacs.d/elpa/triples-0.2.6/triples-test.el at Sun Apr 9 20:02:12 2023
In end of data:
triples-test.el:245:19: Warning: the function ‘kvplist->alist’ is not known to
be defined.
triples-test.el:245:5: Warning: the function ‘kvalist-sort’ is not known to be
defined.
triples-test.el:244:4: Warning: the function ‘kvalist->plist’ is not known to
be defined.
triples-test.el:86:23: Warning: the function ‘sqlite-select’ is not known to
be defined.
Compiling file c:/Users/jayra/AppData/Roaming/.emacs.d/elpa/triples-0.2.6/triples.el at Sun Apr 9 20:02:12 2023
In triples-backup:
triples.el:98:36: Warning: Unused lexical argument `db'
In end of data:
triples.el:322:16: Warning: the function ‘sqlite-rollback’ is not known to be
defined.
triples.el:321:13: Warning: the function ‘sqlite-commit’ is not known to be
defined.
triples.el:316:13: Warning: the function ‘sqlite-transaction’ is not known to
be defined.
triples.el:249:12: Warning: the function ‘sqlite-select’ is not known to be
defined.
triples.el:95:16: Warning: the function ‘sqlite-close’ is not known to be
defined.
triples.el:68:20: Warning: the function ‘sqlite-execute’ is not known to be
defined.
triples.el:67:29: Warning: the function ‘sqlite-open’ is not known to be
defined.
Compiling internal form(s) at Sun Apr 9 20:02:17 2023
Leaving directory ‘c:/Users/jayra/AppData/Roaming/.emacs.d/elpa/ekg-20230304.619’
Compiling file c:/Users/jayra/AppData/Roaming/.emacs.d/elpa/ekg-20230304.619/ekg-embedding.el at Sun Apr 9 20:02:17 2023
Entering directory ‘c:/Users/jayra/AppData/Roaming/.emacs.d/elpa/ekg-20230304.619/’
ekg-embedding.el:32:1: Error: Cannot open load file: No such file or directory, request
Compiling file c:/Users/jayra/AppData/Roaming/.emacs.d/elpa/ekg-20230304.619/ekg-org-roam.el at Sun Apr 9 20:02:17 2023
Compiling file c:/Users/jayra/AppData/Roaming/.emacs.d/elpa/ekg-20230304.619/ekg.el at Sun Apr 9 20:02:17 2023
Emacs version is 28.2
as seen with emacs-version
command.
I'm getting this error when trying the setup instructions from the README:
Debugger entered--Lisp error: (error "Could not find package triples\\.el. Updating recip...")
error("Could not find package %S. Updating recipe reposit..." triples\.el (org-elpa melpa gnu-elpa-mirror nongnu-elpa el-get emacsmirror-mirror))
I think it's because of this line https://github.com/ahyatt/ekg/blob/main/ekg.el#L7. For triples we have (provide triples)
not (provide triples.el)
: https://github.com/ahyatt/triples/blob/main/triples.el#L628
It looks ekg-edit-display-tags
was removed in commit 45e92d9, but there are still some occurances left in the code, specifically in ekg-capture-edit-add-tags
and ekg-capture-edit-remove-tags
.
The (company-capf)
command seems to error out when running in the ekg-capture buffer.
Following is the error backtrace I receive when this error happens:
Debugger entered--Lisp error: (args-out-of-range #<buffer *EKG Capture (note 33775668291)*> 26 25)
company-capf(prefix)
apply(company-capf prefix)
company-call-backend-raw(prefix)
company--force-sync(company-call-backend-raw (prefix) company-capf)
company-call-backend(prefix)
company--begin-new()
company--perform()
company-auto-begin()
The issue seems to be with the ekg--transclude-titled-note-completion
function when the ">t" is not present in the line.
Having a minor check to validate the value of begin
fixes it in my case.
Here's the updated function I use:
(defun ekg--transclude-titled-note-completion ()
"Completion function for file transclusion."
(let ((begin (save-excursion
(search-backward ">t" (line-beginning-position) t)
(+ 1 (point))))
(end (point)))
(unless (> begin end)
(list begin end
(completion-table-dynamic (lambda (_)
(mapcar (lambda (title-cons)
(cons (cdr title-cons)
(car title-cons)))
(ekg-document-titles))))
:exclusive t :exit-function #'ekg--transclude-cap-exit))))
My emacs is built from the master branch on 11th May 2023 and I'm using the latest stable ekg version (v0.3.3)
I got my OpenAI key set up and installed, and I have a few notes in my db. When I try to search with M-x ekg-embedding-search
, I get this error:
Debugger entered--Lisp error: (error "End index out of bounds: 20")
error("End index out of bounds: %s" 20)
seq-subseq(nil 0 20)
cl-subseq(nil 0 20)
(mapcar #'car (cl-subseq embeddings 0 n))
(let* ((embeddings (ekg-embedding-get-all-notes))) (setq embeddings (cdr (sort (mapcar #'(lambda (id-embedding) (cons ... ...)) embeddings) #'(lambda (a b) (> (cdr a) (cdr b)))))) (mapcar #'car (cl-subseq embeddings 0 n)))
ekg-embedding-n-most-similar-notes([-0.024622397 -0.022308419 0.017777454 -0.025162788 -0.019897448 0.013308843 -0.017292488 -0.011382836 0.0035056088 -0.011126497 0.023236781 -0.002326103 0.008847158 -0.007413046 -0.03294995 0.0071082106 0.041568484 0.0007837391 0.005265341 -0.016183997 0.021754174 0.016849091 0.003356655 -0.033476483 -0.017694317 0.0071497792 -0.010011076 -0.010392121 -0.025578473 -0.011064145 0.031120937 -0.008306769 -0.011479829 -0.02566161 -0.010932511 -0.0011093589 -0.0023797955 -0.009657744 0.027795458 -0.0068587996 0.031287212 0.00155189 -0.0058854045 0.009387549 0.0020403196 0.007814875 0.003519465 -0.0045101806 -0.016447263 0.0016566772 ...] 20)
(mapcar #'ekg-get-note-with-id (ekg-embedding-n-most-similar-notes (ekg-embedding text) ekg-notes-size))
(closure ((text . "emacs")) nil (mapcar #'ekg-get-note-with-id (ekg-embedding-n-most-similar-notes (ekg-embedding text) ekg-notes-size)))()
funcall((closure ((text . "emacs")) nil (mapcar #'ekg-get-note-with-id (ekg-embedding-n-most-similar-notes (ekg-embedding text) ekg-notes-size))))
(mapc #'(lambda (note) (ewoc-enter-last ewoc note)) (funcall notes-func))
(let ((ewoc (ewoc-create #'ekg-display-note-insert (propertize name 'face 'ekg-notes-mode-title)))) (mapc #'(lambda (note) (ewoc-enter-last ewoc note)) (funcall notes-func)) (ekg-notes-mode) (progn (set (make-local-variable 'ekg-notes-ewoc) ewoc) (set (make-local-variable 'ekg-notes-fetch-notes-function) notes-func) (set (make-local-variable 'ekg-notes-name) name) (set (make-local-variable 'ekg-notes-hl) (make-overlay 1 1)) (set (make-local-variable 'ekg-notes-tags) tags)) (overlay-put ekg-notes-hl 'face hl-line-face) (forward-line 1) (ekg--note-highlight))
ekg--show-notes("similar to \"emacs\"" (closure ((text . "emacs")) nil (mapcar #'ekg-get-note-with-id (ekg-embedding-n-most-similar-notes (ekg-embedding text) ekg-notes-size))) nil)
(let ((buf (get-buffer-create (format "*ekg %s*" name)))) (set-buffer buf) (ekg--show-notes name notes-func tags) (pop-to-buffer buf))
ekg-setup-notes-buffer("similar to \"emacs\"" (closure ((text . "emacs")) nil (mapcar #'ekg-get-note-with-id (ekg-embedding-n-most-similar-notes (ekg-embedding text) ekg-notes-size))) nil)
ekg-embedding-search("emacs")
Function ekg-removed-unused-tags
or ekg-clean-db
are not removing unused tags after enabling embeddings for me as the embedding exists for the tag. Here is an example movie tag in my ekg notes without any content.
(ekg-tag-used-p "movie")
(:embedding/embedding [-0.020355815 -0.01797225325 -0.0040594423 -0.0263608985 -0.011576385500000001 0.0018720287000000002 -0.011875542749999999 -0.00552889865 -0.00202579035 -0.011079416 0.008641760750000001 0.0342880615 ...])
Another tag fire with content shows like this
(ekg-tag-used-p "fire")
(:embedding/embedding [-0.016680656 0.00043758826 -0.022189735 0.0046408763 0.0002680446 -0.0012848707 -0.032357123 2.224171e-05 -0.004212005 -0.0044804853 0.011297099 0.011150655 ...] :tag/tagged ("283102e8-b0a8-486f-ab8f-614dee337fd3"))
So the embedding shall be deleted when there are no notes for the tag, but I am not sure how to do that.
(defun ekg-remove-unused-tags ()
"Remove all tags that are not used and have no info."
(cl-loop for tag in (seq-filter (lambda (tag) (not (ekg-tag-used-p tag))) (ekg-tags))
do
(ekg-tag-delete tag)))
Code which has the problem
emacs-knowledge-graph could be a better name.
Hello,
I'd like to use a different separator between the metadata and the text from the more practical but arguably less eye pleasing --text follows this line--
to something like a proper line separator. Doing this right now from outside the package is quite cumbersome. I'd request that this string be extracted out as a defvar
that can be edited by the user, so the process becomes easier. I'd suggest the following change and can raise a pull request if you're open to it.
@@ -161,6 +161,9 @@
"Alist of properties that can be on the note and their labels.
The label needs to match the keys in the `ekg-metadata-parsers' alist.")
+(defvar ekg-metadata-separator-text "--text follows this line--"
+ "Separator between the metadata of the note and the note text")
+
(defvar ekg-add-schema-hook nil
"Hook run when ensuring schema for ekg.
This is run on connection. Calls to `triples-add-schema' are
@@ -893,7 +896,7 @@
(goto-char (overlay-end o))
(insert "\n")
(move-overlay o (point-min) (- (overlay-end o) 1))
- (overlay-put o 'after-string (propertize "--text follows this line--\n"
+ (overlay-put o 'after-string (propertize (concat ekg-metadata-separator-text "\n")
'read-only t 'rear-nonsticky t))
(overlay-put o 'category 'ekg-metadata)
(overlay-put o 'modification-hooks '(ekg--metadata-modification))
PS: In my config I use a group of "━" characters to create the visual effect of the line. I plan to later calculate the number of such characters based on the window width as is done in org agenda.
Regards.
I find ekg's reusable tags as title replacement of notes very intriguing.
However, what bothers me quite a bit is its complete (?) sepatation of the org ecosystem, specifically that I should not be able to link from a standard org file to a specific ekg note or at least to a set of notes sharing a set of common tags. (Please correct me, if this is possible already.)
I have the following use case for such a feature:
The other way round: linking from an ekg note to some org id might also be useful - but here I am lacking a concrete use case.
Or perhaps this is doable somehow already?
Best,
Hanspeter
I started use ekg from 2 days, it is awesome to make notes as you read feeds articles books.
But in my mind it will take a boost to make notes on codebase and as you read codebase and understand what the component of the system and you jump between modules you could read all notes about system or read specific note it will boost as you work on os project or a freelancer need to change on system but need a map on some component how it works.
This just feature request and I am in love with this project and it is really workflow boost.
First, thank you so much for this great package, I really like the logic of it and start to get used to it. Especially the new templating system is very convenient.
I have a question, not really an issue. I am not sure where to ask it, I hope you don't mind posting it here.
Is it that there is no option to search for prefixes only? In the example you use with "person/" - given, that address data is in those notes, how would you search for the person living in Rome (especially if no other tag is used)?
Maybe I miss something, but my approach to finding information among the notes is right now: I narrow the search with the broadest tag I know of and then use emacs tools (helm) for searching within the ekg-notes. Prefixes are great for categorizing notes, but how do I find information in the category if that information is not behind the slash?
Also, is there a way to show all notes in an ekg-notes buffer (something like ekg-all-notes)?
Just now found the "discussions". Should I close this issue and post it there?
I have been trying Emacs 29 recently and found out that when I created a note in Emacs 29, I can't view it in Emacs 28 (with ekg-show-notes-with-tag
, for example) and vice versa.
This is what the notes look like in the SQLbrowser when created in different versions of Emacs. Please, note the right column:
Perhaps, built-in support for SQLite in 29th version has an effect here?
Info: develop branch of EKG, GNU Emacs 29.0.90 (build 3, x86_64-w64-mingw32) and GNU Emacs 28.1 (build 2, x86_64-w64-mingw32)
Hello!
This is my first dive into this kind of note taking in Emacs and I find this way to organizing notes to be very interesting.
Starting off, I'm not quite sure about manipulating the tag(s) of a new note. So C-c e
is what I've bound ekg-capture
to. A new note buffer opens and I go to the top where Tags:
under the note properties are. I change it to something like ekg
. I write the note, C-c C-c
to save it. I run M-x ekg-show-notes-with-tag
, there are no notes with the tag ekg
only with the date/[today's-date]
. I can obviously open that note and change the tag with ekg-rename-tag
, but I shouldn't have to do that.
I start a new note, I want the tag be work/some_project_im_on
. I try ekg-rename-tag
in that new note buffer, set it to the new tag, add the note and save. M-x ekg-show-notes-with-tag
and there's no note with the tag work/some_project_im_on
.
Maybe there's a step I'm missing but I assumed the first attempt is what you're supposed to do and from the manual I think I'm not wrong in my assumption.
Thanks, and have a great day!
Here's a transient menu I created for EKG. I'm still very new to it so not sure about any of the commands or bindings, but for the basics it seems to work for me.
(defun setup-ekg-transients () "Set up Transient menus for EKG"
(transient-define-prefix ekg-dispatch ()
"Top level Transient menu for EKG (Emacs Knowledge Graph)"
[["Show"
("st" "Today" ekg-show-notes-for-today)
("slc" "Latest Captured" ekg-show-notes-latest-captured)
("slm" "Latest Mod" ekg-show-notes-latest-modified)
("sx" "Trash" ekg-show-notes-in-trash)
("sd" "Drafts" ekg-show-notes-in-drafts)
"Find Tags"
("tt" "Tag" ekg-show-notes-with-tag)
("taa" "All Tags" ekg-show-notes-with-all-tags)
("ta?" "Any Tag" ekg-show-notes-with-any-tags)
]
["Capture"
("cc" "New Note" ekg-capture)
("cu" "...from URL" ekg-capture-url)
("cb" "...from current buffer" ekg-capture-file)
("ct" "...with tags from buffer" ekg-notes-create)
]
["Query"
("qt" "for terms" ekg-embedding-search)
("qs" "similar to current note" ekg-embedding-show-similar)
("qb" "similar to current buffer" ekg-embedding-show-similar-to-current-buffer)
("qR" "Regenerate embeddings" ekg-embedding-generate-all)
"AI"
("aa" "AI send & append" ekg-llm-send-and-append-note)
("ar" "AI send & replace" ekg-llm-send-and-replace-note)
("aq" "AI query, all notes" ekg-llm-query-with-notes)
]
["Misc"
"Browse"
("bb" "Browse to topic" ekg-notes-browse)
("bu" "Browse to URL" ekg-browse-url)
("bs" "Select & browse to URL" ekg-notes-select-and-browse-url)
"Other"
("gr" "Global rename tag" ekg-global-rename-tag)
("cm" "Change mode of current note" ekg-change-mode)
("e" "This note ..." ekg-notes-dispatch :if-mode ekg-notes-mode)
("q" "Quit this menu" transient-quit-one)
]
])
(global-set-key (kbd "<f6>") 'ekg-dispatch)
(global-set-key (kbd "C-c e") 'ekg-dispatch)
(transient-define-prefix ekg-notes-dispatch ()
"Notes buffer Transient menu for EKG (Emacs Knowledge Graph)"
[["Show Notes"
("a" "with any of this note's tags" ekg-notes-any-note-tags)
("A" "with any of these notes' tags" ekg-notes-any-tags)
("t" "select tag" ekg-notes-tag)
("s" "search for similar" ekg-embedding-show-similar)
]
["Manage"
("c" "create" ekg-notes-create)
("d" "delete" ekg-notes-delete)
("g" "refresh" ekg-notes-refresh)
("k" "kill (hide) note" ekg-notes-kill)
("Q" "quit EKG" kill-buffer-and-window)
("o" "open/edit" ekg-notes-open)
("q" "quit this menu" transient-quit-one)
]
["Browse"
("b" "browse resource" ekg-notes-browse)
("B" "select & browse" ekg-notes-select-and-browse-url)
]
])
(define-key ekg-notes-mode-map (kbd "e") 'ekg-notes-dispatch)
(define-key ekg-notes-mode-map (kbd "?") 'ekg-notes-dispatch) ; help when I'm confused
(define-key ekg-notes-mode-map (kbd "q") 'kill-buffer-and-window) ; I prefer this
)
Invoke this after ekg is loaded (e.g. in the :config
section of use-package
). You can see I set up ?
in the notes mode to bring up a notes-related transient menu, and the main one (which I bind to C-c e
and <f6>
) has what I took to be the main global ekg commands.
Here's what the main one looks like for me:
Feel free to include this in ekg; I put this code in the public domain.
Hi,
I just discovered this package with emacs conf and I find it great.
But when I install it here is the error that appears
Symbol's function definition is void: defvar-keymap
Config:
Emacs 28.2
Archlinux
It takes place in notes buffer, after M-x ekg-capture
and after pressing C-c C-c
Hi,
Trying a fresh install of ekg, I start Emacs 29.1 Mac (with-native-comp) with no .emacs.d directory and with .emacs file with this minimal content
(require 'package)
;;(add-to-list 'package-archives '("melpa" . "https://melpa.org/packages/") t)
;; Comment/uncomment this line to enable MELPA Stable if desired. See `package-archive-priorities`
;; and `package-pinned-packages`. Most users will not need or want to do this.
(add-to-list 'package-archives '("melpa-stable" . "https://stable.melpa.org/packages/") t)
(package-initialize)
(use-package ekg
:ensure t)
I get the following warnings and errors
Warning (bytecomp): the function ‘kvplist->alist’ is not known to be defined.
Warning (bytecomp): the function ‘kvalist-sort’ is not known to be defined.
Warning (bytecomp): the function ‘kvalist->plist’ is not known to be defined.
Warning (bytecomp): Unused lexical argument `db'
Warning (bytecomp): reference to free variable ‘emacsql-sqlite-executable’
Warning (bytecomp): the function ‘emacsql-with-transaction’ is not known to be defined.
Warning (bytecomp): the function ‘emacsql-close’ is not known to be defined.
Warning (bytecomp): the function ‘emacsql’ is not known to be defined.
Warning (bytecomp): the function ‘emacsql-sqlite’ is not known to be defined.
Error (bytecomp): Cannot open load file: No such file or directory, request
⛔ Warning (comp): triples.el:138:24: Warning: Unused lexical argument `db'
⛔ Warning (comp): triples.el:157:31: Warning: reference to free variable `emacsql-sqlite-executable'
⛔ Warning (comp): triples.el:521:11: Warning: macro `triples-with-transaction' defined too late
⛔ Warning (comp): triples.el:400:7: Warning: the function `emacsql-with-transaction' is not known to be defined.
⛔ Warning (comp): triples.el:136:16: Warning: the function `emacsql-close' is not known to be defined.
⛔ Warning (comp): triples.el:108:17: Warning: the function `emacsql' is not known to be defined.
⛔ Warning (comp): triples.el:106:20: Warning: the function `emacsql-sqlite' is not known to be defined.
⛔ Warning (comp): /Users/user00/.emacs.d/elpa/ekg-0.3.2/ekg.el: Error: Invalid function #<subr or>
And no ekg.eln native compiled file.
If I call ekg-capture (without restarting Emacs), I get
triples-rebuild-builtin-database: Invalid function: triples-with-transaction
I restart Emacs, I get
⛔ Warning (comp): /Users/rbenit68/.emacs.d/elpa/ekg-0.3.2/ekg.el: Error: Invalid function #<subr or>
I call ekg-capture, I get
Invalid function: triples-with-transaction
I delete in turn ekg*.elc, triples*.eln, and triples.elc
I can use ekg only when triples*.eln, triples*.elc and ekg.elc are deleted.
Thank you for ekg!
Line 1463 in 1b91bca
ekg-rename-tag
here should be changed to ekg-global-rename-tag
too?
I just did an sql export of your default ekg.db and I see many initial entries in the triples table. I assume you're deep in the lore of some RDF/triple strategy (beyond SQL) with all these
INSERT INTO "triples" VALUES ('tagged','base/type','schema','(:t t)');
INSERT INTO "triples" VALUES ('tagged','schema/property','tag','(:t t)');
INSERT INTO "triples" VALUES ('tagged/tag','base/type','string','(:t t)');
INSERT INTO "triples" VALUES ('text','base/type','schema','(:t t)');
INSERT INTO "triples" VALUES ('text','schema/property','text','(:t t)');
INSERT INTO "triples" VALUES ('text/text','base/unique','t','(:t t)');
...
and then came my single test entry at the end
INSERT INTO "triples" VALUES ('"mytesttag"','base/type','tag','(:t t)');
Could you explain the theory behind what you've done here? What are these triples? What is their purpose? My understanding of RDF/triples is more conventional, e.g., from John Kitchin
(setq triples '((Bob mother Ann)
(Bill father Bob)
(Lucy mother Jane)
(Bob wife Jane)))
and typically (at least) the predicate is referenced by a foaf or some standard. So could you please give some of the lore behind your schema here or point me to some background info for your specific strategy here?
Hi,
I'm getting the following error when calling ekg-org-roam
:
Debugger entered--Lisp error: (void-function string-equal-ignore-case)
I have some ekg notes with no content/text. They are generally pointing to a URL and I am using ekg notes to bookmark them. Since such notes has no text substring-no-properties
generate the args-out-of-range error. Here is the error and the stack trace.
Debugger entered--Lisp error: (args-out-of-range "" 0 10)
substring-no-properties("" 0 10)
(format "%s..." (substring-no-properties ...
ekg-note-snippet(....)
Trying to run ekg-embedding-generate-all
, results in following error
[error] request--callback: peculiar error: 400
error in process sentinel: let*: Problem calling Open AI: (http 400). type: invalid_request_error message: This model’s maximum context length is 8191 tokens, however you requested 61535 tokens (61535 in your prompt; 0 for the completion). Please reduce your prompt; or completion length.
I realized it is some note which has a lot of words. But how do I find that note? Is there an SQL query which I can run to find the note. I also have note export to logseq, so I can scan that as well.
I see that naming of functions is not for me personally intuitive.
M-x ekg-show-tag
is supposed to find notes, is it?
I can't see how "show" is intuitive. To make it intuitive, I would understand better something like:
ekg-show-notes-by-tag
Comment on general structure, no, I can't agree, it is way too simple, and not enough structured. I understand the concept how you started designing it.
In your concept, you are using database power only slightly. As database is "organized, structured", I would propose that you use "structured" and "organized" a bit more.
Your database triples has been designed basically properly, but it is the only table. There is little to reference, little organized, you are giving yourself way too much work, as many things that are supposed to be handled by SQL database, you will need to handle by hand!
For example, you are hard coding predicates, and user has no option to change it on high level, or add new predicates.
Imagine if predicates are in the table:
Table "public.predicates"
┌────────────────────────┬─────────┬───────────┬──────────┬───────────────────────────────────────────────────┐
│ Column │ Type │ Collation │ Nullable │ Default │
├────────────────────────┼─────────┼───────────┼──────────┼───────────────────────────────────────────────────┤
│ predicates_id │ integer │ │ not null │ nextval('predicates_predicates_id_seq'::regclass) │
│ predicates_name │ text │ │ │ │
│ predicates_description │ text │ │ │ │
└────────────────────────┴─────────┴───────────┴──────────┴───────────────────────────────────────────────────┘
Indexes:
"predicates_pkey" PRIMARY KEY, btree (predicates_id)
"predicates_index" UNIQUE, btree (predicates_name)
Referenced by:
TABLE "oid2oid" CONSTRAINT "oid2oid_oid2oid_predicates_fkey" FOREIGN KEY (oid2oid_predicates) REFERENCES predicates(predicates_id)
TABLE "semantictriplets" CONSTRAINT "semantictriplets_semantictriplets_predicates_fkey" FOREIGN KEY (semantictriplets_predicates) REFERENCES predicates(predicates_id)
And predicates shall start with those very rudimentary:
1 is
2 knows
3 registered
etc.
Thus I am suggesting, and of course not at all protesting if not implemented, that things like predicates, objects, and subjects in semantic triplets get their own tables.
Table "public.semantictriplets"
┌──────────────────────────────────────────┬──────────────────────────┬───────────┬──────────┬───────────────────────────────────────────────────────────────┐
│ Column │ Type │ Collation │ Nullable │ Default │
├──────────────────────────────────────────┼──────────────────────────┼───────────┼──────────┼───────────────────────────────────────────────────────────────┤
│ semantictriplets_id │ integer │ │ not null │ nextval('semantictriplets_semantictriplets_id_seq'::regclass) │
│ semantictriplets_datecreated │ timestamp with time zone │ │ not null │ CURRENT_TIMESTAMP │
│ semantictriplets_datemodified │ timestamp with time zone │ │ │ │
│ semantictriplets_usercreated │ text │ │ not null │ CURRENT_USER │
│ semantictriplets_usermodified │ text │ │ not null │ CURRENT_USER │
│ semantictriplets_subjecttriplet │ integer │ │ │ │
│ semantictriplets_subjecttable │ text │ │ │ │
│ semantictriplets_subjectcolumn │ text │ │ │ │
│ semantictriplets_subjectid │ integer │ │ │ │
│ semantictriplets_predicates │ integer │ │ not null │ │
│ semantictriplets_objecttriplet │ integer │ │ │ │
│ semantictriplets_objecttable │ text │ │ │ │
│ semantictriplets_objectcolumn │ text │ │ │ │
│ semantictriplets_objectid │ integer │ │ │ │
│ semantictriplets_description │ text │ │ │ │
│ semantictriplets_functiononsubjectcolumn │ text │ │ │ │
│ semantictriplets_functiononobjectcolumn │ text │ │ │ │
└──────────────────────────────────────────┴──────────────────────────┴───────────┴──────────┴───────────────────────────────────────────────────────────────┘
Indexes:
"semantictriplets_pkey" PRIMARY KEY, btree (semantictriplets_id)
"semantictriplets_index" UNIQUE, btree (semantictriplets_subjecttable, semantictriplets_predicates, semantictriplets_objecttable)
Foreign-key constraints:
"semantictriplets_semantictriplets_objecttriplet_fkey" FOREIGN KEY (semantictriplets_objecttriplet) REFERENCES semantictriplets(semantictriplets_id)
"semantictriplets_semantictriplets_predicates_fkey" FOREIGN KEY (semantictriplets_predicates) REFERENCES predicates(predicates_id)
"semantictriplets_semantictriplets_subjecttriplet_fkey" FOREIGN KEY (semantictriplets_subjecttriplet) REFERENCES semantictriplets(semantictriplets_id)
Referenced by:
TABLE "semantictriplets" CONSTRAINT "semantictriplets_semantictriplets_objecttriplet_fkey" FOREIGN KEY (semantictriplets_objecttriplet) REFERENCES semantictriplets(semantictriplets_id)
TABLE "semantictriplets" CONSTRAINT "semantictriplets_semantictriplets_subjecttriplet_fkey" FOREIGN KEY (semantictriplets_subjecttriplet) REFERENCES semantictriplets(semantictriplets_id)
Triggers:
insert_username_semantictriplets BEFORE INSERT OR UPDATE ON semantictriplets FOR EACH ROW EXECUTE FUNCTION insert_username('semantictriplets_usermodified')
semantictriplets_moddatetime BEFORE UPDATE ON semantictriplets FOR EACH ROW EXECUTE FUNCTION moddatetime('semantictriplets_datemodified')
As you can see each entry would have it's date created
and date modified
and that alone helps you with dates. Multiple users may collaborate and access the database. That is matter of the type of database.
In this particular design, which good purpose is brainstorming, subject is referencing the table, and ID, it can also reference triplet itself, and that means any other tables in the database may be placed in the semantic triplets table. Object is referencing other table or triplet, with the ID number.
Here is very practical example of the entry:
ID 1
Date created "2022-07-06 19:44:02.244679+03"
Date modified "2022-07-06 20:00:08.638015+03"
User created "maddox"
User modified "maddox"
Subject triplet nil
Subject table "people"
Subject column "people_id"
Subject ID 316178
Predicates "registered"
Object triplet nil
Object table "commlines"
Object column "commlines_value"
Object ID 29936
Description "James registered this Lycamobile SIM card."
Function on subject column "get_full_contacts_name"
Function on object column nil
Subject is table people
with column people_id
and ID 316178, which is "James", there is date created, modified, user, predicate is "registered" and there is table commlines
with commlines_value
as subject, the real commline value is phone number, but such need not be specified in the database. The ID for phone number is 29936, there is description, and PostgreSQL (could be Emacs) function to run on columns. As some columns shall be represented in different ways.
Then the object would be represented as:
James registered phone number +256726295314 belonging to Lyca Mobile
Words "phone number" and "belonging to Lyca Mobile" would be constructed by function. as "Lyca" is related to phone, it is not necessarily in the triplet.
Otherwise basic meaning would be:
"James registered commlines_value +256726295314"
I am well aware that everything could be put in semantic triplets. All the database could be placed inside. But that way of doing require liberation of user for user to decide what is subject, predicate, object.
Then you build it, you teach it:
The last 2 above must reference to ID of the triplet like 10, 11.
It is totally possible to have all database based on triplets as foundation.
But with SQL databases, I find that mixture is better.
Personally, I am only brainstorming on that, I did not go implementing
it. But that approach shall leave to user how to define triplets.
If database is generic, then application shall exist which is very
generic, and which allows user to build the new application, by
defining it in triplets.
I recommend reviewing:
OneModel - Record, manage and share any knowledge:
http://onemodel.org/1/e-9223372036854618119.html
That may be the best implementation of semantic triplets in software. I do not know others.
I am aware when it becomes so "basic" that people get confused.
Maybe some basic predicates and objects and subjects, shall be prepared for people.
I see predicates in ekg
like:
and I find those predicates way too high level, if there are no fundamental predicates, "is", "has".
Then I see triplet like:
time-tracked/creation-time base/unique t
or like
33077773945 time-tracked/modified-time 1674284938
or like
33077805874 text/text #("OK fine, my note is here." 0 25 (fontified t))
So yes, you are using triplet database, but in different way how I
would expect it, more high level, and with mixture of a lot of text
instead of relations.
I believe that it is not scalable to keep everything in one database,
as then PostgreSQL or SQLite features can't be efficiently used.
So there are for now 3 discovered types of usage of triplets:
the mixture of maximized freedom of using text, minimized database
structure, which is represented by `ekg', without basic predicates
there is One Model example which asks to define basic predicates
to teach the database, structuring more the objects, subjects,
predicates;
there is my model where table triplets
references other table,
column, by their ID, with predicate to other table column. This
becomes universal "key" among all tables in the database, showing
relations that may not be otherwise defined.
And I also have more universal model that goes beyond. It is UUID to
UUID semantic triplets table:
Table "public.uuid2uuid"
┌─────────────────────────┬──────────────────────────┬───────────┬──────────┬───────────────────┐
│ Column │ Type │ Collation │ Nullable │ Default │
├─────────────────────────┼──────────────────────────┼───────────┼──────────┼───────────────────┤
│ uuid2uuid_uuid │ uuid │ │ not null │ gen_random_uuid() │
│ uuid2uuid_datecreated │ timestamp with time zone │ │ not null │ CURRENT_TIMESTAMP │
│ uuid2uuid_datemodified │ timestamp with time zone │ │ │ │
│ uuid2uuid_usercreated │ text │ │ not null │ CURRENT_USER │
│ uuid2uuid_usermodified │ text │ │ not null │ CURRENT_USER │
│ uuid2uuid_uuidobject │ uuid │ │ not null │ │
│ uuid2uuid_uuidpredicate │ uuid │ │ not null │ │
│ uuid2uuid_uuidsubject │ uuid │ │ not null │ │
│ uuid2uuid_description │ text │ │ │ │
└─────────────────────────┴──────────────────────────┴───────────┴──────────┴───────────────────┘
Indexes:
"uuid2uuid_pkey" PRIMARY KEY, btree (uuid2uuid_uuid)
Objects must have their UUID
If objects do no have their UUID, they can be recorded to get the UUID
Remote UUID may be recorded to have local UUID, which can then
specify what is that remote UUID
Example is recording https://www.example.com as URL in database with
the UUID: 0488c0f0-139a-4173-853d-cdba68b4fa65
There must be functions to find UUID and their representation
Even the UUID predicate is referenced by UUID
Referencing depends of program function, not of database modelling
Adding packages and extensions providing UUID's is easy, it becomes
possible to go acress types of databases
I recommend table design such as: "TABLENAME_uuid", as that way it
becomes easy to fetch list of tables and find their UUID column!
people
tablenotes
tableProgram asks what is note about? People? Which one? James
6b6ea318-dda4-473f-9dcd-f3d79074f569 That becomes subject
Predicate is "has", but "has" also has UUID
(21057255-79fe-44ae-bd52-26df3c56ac8d), it could came from any of
tables, not only predicates
!
Note is created with UUID 144bc138-6470-49f8-8d65-37dd00bd6f6d, it
becomes subject.
New entry is also recorded in uuid2uuid triplets table that:
UUID subject: 6b6ea318-dda4-473f-9dcd-f3d79074f569 (James)
with
UUID predicate: 21057255-79fe-44ae-bd52-26df3c56ac8d (has)
with
UUID object: 144bc138-6470-49f8-8d65-37dd00bd6f6d
then follow various workflows:
Find all notes about James, there are 433
Narrow to those 12 which are related to "mining" (their names need not reflect that word)
Tag with "mining" cefd72ef-b9d7-4ea8-8c5f-d405443d254e
Note subject 144bc138-6470-49f8-8d65-37dd00bd6f6d
has 21057255-79fe-44ae-bd52-26df3c56ac8d
cefd72ef-b9d7-4ea8-8c5f-d405443d254e
Then it becomes possible to tag anything and anything without having
particular database structure.
Some workflow as above. Even all of properties and tags are just
different name for classification, so everything could be stored in
the triplets table with subject few other tables: subjects
,
predicates
and objects
, where each such table could be something
like:
-- ------------------------------------------
-- ------------ Table subjects
-- ------------------------------------------
DROP SEQUENCE subjects_id_seq;
CREATE TABLE subjects (
subjects_id SERIAL NOT NULL PRIMARY KEY,
subjects_uuid UUID NOT NULL DEFAULT gen_random_uuid() UNIQUE,
subjects_datecreated TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL,
subjects_datemodified TIMESTAMP,
subjects_usercreated TEXT NOT NULL DEFAULT current_user,
subjects_usermodified TEXT NOT NULL DEFAULT current_user,
subjects_name TEXT NOT NULL,
subjects_description TEXT
subjects_representation TEXT,
subjects_otheruuid UUID
);
-- Triggers
-- For Date Modified
CREATE TRIGGER subjects_moddatetime
BEFORE UPDATE ON subjects
FOR EACH ROW
EXECUTE PROCEDURE moddatetime(subjects_datemodified);
-- For User Modified
CREATE TRIGGER insert_username_subjects
BEFORE INSERT OR UPDATE ON subjects
FOR EACH ROW
EXECUTE PROCEDURE insert_username(subjects_usermodified);
So 4 tables could do the work:
and instead of triplets referencing primary keys, they can reference
UUID to allow full liberation from database, and allow extensibility.
Tables like subjects
and objects
can have their textual
representation of names. This representation need not go into
triplets
table.
The above structure I find more suitable for generic smart database design.
The killing feature of org mode is the tree view, the design principle of ekg is that heading is like a tag, why not make the tab buffer (ie. buffer created with ekg-show-notes-with-tag or ekg-show-notes-with-all-tags) a org mode like buffer with only headings in this way:
From the ekg documentation:
Because the user may want to modify or create both the text and other database properties at the same time, we use a single buffer that lets the user do both.
But the tag buffer is not editable, for that we have to open the note buffer, in fact the tag buffer only serves as a navigation buffer, the problem is that:
When I run M-x ekg-capture, and then close the buffer with C-c C-c, I get the warning message:
obsolete timestamp with cdr 1
I have some old notes which contains only integer values. I was storing the saving interest credited to my account. I cannot delete/view them. When trying to view them emacs, show them as foreign characters rather than integers. Trying to delete also fails. I can see them in the database
sqlite> SELECT * FROM triples where object LIKE '%340';
33680749501|text/text|340|(:t t)
sqlite> SELECT * FROM triples where object LIKE '%486';
33680744133|text/text|486|(:t t)
Btw, any new notes created with just integer value is saved as string and can be viewed/deleted fine. The problem is with the old notes.
Typo in ekg.el?
Debugger entered--Lisp error: (invalid-read-syntax ")" 236 43) ...
trying to use ekg-tags-including
generates following error
Debugger entered--Lisp error: (wrong-type-argument stringp 1)
string-match("emacs" 1 nil t)
string-match-p("emacs" 1)
(and (not (ekg-tag-trash-p tag)) (string-match-p (regexp-quote substring) tag))
(closure ((substring . "emacs")) (tag) (and (not (ekg-tag-trash-p tag)) (string-match-p (regexp-quote substring) tag)))(1)
#f(compiled-function (elt) #<bytecode 0xa6111d40e37bf16>)(1)
mapcar(#f(compiled-function (elt) #<bytecode 0xa6111d40e37bf16>) ("doc/komorebi github" "lower back pain" "eshani annual event 2023 feb" "imps limit" "ankur loan 2023-01-31" "neft limit" "path variable powershell" "nhvm worksheets download" "bob password reset" "org babel on windows" "python installation" "linkedin hibernate account" "bob password rules" "einsurance account" "links" "notes" "private" "todo" "date/2022-01-09" "date/2023-04-09" "emacs paredit" "adb shell pm list packages" "emacs notmuch - main" "without-mouse" "minimum average balance" "yearly review" "capital gain and loss" "magit" "internet connect" "fire" "edge-flags" "emacs build from source" "smartparens" "windows security memory integrity" "motilal oswal" "google-cli-search" "english-pronuniciation" "aman2" "lisp books" "birthday" "emacs-avy" "utility sink" "blood test" "dates" "bill-pay-mop" "chrome extension - extension shortcut" "emacs iedit" "consult-dir" "emacs-align" "slime" ...))
#f(compiled-function #'sequence #<bytecode 0x1857bf8ef52339f0>)(#f(compiled-function (elt) #<bytecode 0xa6111d40e37bf16>) ("doc/komorebi github" "lower back pain" "eshani annual event 2023 feb" "imps limit" "ankur loan 2023-01-31" "neft limit" "path variable powershell" "nhvm worksheets download" "bob password reset" "org babel on windows" "python installation" "linkedin hibernate account" "bob password rules" "einsurance account" "links" "notes" "private" "todo" "date/2022-01-09" "date/2023-04-09" "emacs paredit" "adb shell pm list packages" "emacs notmuch - main" "without-mouse" "minimum average balance" "yearly review" "capital gain and loss" "magit" "internet connect" "fire" "edge-flags" "emacs build from source" "smartparens" "windows security memory integrity" "motilal oswal" "google-cli-search" "english-pronuniciation" "aman2" "lisp books" "birthday" "emacs-avy" "utility sink" "blood test" "dates" "bill-pay-mop" "chrome extension - extension shortcut" "emacs iedit" "consult-dir" "emacs-align" "slime" ...))
apply(#f(compiled-function #'sequence #<bytecode 0x1857bf8ef52339f0>) #f(compiled-function (elt) #<bytecode 0xa6111d40e37bf16>) ("doc/komorebi github" "lower back pain" "eshani annual event 2023 feb" "imps limit" "ankur loan 2023-01-31" "neft limit" "path variable powershell" "nhvm worksheets download" "bob password reset" "org babel on windows" "python installation" "linkedin hibernate account" "bob password rules" "einsurance account" "links" "notes" "private" "todo" "date/2022-01-09" "date/2023-04-09" "emacs paredit" "adb shell pm list packages" "emacs notmuch - main" "without-mouse" "minimum average balance" "yearly review" "capital gain and loss" "magit" "internet connect" "fire" "edge-flags" "emacs build from source" "smartparens" "windows security memory integrity" "motilal oswal" "google-cli-search" "english-pronuniciation" "aman2" "lisp books" "birthday" "emacs-avy" "utility sink" "blood test" "dates" "bill-pay-mop" "chrome extension - extension shortcut" "emacs iedit" "consult-dir" "emacs-align" "slime" ...) nil)
seq-map(#f(compiled-function (elt) #<bytecode 0xa6111d40e37bf16>) ("doc/komorebi github" "lower back pain" "eshani annual event 2023 feb" "imps limit" "ankur loan 2023-01-31" "neft limit" "path variable powershell" "nhvm worksheets download" "bob password reset" "org babel on windows" "python installation" "linkedin hibernate account" "bob password rules" "einsurance account" "links" "notes" "private" "todo" "date/2022-01-09" "date/2023-04-09" "emacs paredit" "adb shell pm list packages" "emacs notmuch - main" "without-mouse" "minimum average balance" "yearly review" "capital gain and loss" "magit" "internet connect" "fire" "edge-flags" "emacs build from source" "smartparens" "windows security memory integrity" "motilal oswal" "google-cli-search" "english-pronuniciation" "aman2" "lisp books" "birthday" "emacs-avy" "utility sink" "blood test" "dates" "bill-pay-mop" "chrome extension - extension shortcut" "emacs iedit" "consult-dir" "emacs-align" "slime" ...))
seq-filter((closure ((substring . "emacs")) (tag) (and (not (ekg-tag-trash-p tag)) (string-match-p (regexp-quote substring) tag))) ("doc/komorebi github" "lower back pain" "eshani annual event 2023 feb" "imps limit" "ankur loan 2023-01-31" "neft limit" "path variable powershell" "nhvm worksheets download" "bob password reset" "org babel on windows" "python installation" "linkedin hibernate account" "bob password rules" "einsurance account" "links" "notes" "private" "todo" "date/2022-01-09" "date/2023-04-09" "emacs paredit" "adb shell pm list packages" "emacs notmuch - main" "without-mouse" "minimum average balance" "yearly review" "capital gain and loss" "magit" "internet connect" "fire" "edge-flags" "emacs build from source" "smartparens" "windows security memory integrity" "motilal oswal" "google-cli-search" "english-pronuniciation" "aman2" "lisp books" "birthday" "emacs-avy" "utility sink" "blood test" "dates" "bill-pay-mop" "chrome extension - extension shortcut" "emacs iedit" "consult-dir" "emacs-align" "slime" ...))
ekg-tags-including("emacs")
eval-expression((ekg-tags-including "emacs") nil nil 127)
funcall-interactively(eval-expression (ekg-tags-including "emacs") nil nil 127)
command-execute(eval-expression)
Hi @ahyatt
https://github.com/ahyatt/ekg/blob/808921ab6eba83c7b943873265aecb8611dfa24a/ekg.el#LL902C43-L902C43
Since it's in ekg-edit-mode
, would it be better to change "Capture buffer" to "Edit buffer" in header line, to reflect the correct minor mode? 😄 It's been nagging me for a while.
From:
(substitute-command-keys
"\\<ekg-edit-mode-map>Capture buffer. Finish \
`\\[ekg-edit-finalize]'.")
to
(substitute-command-keys
"\\<ekg-edit-mode-map>Edit buffer. Finish \
`\\[ekg-edit-finalize]'.")
Zheng
Edited: add info.
ekg--should-show-id-p: Symbol’s function definition is void: ffap-url-p
I've observed an issue within the ekg-notes buffer where links are not being displayed correctly. Unlike the expected behavior of showing only the link description, the ekg-notes buffer displays both the link itself and its description. You can refer to the attached screenshot for clarification.
Upon inspecting the link displaying logic within org mode, I noticed that org mode uses a folding mechanism for links. However, I encountered difficulty implementing this behavior in a mode derived from fundamental mode. I'm considering exploring the approach used in org-agenda-mode as a potential source of inspiration for solving this issue.
I'm reaching out to inquire if you are familiar with potential workarounds or if there are specific challenges associated with addressing this problem. Any guidance or insights would be greatly appreciated.
Best regards
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.