quickadd icon indicating copy to clipboard operation
quickadd copied to clipboard

[FEATURE REQUEST] Suggestions from existing field values for {{VALUE}} or suggester (API) ?

Open Elaws opened this issue 2 years ago • 42 comments

Problem description

Often, I use {{VALUE}} or the suggester (from QuickAdd API) to complete a field in a note (e.g. : type::). It would be really useful if {{VALUE}} or the suggester could automatically retrieve the possible values for the field (either from all the notes or a chosen subset of notes) and suggest them.

Elaws avatar Nov 26 '22 17:11 Elaws

I support this 100%

I'm thinking of adding it myself as I see a few relatively simple solutions.

  1. add to the list of format variables {{FIELD:<someField>}} so that we can specify a field that is searched through.
  2. for every {{VALUE:<someValue>}} variable, search the filesystem of fields with that name.

I prefer the idea of 1 because it improves performance by only incurring the cost of searching for a field when a user requests it explicitly.

qaptoR avatar Feb 28 '23 14:02 qaptoR

In my mental model, this would replicate metadata inheritance. I would like QuickAdd to pull metadata from fields in the currently open note, so they could be reapplied in the created note. Expanding the value selection to some filterable subset of notes or fields would be a crazy powerful feature.

MichaTarlton avatar Feb 28 '23 16:02 MichaTarlton

Yeah, this would be a fantastic feature to have. I especially like your suggestion about {{FIELD}}, @qaptoR, for the reasons you mentioned. Merging it with {{VALUE}} would be hard to maintain + inhibit performance unnecessarily.

Let's see. What if {{FIELD:Type}} looks for every occurrence of Type in metadata - both Dataview fields & YAML properties. Since both already have indexing, QA doesn't need to do that... so it's probably just O(1) lookup (very fast to do).

Then it's just how we get the subset. I imagine one could either specify

  • a file path (for replicating properties of a specific file),
  • a folder path to get all files within as subset, or
  • a tag to get all files with this tag as subset.

If there's only 1 potential value, then that's used without asking the user. Otherwise, use suggester to prompt user. If there are no values... an input prompt could be used to ask for a specific value.

Any thoughts?

@qaptoR the process for adding format syntax is quite involved at the moment. There are many things to be aware of. If you wish to save the headache of navigating this complexity, I'll be more than happy to add this feature tomorrow.

chhoumann avatar Feb 28 '23 17:02 chhoumann

@chhoumann :

Then it's just how we get the subset. I imagine one could either specify

  • a file path (for replicating properties of a specific file),
  • a folder path to get all files within as subset, or
  • a tag to get all files with this tag as subset.

Maybe more complex to implement, but I was also thinking about the following filters :

  • A combination of the choices you mentioned, e.g. :
Files in PATH and not SUBPATH with TAG_1 and not TAG_2
  • A filter based on a given FIELD and its values (e.g. : only notes with composer:: John Williams).

Elaws avatar Feb 28 '23 18:02 Elaws

@chhoumann I've already implemented it (to my own immediate specification, see below) I just made the pull request here: https://github.com/chhoumann/quickadd/pull/403

It's possible I've missed something, but I've tested it and it works.

The only hiccup I found was not realizing that if you search all frontmatter fields in all markdown files then it searches the templates too. So I have to do a check for frontmatter fields that are of type 'object' because the format syntax when read as frontmatter is an object.

My Specification: I'm not super familiar yet with the obsidian API, I've just been building small tools for linking together different workflows offered by different plugins. So it's possible there's a more performant way to get the values, as I didn't think about the dataview field:: syntax, or that the info might be cached in a more easy to access way.

Essentially, I just get all markdown files from the vault, and loop over them, checking their frontmatter for the <variable> provided. It's definitely a brute force method, but I was more keen on getting the new syntax working than finding the optimal solution at this stage.

qaptoR avatar Feb 28 '23 18:02 qaptoR

So, I just started using it, and it's great. But one major flaw that I'll probably fix, is that if there is no suggestion selectable (meaning no matches) then it won't allow you to accept the current input as the value.

This is definitely for safety, since the FuzzySuggestModal is a generic with type<T>, and the input is always a string. But for string values it should be acceptable.

The problem I'm running into is that: if I want to set a field with a new entry I have to use {{VALUE}}. If I want to use an existing value (and get fuzzy suggest) then I can use {{FIELD:}}, but it can't be both

qaptoR avatar Feb 28 '23 20:02 qaptoR

Thanks for your PR @qaptoR & your input @Elaws!

With the latest update, https://github.com/chhoumann/quickadd/releases/tag/0.11.7, {{FIELD}} has been added as a new feature. I'm considering this release a beta-test of the {{FIELD}} feature, and have asked for feedback in the release notes: image

I've also gone ahead and fixed the issue that @qaptoR mentioned: Supports when there are no fields like the one you specify in {{FIELD}} in your vault. Will just ask you to input a value manually. Similar to {{VALUE}}.

I want to more towards what @Elaws suggested, allowing for a more sophisticated language to query for these. I'm strongly considering using dataview for this, as it is familiar syntax to many Obsidian users. Need to consider it a bit more, though...

Anyway, hoping to get some more feedback in here. Looking to add dataview support (inline properties) to this soon, so leaving this issue up + for further consideration & feedback.

chhoumann avatar Mar 07 '23 13:03 chhoumann

Forgive my ignorance, but are there defined capabilities and directions for the {{FIELD}} feature as of current?

From my quick study and testing, adding {{FIELD:fieldname}} to a template, will search all frontmatter or DV fields with that name, and then display variables from those fields in a suggester during quick add. It does not display unique variables, so if multiple notes have the same field variable, e.g. FIELD: uniquename, I would see multiple entries for uniquename in the suggester for each note it appears in, across the vault. Am I missing any additional functionality?

Also, am I missing a way to define or filter the fieldname input? My ideal use case would be for quickadd to take FIELD: fieldname from the active file. I see that adding a query ability is in the cards but, it feels as if there is some "capture from active file" feature in QA that I keep missing.

MichaTarlton avatar Mar 08 '23 11:03 MichaTarlton

No ignorance on your part, @MichaTarlton. You raise some very valid concerns.

I have added documentation for the feature. I'm including it here as a point for discussion:

Suggest the values of FIELDNAME anywhere {{FIELD:FIELDNAME}} is used. Fields are YAML fields, and the values represent any value this field has in your vault. If there exists no such field or value, you are instead prompted to enter one. This is currently in beta, and the syntax can change—leave your thoughts here.

Dataview fields are not currently supported. I'm still figuring how the integration should be managed. As it stands, it will look for any possible value contained in the specified YAML field (case-sensitive!), across your vault.

And for your last question: what would you expect to happen if

  • you have no file open? ⇒ Error? Prompt? Suggest across vault?
  • the file you have opened doesn't have fieldname? ⇒ Error? Prompt? Suggest across vault?
  • the file you have opened has fieldname, but it has no value? ⇒ Prompt? Use the empty value?

One of the things I'm pondering is how to encode these things in a query/with format syntax.

However, some things I think make sense:

  • removing case sensitivity.
  • only showing unique values.

Which I'll be implementing now.

chhoumann avatar Mar 08 '23 12:03 chhoumann

Thank you for putting my mind to rest.

I think DV uses case-sensitive field querying, so some people might actually be using case uniqueness (though in my situation it's just a pain in my ass).

To your Qs, shooting from the hip I would expect:

  1. Insertion of the undefined entry that usually shows up in such cases. But a prompt which searches across the vault, sounds better.
  2. Same as above.
  3. Use the empty value. But this would probably be a highly personal preference. I could see where I may want it to trigger a prompt instead.

MichaTarlton avatar Mar 08 '23 13:03 MichaTarlton

Thank you for the input!

Luckily, I hadn't removed the case sensitivity yet. I was under the impression that it didn't, but I'm glad to hear that my assumption might be wrong before I acted on it. Will wait for further input / more info on that matter

I've gone ahead and made QA only consider unique values. No need to put every value in the suggester. That is included in 0.11.

I'm also considering what to do instead of undefined across the board. It is really just is because the implementation details spill into the plugin. The value is undefined in JavaScript, so if none is given, it'll just get put in our notes (or an error). However, I'd like to make the UX a bit more clear as to when undefined could occur, and give options for recovery or alternatives. Perhaps a setting that could customize this behavior for users? Although I don't think adding settings for everything necessarily makes the plugin any simpler. Anyway, that's a bit besides the scope of this discussion.

chhoumann avatar Mar 08 '23 13:03 chhoumann

Probably should digress this into another thread, but what I've seen some template using plugins do is let you define the "no data" output. Though with this I can see users wanting flexible case-specific "undefined-s". Maybe a user chosen "undefined" and then one which can be passed to templater for the hyper-specific stuff?

MichaTarlton avatar Mar 08 '23 18:03 MichaTarlton

Dataview fields are not currently supported. I'm still figuring how the integration should be managed.

Looking forward to trying it ! I will test this new feature then : for now, I only use dataview fields, because there were some drawbacks with YAML fields back then (maybe this was fixed, I should investigate).

Even if some suggested values are found, is there still a possibility to manually add a new value ? If not, it would be very useful for all notes where field values are not fixed once and for all.

Elaws avatar Mar 09 '23 22:03 Elaws

Completely agree, writing values manually even with suggested values is a great idea.

Just added this functionality. Will be in next release. Obsidian_ZJWyUxpX1w

chhoumann avatar Mar 10 '23 08:03 chhoumann

Hi guys, I have a small suggestion that will increase the usability of this feature tremendously, I recently started using the QuickAdd plugin. I am fairly new to this so please excuse me if this is the wrong place to suggest.

My suggestion is to get the list of auto-suggested values for fields from another plugin called MetaData Menu. It's not mandatory that the user needs to use this plugin but can be presented as a choice for better suggestions.

In case you are not aware of this plugin what it does is it lets define fields inside a file class and there we can define the fields and field type for every metadata field, there will be different suggestions eg boolean, select from the list, multi-select as an array. Metadata menu suggestions are highly configurable as it integrates Dataview to narrow down the selected options as the user wants.

Let me know your thoughts on this. I am willing to put some effort myself to get this moving but I am looking for some guidance as I do not have any experience with plugins. Will appreciate any help you can give me.

hydraInsurgent avatar Mar 13 '23 06:03 hydraInsurgent

Hi @hydraInsurgent

Thank you for your thoughts! I've only had sparse interaction with MetaData Menu. This is because I've developed MetaEdit, so I haven't had the need for MDM. MetaEdit has a similar(?) feature to the fileclass system MDM has going on. Although, I haven't really looked much at it, so I could be totally wrong.

The difficulty in supporting other plugins is that we have to keep the interactions updated. For example, QuickAdd currently hooks into Natural Language Dates & Templater. Templater doesn't have an API, so I've had to manually find the appropriate functions and parameters to use. Since they don't have an 'official' API, those functions and parameters can change at any time, making maintenance of QuickAdd harder. The plugin could break due to an update in any of the plugins it depends on.

DataView on the other hand does have an API, which is what makes it easy for plugin developers to use in their own plugins. And why it would be easier to support for the {{FIELD}} feature.

Please note that this is not me saying no to your suggestion, just thinking out loud. I think it could make sense if

  • there is enough demand
  • we figure out how to properly support any such integration - and what about other, similar plugins?
  • there isn't any QuickAdd-native feature or functionality that would be simpler, yet achieve the same or more

But obviously, there could be (are) things I'm not seeing. Welcoming any thoughts :)

chhoumann avatar Mar 22 '23 13:03 chhoumann

Enhancement coming in the next release: {{FIELD:fieldName}} now shows Enter value for fieldName in suggester to differentiate between prompts.

This comes from a conversation on Discord regarding the feature. It can be hard to differentiate between the suggesters when a template has multiple {{FIELD}}s.

image

chhoumann avatar Mar 22 '23 13:03 chhoumann

Note: should prioritize somehow filtering template values out, so users don't see values from templates here.

Open question: Should this be opt in? Opt out? Or just default? Something else?

chhoumann avatar Mar 24 '23 23:03 chhoumann

I'd say opt in, though perhaps depends on how one defines where the templates are stored.

MichaTarlton avatar Mar 27 '23 15:03 MichaTarlton

FWIW, if you use:

tag:
- x
- y
- z

x, y, z don't show up as options for {{FIELD:tag}}.

anvimea avatar Mar 27 '23 19:03 anvimea

@anvimea : Are you using YAML metadata format (enclosing with ---) ?

Have there been some progress concerning support for dataview fields (field::) ?

Thanks !

Elaws avatar Apr 10 '23 15:04 Elaws

@anvimea : Are you using YAML metadata format (enclosing with ---) ?

Have there been some progress concerning support for dataview fields (field::) ?

Thanks !

Yep, using YAML metadata format, and I just tested it again - still doesn't work.

anvimea avatar Apr 10 '23 15:04 anvimea

greetings!

(this could be the same as what @anvimea has described)

Recently I was testing the {FIELD} option and I noticed that the comma separated values of the key is not listed as separate items, instead, the whole line is considered

--- 
xy-key: va1,val2,va3 
---

with {{FIELD:xy-key}}, the suggester prompts 'va1,val2,va3' and not as va1 + val2 + va3

I suppose this is the current behavior. If yes, can this be added as an option.

thanks!

modymp avatar Apr 11 '23 12:04 modymp

greetings!

(this could be the same as what @anvimea has described)

Recently I was testing the {FIELD} option and I noticed that the comma separated values of the key is not listed as separate items, instead, the whole line is considered

--- 
xy-key: va1,val2,va3 
---

with {{FIELD:xy-key}}, the suggester prompts 'va1,val2,va3' and not as va1 + val2 + va3

I suppose this is the current behavior. If yes, can this be added as an option.

thanks!

Have also noticed this behavior!

anvimea avatar Apr 11 '23 12:04 anvimea

After invoking a new note from a template, I hope to populate the YAML front matter with content I select from a drop-down list. Is this possible? As an example, I would like a "context" field in my Person template that would include options for me to select from "Colleague" "Friend" "Family" etc. when I create a note.

On Discord, someone suggested the use of {{FIELD:<FIELDNAME>}} to get this done.

I am also trying to get it to create a title using values from a prompt and templater. I am likely doing this wrong, but I have not been able to get it to work with either a Capture or Template QuickAdd. Here's the template I am using:

---
<%* let title = tp.file.title; 
	if (title.startsWith('Untitled')) { 
		title = await tp.system.prompt('Name (Last, First)');
		await tp.file.rename(`${title}`);
	}
-%>
title: <% `${title}` %>
context: {{FIELD:context}}
---
# <% `${title}` %>


## Contact Information

[Email](mailto:)
ONID: 

Any suggestions would be greatly appreciated.

mundorfd avatar Apr 12 '23 18:04 mundorfd

After invoking a new note from a template, I hope to populate the YAML front matter with content I select from a drop-down list. Is this possible? As an example, I would like a "context" field in my Person template that would include options for me to select from "Colleague" "Friend" "Family" etc. when I create a note.

On Discord, someone suggested the use of {{FIELD:<FIELDNAME>}} to get this done.

I am also trying to get it to create a title using values from a prompt and templater. I am likely doing this wrong, but I have not been able to get it to work with either a Capture or Template QuickAdd. Here's the template I am using:

---
<%* let title = tp.file.title; 
	if (title.startsWith('Untitled')) { 
		title = await tp.system.prompt('Name (Last, First)');
		await tp.file.rename(`${title}`);
	}
-%>
title: <% `${title}` %>
context: {{FIELD:context}}
---
# <% `${title}` %>


## Contact Information

[Email](mailto:)
ONID: 

Any suggestions would be greatly appreciated.

Hey @mundorfd I stumbled on your comment since I was trying to figure out something similar. I figured out a solution that works for me and executes both templater and quickadd. You might find useful.

Regarding your title problem, the following worked (I needed to include the templater tR variable to output the result):

---
<%*
  let title = tp.file.title
  if (title.startsWith("Untitled")) {
    title = await tp.system.prompt("Title");
    await tp.file.rename(title);
  } 
  tR += ""
%>
---
# <%* tR += title %>

Regarding your context field, I wanted something similar but for my tags field. I believe the templater suggester should work for you context field:

---
tags: [<% tp.system.suggester(["Friend contact, Acquaintance contact", "Colleague contact", "Business contact", "Conference contact", "Family contact"], ["contact/friend", "contact/acquaintance", "contact/colleague", "contact/business", "contact/conference", "contact/family"]) %>]
company: "{{VALUE:Company}}"
aliases: [<% tp.file.title.replace(/^(\w+)\s(\w)\w+$/,'$1 $2.') %>]
---

NaturallyAsh avatar Apr 13 '23 19:04 NaturallyAsh

Thank you @NaturallyAsh - I will give this a try!

mundorfd avatar Apr 13 '23 21:04 mundorfd

Borrowing from @NaturallyAsh's help, here is what I have now as my template (Thank you! works exactly as I had hoped):

---
<%*
  let title = tp.file.title
  if (title.startsWith("Untitled")) {
    title = await tp.system.prompt("Title");
    await tp.file.rename(title);
  } 
  tR += ""
%>
context: <% tp.system.suggester(["Ecampus Colleague", "Instructor", "Intern", "Friend", "Family"], ["Ecampus Colleague", "Instructor", "Intern", "Friend", "Family"]) %>
---

# <%* tR += title %>

I have added some code for DataView to show me all files linked to the person.

## Linked files

```dataview
LIST FROM [[]]
SORT title ASC



mundorfd avatar Apr 13 '23 22:04 mundorfd

I noticed while using Capture and using this code:

  • [{{FIELD:Ingredient}}, {{VALUE:Amount}} {{VALUE:Unit}}]

That the suggester will show last, while I stated to be shown first. So the order it is shown now is: 1 Amount 2 Unit 3 Ingredient

Is this a bug or am I doing something wrong?

image

OmniNaut avatar Apr 25 '23 05:04 OmniNaut

@OmniNaut, I can't speak for the developer of this plugin, but I did contribute the initial code for this feature implementation, so unless something has changed and I'm corrected this is the result of the order in which each of the regex substitutions types are implemented in the code. This is because code executes top to bottom and something has to be executed first, last etc. ie. First replace all VALUE, then replace all FIELD, etc.

It would be a more complex solution to queue each individual substitution and then have them happen in the order they're written. And since the order you enter the data doesn't change the final result it would be more work for a feature that adds marginal value to the overall functionality of the plugin. that said, marginal value here is relative to each individual.

The reason that you are noticing it though, is that the FIELD syntax is new, and requires it's own regex to make substitutions, whereas before there was only VALUE substitutions, so they did just happen in the order they were written as you've said in your comment simply because the regex query returns the matches in the order they're found, which is in string order (or the order they're written).

So to answer your question, no it is not a bug, and no you are not doing something wrong, it is working just as expected. Granted, it should possibly be documented.

Just as an example of how complex the solution would be, this is an algorithm to solve the issue off the top of my head:

run a pass to get all matches for VALUE into var A
run a pass to get all matches for FIELD into var B
# where A and B are array types

loop while A and B are not size 0:
if position of A[0] is < B[0] then 
C.push(A.pop_front(): A_Funcref) else 
C.push(B.pop_front(): B_Funcref)
# where C is an ordered dictionary type 

for key, value in C: value.call(key)

except now imagine that there is eventually a new substitution NEWSUBA and NEWSUBB, so that now building C becomes more and more complicated whenever a new type is added, since you can only compare two values at a time.

Versus the code as it stands now:

run a pass to substitute all VALUE
run a pass to substitute all FIELD
run a pass to substitute all NEWSUBA
... etc.

BUT, that is just my off the cuff perspective, and maybe there is someone who doesn't think it would be so difficult to maintain that feature and would like to implement it themselves, or possibly another solution using regex or javascript features that I'm not aware of that could result in a much simpler solution. For example, there may be a library that can merge and sort arrays together to build C.

Anyway, I hope this helps contribute to the conversation. I don't want to shut down anyone's ideas, only illustrate the picture as I currently see it.

qaptoR avatar Apr 26 '23 11:04 qaptoR