diff --git a/.github/workflows/update-dblp-gql.yml b/.github/workflows/update-dblp-gql.yml new file mode 100644 index 000000000000..c30e42a84568 --- /dev/null +++ b/.github/workflows/update-dblp-gql.yml @@ -0,0 +1,32 @@ +name: Update DBLP GQL Publications + +on: + schedule: + - cron: '0 6 * * 2' # Every Tuesday at 06:00 UTC + workflow_dispatch: # Allow manual runs + +jobs: + update-dblp-gql: + runs-on: ubuntu-latest + permissions: + contents: write + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: 20 + + - name: Update DBLP GQL publications + run: node code/updateDblpGql.js + + - name: Commit updated scroll file + run: | + git config user.name "github-actions[bot]" + git config user.email "github-actions[bot]@users.noreply.github.com" + git add concepts/graph-query-language.scroll + git diff --cached --quiet || git commit -m "Update DBLP GQL publications (weekly)" + git push diff --git a/code/updateDblpGql.js b/code/updateDblpGql.js new file mode 100644 index 000000000000..a64c6d628eb0 --- /dev/null +++ b/code/updateDblpGql.js @@ -0,0 +1,162 @@ +'use strict' + +/** + * Fetches recent DBLP publications about GQL (Graph Query Language / ISO 39075) + * and updates concepts/graph-query-language.scroll with a `dblp` section. + * + * Searches run against the public DBLP JSON API (no key required). + * Results are filtered to the most recent YEARS years and deduplicated. + */ + +const https = require('https') +const fs = require('fs') +const path = require('path') + +const ROOT = path.join(__dirname, '..') +const SCROLL_FILE = path.join(ROOT, 'concepts', 'graph-query-language.scroll') +const DBLP_SEARCH_URL = 'https://dblp.org/search?q=GQL+graph+query+language' +const YEARS = 5 + +// DBLP search queries whose hits are merged and deduplicated. +// "GQL graph" matches papers about the ISO GQL standard. +// "SQL/PGQ" matches the companion SQL Property Graph Query standard. +const QUERIES = [ + 'GQL graph', + 'SQL/PGQ' +] + +// ─── HTTP helpers ───────────────────────────────────────────────────────────── + +function fetchUrl(url, timeoutMs = 15000) { + return new Promise((resolve, reject) => { + const req = https.get(url, { headers: { 'User-Agent': 'PLDB-DblpGql/1.0 (+https://pldb.info)' } }, res => { + if (res.statusCode < 200 || res.statusCode >= 300) { + res.resume() + return reject(new Error(`HTTP ${res.statusCode}`)) + } + let data = '' + res.setEncoding('utf8') + res.on('data', chunk => { data += chunk }) + res.on('end', () => resolve(data)) + res.on('error', reject) + }) + req.on('error', reject) + req.setTimeout(timeoutMs, () => req.destroy(new Error('DBLP fetch timed out'))) + }) +} + +function dblpApiUrl(query) { + return `https://dblp.org/search/publ/api?q=${encodeURIComponent(query)}&format=json&h=100&f=0` +} + +// ─── DBLP parsing ───────────────────────────────────────────────────────────── + +function decodeEntities(s) { + return String(s || '') + .replace(/&/g, '&') + .replace(/</g, '<') + .replace(/>/g, '>') + .replace(/"/g, '"') + .replace(/'|'/g, "'") + .replace(/&#(\d+);/g, (_, n) => String.fromCharCode(Number(n))) +} + +function getAuthors(info) { + const rawAuthors = info.authors?.author + const list = Array.isArray(rawAuthors) + ? rawAuthors + : rawAuthors && typeof rawAuthors === 'object' + ? [rawAuthors] + : [] + return list + .filter(a => a && typeof a.text === 'string') + .map(a => a.text) + .join(' and ') +} + +function safePsvCell(value) { + return String(value || '').replace(/\|/g, '-').replace(/\n/g, ' ').trim() +} + +async function fetchDblpQuery(query) { + const raw = await fetchUrl(dblpApiUrl(query)) + const data = JSON.parse(raw) + return data.result?.hits?.hit || [] +} + +// ─── Main export ────────────────────────────────────────────────────────────── + +async function updateDblpGql() { + const minYear = new Date().getFullYear() - YEARS + const seen = new Set() + const pubs = [] + + for (const q of QUERIES) { + let hits + try { + hits = await fetchDblpQuery(q) + } catch (err) { + console.warn(` ⚠️ DBLP query "${q}" failed: ${err.message}`) + continue + } + for (const h of hits) { + const info = h.info || {} + const year = parseInt(info.year || 0, 10) + if (year < minYear) continue + const key = info.key || info.url || '' + if (!key || seen.has(key)) continue + seen.add(key) + + const eeRaw = info.ee + const url = Array.isArray(eeRaw) ? eeRaw[0] : (eeRaw || info.url || '') + + pubs.push({ + year, + title: decodeEntities((info.title || '').replace(/\.$/, '')), + doi: info.doi || '', + url, + authors: getAuthors(info) + }) + } + } + + // Sort by year descending, then title ascending + pubs.sort((a, b) => b.year - a.year || a.title.localeCompare(b.title)) + + const hitCount = pubs.length + + // Build PSV table (indented under `publications`) + const psvHeader = 'year|title|doi|url' + const psvRows = pubs.map(p => + [p.year, safePsvCell(p.title), safePsvCell(p.doi), safePsvCell(p.url)].join('|') + ) + const psvLines = [psvHeader, ...psvRows] + + // Build the dblp block (Scroll indentation: 1 space for children, 2 for grandchildren) + const dblpBlock = + `dblp ${DBLP_SEARCH_URL}\n` + + ` hits ${hitCount}\n` + + ` publications\n` + + psvLines.map(line => ` ${line}`).join('\n') + + '\n' + + // Read scroll file and replace or append the dblp block + let content = fs.readFileSync(SCROLL_FILE, 'utf8') + + // Match from "dblp " through its indented children (lines starting with space/tab) + const dblpBlockRe = /^dblp [^\n]*\n(?:[ \t][^\n]*\n?)*/m + if (dblpBlockRe.test(content)) { + content = content.replace(dblpBlockRe, dblpBlock) + } else { + content = content.trimEnd() + '\n\n' + dblpBlock + } + + fs.writeFileSync(SCROLL_FILE, content) + console.log(` ✅ DBLP GQL: ${hitCount} publications (${minYear}–present) written to graph-query-language.scroll`) +} + +module.exports = { updateDblpGql } + +if (require.main === module) { + updateDblpGql().catch(err => { console.error(err); process.exit(1) }) +} diff --git a/concepts/graph-query-language.scroll b/concepts/graph-query-language.scroll index b10dc14730fd..e81a9459d8f5 100644 --- a/concepts/graph-query-language.scroll +++ b/concepts/graph-query-language.scroll @@ -52,3 +52,22 @@ wikipedia https://en.wikipedia.org/wiki/Graph_Query_Language backlinksCount 500 revisionCount 190 appeared 2024 + +dblp https://dblp.org/search?q=GQL+graph+query+language + hits 14 + publications + year|title|doi|url + 2026|GQL Rules: Deterministic and Safe Rule-Based Materialization for Property Graphs|10.1109/ACCESS.2026.3686122|https://doi.org/10.1109/ACCESS.2026.3686122 + 2025|GQL and SQL/PGQ: Theoretical Models and Expressive Power|10.14778/3725688.3725707|https://www.vldb.org/pvldb/vol18/p1798-libkin.pdf + 2025|Mine Graph Rule: A New GQL Operator for Mining Association Rules in Property Graph Databases|10.1007/S00778-025-00934-8|https://doi.org/10.1007/s00778-025-00934-8 + 2025|Parallel and Distributed SQL/PGQ Query Processing for Property Graphs|10.1007/978-3-032-02215-8_12|https://doi.org/10.1007/978-3-032-02215-8_12 + 2025|Towards Cross-Model Efficiency in SQL/PGQ|10.1145/3735546.3735860|https://doi.org/10.1145/3735546.3735860 + 2025|Towards Cross-Model Efficiency in SQL/PGQ|10.48550/ARXIV.2505.07595|https://doi.org/10.48550/arXiv.2505.07595 + 2024|Chimera: A system design of dual storage and traversal-join unified query processing for SQL/PGQ|10.14778/3705829.3705845|https://www.vldb.org/pvldb/vol18/p279-lee.pdf + 2024|GQL and SQL/PGQ: Theoretical Models and Expressive Power|10.48550/ARXIV.2409.01102|https://doi.org/10.48550/arXiv.2409.01102 + 2023|DuckPGQ: Bringing SQL/PGQ to DuckDB|10.14778/3611540.3611614|https://www.vldb.org/pvldb/vol16/p4034-wolde.pdf + 2023|Evaluating Regular Path Queries in GQL and SQL/PGQ: How Far Can The Classical Algorithms Take Us?|10.48550/ARXIV.2306.02194|https://doi.org/10.48550/arXiv.2306.02194 + 2023|SQL/PGQ: A Systems Perspective (Abstract)||https://ceur-ws.org/Vol-3409/keynote04.pdf + 2022|Graph Pattern Matching in GQL and SQL/PGQ|10.1145/3514221.3526057|https://doi.org/10.1145/3514221.3526057 + 2022|Graph Query Language (GQL)-structured Algorithms for Geospatial Intelligence on Public Transportation|10.1109/ICTC55196.2022.9952570|https://doi.org/10.1109/ICTC55196.2022.9952570 + 2021|Graph Pattern Matching in GQL and SQL/PGQ||https://arxiv.org/abs/2112.06217