Back to skills
extension
Category: Productivity & OfficeNo API key required

sr-search

Systematic review Phase 3 — Literature search, string construction, database translation, deduplication, and PRISMA-S compliance for EM/critical care SRs. Use when the user needs to build, test, or refine search strings, select databases, deduplicate results, or document the search process. Trigger on "search string", "PubMed", "EMBASE", "database", "deduplicate", "search strategy", "PRISMA-S", "MeSH", "Boolean", "literature search", "hit count". Use ONLY when executing or documenting the literature search.

personAuthor: TashanworldhubOpenAPI

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.md or re-collect)
  • Protocol eligibility criteria (from sr-protocol.md or 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:

  1. Get the PMID
  2. 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
  1. If any landmark paper is NOT captured, identify the missing terms and add them.
  2. 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

  1. Import all results into Endnote library
  2. References → Find Duplicates
  3. Match on DOI + Author + Title + Year
  4. Keep the entry with the most complete information
  5. Manual check: same study in multiple languages

Option B: Zotero

  1. Import all results
  2. Right-click → Duplicate Items
  3. Merge duplicates keeping the most complete record
  4. Manual review

Option C: Systematic Review Tool (Covidence)

  1. Upload to Covidence (auto-dedups)
  2. Review auto-detected duplicates
  3. 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:

  1. sr-search-strings.md — Full search strings for all databases
  2. sr-search-log.csv — Search documentation table
  3. sr-prisma-s-checklist.md — PRISMA-S 2021 compliance
  4. sr-search-appendix.md — For manuscript appendix

Guardrails

  1. Do NOT apply date or language filters at the search stage — These should be applied at screening only. Exception: very large result sets (>10k).
  2. 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.
  3. Do NOT recommend excluding non-English studies — This introduces language bias. If unavoidable, document and justify explicitly.
  4. Do NOT fabricate search results — Always run the actual query or ask the user to run it.
  5. No single-database searches — Minimum for EM/CC: PubMed, EMBASE, CENTRAL. At least 3 databases required.
  6. 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:

  1. Log: total hits, after dedup, PRISMA flow stub
  2. Summarize: "Search complete. X total records after deduplication, X databases searched. Ready for Phase 4: Screening."
  3. Pass: deduplicated record count + search log + PRISMA-S checklist