Spaces:
Running
Running
const express = require('express'); | |
const axios = require('axios'); | |
const xml2js = require('xml2js'); | |
const NodeCache = require('node-cache'); | |
const router = express.Router(); | |
const cache = new NodeCache({ stdTTL: 3600 }); | |
const PUBMED_BASE_URL = 'https://eutils.ncbi.nlm.nih.gov/entrez/eutils'; | |
async function searchPubMed(query, maxResults = 100) { | |
try { | |
const cacheKey = `search_${query}_${maxResults}`; | |
const cached = cache.get(cacheKey); | |
if (cached) return cached; | |
// Add filters to exclude review papers, meta-analyses, and systematic reviews | |
const filteredQuery = `(${query}) NOT Review[Publication Type] NOT Meta-Analysis[Publication Type] NOT Systematic Review[Publication Type]`; | |
const searchUrl = `${PUBMED_BASE_URL}/esearch.fcgi?db=pubmed&term=${encodeURIComponent(filteredQuery)}&retmax=${maxResults}&retmode=json`; | |
const response = await axios.get(searchUrl); | |
const result = { | |
count: parseInt(response.data.esearchresult.count), | |
ids: response.data.esearchresult.idlist || [] | |
}; | |
cache.set(cacheKey, result); | |
return result; | |
} catch (error) { | |
throw new Error(`PubMed search failed: ${error.message}`); | |
} | |
} | |
async function fetchPaperDetails(ids) { | |
try { | |
if (!ids.length) return []; | |
const cacheKey = `details_${ids.join(',')}`; | |
const cached = cache.get(cacheKey); | |
if (cached) return cached; | |
const fetchUrl = `${PUBMED_BASE_URL}/efetch.fcgi?db=pubmed&id=${ids.join(',')}&retmode=xml`; | |
// Add timeout and retry logic for PubMed API | |
const response = await axios.get(fetchUrl, { | |
timeout: 10000, | |
headers: { | |
'User-Agent': 'PubMedAIExplorer/1.0' | |
} | |
}); | |
const parser = new xml2js.Parser(); | |
const result = await parser.parseStringPromise(response.data); | |
const papers = []; | |
const articles = result.PubmedArticleSet?.PubmedArticle || []; | |
articles.forEach(article => { | |
const medlineCitation = article.MedlineCitation?.[0]; | |
if (medlineCitation) { | |
const articleData = medlineCitation.Article?.[0]; | |
if (articleData) { | |
papers.push({ | |
pmid: medlineCitation.PMID?.[0]?._, | |
title: articleData.ArticleTitle?.[0] || 'No title', | |
abstract: articleData.Abstract?.AbstractText?.[0]?._ || articleData.Abstract?.AbstractText?.[0] || '', | |
authors: articleData.AuthorList?.Author?.map(author => { | |
const lastName = author.LastName?.[0] || ''; | |
const foreName = author.ForeName?.[0] || ''; | |
return `${foreName} ${lastName}`.trim(); | |
}) || [], | |
journal: articleData.Journal?.Title?.[0] || '', | |
pubDate: medlineCitation.DateCompleted?.[0]?.Year?.[0] || | |
medlineCitation.DateRevised?.[0]?.Year?.[0] || '' | |
}); | |
} | |
} | |
}); | |
cache.set(cacheKey, papers); | |
return papers; | |
} catch (error) { | |
throw new Error(`Failed to fetch paper details: ${error.message}`); | |
} | |
} | |
router.get('/search', async (req, res) => { | |
try { | |
const { query, maxResults = 20 } = req.query; | |
if (!query) { | |
return res.status(400).json({ error: 'Query parameter is required' }); | |
} | |
const searchResult = await searchPubMed(query, maxResults); | |
const papers = await fetchPaperDetails(searchResult.ids.slice(0, 10)); | |
res.json({ | |
totalCount: searchResult.count, | |
papers: papers | |
}); | |
} catch (error) { | |
res.status(500).json({ error: error.message }); | |
} | |
}); | |
router.get('/paper/:pmid', async (req, res) => { | |
try { | |
const { pmid } = req.params; | |
console.log(`Fetching paper details for PMID: ${pmid}`); | |
const papers = await fetchPaperDetails([pmid]); | |
console.log(`Found ${papers.length} papers for PMID: ${pmid}`); | |
if (papers.length === 0) { | |
console.log(`No paper found for PMID: ${pmid}`); | |
return res.status(404).json({ error: 'Paper not found' }); | |
} | |
console.log(`Returning paper:`, papers[0].title); | |
res.json(papers[0]); | |
} catch (error) { | |
const { pmid } = req.params; | |
console.error(`Error fetching paper ${pmid}:`, error.message); | |
res.status(500).json({ error: error.message }); | |
} | |
}); | |
module.exports = router; |