From 2646f0a0785084c17d8004cef72d426681f2a136 Mon Sep 17 00:00:00 2001 From: Gregory Jefferis Date: Mon, 10 Jan 2022 12:59:52 +0000 Subject: [PATCH] implement where searches for ids/meta * new syntax might need thinking about --- R/name.R | 44 ++++++++++++++++++++++++++++++-------- man/neuprint_search.Rd | 29 ++++++++++++++++++++----- tests/testthat/test-name.R | 4 ++++ 3 files changed, 63 insertions(+), 14 deletions(-) diff --git a/R/name.R b/R/name.R index 42897345..ac5a2cb2 100644 --- a/R/name.R +++ b/R/name.R @@ -273,6 +273,11 @@ neuprint_get_roiInfo <- function(bodyids, dataset = NULL, all_segments = FALSE, #' #' # starts with MBON #' neuprint_search("type:MBON.*", meta=FALSE) +#' +#' # full access to WHERE cypher queries over nodes (i.e. neurons) in neo4j +#' # NB fields must be prefixed with n. to indicate that they are node properties. +#' # note also that exists(n.somaLocation) is cypher to ensure soma==TRUE +#' neuprint_search("where:exists(n.somaLocation) AND n.post>30000 AND NOT n.cropped") #' } #' #' \dontrun{ @@ -317,14 +322,22 @@ neuprint_search <- function(search, field = "name", fixed=FALSE, exact=NULL, if(isFALSE(fixed) && isFALSE(exact)) warning("Ignoring exact=FALSE as regular expression searches are always exact!") nodetype = ifelse(all_segments,'Segment','Neuron') - fieldtype=neuprint_typeof(field, type = 'neo4j') - if(fieldtype=="STRING") { - search=glue('\\"{search}\\"') - operator=ifelse(fixed, ifelse(exact, "=", "CONTAINS"), "=~") - } else operator="=" + + if(isTRUE(tolower(field)=='where')) { + # this is a raw CYPHER query + where=search + } else { + fieldtype=neuprint_typeof(field, type = 'neo4j') + if(fieldtype=="STRING") { + search=glue('\\"{search}\\"') + operator=ifelse(fixed, ifelse(exact, "=", "CONTAINS"), "=~") + } else operator="=" + where=glue("n.{field} {operator} {search}") + } + cypher = glue(" MATCH (n:`{nodetype}`) \\ - WHERE n.{field} {operator} {search} \\ + WHERE {where} \\ RETURN n.bodyId") nc = neuprint_fetch_custom(cypher=cypher, conn=conn, dataset = dataset, ...) foundbodyids=unlist(nc$data) @@ -351,9 +364,9 @@ neuprint_search <- function(search, field = "name", fixed=FALSE, exact=NULL, #' @return For \code{neuprint_ids}, a character vector of bodyids (of length 0 #' when there are none and \code{mustWork=FALSE}). #' @export -#' @seealso \code{\link[neuprintr]{neuprint_search}} -#' @section Query syntax: It is probably best just to look at the examples, but -#' the query syntax is as follows where square brackets denote optional parts: +#' @section Standard query syntax: It is probably best just to look at the +#' examples, but the query syntax is as follows where square brackets denote +#' optional parts: #' #' \code{[!/][:]} #' @@ -367,6 +380,19 @@ neuprint_search <- function(search, field = "name", fixed=FALSE, exact=NULL, #' Finally the query itself is a plain text (fixed) or regular expression #' query. #' +#' +#' @section Extended query syntax: As a stepping stone to writing full CYPHER +#' queries against Neo4J you can used the special \code{where} keyword to +#' introduce your queries: +#' +#' \code{where:} +#' +#' e.g. +#' +#' \code{"where:exists(n.somaLocation) AND n.post>30000 AND NOT n.cropped"} +#' +#' Note that properties of individual nodes (i.e. neurons) must be prefixed +#' with \code{n.} as would be typical in a CYPHER query. #' @examples #' \donttest{ #' # exact match against whole type diff --git a/man/neuprint_search.Rd b/man/neuprint_search.Rd index 209f9492..b35e41ad 100644 --- a/man/neuprint_search.Rd +++ b/man/neuprint_search.Rd @@ -87,9 +87,10 @@ For \code{neuprint_ids}, a character vector of bodyids (of length 0 ids. Queries are by default partial and fixed (i.e. non-regex) against type. Returns a character vector of bodyids. } -\section{Query syntax}{ - It is probably best just to look at the examples, but - the query syntax is as follows where square brackets denote optional parts: +\section{Standard query syntax}{ + It is probably best just to look at the + examples, but the query syntax is as follows where square brackets denote + optional parts: \code{[!/][:]} @@ -104,6 +105,21 @@ For \code{neuprint_ids}, a character vector of bodyids (of length 0 query. } +\section{Extended query syntax}{ + As a stepping stone to writing full CYPHER + queries against Neo4J you can used the special \code{where} keyword to + introduce your queries: + + \code{where:} + + e.g. + + \code{"where:exists(n.somaLocation) AND n.post>30000 AND NOT n.cropped"} + + Note that properties of individual nodes (i.e. neurons) must be prefixed + with \code{n.} as would be typical in a CYPHER query. +} + \examples{ \donttest{ neuprint_search(".*DA2.*") @@ -118,6 +134,11 @@ neuprint_search("type:MBON[0-9]+", meta=FALSE) # starts with MBON neuprint_search("type:MBON.*", meta=FALSE) + +# full access to WHERE cypher queries over nodes (i.e. neurons) in neo4j +# NB fields must be prefixed with n. to indicate that they are node properties. +# note also that exists(n.somaLocation) is cypher to ensure soma==TRUE +neuprint_search("where:exists(n.somaLocation) AND n.post>30000 AND NOT n.cropped") } \dontrun{ @@ -154,6 +175,4 @@ neuprint_ids("/name:.*MBON0[1-4].*") \seealso{ \code{\link{neuprint_get_meta}}, \code{\link{neuprint_get_neuron_names}} - -\code{\link[neuprintr]{neuprint_search}} } diff --git a/tests/testthat/test-name.R b/tests/testthat/test-name.R index 4fd79c7e..a5aaac73 100644 --- a/tests/testthat/test-name.R +++ b/tests/testthat/test-name.R @@ -73,6 +73,10 @@ test_that("test searches on non-string fields", { expect_is(neuprint_ids("cropped:false"), 'character') }) +test_that("test where searches", { + expect_is(neuprint_ids("where:exists(n.somaLocation) AND n.post>10000 AND NOT n.cropped"), "character") +}) + test_that("test bad dataset specification ", { expect_error(neuprint_search(".*DA2.*", dataset = 'hemibrain1')) })