source: trunk/src/groovy/dbnp/query/Search.groovy @ 1483

Last change on this file since 1483 was 1483, checked in by t.w.abma@…, 10 years ago
  • fixed Search (code was committed with merge conflict messages in the source)
  • Property svn:keywords set to Rev Author Date
File size: 10.2 KB
Line 
1/**
2 * Search Domain Class
3 *
4 * Abstract class containing search criteria and search results when querying.
5 * Should be subclassed in order to enable searching for different entities.
6 *
7 * @author  Robert Horlings (robert@isdat.nl)
8 * @since       20110118
9 * @package     dbnp.query
10 *
11 * Revision information:
12 * $Rev: 1483 $
13 * $Author: t.w.abma@umcutrecht.nl $
14 * $Date: 2011-02-03 09:02:15 +0000 (do, 03 feb 2011) $
15 */
16package dbnp.query
17
18import nl.grails.plugins.gdt.*
19import java.util.List;
20import java.text.DateFormat;
21import java.text.SimpleDateFormat
22import java.util.List;
23
24import org.springframework.context.ApplicationContext
25import org.springframework.web.context.request.RequestContextHolder;
26import org.codehaus.groovy.grails.commons.ApplicationHolder;
27
28import dbnp.authentication.*
29
30import org.dbnp.gdt.*
31
32class Search {
33        public String entity;
34        public SecUser user;
35        public Date executionDate;
36        public int id;  // Is only used when this query is saved in session
37
38        protected List criteria;
39        protected List results;
40        protected Map resultFields = [:];
41
42        public List getCriteria() { return criteria; }
43        public void setCriteria( List c ) { criteria = c; }
44
45        public List getResults() { return results; }
46        public void setResults( List r ) { results = r; }
47       
48        public Map getResultFields() { return resultFields; }
49        public void setResultFields( Map r ) { resultFields = r; }
50
51        /**
52         * Returns the number of results found by this search
53         * @return
54         */
55        public int getNumResults() {
56                if( results )
57                        return results.size();
58
59                return 0;
60        }
61
62        /**
63         * Executes a search based on the given criteria. Should be filled in by
64         * subclasses searching for a specific entity
65         *
66         * @param       c       List with criteria to search on
67         */
68        public void execute( List c ) {
69                setCriteria( c );
70                execute();
71        }
72
73        /**
74         * Executes a search based on the given criteria. Should be filled in by
75         * subclasses searching for a specific entity
76         */
77        public void execute() {
78                this.executionDate = new Date();
79        }
80
81        /**
82         * Returns a list of criteria targeted on the given entity
83         * @param entity        Entity to search criteria for
84         * @return                      List of criteria
85         */
86        protected List getEntityCriteria( String entity ) {
87                return criteria?.findAll { it.entity == entity }
88        }
89       
90        /**
91         * Filters a list with entities, based on the given criteria and a closure to check whether a criterion is matched
92         *
93         * @param entities      Original list with entities to check for these criteria
94         * @param criteria      List with criteria to match on
95         * @param check         Closure to see whether a specific entity matches a criterion. Gets two arguments:
96         *                                              element         The element to check
97         *                                              criterion       The criterion to check on.
98         *                                      Returns true if the criterion holds, false otherwise
99         * @return                      The filtered list of entities
100         */
101        protected List filterEntityList( List entities, List<Criterion> criteria, Closure check ) {
102                if( !entities || !criteria || criteria.size() == 0 ) {
103                        return entities;
104                }
105
106                return entities.findAll { entity ->
107                        for( criterion in criteria ) {
108                                if( !check( entity, criterion ) ) {
109                                        return false;
110                                }
111                        }
112                        return true;
113                }
114        }
115       
116        /**
117         * Prepares a value from a template entity for comparison, by giving it a correct type
118         *
119         * @param value         Value of the field
120         * @param type          TemplateFieldType       Type of the specific field
121         * @return                      The value of the field in the correct entity
122         */
123        public static def prepare( def value, TemplateFieldType type ) {
124                switch (type) {
125                        case TemplateFieldType.DATE:
126                                try {
127                                        return new SimpleDateFormat( "yyyy-MM-dd" ).parse( value )
128                                } catch( Exception e ) {
129                                        return value.toString();
130                                }
131                        case TemplateFieldType.RELTIME:
132                                try {
133                                        if( value instanceof Number ) {
134                                                return new RelTime( value );
135                                        } else if( value.toString().isNumber() ) {
136                                                return new RelTime( Long.parseLong( value.toString() ) ) 
137                                        } else {
138                                                return new RelTime( value );
139                                        }
140                                } catch( Exception e ) {
141                                        try {
142                                                return Long.parseLong( value )
143                                        } catch( Exception e2 ) {
144                                                return value.toString();
145                                        }
146                                }
147                        case TemplateFieldType.DOUBLE:
148                                try {
149                                        return Double.valueOf( value )
150                                } catch( Exception e ) {
151                                        return value.toString();
152                                }
153                        case TemplateFieldType.BOOLEAN:
154                                try {
155                                        return Boolean.valueOf( value )
156                                } catch( Exception e ) {
157                                        return value.toString();
158                                }
159                        case TemplateFieldType.LONG:
160                                try {
161                                        return Long.valueOf( value )
162                                } catch( Exception e ) {
163                                        return value.toString();
164                                }
165                        case TemplateFieldType.STRING:
166                        case TemplateFieldType.TEXT:
167                        case TemplateFieldType.STRINGLIST:
168                        case TemplateFieldType.TEMPLATE:
169                        case TemplateFieldType.MODULE:
170                        case TemplateFieldType.FILE:
171                        case TemplateFieldType.ONTOLOGYTERM:
172                        default:
173                                return value.toString();
174                }
175
176        }
177       
178       
179        /**
180         * Filters the given list of studies on the study criteria
181         * @param studies               Original list of studies
182         * @param entity                Name of the entity to check the criteria for
183         * @param valueCallback Callback having a study and criterion as input, returning the value of the field to check on
184         * @return                              List with all studies that match the Criteria
185         */
186        protected List filterOnTemplateEntityCriteria( List studies, String entityName, Closure valueCallback ) {
187                def criteria = getEntityCriteria( entityName );
188               
189                def checkCallback = { study, criterion ->
190                        def value = valueCallback( study, criterion );
191                       
192                        if( value == null )
193                                return false
194                        if( value instanceof Collection ) {
195                                return criterion.matchAny( value )
196                        } else {
197                                return criterion.match( value );
198                        }
199                }
200
201                // Save the value of this entity for later use
202                saveResultFields( studies, criteria, valueCallback );
203               
204                return filterEntityList( studies, criteria, checkCallback);
205        }
206
207        /**
208        * Filters the given list of entities on the module criteria
209        * @param entities       Original list of entities. Entities should expose a giveUUID() method to give the token.
210        * @return                       List with all entities that match the module criteria
211        */
212        protected List filterOnModuleCriteria( List entities ) {
213                // An empty list can't be filtered more than is has been now
214                if( !entities || entities.size() == 0 )
215                        return [];
216                       
217                // Determine the moduleCommunicationService
218                def ctx = (ApplicationContext)ApplicationHolder.getApplication().getMainContext();
219                def moduleCommunicationService = ctx.getBean("moduleCommunicationService");
220                       
221                // Loop through all modules and check whether criteria have been given
222                // for that module
223                AssayModule.list().each { module ->
224                        // Remove 'module' from module name
225                        def moduleName = module.name.replace( 'module', '' ).trim()
226                        def moduleCriteria = getEntityCriteria( moduleName );
227                       
228                        if( moduleCriteria && moduleCriteria.size() > 0 ) {
229                                // Retrieve the data from the module
230                                def tokens = entities.collect { it.giveUUID() }.unique();
231                                def fields = moduleCriteria.collect { it.field }.unique();
232                               
233                                def callUrl = module.url + '/rest/getQueryableFieldData?entity=' + this.entity
234                                tokens.sort().each { callUrl += "&tokens=" + it.encodeAsURL() }
235                                fields.sort().each { callUrl += "&fields=" + it.encodeAsURL() }
236                               
237                                try {
238                                        def json = moduleCommunicationService.callModuleRestMethodJSON( module.url, callUrl );
239
240                                        // The data has been retrieved. Now walk through all criteria to filter the samples
241                                        entities = filterEntityList( entities, moduleCriteria, { entity, criterion ->
242                                                // Find the value of the field in this sample. That value is still in the
243                                                // JSON object
244                                                def token = entity.giveUUID()
245                                                if( !json[ token ] || json[ token ][ criterion.field ] == null )
246                                                        return false;
247
248                                                // Check whether a list or string is given
249                                                def value = json[ token ][ criterion.field ];
250                                               
251                                                // Save the value of this entity for later use
252                                                saveResultField( entity.id, criterion.entity + " " + criterion.field, value )
253
254                                                if( !( value instanceof Collection ) ) {
255                                                        value = [ value ];
256                                                }
257                                               
258                                                // Loop through all values and match any
259                                                for( val in value ) {
260                                                        // Convert numbers to a long or double in order to process them correctly
261                                                        val = val.toString();
262                                                        if( val.isLong() ) {
263                                                                val = Long.parseLong( val );
264                                                        } else if( val.isDouble() ) {
265                                                                val = Double.parseDouble( val );
266                                                        }
267                                                       
268                                                        if( criterion.match( val ) )
269                                                                return true;
270                                                }
271                                               
272                                                return false;
273                                        });
274                                                                               
275                                } catch( Exception e ) {
276                                        log.error( "Error while retrieving data from " + module.name + ": " + e.getMessage() )
277                                }
278                        }
279                }
280               
281                return entities;
282        }
283       
284        /**
285         * Saves data about template entities to use later on. This data is copied to a special
286         * structure to make it compatible with data fetched from other modules. Ses also saveResultField() method
287         * @param entities                      List of template entities to find data in
288         * @param criteria                      Criteria to search for
289         * @param valueCallback         Callback to retrieve a specific field from the entity
290         */
291        protected void saveResultFields( entities, criteria, valueCallback ) {
292                for( criterion in criteria ) {
293                        for( entity in entities ) {
294                                if( criterion.field )
295                                        saveResultField( entity.id, criterion.entity + ' ' + criterion.field, valueCallback( entity, criterion ) )
296                        }
297                }
298        }
299
300       
301        /**
302         * Saves a specific field of an object to use later on. Especially useful when looking up data from other modules.
303         * @param id            ID of the object
304         * @param fieldName     Field name that has been searched
305         * @param value         Value of the field
306         */
307        protected void saveResultField( id, fieldName, value ) {
308                if( resultFields[ id ] == null )
309                        resultFields[ id ] = [:]
310               
311                resultFields[ id ][ fieldName ] = value;
312        }
313       
314        /**
315         * Removes all data from the result field map
316         */
317        protected void clearResultFields() {
318                resultFields = [:]
319        }
320       
321        /**
322         * Returns the saved field data that could be shown on screen. This means, the data is filtered to show only data of the query results.
323         *
324         * Subclasses could filter out the fields they don't want to show on the result screen (e.g. because they are shown regardless of the
325         * query.)
326         * @return      Map with the entity id as a key, and a field-value map as value
327         */
328        public Map getShowableResultFields() {
329                def resultIds = getResults()*.id;
330                return getResultFields().findAll {
331                        resultIds.contains( it.key )
332                }
333        }
334       
335        public String toString() {
336                return ( this.entity ? this.entity + " search" : "Search" ) + " " + this.id
337        }
338}
Note: See TracBrowser for help on using the repository browser.