Giter VIP home page Giter VIP logo

ekg's Introduction

ekg

Quick intro

The ekg module is a simple but opinionated note taking application, for emacs. It is a substitute for such other emacs applications such as org-roam or denote. ekg stands for emacs knowledge graph.

All of ekg’s data is stored in a sqlite database. Notes are organized around tags, and you can view many notes by looking at one or more tags.

./screenshots/ekg-tag-view.jpg

Editing a note from one of these buffers, or capturing a new note, lets you edit both the note’s text content and its metadata (mostly tags you add to it) in a separate buffer.

./screenshots/ekg-edit.jpg

ekg has functionality to have notes created with templates that vary based on the tags you are using. It also supports saving unfinished notes as drafts.

Notes can be on file resources and web resources. You can use notes on file resources to have a stored annotation for files.

ekg supports import from org-roam, and import and export to logseq. It also supports inline commands, which can be used for transclusion and any other functionality you may need.

There is support for attaching Large Language Model (LLM) “embeddings” to notes, for use in search and similarity search, via the llm package. This allows you to search based on semantics, as opposed to text matching. You can also use LLM chat in your notes, getting an LLM to respond to your notes based on a default prompt, or new prompts that you add.

Additionally, a video demonstration and explanation of ekg is on YouTube. This README has only the most basic information, but the full information can be found in the manual, which can be viewed in the Emacs info after installing.

Design of ekg

There are a few core ideas driving the design of ekg. The first is that a title and a tag are the same thing. This isn’t unique to ekg: other tools such as Logseq also consider tags to be equivalent to pages of the same name, although in Logseq this functionality is limited since tags can only be single words. 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. 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. 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.

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 the context, both are denoted by tags.

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.

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. 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.

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.

The benefit of this is that it’s now possible to narrow in on just tags of a certain type if necessary.

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.

Functionality such as templates and LLM prompting can work with prefixed tags, so when looking for templates or prompts for a tag with some hierarchy, ekg will look at the tag hierarchy from the top to the bottom to add any relevant information.

Installation

ekg is currently hosted on MELPA. It requires Emacs 28.1 or higher and the the following packages:

  • triples, which is a way to express a graph in sqlite in emacs, alongside basic querying functionality.
  • llm, a way to depend on LLMs via an interface, so the user can choose which provider they want to use.

Emacs versions prior to version 29 are dependent on emacsql to provide sqlite functionality, which itself has a dependency on having a sqlite binary available on your system.

An example installation using use-package is below:

(use-package ekg
  :bind (([f11] . ekg-capture)))

If you are using embedding and llm functionality, an example in which you use Open AI’s API is the following:

(use-package ekg
  :bind (([f11] . ekg-capture))
  :init
  (require 'ekg-embedding)
  (ekg-embedding-generate-on-save)
  (require 'ekg-llm)
  (require 'llm-openai)  ;; The specific provider you are using must be loaded.
  (let ((my-provider (make-llm-openai :key "my-openai-api-key")))
    (setq ekg-llm-provider my-provider
          ekg-embedding-provider my-provider)))

Note: make sure to never check in an API key, so if you check in your configuration, set up your API key in a loaded elisp file you do not check in.

If you’d like to enable auto-saving while editing:

(use-package ekg
  :config
  (require 'ekg-auto-save)
  (add-hook 'ekg-capture-mode-hook #'ekg-auto-save-mode)
  (add-hook 'ekg-edit-mode-hook #'ekg-auto-save-mode))

Usage

To create a note, run ekg-capture to capture a text note, or ekg-capture-url to capture a website. The note will, by default, be tagged with the current date. This can be changed by changing the variable ekg-capture-auto-tag-funcs, which is a series of functions that are called and can add tags.

At the top of the note is a special section, which will be unfamiliar. This is the metadata section, where it stores and displays editable metadata, notably tags, but possibly other data such as URLs. If you want to change tags, just edit them in the metadata section. Tags will autocomplete based on the tags you’ve already created.

The basic read operation in ekg is to show a list of notes according to some rule. An example is ekg-show-notes-with-tag. The notes displayed can be navigated between using n and p, and interacted with. The following section has a complete list of commands and keybindings.

Command summary

Global commands, can be run everywhere, and most should be bound to useful keybindings:

CommandDescription
ekg-captureCapture a new note
ekg-capture-urlCapture a new note (or open an existing note) about a URL
ekg-capture-fileCapture a new note (or open an existing note) about a file
ekg-show-notes-with-tagOpen a buffer for notes matching the single tag given
ekg-show-notes-with-any-tagsOpen a buffer for notes matching any of the given tags
ekg-show-notes-with-all-tagsOpen a buffer for notes matching all of the given tags
ekg-show-notes-with-tag-prefixOpen a buffer for notes matching any tag with a tag with the given prefix
ekg-show-notes-in-trashOpen a tag buffer that shows all notes in the trash
ekg-show-notes-in-draftsOpen a tag buffer that shows all draft notes (saved but not finalized)
ekg-show-notes-for-todayOpen a tag buffer that shows notes with today’s tag
ekg-show-notes-latest-capturedOpen a buffer that shows the latest notes that have been captured
ekg-show-notes-latest-modifiedOpen a buffer that shows the latest notes that have been modified
ekg-browse-urlOpen a URL stored as a resource to a note, completing by note title

These are also global commands, but for more occasional or specialized uses:

CommandDescription
ekg-global-rename-tagRename a tag, updating all references to it
ekg-upgrade-dbAfter upgrading, update any obsoletely stored data
ekg-clean-dbRemove unused data from the database, including empty tags

Commands relevant to capture buffers:

CommandDescription
ekg-change-modeChange note major-mode
ekg-capture-finalizeFinish and save (bound to C-c C-c)
ekg-capture-abortTrash the note (bound to C-c C-k)

Commands relevant to edit buffers:

CommandDescription
ekg-change-modeChange note major-mode
ekg-edit-abortAbort all edits (bound to C-c C-k)
ekg-edit-finalizeFinish and save (bound to C-c C-c)

Commands relevant to note view buffers:

CommandDescriptionBinding
ekg-notes-tagOpen another tag buffer selecting from tags of current notet
ekg-notes-openEdit the currently selected noteo
ekg-notes-deleteDelete the currently selected noted
ekg-notes-browseOpen the resource, if one existsb
ekg-notes-select-and-browse-urlSelect from the URLs in the current note buffer, and browse.B
ekg-notes-refreshRefresh the tag, refetching all the data displayedg
ekg-notes-createAdd a note with all the tags displayed in the bufferc
ekg-notes-nextMove selection to the next noten
ekg-notes-previousMove selection to the previous notep
ekg-notes-any-note-tagsOpen another tag buffer showing any of the tags in the current notea
ekg-notes-any-tagsOpen another tag buffer showing any of the tags in any note in the bufferA
ekg-notes-killKill a note from the current view (does not change the database)k

ekg's People

Contributors

ahyatt avatar cuprum avatar gleek avatar jayrajput avatar leadpipe avatar ptonner avatar puercopop avatar qingshuizheng avatar syohex avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

ekg's Issues

Ensure `ekg-note-id' after resource URL is deleted

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

Question: searching for tag prefixes

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)?

Thank you!

Just now found the "discussions". Should I close this issue and post it there?

`ekg-clean-db` returns error: (wrong-type-argument stringp nil)

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

Error when searching using ekg-embedding: end index out of bounds

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")

Double warning message

115
When the cursor is after "Tit" as in the screenshot, I call completion-at-point (C-M-i).
After that completion happens and messages appears as follow:
935

It's very strange. I tested this on develop branch.
Win 10, Emacs 28.1.

Names of functions & structure

Functions not intuitive, think of renaming

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

General structure comments

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.

Entry example

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.

Teaching triplets

Then you build it, you teach it:

  1. person "has" notes
  2. person has name
  3. note has date
  4. note has text
  5. tag has date
  6. note has Emacs mode
  7. org-mode is Emacs mode
  8. text-mode is Emacs mode
  9. James is person
  10. James registered note (note ID is 10)
  11. note (10) has text (ID is 11)
  12. text (11) is "My text here"

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.

Reference

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:

  • tagged/tag
  • base/type
  • time-tracked/creation-time

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.

Different uses of triplets in relational databases

So there are for now 3 discovered types of usage of triplets:

  1. the mixture of maximized freedom of using text, minimized database
    structure, which is represented by `ekg', without basic predicates

  2. there is One Model example which asks to define basic predicates
    to teach the database, structuring more the objects, subjects,
    predicates;

  3. 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.

Universal UUID triplet table

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)

Requirement by UUID triplet table

  • 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!

Example of universal UUID triplet

People

  1. Create people table
  2. Assign UUID to each entry
  3. James has UUID 6b6ea318-dda4-473f-9dcd-f3d79074f569

Notes

  1. Create notes table
  2. Assign UUID to each entry

Create note about James

  1. Program asks what is note about? People? Which one? James
    6b6ea318-dda4-473f-9dcd-f3d79074f569 That becomes subject

  2. Predicate is "has", but "has" also has UUID
    (21057255-79fe-44ae-bd52-26df3c56ac8d), it could came from any of
    tables, not only predicates!

  3. Note is created with UUID 144bc138-6470-49f8-8d65-37dd00bd6f6d, it
    becomes subject.

  4. 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

User wants some tags?

  1. Create table tags, each get assigned UUID

then follow various workflows:

  1. Find all notes about James, there are 433

  2. Narrow to those 12 which are related to "mining" (their names need not reflect that word)

  3. 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.

User wants some properties?

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:

  • subjects
  • predicats
  • objects
  • triplets

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.

Explanation please?

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?

ewoc-locate: Wrong type argument: ewoc, nil

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.

Undefined function `ekg-org-roam-import-tags-from-links`

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()

ekg-capture-url fails due to uninitialized ekg-db

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?

Searching for program: No such file or directory, sqlite3

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?

Make the metadata separator configurable

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.

Don't split title with `ekg--split-metadata-string`

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?

Transient menu for EKG

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:
image

Feel free to include this in ekg; I put this code in the public domain.

Ideas

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:

  1. searching by tags only
  2. searching by tags and including name words as tags
  3. etc.
  4. etc. various combinations

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.

Company error in the ekg-capture buffer

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)

Emacs 29 issue on Windows

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:

28

29

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)

obsolete timestamp with cdr 1

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

Renaming the tag/adding a tag to the current new note

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!

byte compiling warnings/error + native compiling warnings/error

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!

transclue-note doesn't play well with text around

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?

Original text:
image

Run C-c C-c :
image

Open the note again:
image

Zheng

Edit: update img

Feature request: make ekg notes availble as link targets from org ecosystem

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:

  • I would use ekg for knowledge base note taking (knowledge that is useful in the long term)
  • I would use org for task, project and agenda note taking
  • I frequently want to link from an open task/project to some knowledge that I already know to be useful for solving that task/project.

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

Windows Installation Error - No applicable method - emacsql

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.

ekg-org-roam and MELPA recipe(s)?

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?

Rename "Capture buffer" to "Edit buffer" in `ekg-edit-mode` header-line?

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.

startup warning message

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.

feature: Take notes on codebase.

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.

Error running ekg-embedding-show-similar for note with no text

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(....)

Could not find package `triples.el`

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

Feature request: provide a org heading like view of ekg tag buffer

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:

  1. the heading/tags buffer is a org mode like buffer with tags/headings and items under headings, with outline-minor-mode enabled to enable headline folding.
  2. treat the tags as sets, level 1 heading/tag is the parent set, notes with level 2 heading/tag is put under level 1 heading/tag.
  3. the user specified tags are put on the first line, and are removed from sub headings in the view, because all note in the tree has them.
  4. an item under heading is a note summary, including meta data other than tags, and note can be opened with a key binding.
  5. the headings/tags (including tags on the first line) are editable, editing operation changes the global tag name, move items across headings change their tags, delete an item remove it to trash.

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:

  1. the full note is shown, it is not economic in screen area, that's a problem with maybe a hundred notes.
  2. the hierarchy of the view is not clear, navigation to a specific note is inconvenient.
  3. tags refactor (global tag rename, change notes tags in batch) is inefficient.

defvar-keymap error

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

Cannot Delete Old Notes with Integer Content

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.

Error with (ekg-org-roam-import)

Hi,

I'm getting the following error when calling ekg-org-roam:

Debugger entered--Lisp error: (void-function string-equal-ignore-case)

ekg-tags-including generating error

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)

Function ekg-remove-unused-tags not removing tags after enabling embeddings

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

`(ekg-capture-finialize)` fails without LLM provider

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!

Links are not formatted properly in ekg-notes

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.

image

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

`s` and `kv` dependencies.

It looks like in addition to triples, the package also depends on s and kv.

Should this be mentioned in installation instructions?

Failure Running ekg-embedding-generate-all - Model Max Content Length

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.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.