{"id":822,"date":"2025-02-07T04:10:02","date_gmt":"2025-02-06T20:10:02","guid":{"rendered":"https:\/\/yanghaixiong.com\/?p=822"},"modified":"2025-02-07T04:10:02","modified_gmt":"2025-02-06T20:10:02","slug":"obsidian%e6%8f%92%e4%bb%b6dataview-%e5%ae%9e%e7%94%a8%e6%a1%88%e4%be%8b%e8%ae%b2%e8%a7%a3%ef%bc%88%e9%ab%98%e7%ba%a7%e7%af%87%ef%bc%89%ef%bc%88%e5%8d%81%e5%9b%9b%ef%bc%89","status":"publish","type":"post","link":"https:\/\/yanghaixiong.com\/blog\/skills\/822\/","title":{"rendered":"Obsidian\u63d2\u4ef6Dataview \u2014\u2014 \u5b9e\u7528\u6848\u4f8b\u8bb2\u89e3\uff08\u9ad8\u7ea7\u7bc7\uff09\uff08\u5341\u56db\uff09"},"content":{"rendered":"
\u8fd9\u7bc7\u6587\u7ae0\u662f Obsidian Dataview \u7cfb\u5217<\/a> \u7cfb\u5217 15 \u7bc7\u6587\u7ae0\u4e2d\u7684\u7b2c 14 \u7bc7<\/div>\n

\n [!quote] \u80cc\u666f
\n \u4e3b\u8981\u68b3\u7406\u4e86 Dataview \u793a\u4f8b\u5e93\u00a0Dataview Example Valut<\/strong>\u00a0\u4e2d\u7684\u4e00\u4e9b\u6848\u4f8b\n<\/p><\/blockquote>\n

\u4e09\u3001\u9ad8\u7ea7\u7bc7\uff1aDataview \u9ad8\u7ea7\u6280\u5de7\u4e0e\u63a2\u7d22<\/h2>\n

\u9ad8\u7ea7\u7bc7\u7684\u5185\u5bb9\u4e3b\u8981\u662f\u4e00\u4e9b\u4e0d\u5e38\u7528\uff0c\u4f46\u662f\u5b9e\u7528\uff0c\u9700\u8981\u66f4\u591a\u7684\u4ee3\u7801\u7684\u5185\u5bb9\uff0c\u6216\u8005\u9700\u8981\u7ed3\u5408\u7b2c\u4e09\u65b9\u63d2\u4ef6\u7684\u5185\u5bb9\u3002<\/p>\n

1. \u8868\u683c\u5217\u6c42\u548c<\/h3>\n

\u5728\u7535\u5b50\u8868\u683c\u4e2d\uff0c\u6211\u4eec\u53ef\u4ee5\u5bf9\u5217\u6570\u636e\u8fdb\u884c\u8fd0\u7b97\uff0c\u5982\u6c42\u548c\u3001\u672a\u5e73\u5747\u503c\u7b49\u3002\u4e0b\u9762\u6211\u4eec\u6765\u770b\u4e00\u4e0b\u5982\u4f55\u5728 Dataview \u67e5\u8be2\u7684\u7ed3\u679c\u4e2d\u5b9e\u73b0\u5bf9\u5217\u6570\u636e\u7684\u6c42\u548c\u3002<\/p>\n

const query = `TABLE praying, training, situps, steps\nFROM \"10 Example Data\/dailys\"\nWHERE file.day.month = 2`\n\nconst nameOfTotalRow = \"Sums\";\n\nlet DQL = await dv.tryQuery(query);\nconst sums = [nameOfTotalRow];\n\n\/\/ \u5982\u679c\u5728 DQL \u67e5\u8be2\u8bed\u53e5\u4e2d\u6dfb\u52a0\u4e86 `WITHOUT ID`\uff0c\u8fd9\u91cc\u5c31\u9700\u8981\u6539\u6210\u4ece `0` \u5f00\u59cb\u904d\u5386\nfor (let i = 1; i < DQL.headers.length; i++) {\n    let sum = 0;\n    const dataType = getDatatypeOfColumn(i, DQL.values)\n\n    \/\/ \u53ea\u6709\u6570\u5b57\u548c\u6301\u7eed\u65f6\u95f4\u7684\u6570\u636e\u7c7b\u578b\u624d\u4f1a\u88ab\u8ba1\u7b97\n    if (![\"number\", \"duration\"].includes(dataType)) {\n        sums.push(\"\")\n        continue;\n    }\n\n    \/\/ \u8ba1\u7b97\u6bcf\u4e00\u5217\u7684\u603b\u548c\n    for (let k = 0; k < DQL.values.length; k++) {\n        \/\/ \u884c `k`, \u5217 `i` \u7684\u503c\n        let currentValue = DQL.values[k][i];\n        if (currentValue) sum += currentValue \n    }\n    if (!sum) sum = \"\"\n    sums.push(dataType === \"duration\" ? dv.luxon.Duration.fromMillis(sum) : sum);\n}\n\nfunction getDatatypeOfColumn(columnNo, values) {\n    let i = 0;\n    let datatype;\n    while (i < DQL.values[0].length && (!datatype || datatype === \"null\")) {\n        datatype = dv.func.typeof(DQL.values[i][columnNo])\n        i++;\n    }\n    return datatype;\n}\n\n\/\/ \u6dfb\u52a0\u5206\u9694\u7ebf\nlet hrArray = Array(DQL.headers.length).fill('<hr style=\"padding:0; margin:0 -10px;\">');\nDQL.values.push(hrArray)\nDQL.values.push(sums)\n\ndv.table(DQL.headers, DQL.values)\n<\/code><\/pre>\n

\u7ed3\u679c\uff1a
\n\"image.png|1000\"<\/p>\n

2. \u5728\u6587\u6863\u4e2d\u641c\u7d22\u6587\u5b57<\/h3>\n

\u8981\u5728\u6587\u6863\u4e2d\u641c\u7d22\u6307\u5b9a\u7684\u5355\u8bcd\uff0c\u6211\u4eec\u9996\u5148\u9700\u8981\u5c06\u6587\u4ef6\u8bfb\u5230\u5185\u5bb9\u4e2d\uff0c\u53ef\u4f7f\u7528\u00a0dv.io.load()<\/code>\u00a0\u65b9\u6cd5\uff0c\u7136\u540e\u518d\u901a\u8fc7\u6b63\u5219\u53bb\u5339\u914d\u6587\u672c\u3002<\/p>\n

const word = \"but\"\n\nconst regex = new RegExp(\"(\\\\S+\\\\s?){0,2}(\\\\b\"+word+\"\\\\b)(\\\\s\\\\S+){0,2}\", \"gi\")\nconst pages = await Promise.all(\n    dv.pages('\"30 Dataview Resources\"')\n    .map(async (page) => {\n        const content = await dv.io.load(page.file.path);\n        const matches = content.match(regex);\n        return {\n            link: page.file.link,\n            count: ( matches || []).length,\n            matches\n        };\n    })\n)\n\ndv.table(\n        [\"Note\", \"Count\", `Matches for \"${word}\"`],\n            pages\n            .filter(p => p.count)\n            .sort((a, b) => b.count - a.count)\n            .map(p => [p.link, p.count, p.matches])  \n    );\n<\/code><\/pre>\n

\u7ed3\u679c\uff1a
\n\"image.png|1000\"
\n\u4e0a\u9762\u7684\u6b63\u5219\u8868\u8fbe\u5f0f\u4e2d \\b<\/code> \u7528\u4e8e\u5339\u914d\u5355\u8bcd\u8fb9\u754c\uff08\u82f1\u6587\u53e5\u5b50\u5355\u8bcd\u4e4b\u95f4\u4ee5\u7a7a\u683c\u5206\u9694\uff09\uff0c\u7136\u540e\u6700\u5de6\u8fb9\u7684 (\\\\S+\\\\s?){0,2}<\/code> \u548c\u53f3\u8fb9\u7684 (\\\\s\\\\S+){0,2}<\/code> \u7528\u4e8e\u5339\u914d\u76ee\u6807\u5355\u8bcd\u524d\u540e\u7684\u4e24\u4e2a\u76f8\u90bb\u5355\u8bcd\u3002\u57fa\u4e2d +<\/code> \u7b26\u8868\u793a\u5339\u914d 1 \u6b21\u6216\u591a\u6b21\uff0c?<\/code> \u8868\u793a\u5339\u914d 0 \u6b21\u6216\u591a\u6b21\uff0c\\s<\/code> \u8868\u793a\u5339\u914d\u4e00\u4e2a\u7a7a\u767d\u5b57\u7b26\uff08\u5305\u62ec\u7a7a\u683c\u3001\u5236\u8868\u7b26\u3001\u6362\u9875\u7b26\u548c\u6362\u884c\u7b26)\uff0c\\S<\/code> \u8868\u793a\u5339\u914d\u4e00\u4e2a\u975e\u7a7a\u767d\u7b26\u3002dv.io.load()<\/code> \u65b9\u6cd5\u7528\u4e8e\u5c06\u6587\u4ef6\u52a0\u8f7d\u5230\u5185\u5b58\u4e2d\u3002<\/p>\n

3. \u4f7f\u7528\u9009\u9879\u5361\u5207\u6362\u6570\u636e<\/h3>\n

\u5728\u67e5\u8be2\u6570\u636e\u65f6\uff0c\u6709\u7684\u6570\u636e\u4e0d\u540c\u7684\u72b6\u6001\u4f1a\u6709\u4e0d\u540c\u7684\u7ed3\u679c\uff0c\u6211\u4eec\u53ef\u4ee5\u6309\u72b6\u6001\u6765\u8fdb\u884c\u6761\u4ef6\u663e\u793a\u3002\u5c06\u72b6\u6001\u4f5c\u4e3a\u9009\u9879\u5361\uff0c\u800c\u5176\u5173\u8054\u7684\u5185\u5bb9\u4f5c\u4e3a\u9009\u9879\u5361\u5185\u5bb9\u3002<\/p>\n

const createButton = name => {\n    const btn = dv.el('button', name)\n    btn.addEventListener('click', () => {\n        event.preventDefault()\n        removeTable()\n        renderTable(name)\n    })\n\n    return btn\n}\n\nconst buttons = ['Watching', 'Going to watch', 'Watched all', 'Stopped watching']\n\nconst renderTable = name => {\n    const pages = dv.pages('\"10 Example Data\/shows\"').where(p => p.status === name)\n    dv.header(2, name)\n    dv.table(\n        ['Title', 'Rating', 'Runtime', 'Seasons', 'Episodes'],\n        pages.map(p => {\n            let watchedEp = 0\n            const totalEp = p.episodes\n\n            p.file.tasks.forEach(t => {\n                if (t.checked) {\n                    watchedEp++\n                }\n            })\n\n            return [p.file.link, p.rating, p.runtime, p.seasons, `${watchedEp}\/${totalEp}`]\n        })\n    )\n}\n\nconst removeTable = () => {\n    this.container.lastChild.remove()\n    this.container.lastChild.remove()\n}\n\nbuttons.forEach(button => createButton(button))\n\nrenderTable('Watching')\n<\/code><\/pre>\n

\u7ed3\u679c\uff1a
\n\"image.png|1000\"
\n\u4e0a\u9762\u7684\u4ee3\u7801\u4e2d\uff0c\u6211\u4eec\u4f7f\u7528\u00a0dv.el()<\/code>\u00a0\u6765\u521b\u5efa\u4e86\u6309\u94ae\u5e76\u6dfb\u52a0\u4e86\u4e8b\u4ef6\u5904\u7406\u903b\u8f91\u3002\u5728\u9009\u9879\u5361\u88ab\u9009\u4e2d\u65f6\uff0c\u6839\u636e\u9009\u9879\u5361\u540d\u53bb\u8fc7\u6ee4\u67e5\u8be2\u7ed3\u679c\uff0c\u5e76\u5c06\u4e0a\u4e00\u6b21\u6e32\u67d3\u7684 HTML \u8282\u70b9\u79fb\u9664\u6389\u3002<\/p>\n

\u8fdb\u4e00\u6b65\uff0c\u6211\u4eec\u8fd8\u53ef\u4ee5\u5b9e\u73b0\u540c\u4e00\u4efd\u6570\u636e\u7ed3\u679c\u4ee5\u4e0d\u540c\u7684\u65b9\u5f0f\u6e32\u67d3\uff1a<\/p>\n

const views = ['Table', 'List', 'Tasks']\n\nconst changeView = viewName => {\n    removeView()\n\n    if (viewName === 'Table') {\n        dv.header(2, 'Some table')\n        dv.table(['File', 'Day'], dv.pages('\"10 Example Data\/dailys\"').limit(7).map(p => [p.file.link, p.day]))\n    }\n\n    if (viewName == 'List') {\n        dv.list(dv.pages('\"10 Example Data\/dailys\"').limit(7).file.name)\n    }\n\n    if (viewName == 'Tasks') {\n        dv.taskList(dv.page(\"10 Example Data\/projects\/project_2\").file.tasks)\n    }\n}\n\nconst createButtons = () => {\n    const buttonContainer = dv.el('div', '', {cls: 'tabButtons'})\n    views.forEach(view => {\n        const button = dv.el('button', view)\n\n        button.addEventListener('click', event => {\n            event.preventDefault()\n            changeView(view)\n        })\n\n        buttonContainer.append(button)\n    })\n}\n\nconst removeView = () => {\n    Array.from(this.container.children).forEach(el => {\n        if (!el.classList.contains('tabButtons')) {\n            el.remove()\n        }\n    })\n}\n\ncreateButtons()\n<\/code><\/pre>\n

4. \u4f7f\u7528\u4e0d\u540c\u7684\u8868\u60c5\u7b26\u6765\u663e\u793a\u65f6\u95f4\u7f00<\/h3>\n

\u8fd9\u4e2a\u6848\u4f8b\u6211\u4eec\u67e5\u8be2\u4efb\u52a1\u8ba1\u5212\u6570\u636e\uff0c\u6765\u83b7\u53d6\u672a\u5b8c\u6210\u7684\u4efb\u52a1\u8ddd\u79bb\u73b0\u5728\u8fc7\u53bb\u4e86\u591a\u957f\u65f6\u95f4\uff0c\u5e76\u5bf9\u5176\u6309\u65f6\u95f4\u957f\u5ea6\u81ea\u5b9a\u4e49\u4e0d\u540c\u7684\u8868\u60c5\u7b26\u6765\u663e\u793a\u5f97\u5206\u3002<\/p>\n