Search term accepts regular expressions so the script can be used for ‘range or so-called proximity search’: e.g. <searchterm1>.*?<searchterm2>
will find the two terms in their closest vicinity of one another and the full paragraph will be printed for context. The <
and >
are not part of the syntax, of course.
- Same thing as in the Obsidian search modal, but there you need to put in at least the opening
/
slash to indicate we want to use regex. - The search done in the DV query like this is more superior as one can copy out the results for further processing.
This proximity search helped me a great deal years ago when I started out being more efficient researching for material among my 3k+ books and papers (was and am using DocFetcher for PDFs).
EDIT.:
In the meantime I added abililty for further filtering:
<%*
const currentFile = app.workspace.getActiveFile();
if (!currentFile) return;
let folderRegex = /(.*)([/\\])/; // Default regex for full vault – all OS environments are taken into consideration
const userinput = await tp.system.suggester(
["Query Full Vault", "Specify Unique Folder"],
["Query Full Vault", "Specify Unique Folder"],
false,
"Choose an option:"
);
if (userinput === "Specify Unique Folder") {
const items = app.vault.getAllLoadedFiles().filter(x => x instanceof tp.obsidian.TFolder);
const selectedItem = (await tp.system.suggester((item) => item.path, items)).path;
if (selectedItem) {
// Update folderRegex based on user selection
folderRegex = new RegExp(`(${selectedItem.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')})([/\\\\])`);
} else {
return; // Exit if user cancels folder selection
}
}
const searchTerm = await tp.system.prompt("Enter search term:");
if (!searchTerm) return;
const filterRegex = await tp.system.prompt("Exclude from results if string is present (or regex matched) in paragraph:");
const additionalFilter = await tp.system.prompt("Filter search results by string or regex:");
const editor = app.workspace.activeEditor.editor;
const fileContent = await app.vault.read(currentFile);
const dynamicContent = `
\%\%
\`\`\`dataviewjs
// Define the folder regex for filtering allPages
const folderRegex = ${folderRegex}; // Dynamically updated folder regex
const searchTerm = "${searchTerm}"; // Injecting searchTerm here as well
const filterRegex = ${filterRegex ? `new RegExp(\`${filterRegex}\`, "gmi")` : 'null'}; // Exclude from results regex
const additionalFilter = ${additionalFilter ? `new RegExp(\`${additionalFilter}\`, "gmi")` : 'null'}; // Additional filter regex
// Query all pages and filter based on folderRegex
const allPages = dv.pages("").filter(page => {
const path = page.file.path;
return folderRegex.test(path);
});
// Crawl content and render list
const pages = await Promise.all(
allPages.map(async (page) => {
const content = await dv.io.load(page.file.path);
const regexPattern = new RegExp(\`.*(\${searchTerm}).*\`, "gmi");
const regexMatches = content.match(regexPattern);
if (regexMatches && regexMatches.length > 0) {
// Apply replacements to each matched content before storing in pages
const processedContent = regexMatches
.map(match => {
let processed = match.replace(/>/g, '>'); // Replace > with >
processed = processed.replace(/(^>\\s*)|(^-\\s*)/gm, ''); // Remove > and spaces at the start of each line
processed = processed.replace(new RegExp(\`(\${searchTerm})(?![^\\\\[]*\\\\]\\\\])\`, "gmi"), '==\$&==');
return processed;
})
.join('<br><br>'); // Separate each processed match with two line breaks
return {
link: page.file.link,
content: processedContent
};
}
return null;
})
);
// Apply additional filters and build the list string with proper formatting
const listOutput = pages
.filter(p => p !== null)
.filter(p => !filterRegex || !filterRegex.test(p.content)) // Exclude matches that meet the exclude filter condition
.filter(p => !additionalFilter || additionalFilter.test(p.content)) // Apply additional filter if provided
.map(p => \`\${p.link}\\n\${p.content}\` + \`<br><br>\`); // Include breaks between list items as well
// Output the formatted list excluding the searchTerm line
dv.list(listOutput.filter(line => !line.includes('const searchTerm')));
\`\`\`
\%\%
`;
this.app.workspace.activeLeaf.view.editor.setCursor({line: 99999, ch: 0});
const updatedContent = fileContent + '\n\n' + dynamicContent;
await app.vault.modify(currentFile, updatedContent);
_%>
- If one wants no filters to be applied, an enter must be pressed in the box (can do it twice in succession and be done with it).