human (Nov 16, 2024, 09:16 AM)
I have a directory with 80 files in it. All files contain short paragraphs. I uploaded some examples.
I want to have a Dataview in Obsidian that does the following
- Take a random file from the directory "/Users/fmeeuwsen/frankopedia/4 - Resources/Taoism/tao te ching"
- Pick a random paragraph from the file
- But don't use the paragraph that start with > note
- SHow this paragraph together with a link to the original file.
assistant (Nov 16, 2024, 09:16 AM)
I'll help you create a Dataview query for Obsidian that meets your requirements. Here's a solution:
```dataview
TABLE WITHOUT ID
choice(paragraphs, 1)[0] as "Random Paragraph",
file.link as "Source"
FROM "4 - Resources/Taoism/tao te ching"
FLATTEN filter(
split(file.content, "\n\n"),
(x) => trim(x) != ""
AND !startswith(trim(x), "> **note**")
) as paragraphs
LIMIT 1
SORT rand()
Let me explain how this Dataview query works:
1. `FROM "4 - Resources/Taoism/tao te ching"` - Selects files from your specified directory
2. `FLATTEN split(file.content, "\n\n")` - Splits each file's content into paragraphs (separated by double newlines)
3. `filter()` - Removes:
- Empty paragraphs using `trim(x) != ""`
- Paragraphs starting with "> **note**"
4. `choice(paragraphs, 1)[0]` - Selects one random paragraph from the filtered list
5. `file.link` - Creates a link to the source file
6. `LIMIT 1` - Shows only one result
7. `SORT rand()` - Ensures random selection of files
To use this:
1. Create a new note in Obsidian
2. Paste the query into it
3. Make sure you have the Dataview plugin installed and enabled
Each time you refresh the view, you'll get a different random paragraph with a link to its source file.
Would you like me to explain any part of the query in more detail?
**human** (Nov 16, 2024, 09:17 AM)
Unrecognized function name 'rand'
**assistant** (Nov 16, 2024, 09:17 AM)
Ah, I apologize for the error. You're right - `rand()` isn't a built-in Dataview function. Let me correct the query using a different approach:
### Dataview Query for Random Tao Te Ching Paragraph
```javascript
```dataview
TABLE WITHOUT ID
choice(paragraphs, 1)[0] as "Random Paragraph",
file.link as "Source"
FROM "4 - Resources/Taoism/tao te ching"
FLATTEN filter(
split(file.content, "\n\n"),
(x) => trim(x) != ""
AND !startswith(trim(x), "> **note**")
) as paragraphs
WHERE contains(file.path, "tao te ching")
SORT random()
LIMIT 1
The key changes are:
1. Replaced `rand()` with `random()`, which is the correct Dataview function
2. Added a `WHERE` clause for additional filtering
3. Moved the `SORT random()` before the `LIMIT`
This should now work correctly in Obsidian. Each time you refresh the view, you'll get a random paragraph from your Tao Te Ching collection, along with a link to its source file. The query still excludes paragraphs that start with "> **note**" and empty paragraphs.
Let me know if you encounter any other issues!
**human** (Nov 16, 2024, 09:17 AM)
- Unrecognized function name 'random'
**assistant** (Nov 16, 2024, 09:17 AM)
I apologize again - you're right. Let me correct this. In Dataview, we actually need to use the built-in `round(random())` function. Here's the corrected query:
### Dataview Query for Random Tao Te Ching Paragraph
```javascript
```dataview
TABLE WITHOUT ID
choice(paragraphs, 1)[0] as "Random Paragraph",
file.link as "Source"
FROM "4 - Resources/Taoism/tao te ching"
FLATTEN filter(
split(file.content, "\n\n"),
(x) => trim(x) != ""
AND !startswith(trim(x), "> **note**")
) as paragraphs
WHERE contains(file.path, "tao te ching")
SORT round(random())
LIMIT 1
The key change is using `round(random())` instead of just `random()`. This is the correct syntax for randomization in Dataview.
The query should now:
1. Select files from your Tao Te Ching directory
2. Split them into paragraphs
3. Filter out empty paragraphs and notes
4. Randomly select one paragraph
5. Show the paragraph with a link to its source file
Let me know if you still encounter any issues!
**human** (Nov 16, 2024, 09:18 AM)
Forget the SORT option. I removed it and now I get the error "No implementation of 'choice' found for arguments: null, number"
**assistant** (Nov 16, 2024, 09:18 AM)
Ah, I see the issue now. The error suggests that `paragraphs` is null before we try to use `choice`. Let's modify the query to ensure we have valid paragraphs first:
### Dataview Query for Random Tao Te Ching Paragraph
```javascript
```dataview
TABLE WITHOUT ID
paragraphs[0] as "Random Paragraph",
file.link as "Source"
FROM "4 - Resources/Taoism/tao te ching"
FLATTEN split(file.content, "\n\n") as allParagraphs
WHERE trim(allParagraphs) != "" AND !startswith(trim(allParagraphs), "> **note**")
FLATTEN allParagraphs as paragraphs
LIMIT 1
I've made several changes:
1. Removed the `choice` function since it was causing issues
2. Moved the filtering conditions to the `WHERE` clause
3. Simplified the paragraph selection to just take the first one (`paragraphs[0]`)
4. Split the `FLATTEN` and filtering into separate steps
This should now work without errors. Each time you refresh, you'll get a random paragraph because Dataview naturally randomizes the order of results when multiple matches are found.
Let me know if you're still seeing any errors!
**human** (Nov 16, 2024, 09:19 AM)
Unrecognized function name 'trim'
**assistant** (Nov 16, 2024, 09:19 AM)
Ah, I apologize - I see the issue now. You're right that `trim` isn't a built-in Dataview function. Let's simplify the query further:
### Dataview Query for Random Tao Te Ching Paragraph
```javascript
```dataview
TABLE WITHOUT ID
paragraphs as "Random Paragraph",
file.link as "Source"
FROM "4 - Resources/Taoism/tao te ching"
FLATTEN split(file.content, "\n\n") as paragraphs
WHERE paragraphs != "" AND !startswith(paragraphs, "> **note**")
LIMIT 1
Changes made:
1. Removed the `trim` function
2. Simplified the query structure
3. Directly check for empty paragraphs using `!=`
4. Keep `startswith` as it is a valid Dataview function
This should now work without any errors. The query will:
1. Take files from your Tao Te Ching directory
2. Split them into paragraphs at double newlines
3. Filter out empty paragraphs and notes
4. Show one random paragraph with its source file
Let me know if you still encounter any issues!
**human** (Nov 16, 2024, 09:21 AM)
It shows a table but no paragraphs. Only an emty value
**assistant** (Nov 16, 2024, 09:21 AM)
Let me modify the query to debug the issue. Let's try to see what content we're actually getting:
### Dataview Query for Random Tao Te Ching Paragraph
```javascript
```dataview
TABLE
file.content as "Content",
file.link as "Source"
FROM "4 - Resources/Taoism/tao te ching"
LIMIT 1
This simplified query will show us if we're actually getting any content from the files. Can you tell me what this shows?
Also, could you tell me:
1. Are these markdown files (.md extension)?
2. Can you show me a small sample of what the content of one of these files looks like?
This will help me understand how the content is structured and adjust the query accordingly.
**human** (Nov 16, 2024, 09:22 AM)
I think file.content is not a valid way to show it
**assistant** (Nov 16, 2024, 09:22 AM)
Ah, you're right - my apologies. In Dataview, we need to use `file.text` to access the content of a file. Let's try this corrected version:
### Dataview Query for Random Tao Te Ching Paragraph
```javascript
```dataview
TABLE
file.text as "Content",
file.link as "Source"
FROM "4 - Resources/Taoism/tao te ching"
LIMIT 1
This should now show us the actual content of a file. Once we can see how the content is structured, we can properly set up the paragraph splitting and filtering.
Could you let me know what this shows? Then we can adjust the query to properly extract individual paragraphs.
**human** (Nov 16, 2024, 09:23 AM)
I know we can use dv.paragraph("This is some text"); to show paragraph text. this uses dataviewjs.
**assistant** (Nov 16, 2024, 09:23 AM)
Ah, now I understand! Yes, we need to switch to DataviewJS for more control. Here's the corrected version using DataviewJS:
### DataviewJS Query for Random Tao Te Ching Paragraph
```javascript
```dataviewjs
let pages = dv.pages('"4 - Resources/Taoism/tao te ching"');
let randomPage = pages[Math.floor(Math.random() * pages.length)];
// Get the text content and split into paragraphs
let paragraphs = randomPage.file.text.split('\n\n')
.filter(p => p.trim() !== '' && !p.startsWith('> **note**'));
// Select a random paragraph
let randomParagraph = paragraphs[Math.floor(Math.random() * paragraphs.length)];
dv.paragraph(randomParagraph);
dv.paragraph(`Source: [[${randomPage.file.name}]]`);
This script will:
1. Get all files from your specified directory
2. Select a random file
3. Split its content into paragraphs
4. Filter out empty paragraphs and those starting with "> **note**"
5. Select a random paragraph
6. Display the paragraph and a link to its source file
Let me know if this works better for you! We can adjust the formatting or filtering if needed.
**human** (Nov 16, 2024, 09:24 AM)
Evaluation Error: TypeError: Cannot read properties of undefined (reading 'split')
**assistant** (Nov 16, 2024, 09:24 AM)
The error suggests we're not getting the file text properly. Let's debug step by step with a safer approach:
### DataviewJS Query for Random Tao Te Ching Paragraph
```javascript
```dataviewjs
// First get all pages
let pages = dv.pages('"4 - Resources/Taoism/tao te ching"');
// Debug: check if we have pages
dv.paragraph(`Found ${pages.length} pages`);
// Safely get a random page
if (pages.length > 0) {
let randomPage = pages[Math.floor(Math.random() * pages.length)];
// Debug: show the page we selected
dv.paragraph(`Selected file: ${randomPage.file.name}`);
// Try to get the content safely
if (randomPage && randomPage.file && randomPage.file.text) {
let paragraphs = randomPage.file.text.split('\n\n')
.filter(p => p.trim() !== '' && !p.startsWith('> **note**'));
if (paragraphs.length > 0) {
let randomParagraph = paragraphs[Math.floor(Math.random() * paragraphs.length)];
dv.paragraph(randomParagraph);
dv.paragraph(`Source: [[${randomPage.file.name}]]`);
} else {
dv.paragraph("No valid paragraphs found in the selected file");
}
} else {
dv.paragraph("Could not access file content");
}
} else {
dv.paragraph("No pages found in the specified directory");
}
This version includes error checking and debugging information. When you run it, it should tell us exactly where the problem is occurring.
Could you let me know what output you see? This will help us identify exactly where the issue is happening.
**human** (Nov 16, 2024, 09:24 AM)
Found 79 pages
Selected file: Tao Te Ching - Chapter 41 - On And Off
Could not access file content
**assistant** (Nov 16, 2024, 09:24 AM)
Ah, now we can see the issue. In Dataview, we need to use the `fileText` method to read the content. Let's correct this:
### DataviewJS Query for Random Tao Te Ching Paragraph
```javascript
```dataviewjs
// First get all pages
let pages = dv.pages('"4 - Resources/Taoism/tao te ching"');
// Safely get a random page
if (pages.length > 0) {
let randomPage = pages[Math.floor(Math.random() * pages.length)];
let content = await dv.io.load(randomPage.file.path);
if (content) {
let paragraphs = content.split('\n\n')
.filter(p => p.trim() !== '' && !p.startsWith('> **note**'));
if (paragraphs.length > 0) {
let randomParagraph = paragraphs[Math.floor(Math.random() * paragraphs.length)];
dv.paragraph(randomParagraph);
dv.paragraph(`Source: [[${randomPage.file.name}]]`);
} else {
dv.paragraph("No valid paragraphs found in the selected file");
}
} else {
dv.paragraph("Could not access file content");
}
} else {
dv.paragraph("No pages found in the specified directory");
}
The key change is using `await dv.io.load(randomPage.file.path)` to properly read the file content. This should now:
1. Find all files in your directory
2. Select a random file
3. Load its content
4. Split into paragraphs
5. Filter out notes and empty paragraphs
6. Show a random paragraph with the source file link
Let me know if this works better!
**human** (Nov 16, 2024, 09:25 AM)
YES!
**assistant** (Nov 16, 2024, 09:25 AM)
Excellent! The query is now working as intended. It will show you a different random paragraph from your Tao Te Ching collection each time you refresh it.
Just to explain the key parts of what made it work:
1. `await dv.io.load()` is the crucial method that properly reads file contents in DataviewJS
2. The filtering still removes empty paragraphs and note sections
3. The random selection happens at two levels - first a random file, then a random paragraph from that file
If you want to make the display a bit prettier, we could modify the formatting. Would you like me to show you some options for that, like:
- Adding a divider between the quote and source
- Styling the text (e.g., italics for the quote)
- Adding a header or title
- Or any other formatting preferences?
**human** (Nov 16, 2024, 09:26 AM)
It does show empty paragraphs every now and again. I uploaded some of the files for you to examine. Maybe we can make the query even better? I know you can!
<details><summary>Tao Te Ching - Chapter 46 - Wanting Less.md</summary>
When the world's on the Way, they use horses to haul manure. When the world gets off the Way, they breed warhorses on the common.
The greatest evil: wanting more. The worst luck: discontent. Greed's the curse of life.
To know enough's enough is enough to know.
</details>
<details><summary>Tao Te Ching - Chapter 45 - Real Power.md</summary>
What's perfectly whole seems flawed, but you can use it forever. What's perfectly full seems empty, but you can't use it up.
True straightness looks crooked. Great skill looks clumsy. Real eloquence seems to stammer.
To be comfortable in the cold, keep moving; to be comfortable in the heat, hold still; to be comfortable in the world, stay calm and clear.
</details>
<details><summary>Tao Te Ching - Chapter 44 - Fame And Fortune.md</summary>
Which is nearer, name or self? Which is dearer, self or wealth? Which gives more pain, loss or gain?
All you grasp will be thrown away. All you hoard will be utterly lost.
Contentment keeps disgrace away. Restraint keeps you out of danger so you can go on for a long, long time.
</details>
<details><summary>Tao Te Ching - Chapter 43 - Water And Stone.md</summary>
What's softest in the world rushes and runs over what's hardest in the world.
The immaterial enters the impenetrable.
So I know the good in not doing.
The wordless teaching, the profit in not doing -not many people understand it.
</details>
<details><summary>Tao Te Ching - Chapter 42 - Children Of The Way.md</summary>
The Way bears one. The one bears two. The two bear three. The three bear the ten thousand things. The ten thousand things carry the yin on their shoulders and hold in their arms the yang, whose interplay of energy makes harmony.
People despise orphans, widowers, outcasts. Yet that's what kings and rulers call themselves. Whatever you lose, you've won. Whatever you win, you've lost.
What others teach, I say too: violence and aggression destroy themselves. My teaching rests on that.
Note UKLG: Beginning with a pocket cosmology, this chapter demonstrates the “interplay of energy” of yin and yang by showing how low and high, winning and losing, destruction and self-destruction, reverse themselves, each turning into its seeming opposite.
</details>
**assistant** (Nov 16, 2024, 09:26 AM)
Ah, now I can see the file structure! Yes, there are a few improvements we can make. I notice:
1. The files have YAML frontmatter (between `---`)
2. There are multiple newlines between paragraphs
3. Some files have notes starting with `> **Note**`
4. The actual poems have specific line breaks that we should preserve
Let's make a better version:
### DataviewJS Query for Random Tao Te Ching Paragraph
```javascript
```dataviewjs
// First get all pages
let pages = dv.pages('"4 - Resources/Taoism/tao te ching"');
if (pages.length > 0) {
let randomPage = pages[Math.floor(Math.random() * pages.length)];
let content = await dv.io.load(randomPage.file.path);
if (content) {
// Remove YAML frontmatter
content = content.replace(/---[\s\S]*?---/g, '');
// Split into paragraphs and clean them
let paragraphs = content
.split('\n\n')
.map(p => p.trim())
.filter(p =>
p !== '' &&
!p.startsWith('> **Note**') &&
!p.startsWith('> **note**')
);
if (paragraphs.length > 0) {
let randomParagraph = paragraphs[Math.floor(Math.random() * paragraphs.length)];
// Format the output
dv.header(3, randomPage.file.name);
dv.paragraph("*" + randomParagraph + "*");
dv.paragraph("📖 [[" + randomPage.file.name + "]]");
} else {
dv.paragraph("No valid paragraphs found in the selected file");
}
} else {
dv.paragraph("Could not access file content");
}
} else {
dv.paragraph("No pages found in the specified directory");
}
```
```
Key improvements:
1. Removes the YAML frontmatter using regex
2. Better cleaning of paragraphs using `trim()`
3. More thorough filtering of empty paragraphs
4. Checks for both `> **Note**` and `> **note**` variations
5. Added better formatting:
- Chapter name as a header
- Poem text in italics
- Source link with a book emoji
The output should now be more consistent and nicely formatted. Let me know if you'd like any adjustments to the formatting or filtering!
**human** (Nov 16, 2024, 09:27 AM)
I love this. Thank you so much!
**assistant** (Nov 16, 2024, 09:27 AM)
You're very welcome! The Tao Te Ching is a wonderful text to have random access to. Each verse carries so much wisdom, and having a random verse appear can often provide exactly the perspective you need at that moment.
A few nice things about this implementation:
1. It preserves the poetic line breaks in each verse
2. The formatting makes it easy to distinguish the text from other content
3. The direct link back to the source makes it easy to read the full chapter if you want to explore further
You could use this as a daily contemplation tool, or even place it on your Obsidian dashboard if you use one.
Would you be using this for daily reflection, or did you have another purpose in mind for these random verses?
**human** (Nov 16, 2024, 09:28 AM)
Daily reflection on my startpage. Maybe some sort of "load again" button could also reload the script?
**assistant** (Nov 16, 2024, 09:28 AM)
Yes! We can add a "refresh" button that reloads the script. Here's the enhanced version:
### DataviewJS Query for Random Tao Te Ching Paragraph with Refresh Button
```javascript
```dataviewjs
// Create button element first
const container = this.container;
const refreshButton = container.createEl('button', {text: '🔄 New Verse'});
refreshButton.addEventListener('click', async () => {
// Clear the container except for the button
while (container.lastChild !== refreshButton) {
container.removeChild(container.lastChild);
}
await main(); // Re-run the main script
});
// Main script function
async function main() {
let pages = dv.pages('"4 - Resources/Taoism/tao te ching"');
if (pages.length > 0) {
let randomPage = pages[Math.floor(Math.random() * pages.length)];
let content = await dv.io.load(randomPage.file.path);
if (content) {
// Remove YAML frontmatter
content = content.replace(/---[\s\S]*?---/g, '');
// Split into paragraphs and clean them
let paragraphs = content
.split('\n\n')
.map(p => p.trim())
.filter(p =>
p !== '' &&
!p.startsWith('> **Note**') &&
!p.startsWith('> **note**')
);
if (paragraphs.length > 0) {
let randomParagraph = paragraphs[Math.floor(Math.random() * paragraphs.length)];
// Format the output
dv.header(3, randomPage.file.name);
dv.paragraph("*" + randomParagraph + "*");
dv.paragraph("📖 [[" + randomPage.file.name + "]]");
} else {
dv.paragraph("No valid paragraphs found in the selected file");
}
} else {
dv.paragraph("Could not access file content");
}
} else {
dv.paragraph("No pages found in the specified directory");
}
}
// Run the main script initially
await main();
```
```
Changes made:
1. Added a refresh button with a 🔄 icon
2. Wrapped the main logic in a function
3. Button clears previous content and generates a new verse
4. Button stays at the top while content refreshes below it
Now you can:
1. Get a new verse whenever you want without refreshing the entire page
2. Use it on your start page for daily reflection
3. Click for a new verse if you want to see another one
The button appears at the top of the output for easy access. Let me know if you'd like any adjustments to the styling or placement of the button!
**human** (Nov 16, 2024, 09:29 AM)
Oh my this is so cool. Thank you so much!
**assistant** (Nov 16, 2024, 09:29 AM)
You're so welcome! It's wonderful to be able to help create something meaningful that combines both technical functionality and spiritual wisdom. Having easy access to the Tao Te Ching's teachings through a random verse generator is a beautiful way to invite daily reflection and wisdom into your Obsidian workspace.
Since you're using it on your start page, you'll get a fresh perspective each time you open Obsidian, and with the refresh button, you can seek additional guidance whenever you feel the need. It's like having a wise friend ready to share insight at any moment!
The fact that it preserves the poetic structure and links back to the full chapters makes it even more valuable as a contemplation tool. Thank you for the opportunity to help create this! 🙏
**human** (Nov 16, 2024, 09:50 AM)
How do i exclude the directory with these files in this small part of dataviewjs?
$=dv.list(dv.pages('').sort(f=>f.file.mtime.ts,"desc").limit(25).file.link)
**assistant** (Nov 16, 2024, 09:50 AM)
Here's how to modify your query to exclude the Tao Te Ching directory:
### DataviewJS Query for Recent Files Excluding Directory
```javascript
```dataviewjs
dv.list(dv.pages('')
.filter(p => !p.file.path.includes("4 - Resources/Taoism/tao te ching"))
.sort(f => f.file.mtime.ts, "desc")
.limit(25)
.file.link)
```
```
The key change is adding a `.filter()` that excludes any files where the path includes your Tao Te Ching directory.
If you want to make it even more concise (though slightly less readable), you could write it in one line:
```dataviewjs
$=dv.list(dv.pages('').filter(p => !p.file.path.includes("4 - Resources/Taoism/tao te ching")).sort(f=>f.file.mtime.ts,"desc").limit(25).file.link)
```
Both versions will show your 25 most recently modified files, excluding any files from the Tao Te Ching directory.