root/trunk/grails-app/controllers/dbnp/query/AdvancedQueryController.groovy @ 1458

Revision 1458, 5.7 KB (checked in by robert@…, 3 years ago)

- Implemented a cache for module rest calls, to increase performance (see moduleCommunicationService)
- Implemented searching in module data.

  • Property svn:keywords set to Rev Author Date
Line 
1package dbnp.query
2import dbnp.modules.*
3import org.dbnp.gdt.*
4
5// TODO: Make use of the searchable-plugin possibilities instead of querying the database directly
6
7/**
8 * Basic web interface for searching within studies
9 *
10 * @author Robert Horlings (robert@isdat.nl)
11 */
12class AdvancedQueryController {
13        def moduleCommunicationService;
14       
15        def entitiesToSearchFor = [ 'Study': 'Studies', 'Sample': 'Samples']
16    def index = {
17                [entitiesToSearchFor: entitiesToSearchFor, searchableFields: getSearchableFields()]
18    }
19
20        /**
21         * Searches for studies or samples based on the user parameters.
22         *
23         * @param       entity          The entity to search for ( 'Study' or 'Sample' )
24         * @param       criteria        HashMap with the values being hashmaps with field, operator and value.
25         *                                              [ 0: [ field: 'Study.name', operator: 'equals', value: 'term' ], 1: [..], .. ]
26         */
27        def search = {
28                if( !params.criteria ) {
29                        flash.error = "No criteria given to search for. Please try again.";
30                        redirect( action: 'index' )
31                }
32
33                if( !params.entity || !entitiesToSearchFor*.key.contains( params.entity ) ) {
34                        flash.error = "No or incorrect entity given to search for. Please try again.";
35                        redirect( action: 'index', params: [ criteria: parseCriteria( params.criteria ) ] )
36                }
37
38                // Create a search object and let it do the searching
39                Search search;
40                String view;
41                switch( params.entity ) {
42                        case "Study":   search = new StudySearch();             view = "studyresults";  break;
43                        case "Sample":  search = new SampleSearch();    view = "sampleresults"; break;
44                       
45                        // This exception will only be thrown if the entitiesToSearchFor contains more entities than
46                        // mentioned in this switch structure.
47                        default:                throw new Exception( "Can't search for entities of type " + params.entity );   
48                }
49               
50                search.execute( parseCriteria( params.criteria ) );
51               
52                render( view: view, model: [search: search] );
53        }
54       
55        /**
56         * Returns a map of entities with the names of the fields the user can search on
57         * @return
58         */
59        protected def getSearchableFields() {
60                def fields = [:];
61               
62                // Retrieve all local search fields
63                getEntities().each {
64                        def entity = getEntity( 'dbnp.studycapturing.' + it );
65                       
66                        if( entity ) {
67                                def domainFields = entity.giveDomainFields();
68                                def templateFields = TemplateField.findAllByEntity( entity )
69                               
70                                def fieldNames = ( domainFields + templateFields ).collect { it.name }.unique() + 'Template'
71                               
72                                fields[ it ] = fieldNames.sort { a, b -> a[0].toUpperCase() + a[1..-1] <=> b[0].toUpperCase() + b[1..-1] };
73                        }
74                }
75               
76                // Loop through all modules and check which fields are searchable
77                // Right now, we just combine the results for different entities
78                AssayModule.list().each { module ->
79                        def callUrl = module.url + '/rest/getQueryableFields'
80                        try {
81                                def json = moduleCommunicationService.callModuleRestMethodJSON( module.url, callUrl );
82                                def moduleFields = [];
83                                entitiesToSearchFor.each { entity ->                                   
84                                        if( json[ entity.key ] ) {
85                                                json[ entity.key ].each { field ->
86                                                        moduleFields << field.toString();
87                                                }
88                                        }
89                                }
90                               
91                                // Remove 'module' from module name
92                                def moduleName = module.name.replace( 'module', '' ).trim()
93                               
94                                fields[ moduleName ] = moduleFields.unique();
95                        } catch( Exception e ) {
96                                log.error( "Error while retrieving queryable fields from " + module.name + ": " + e.getMessage() )
97                        }
98                }
99               
100                return fields;
101        }
102       
103        /**
104         * Parses the criteria from the query form given by the user
105         * @param       c       Data from the input form and had a form like
106         *
107         *      [
108         *              0: [entityfield:a.b, operator: b, value: c],
109         *              0.entityfield: a.b,
110         *              0.operator: b,
111         *              0.field: c
112         *              1: [entityfield:f.q, operator: e, value: d],
113         *              1.entityfield: f.q,
114         *              1.operator: e,
115         *              1.field: d
116         *      ]
117         *
118         * @return      List with Criterion objects
119         */
120        protected List parseCriteria( def c ) {
121                ArrayList list = [];
122               
123                // Loop through all keys of c and remove the non-numeric ones
124                c.each {
125                        if( it.key ==~ /[0-9]+/ ) {
126                                def formCriterion = it.value;
127                                Criterion criterion = new Criterion();
128                               
129                                // Split entity and field
130                                def field = formCriterion.entityfield?.split( /\./ );
131                               
132                                if( field.size() > 1 ) {
133                                        criterion.entity = field[0].toString();
134                                        criterion.field = field[1].toString();
135                                } else {
136                                        criterion.entity = null;
137                                        criterion.field = field;
138                                }
139                               
140                                // Convert operator string to Operator-enum field
141                                switch( formCriterion.operator ) {
142                                        case ">=":                      criterion.operator = Operator.gte; break;
143                                        case ">":                       criterion.operator = Operator.gt;  break;
144                                        case "<":                       criterion.operator = Operator.lte; break;
145                                        case "<=":                      criterion.operator = Operator.lt;  break;
146                                        case "contains":        criterion.operator = Operator.contains; break;
147                                        case "equals":          criterion.operator = Operator.equals; break;
148                                }
149                               
150                                // Copy value
151                                criterion.value = formCriterion.value;
152                                 
153                                list << criterion;
154                        }
155                }
156               
157                return list;
158        }
159       
160        /**
161         * Returns all entities for which criteria can be entered
162         * @return
163         */
164        protected def getEntities() {
165                return [ 'Study', 'Subject', 'Sample', 'Event', 'SamplingEvent', 'Assay' ]
166        }
167       
168        /**
169        * Creates an object of the given entity.
170        *
171        * @return False if the entity is not a subclass of TemplateEntity
172        */
173   protected def getEntity( entityName ) {
174           // Find the templates
175           def entity
176           try {
177                   entity = Class.forName(entityName, true, this.getClass().getClassLoader())
178
179                   // succes, is entity an instance of TemplateEntity?
180                   if (entity.superclass =~ /TemplateEntity$/ || entity.superclass.superclass =~ /TemplateEntity$/) {
181                           return entity;
182                   } else {
183                           return false;
184                   }
185           } catch( ClassNotFoundException e ) {
186                        log.error "Class " + entityName + " not found: " + e.getMessage()
187                        return null;
188           }
189
190   }
191
192}
Note: See TracBrowser for help on using the browser.