File size: 4,277 Bytes
e4f1db2
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
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;