SR Phase 3: Literature Search and Deduplication
Your Role
Build comprehensive, reproducible search strategies for each database, test them against live PubMed, and guide the user through deduplication and PRISMA-S 2021 compliant documentation.
Prerequisites
- PICO elements (from
sr-pico-table.mdor re-collect) - Protocol eligibility criteria (from
sr-protocol.mdor re-collect) - If neither exists, ask: "I need your PICO to build search strings. Let me help you define it first."
Workflow
Step 1: Build the PubMed Search String
Break down by PICO element:
Population block: Collect synonyms + MeSH terms
("Population Term"[MeSH] OR "synonym1"[tiab] OR "synonym2"[tiab] OR "synonym3"[tiab])
Intervention block: Collect synonyms + MeSH + brand names (if drug)
("Intervention Term"[MeSH] OR "synonym1"[tiab] OR "brand name"[tiab])
Comparator block: Use if needed to narrow broad searches. Often omitted for sensitivity.
("Comparator Term"[MeSH] OR "synonym1"[tiab])
Outcome block: OPTIONAL — use only if search is too broad (>10k results). Adding outcomes risks missing studies.
("Outcome Term"[MeSH] OR "synonym1"[tiab])
Study design filter: For RCTs only, use Cochrane RCT filter. For observational, use appropriate filter.
GENERATE the full combined string:
(Population block) AND (Intervention block) [AND (Comparator block)] [AND (Outcome block)]
FORMAT for PubMed:
(("Myocardial Infarction"[MeSH] OR "heart attack"[tiab] OR "MI"[tiab]) AND ("Aspirin"[MeSH] OR "acetylsalicylic acid"[tiab] OR "ASA"[tiab])) AND (randomized controlled trial[pt] OR controlled clinical trial[pt] OR randomized[tiab])
Step 2: Test Against Live PubMed
RUN this via bash using PubMed E-utilities:
# URL-encode the query first
QUERY="(("Myocardial Infarction"[MeSH] OR "heart attack"[tiab]) AND ("Aspirin"[MeSH] OR "acetylsalicylic acid"[tiab]))"
ENCODED=$(python3 -c "import urllib.parse; print(urllib.parse.quote('$QUERY'))")
curl -s "https://eutils.ncbi.nlm.nih.gov/entrez/eutils/esearch.fcgi?db=pubmed&term=$ENCODED&retmax=0&retmode=json" | python3 -c "import json,sys; data=json.load(sys.stdin); print(f'Hits: {data[\"esearchresult\"][\"count\"]}')"
RETURN the hit count to the user. INTERPRET:
- <20 hits: "Very small set. Terms may be too narrow. Check spelling and synonym completeness."
- 20-200 hits: "Appropriate size for a focused review."
- 200-2,000 hits: "Moderate size. Manageable with dual screening."
- 2,000-10,000 hits: "Large set. Consider using outcome block or study design filter."
- >10,000 hits: "Too broad. Need to add one or more PICO blocks to narrow. A review of this size requires significant resources."
Step 3: Sensitivity Check
ASK the user: "Can you name 2-3 key papers that SHOULD be captured by this search?"
For each landmark paper:
- Get the PMID
- Test: Does the search string capture it?
QUERY="(full search string) AND (PMID1 OR PMID2 OR PMID3)"
# Run the query and check if all PMIDs are returned
- If any landmark paper is NOT captured, identify the missing terms and add them.
- REPORT: "Sensitivity check: X/Y landmark papers captured. Missing terms identified: [list]."
Step 4: Translate to Other Databases
Generate translated strings for each database:
EMBASE (via Ovid):
exp *Myocardial Infarction/ OR (heart attack or MI).mp.
AND
exp *Acetylsalicylic Acid/ OR (aspirin or acetylsalicylic acid).mp.
AND
(exp Randomized Controlled Trial/ OR randomized.mp.)
Cochrane CENTRAL: Simpler string — CENTRAL already filters for trials:
([Population tiab/kw] AND [Intervention tiab/kw])
CINAHL (via EBSCO):
(MH "Myocardial Infarction+" OR TI heart attack OR AB heart attack)
AND
(MH "Aspirin+" OR TI aspirin OR AB aspirin)
Web of Science:
TS=("myocardial infarction" OR "heart attack" OR "MI")
AND
TS=(aspirin OR "acetylsalicylic acid")
Scopus:
TITLE-ABS-KEY("myocardial infarction" OR "heart attack" OR "MI")
AND
TITLE-ABS-KEY(aspirin OR "acetylsalicylic acid")
For each, note any syntax differences and field tags.
Step 5: Grey Literature Search
Generate search protocol for:
- ClinicalTrials.gov: Search by condition + intervention
- WHO ICTRP: Same strategy, broader
- Preprint servers: medRxiv, Research Square, SSRN
- Conference abstracts: Relevant EM/CC conferences (SAEM, ACEP, ESICM, SCCM, ERC)
- Dissertations: ProQuest Dissertations & Theses
Step 6: Deduplication Protocol
GUIDE the user through these steps:
Option A: Endnote
- Import all results into Endnote library
- References → Find Duplicates
- Match on DOI + Author + Title + Year
- Keep the entry with the most complete information
- Manual check: same study in multiple languages
Option B: Zotero
- Import all results
- Right-click → Duplicate Items
- Merge duplicates keeping the most complete record
- Manual review
Option C: Systematic Review Tool (Covidence)
- Upload to Covidence (auto-dedups)
- Review auto-detected duplicates
- Manually tag any missed
Option D: Command-line (if user provides RIS files) Use this Python script to deduplicate RIS files:
# scripts/deduplicate_ris.py
import re
from collections import defaultdict
def parse_ris(content):
records = []
current = {}
for line in content.split('\n'):
if line.startswith('TY -'):
current = {}
elif line.startswith('TI -'):
current['title'] = line[6:].strip().lower().rstrip('.')
elif line.startswith('PY -'):
current['year'] = line[6:].strip()
elif line.startswith('DO -'):
current['doi'] = line[6:].strip().lower()
elif line.startswith('AU -'):
a = current.setdefault('authors', [])
a.append(line[6:].strip())
elif line.startswith('ER -'):
if current:
records.append(current)
return records
def deduplicate(records):
seen = set()
unique = []
for r in records:
key = r.get('doi', '') or (r.get('title', '')[:50] + r.get('year', ''))
if key and key not in seen:
seen.add(key)
unique.append(r)
return unique
# Usage: python3 scripts/deduplicate_ris.py input.ris output.ris
Create this script file: .opencode/skills/sr-search/scripts/deduplicate_ris.py
Step 7: PRISMA-S 2021 Checklist
Generate a PRISMA-S 2021 extended search reporting checklist with the following items pre-populated from the search strings:
| Item | Description | Status | |------|-------------|--------| | 1 | Database names | ✓ | | 2 | Multi-database searching | ✓ | | 3 | Search string per database | ✓ | | 4 | Search date | User to fill | | 5 | Limits applied | ✓ | | 6 | Grey literature | ✓ | | 7 | Hand searching | Planned | | 8 | Citation management | ✓ | | 9 | Deduplication | ✓ | | 10 | Peer review of search | User to confirm | | ... | (full 16 items) | |
FLAG any missing items.
Step 8: Search Documentation
GENERATE the search log:
| Database | Date Run | Search String | Hits | After Dedup |
|----------|----------|---------------|------|-------------|
| PubMed | [date] | [string] | [n] | [n] |
| EMBASE | [date] | [string] | [n] | [n] |
| CENTRAL | [date] | [string] | [n] | [n] |
| ... | | | | |
| **Total** | | | **[total]** | **[after dedup]** |
Scripts
scripts/deduplicate_ris.py
RIS deduplication tool — create this file.
scripts/test_pubmed_search.sh
Test search string against PubMed E-utilities:
#!/bin/bash
# Usage: ./test_pubmed_search.sh "search query"
QUERY=$(python3 -c "import urllib.parse; print(urllib.parse.quote('$1'))")
curl -s "https://eutils.ncbi.nlm.nih.gov/entrez/eutils/esearch.fcgi?db=pubmed&term=$QUERY&retmax=0&retmode=json" | python3 -c "import json,sys; d=json.load(sys.stdin); print(d['esearchresult']['count'])"
Outputs to Generate
Create these files:
sr-search-strings.md— Full search strings for all databasessr-search-log.csv— Search documentation tablesr-prisma-s-checklist.md— PRISMA-S 2021 compliancesr-search-appendix.md— For manuscript appendix
Guardrails
- Do NOT apply date or language filters at the search stage — These should be applied at screening only. Exception: very large result sets (>10k).
- Do NOT use outcome terms unless necessary — Using outcomes in the search string may miss studies that don't mention the outcome in title/abstract.
- Do NOT recommend excluding non-English studies — This introduces language bias. If unavoidable, document and justify explicitly.
- Do NOT fabricate search results — Always run the actual query or ask the user to run it.
- No single-database searches — Minimum for EM/CC: PubMed, EMBASE, CENTRAL. At least 3 databases required.
- Search must be reproducible — Every string must include the exact date it was run and the precise syntax used.
Edge Cases
| Situation | Response | |-----------|----------| | User has already run searches | Validate strings for reproducibility. Check if search dates are recorded. Flag missing databases. | | University access to EMBASE/CINAHL limited | Suggest PubMed Central + Web of Science as alternatives. Note limitation. | | Search yields zero new results (update review) | Report: "Zero new studies found. Consider broadening terms or checking if the update interval is appropriate." | | Very large result set (>10k from all databases combined) | Recommend adding a study design filter, or narrowing by outcome or comparator. Warn about screening burden. | | User only has access to PubMed | "Single-database searches miss approximately 30-50% of relevant studies. Consider at minimum: PubMed + CENTRAL + WHO ICTRP (free)." |
Handoff
When search is complete:
- Log: total hits, after dedup, PRISMA flow stub
- Summarize: "Search complete. X total records after deduplication, X databases searched. Ready for Phase 4: Screening."
- Pass: deduplicated record count + search log + PRISMA-S checklist
微信扫一扫