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

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