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

Last change on this file since 1473 was 1473, checked in by s.h.sikkema@…, 10 years ago

Fixed tests and removed obsolete ones so grails test-app works with all tests passed

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