scopusflow: A literature search you can rerun
A spreadsheet exported from Scopus contains papers, but it does not contain the search that found them. The exact query, retrieval date, paging choices and number of records returned by each part of the search often end up in notes or browser history. When the search has to be updated, it can be difficult to tell whether a changed result reflects the literature or the method.
I wrote scopusflow to keep those decisions with the records. A search begins as a plan, each completed part is cached and the final object can write its own search record. There are matching packages for R and Python, developed in the same GitHub repository.
This example uses a small corpus bundled with the R package. A stand-in server answers the same requests as the Scopus API, so the article can be rebuilt without an API key or a network request to Elsevier.
library(scopusflow)
packageVersion('scopusflow')#> [1] '0.4.0'Describe the search first
The bundled example follows research on graphene supercapacitors. Query helpers add the field syntax, while scopus_plan() divides the work into yearly cells. The resulting object can be inspected and saved before any quota is spent.
query <- scopus_query('graphene supercapacitor', .field = 'TITLE-ABS-KEY')
plan <- scopus_plan(query, years = 2015:2024, partition = 'year')
query#> [1] "TITLE-ABS-KEY(graphene supercapacitor)"plan#> <scopus_plan> (10 cells, view "STANDARD", partition "year")
#> # A tibble: 10 × 6
#> cell query date year view page_size
#> * <int> <chr> <chr> <int> <chr> <int>
#> 1 1 TITLE-ABS-KEY(graphene supercapacitor) 2015 2015 STANDARD 200
#> 2 2 TITLE-ABS-KEY(graphene supercapacitor) 2016 2016 STANDARD 200
#> 3 3 TITLE-ABS-KEY(graphene supercapacitor) 2017 2017 STANDARD 200
#> 4 4 TITLE-ABS-KEY(graphene supercapacitor) 2018 2018 STANDARD 200
#> 5 5 TITLE-ABS-KEY(graphene supercapacitor) 2019 2019 STANDARD 200
#> 6 6 TITLE-ABS-KEY(graphene supercapacitor) 2020 2020 STANDARD 200
#> 7 7 TITLE-ABS-KEY(graphene supercapacitor) 2021 2021 STANDARD 200
#> 8 8 TITLE-ABS-KEY(graphene supercapacitor) 2022 2022 STANDARD 200
#> 9 9 TITLE-ABS-KEY(graphene supercapacitor) 2023 2023 STANDARD 200
#> 10 10 TITLE-ABS-KEY(graphene supercapacitor) 2024 2024 STANDARD 200The plan already supports a draft methods description. Any details unavailable at this stage are marked as unrecorded. The report follows PRISMA-S, the reporting extension for literature searches (Rethlefsen et al., 2021).
cat(format(scopus_search_report(plan), style = 'paragraph'))#> The search described here has not been run. The search expression is TITLE-ABS-KEY(graphene supercapacitor), limited to publication years 2015 to 2024. It would be partitioned into 10 cells, one per year, each retrieved through the STANDARD view in pages of 200 records, under a paging mode chosen when the search is run. The PRISMA-S items this record cannot supply, among them peer review of the strategy, grey literature and any other database searched, remain yours to report.Resume from the last completed cell
The bundled corpus now stands in for the result of that same plan. The cache is deliberately empty at the start of the example.
source('stand_in.R')
stand_in <- scopus_stand_in(example_records)
options(scopusflow.api_key = 'offline-demo-key')
cache <- file.path(tempdir(), 'scopusflow-blog-cache')
unlink(cache, recursive = TRUE)
httr2::with_mocked_responses(stand_in$handler, {
records <- scopus_fetch_plan(plan, cache_dir = cache, resume = TRUE)
})
c(records = nrow(records), requests = stand_in$state$requests)#> records requests
#> 138 10Each year is written to its own checkpoint. Repeating the plan reloads those files and makes no further request.
requests_before <- stand_in$state$requests
httr2::with_mocked_responses(stand_in$handler, {
repeated <- scopus_fetch_plan(plan, cache_dir = cache, resume = TRUE)
})
c(records = nrow(repeated),
new_requests = stand_in$state$requests - requests_before)#> records new_requests
#> 138 0The practical benefit is clearest after an interruption. Work already completed is retained, while the query, years, view and page size stored beside each checkpoint prevent an incompatible plan from silently reusing it.
Inspect the result
The records retain their plan, retrieval time, package version and count for each cell. That provenance is enough to plot the development of the bundled literature and later distinguish a changed search from a changed method.
httr2::with_mocked_responses(stand_in$handler, {
trend <- scopus_trend('graphene supercapacitor', years = 2015:2024,
field = 'TITLE-ABS-KEY')
})
plot_scopus_trend(trend) +
ggplot2::labs(
title = 'Records in the demonstration corpus',
caption = 'Offline OpenAlex-derived demonstration records, 2015-2024'
) +
ggplot2::theme(
plot.title = ggplot2::element_text(size = 11, face = 'bold'),
axis.title = ggplot2::element_text(size = 10),
axis.text = ggplot2::element_text(size = 9),
plot.caption = ggplot2::element_text(size = 8.5, colour = 'grey35')
)
An update can be compared by DOI. In this illustration, the baseline ends in 2023 and the later harvest includes the 2024 records.
baseline <- records[records$year <= 2023, ]
changes <- scopus_diff_dois(old = baseline, new = records)
changes#> <scopus_doi_diff> 14 added, 0 removed, 113 unchanged
#> # A tibble: 127 × 2
#> doi status
#> <chr> <fct>
#> 1 10.1002/adfm.202315137 added
#> 2 10.1002/asia.202400548 added
#> 3 10.1002/slct.202302535 added
#> 4 10.1016/j.cej.2024.148822 added
#> 5 10.1016/j.diamond.2024.110842 added
#> 6 10.1016/j.isci.2024.111696 added
#> 7 10.1016/j.jallcom.2024.175000 added
#> 8 10.1016/j.jallcom.2024.177248 added
#> 9 10.1016/j.jpowsour.2024.234127 added
#> 10 10.1016/j.jpowsour.2024.236149 added
#> # ℹ 117 more rowsLet the result write the record
The search record is generated from the object that contains the publications. It reports what the software knows and identifies the PRISMA-S items that still require the review author’s judgement, such as citation searching or peer review of the strategy. Because this run used a stand-in rather than Scopus, the compact summary below omits the database, platform and retrieval date that a live report would supply.
| Detail | Value |
|---|---|
| Search expression | TITLE-ABS-KEY(graphene supercapacitor) |
| Years | 2015 to 2024 |
| Records in bundled corpus | 138 |
| Records with a DOI | 127 |
| PRISMA-S checklist items | 16 |
The same records can then leave R as a DOI list, BibTeX, RIS or a table for bibliometrix. Saving the native object preserves the attached provenance for a future update.
Limits
The offline corpus demonstrates the workflow. Its results do not represent the contents of Scopus. A live search still depends on Elsevier’s coverage, indexing and quota, and abstract retrieval uses a separate allowance. Scopus records also cannot be redistributed, which is why this article uses records obtained from OpenAlex and reshaped to the package’s return format.
The R reference and Python reference cover cursor paging, de-duplication, topic comparisons and live authentication. For a review, I would keep the plan under version control, save the native result object and archive the generated search record with the screening data.
References
Rethlefsen, M. L., Kirtley, S., Waffenschmidt, S., Ayala, A. P., Moher, D., Page, M. J., & Koffel, J. B. (2021). PRISMA-S: An extension to the PRISMA statement for reporting literature searches in systematic reviews. Systematic Reviews, 10(1), Article 39. https://doi.org/10.1186/s13643-020-01542-z
Comments are provided by Disqus and are not loaded automatically. Loading them connects your browser to Disqus, which may use cookies and process data under its privacy policy. See this site's privacy notice for details.