Skip to content

Commit 0107c22

Browse files
authored
Merge pull request #42 from VirtualFlyBrain/feature/extend-preview-columns-for-v2-parity
extend the Cypher + preview_columns for three queries
2 parents 99bc52e + bc44dcd commit 0107c22

1 file changed

Lines changed: 62 additions & 14 deletions

File tree

src/vfbquery/vfb_queries.py

Lines changed: 62 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1262,7 +1262,14 @@ def SimilarMorphologyTo_to_schema(name, take_default):
12621262
"default": take_default,
12631263
}
12641264
preview = 5
1265-
preview_columns = ["id","score","name","tags","thumbnail"]
1265+
# Match the v1.10.1 SimilarMorphologyTo* preview shape and add the new
1266+
# type column this PR exposes — keeps term-info previews in sync with
1267+
# the full /run_query response. source/source_id are intentionally
1268+
# omitted; they're noisy in compact previews and only meaningful when
1269+
# the user opens the full table. Keep score before name so preview
1270+
# sorting continues to default to score-descending under the current
1271+
# header-order-based preview sort selection.
1272+
preview_columns = ["id", "score", "name", "tags", "type", "template", "technique", "thumbnail"]
12661273

12671274
return Query(query=query, label=label, function=function, takes=takes, preview=preview, preview_columns=preview_columns)
12681275

@@ -2453,19 +2460,50 @@ def get_similar_neurons(neuron, similarity_score='NBLAST_score', return_datafram
24532460
count_df = pd.DataFrame.from_records(get_dict_cursor()(count_results))
24542461
total_count = count_df['total_count'][0] if not count_df.empty else 0
24552462

2456-
main_query = f"""MATCH (c1:Class)<-[:INSTANCEOF]-(n1)-[r:has_similar_morphology_to]-(n2)-[:INSTANCEOF]->(c2:Class)
2463+
# Extends the v1.10.1 channel/template/technique pattern. Adds:
2464+
# - type = pipe-joined parent class labels (n2 -[:INSTANCEOF]-> Class)
2465+
# matches v2 prod's `Type` column from SOLR's `types` collection
2466+
# - template = `[symbol](short_form)` markdown of the alignment template
2467+
# - technique = imaging technique label (channel -[:is_specified_output_of]-> Class)
2468+
#
2469+
# Each OPTIONAL branch is wrapped in a CALL subquery so the outer query
2470+
# carries one row per n2 throughout. Without this, an n2 with N
2471+
# cross-references × M alignments × K types would produce N×M×K rows
2472+
# that DISTINCT then collapses at the end — wasteful, especially on
2473+
# densely-typed neurons. Each subquery either aggregates (for `type`)
2474+
# or LIMIT 1s (for the single representative cross-ref / alignment
2475+
# the V2 row needs), so n2 stays the row key end-to-end.
2476+
main_query = f"""MATCH (c1:Class)<-[:INSTANCEOF]-(n1:Individual)-[r:has_similar_morphology_to]-(n2:Individual)-[:INSTANCEOF]->(c2:Class)
24572477
WHERE n1.short_form = '{neuron}' and exists(r.{similarity_score})
2458-
WITH c1, n1, r, n2, c2
2459-
OPTIONAL MATCH (n2)-[rx:database_cross_reference]->(site:Site)
2460-
WHERE site.is_data_source
2461-
WITH n2, r, c2, rx, site
2462-
OPTIONAL MATCH (n2)<-[:depicts]-(:Individual)-[ri:in_register_with]->(:Template)-[:depicts]->(templ:Template)
2463-
RETURN DISTINCT n2.short_form as id,
2464-
apoc.text.format("[%s](%s)", [n2.label, n2.short_form]) AS name,
2478+
WITH DISTINCT r, n2
2479+
CALL {{
2480+
WITH n2
2481+
OPTIONAL MATCH (n2)-[:INSTANCEOF]->(typ:Class)
2482+
RETURN apoc.text.join([l IN collect(DISTINCT typ.label) WHERE l IS NOT NULL AND l <> ''], '|') AS type
2483+
}}
2484+
CALL {{
2485+
WITH n2
2486+
OPTIONAL MATCH (n2)-[rx:database_cross_reference]->(site:Site)
2487+
WHERE site.is_data_source
2488+
WITH rx, site LIMIT 1
2489+
RETURN rx, site
2490+
}}
2491+
CALL {{
2492+
WITH n2
2493+
OPTIONAL MATCH (n2)<-[:depicts]-(channel:Individual)-[ri:in_register_with]->(:Template)-[:depicts]->(templ:Template)
2494+
OPTIONAL MATCH (channel)-[:is_specified_output_of]->(technique:Class)
2495+
WITH ri, templ, technique LIMIT 1
2496+
RETURN ri, templ, technique
2497+
}}
2498+
RETURN n2.short_form as id,
2499+
apoc.text.format("[%s](%s)", [n2.label, n2.short_form]) AS name,
24652500
r.{similarity_score}[0] AS score,
2466-
apoc.text.join(n2.uniqueFacets, '|') AS tags,
2501+
apoc.text.join(coalesce(n2.uniqueFacets, []), '|') AS tags,
2502+
type,
24672503
REPLACE(apoc.text.format("[%s](%s)",[COALESCE(site.symbol[0],site.label),site.short_form]), '[null](null)', '') AS source,
24682504
REPLACE(apoc.text.format("[%s](%s)",[rx.accession[0], (site.link_base[0] + rx.accession[0])]), '[null](null)', '') AS source_id,
2505+
REPLACE(apoc.text.format("[%s](%s)",[COALESCE(templ.symbol[0],templ.label),templ.short_form]), '[null](null)', '') AS template,
2506+
coalesce(technique.label, '') AS technique,
24692507
REPLACE(apoc.text.format("[![%s](%s '%s')](%s)",[COALESCE(n2.symbol[0],n2.label) + " aligned to " + COALESCE(templ.symbol[0],templ.label), REPLACE(COALESCE(ri.thumbnail[0],""),"thumbnailT.png","thumbnail.png"), COALESCE(n2.symbol[0],n2.label) + " aligned to " + COALESCE(templ.symbol[0],templ.label), templ.short_form + "," + n2.short_form]), "[![null]( 'null')](null)", "") as thumbnail
24702508
ORDER BY score DESC"""
24712509

@@ -2478,9 +2516,13 @@ def get_similar_neurons(neuron, similarity_score='NBLAST_score', return_datafram
24782516
# Convert the results to a DataFrame
24792517
df = pd.DataFrame.from_records(get_dict_cursor()(results))
24802518

2481-
columns_to_encode = ['name', 'source', 'source_id', 'thumbnail']
2519+
# template is a `[symbol](short_form)` markdown link — must be encoded the
2520+
# same way as name/source/source_id/thumbnail so the V2 frontend's link
2521+
# parser renders it consistently. type/technique are plain text and
2522+
# don't need encoding.
2523+
columns_to_encode = ['name', 'source', 'source_id', 'template', 'thumbnail']
24822524
df = encode_markdown_links(df, columns_to_encode)
2483-
2525+
24842526
if return_dataframe:
24852527
return df
24862528
else:
@@ -2490,8 +2532,11 @@ def get_similar_neurons(neuron, similarity_score='NBLAST_score', return_datafram
24902532
"score": {"title": "Score", "type": "numeric", "order": 1, "sort": {0: "Desc"}},
24912533
"name": {"title": "Name", "type": "markdown", "order": 1, "sort": {1: "Asc"}},
24922534
"tags": {"title": "Tags", "type": "tags", "order": 2},
2493-
"source": {"title": "Source", "type": "metadata", "order": 3},
2494-
"source_id": {"title": "Source ID", "type": "metadata", "order": 4},
2535+
"type": {"title": "Type", "type": "text", "order": 3},
2536+
"source": {"title": "Source", "type": "metadata", "order": 4},
2537+
"source_id": {"title": "Source ID", "type": "metadata", "order": 5},
2538+
"template": {"title": "Template", "type": "markdown", "order": 6},
2539+
"technique": {"title": "Imaging Technique", "type": "text", "order": 7},
24952540
"thumbnail": {"title": "Thumbnail", "type": "markdown", "order": 9}
24962541
},
24972542
"rows": [
@@ -2502,8 +2547,11 @@ def get_similar_neurons(neuron, similarity_score='NBLAST_score', return_datafram
25022547
"name",
25032548
"score",
25042549
"tags",
2550+
"type",
25052551
"source",
25062552
"source_id",
2553+
"template",
2554+
"technique",
25072555
"thumbnail"
25082556
]
25092557
}

0 commit comments

Comments
 (0)