11/*
2- * Copyright (c) 2020 Informatics Matters Ltd.
2+ * Copyright (c) 2021 Informatics Matters Ltd.
33 *
44 * Licensed under the Apache License, Version 2.0 (the "License");
55 * you may not use this file except in compliance with the License.
@@ -54,6 +54,11 @@ public class FragnetSearchRouteBuilder extends AbstractFragnetSearchRouteBuilder
5454 private List <Map <String , String >> suppliers ;
5555 private Map <String , String > supplierMappings ;
5656
57+ private final Counter moleculeSearchRequestsTotal = Counter .build ()
58+ .name ("requests_molecule_total" )
59+ .help ("Total number of molecule search requests" )
60+ .register ();
61+
5762 private final Counter neighbourhoodSearchRequestsTotal = Counter .build ()
5863 .name ("requests_neighbourhood_total" )
5964 .help ("Total number of neighbourhood search requests" )
@@ -74,6 +79,11 @@ public class FragnetSearchRouteBuilder extends AbstractFragnetSearchRouteBuilder
7479 .help ("Total duration of calculations" )
7580 .register ();
7681
82+ private final Counter moleculeSearchNeo4jSearchDuration = Counter .build ()
83+ .name ("duration_molecule_neo4j_ns" )
84+ .help ("Total duration of molecule Neo4j cypher query" )
85+ .register ();
86+
7787 private final Counter neighbourhoodSearchNeo4jSearchDuration = Counter .build ()
7888 .name ("duration_neighbourhood_neo4j_ns" )
7989 .help ("Total duration of neighbourhood Neo4j cypher query" )
@@ -84,6 +94,16 @@ public class FragnetSearchRouteBuilder extends AbstractFragnetSearchRouteBuilder
8494 .help ("Total duration of mcs determination" )
8595 .register ();
8696
97+ private final Counter moleculeSearchHitsTotal = Counter .build ()
98+ .name ("results_molecules_hits_molecules" )
99+ .help ("Total number of molecule search hits" )
100+ .register ();
101+
102+ private final Counter moleculeSearchMissesTotal = Counter .build ()
103+ .name ("results_molecules_misses_molecules" )
104+ .help ("Total number of molecule search misses" )
105+ .register ();
106+
87107 private final Counter neighbourhoodSearchHitsTotal = Counter .build ()
88108 .name ("results_neighbourhood_hits_molecules" )
89109 .help ("Total number of molecules found for neighbourhood search" )
@@ -160,6 +180,27 @@ public void configure() throws Exception {
160180 getUserInfo (exch );
161181 })
162182 .endRest ()
183+ // Is this molecule part of the fragment network
184+ // example:
185+ // curl "$FRAGNET_SERVER/fragnet-search/rest/v2/search/molecule/OC(Cn1ccnn1)C1CC1"
186+ .get ("molecule/{smiles}" ).description ("Molecule search" )
187+ .param ().name ("smiles" ).type (RestParamType .path ).description ("SMILES query" ).endParam ()
188+ .produces ("application/json" )
189+ .route ()
190+ .process ((Exchange exch ) -> {
191+ executeMoleculeQuery (exch );
192+ })
193+ .endRest ()
194+ .post ("molecule/" ).description ("Molecule search" )
195+ .bindingMode (RestBindingMode .off )
196+ .param ().name ("molfile" ).type (RestParamType .body ).description ("Molfile query" ).endParam ()
197+ .produces ("application/json" )
198+ .route ()
199+ .process ((Exchange exch ) -> {
200+ executeMoleculeQuery (exch );
201+ })
202+ .marshal ().json (JsonLibrary .Jackson )
203+ .endRest ()
163204 // example:
164205 // curl "$FRAGNET_SERVER/fragnet-search/rest/v2/search/neighbourhood/c1ccc%28Nc2nc3ccccc3o2%29cc1?hac=3&rac=1&hops=2&calcs=LOGP,SIM_RDKIT_TANIMOTO"
165206 .get ("neighbourhood/{smiles}" ).description ("Neighbourhood search" )
@@ -291,15 +332,15 @@ void executeAvailabilityQuery(Exchange exch) {
291332 try {
292333 Availability availability = getAvailability (smiles );
293334 if (availability == null || availability .getItems ().size () == 0 ) {
294- message .setBody ("{\" error\" : \" NeighbourhoodQuery Failed\" ,\" message\" , \" SMILES not found\" }" );
335+ message .setBody ("{\" error\" : \" AvailabilityQuery Failed\" ,\" message\" : \" SMILES not found\" }" );
295336 message .setHeader (Exchange .HTTP_RESPONSE_CODE , 404 );
296337 } else {
297338 message .setBody (availability );
298339 message .setHeader (Exchange .HTTP_RESPONSE_CODE , 200 );
299340 }
300341 } catch (Exception ex ) {
301- LOG .log (Level .SEVERE , "NeighbourhoodQuery Failed" , ex );
302- message .setBody ("{\" error\" : \" NeighbourhoodQuery Failed\" ,\" message\" , \" " + ex .getLocalizedMessage () + "\" }" );
342+ LOG .log (Level .SEVERE , "AvailabilityQuery Failed" , ex );
343+ message .setBody ("{\" error\" : \" AvailabilityQuery Failed\" ,\" message\" : \" " + ex .getLocalizedMessage () + "\" }" );
303344 message .setHeader (Exchange .HTTP_RESPONSE_CODE , 500 );
304345 }
305346 }
@@ -416,7 +457,8 @@ void executeExpansionQuery(Exchange exch, String conentType) {
416457
417458 if (result .getSize () == 0 ) { // no results found
418459 LOG .info ("ExpansionQuery found no results" );
419- writeErrorResponse (message , 404 , "{\" error\" : \" No Results\" ,\" message\" : \" ExpansionQuery molecule not found in the database\" }" );
460+ writeErrorResponse (message , 404 ,
461+ "{\" error\" : \" No Results\" ,\" message\" : \" ExpansionQuery molecule not found in the database or could not be expanded\" }" );
420462 } else {
421463 message .setBody (result );
422464 message .setHeader (Exchange .HTTP_RESPONSE_CODE , 200 );
@@ -526,6 +568,67 @@ void executeExpansionMultiQuery(Exchange exch) {
526568 }
527569 }
528570
571+ void executeMoleculeQuery (Exchange exch ) {
572+
573+ LOG .info ("Executing executeMoleculeQuery" );
574+
575+ moleculeSearchRequestsTotal .inc ();
576+
577+ Message message = exch .getIn ();
578+
579+ long t0 = System .nanoTime ();
580+ String username = getUsername (exch );
581+
582+ try {
583+ String queryMol = null ;
584+ String mimeType = message .getHeader (Exchange .CONTENT_TYPE , String .class );
585+ LOG .info ("mime-type is " + mimeType );
586+ if (mimeType == null ) {
587+ mimeType = Constants .MIME_TYPE_SMILES ;
588+ }
589+ if (Constants .MIME_TYPE_SMILES .equals (mimeType )) {
590+ queryMol = message .getHeader ("smiles" , String .class );
591+ } else if (Constants .MIME_TYPE_MOLFILE .equals (mimeType )) {
592+ queryMol = message .getBody (String .class );
593+ } else {
594+ throw new IllegalStateException ("Only support SMILES using GET or molfile using POST" );
595+ }
596+
597+ if (queryMol == null || queryMol .isEmpty ()) {
598+ throw new IllegalArgumentException ("Query molecule must be specified" );
599+ }
600+
601+ MoleculeNode molNode ;
602+ try (Session session = graphdb .getSession ()) {
603+ // execute the query
604+ MoleculeQuery query = new MoleculeQuery (session );
605+
606+ long n0 = System .nanoTime ();
607+ molNode = query .execute (queryMol , mimeType );
608+ long n1 = System .nanoTime ();
609+ moleculeSearchNeo4jSearchDuration .inc ((double ) (n1 - n0 ));
610+ if (molNode == null ) {
611+ moleculeSearchMissesTotal .inc (1.0d );
612+ // throw 404
613+ message .setBody ("{\" error\" : \" MoleculeQuery Failed\" ,\" message\" : \" Molecule not found\" }" );
614+ message .setHeader (Exchange .HTTP_RESPONSE_CODE , 404 );
615+ } else {
616+ moleculeSearchHitsTotal .inc (1.0d );
617+ message .setBody (molNode );
618+ message .setHeader (Exchange .HTTP_RESPONSE_CODE , 200 );
619+ }
620+ }
621+
622+ } catch (Exception ex ) {
623+ LOG .log (Level .SEVERE , "MoleculeQuery Failed" , ex );
624+ neighbourhoodSearchErrorsTotal .inc ();
625+ message .setBody ("{\" error\" : \" MoleculeQuery Failed\" ,\" message\" :\" " + ex .getLocalizedMessage () + "\" }" );
626+ message .setHeader (Exchange .HTTP_RESPONSE_CODE , 500 );
627+
628+ long t1 = System .nanoTime ();
629+ writeErrorToQueryLog (username , "MoleculeQuery" , t1 - t0 , ex .getLocalizedMessage ());
630+ }
631+ }
529632
530633 void executeNeighbourhoodQuery (Exchange exch ) {
531634
0 commit comments