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

Last change on this file since 1800 was 1800, checked in by robert@…, 9 years ago

Implemented searching in 'all fields'

  • Property svn:keywords set to Rev Author Date
File size: 25.4 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: 1800 $
13 * $Author: robert@isdat.nl $
14 * $Date: 2011-05-02 15:03:36 +0000 (ma, 02 mei 2011) $
15 */
16package dbnp.query
17
18import org.dbnp.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
30/**
31 * Available boolean operators for searches
32 * @author robert
33 *
34 */
35enum SearchMode {
36        and, or
37}
38
39class Search {
40        /**
41         * User that is performing this search. This has impact on the search results returned.
42         */
43        public SecUser user;
44
45        /**
46         * Date of execution of this search
47         */
48        public Date executionDate;
49
50        /**
51         * Public identifier of this search. Is only used when this query is saved in session
52         */
53        public int id;
54
55        /**
56         * Human readable entity name of the entities that can be found using this search
57         */
58        public String entity;
59
60        /**
61         * Mode to search: OR or AND.
62         * @see SearchMode
63         */
64        public SearchMode searchMode = SearchMode.and
65
66        protected List criteria;
67        protected List results;
68        protected Map resultFields = [:];
69
70        /**
71         * Constructor of this search object. Sets the user field to the
72         * currently logged in user
73         * @see #user
74         */
75        public Search() {
76                def ctx = ApplicationHolder.getApplication().getMainContext();
77                def authenticationService = ctx.getBean("authenticationService");
78                def sessionUser = authenticationService?.getLoggedInUser();
79
80                if( sessionUser )
81                        this.user = sessionUser;
82                else
83                        this.user = null
84        }
85
86        /**
87         * Returns the number of results found by this search
88         * @return
89         */
90        public int getNumResults() {
91                if( results )
92                        return results.size();
93
94                return 0;
95        }
96
97        /**
98         * Executes a search based on the given criteria. Should be filled in by
99         * subclasses searching for a specific entity
100         *
101         * @param       c       List with criteria to search on
102         */
103        public void execute( List c ) {
104                setCriteria( c );
105                execute();
106        }
107
108        /**
109         * Executes a search based on the given criteria.
110         */
111        public void execute() {
112                this.executionDate = new Date();
113
114                switch( searchMode ) {
115                        case SearchMode.and:
116                                executeAnd();
117                                break;
118                        case SearchMode.or:
119                                executeOr();
120                                break;
121                }
122
123                // Save the value of this results for later use
124                saveResultFields();
125        }
126
127        /**
128         * Executes an inclusive (AND) search based on the given criteria. Should be filled in by
129         * subclasses searching for a specific entity
130         */
131        protected void executeAnd() {
132
133        }
134
135        /**
136         * Executes an exclusive (OR) search based on the given criteria. Should be filled in by
137         * subclasses searching for a specific entity
138         */
139        protected void executeOr() {
140
141        }
142
143        /**
144         * Default implementation of an inclusive (AND) search. Can be called by subclasses in order
145         * to simplify searches.
146         *
147         * Filters the list of objects on study, subject, sample, event, samplingevent and assaycriteria,
148         * based on the closures defined in valueCallback. Afterwards, the objects are filtered on module
149         * criteria
150         *
151         * @param objects       List of objects to search in
152         */
153        protected void executeAnd( List objects ) {
154                // If no criteria are found, return all studies
155                if( !criteria || criteria.size() == 0 ) {
156                        results = objects;
157                        return;
158                }
159
160                // Perform filters
161                objects = filterOnStudyCriteria( objects );
162                objects = filterOnSubjectCriteria( objects );
163                objects = filterOnSampleCriteria( objects );
164                objects = filterOnEventCriteria( objects );
165                objects = filterOnSamplingEventCriteria( objects );
166                objects = filterOnAssayCriteria( objects );
167
168                // Filter on criteria for which the entity is unknown
169                objects = filterOnAllFieldsCriteria( objects );
170               
171                // Filter on module criteria
172                objects = filterOnModuleCriteria( objects );
173
174                // Save matches
175                results = objects;
176        }
177
178        /**
179        * Default implementation of an exclusive (OR) search. Can be called by subclasses in order
180        * to simplify searches.
181        *
182        * Filters the list of objects on study, subject, sample, event, samplingevent and assaycriteria,
183        * based on the closures defined in valueCallback. Afterwards, the objects are filtered on module
184        * criteria
185        *
186        * @param allObjects     List of objects to search in
187        */
188   protected void executeOr( List allObjects ) {
189                // If no criteria are found, return all studies
190                if( !criteria || criteria.size() == 0 ) {
191                        results = allObjects;
192                        return;
193                }
194
195                // Perform filters on those objects not yet found by other criteria
196                def objects = []
197                objects = ( objects + filterOnStudyCriteria( allObjects - objects ) ).unique();
198                objects = ( objects + filterOnSubjectCriteria( allObjects - objects ) ).unique();
199                objects = ( objects + filterOnSampleCriteria( allObjects - objects ) ).unique();
200                objects = ( objects + filterOnEventCriteria( allObjects - objects ) ).unique();
201                objects = ( objects + filterOnSamplingEventCriteria( allObjects - objects ) ).unique();
202                objects = ( objects + filterOnAssayCriteria( allObjects - objects ) ).unique();
203               
204                // Filter on criteria for which the entity is unknown
205                objects = ( objects + filterOnAllFieldsCriteria( allObjects - objects ) ).unique();
206               
207                // All objects (including the ones already found by another criterion) are sent to
208                // be filtered on module criteria, in order to have the module give data about all
209                // objects (for showing purposes later on)
210                objects = ( objects + filterOnModuleCriteria( allObjects ) ).unique();
211               
212                // Save matches
213                results = objects;
214   }
215
216               
217        /************************************************************************
218         *
219         * These methods are used in querying and can be overridden by subclasses
220         * in order to provide custom searching
221         *
222         ************************************************************************/
223
224        /**
225         * Returns a closure for the given entitytype that determines the value for a criterion
226         * on the given object. The closure receives two parameters: the object and a criterion.
227         *
228         * For example: when searching for studies, the object given to the closure is a Study.
229         * Also, when searching for samples, the object given is a Sample. When you have the criterion
230         *
231         *      sample.name equals 'sample 1'
232         *
233         * and searching for samples, it is easy to determine the value of the object for this criterion:
234         *     
235         *      object.getFieldValue( "name" )
236         *
237         *
238         * However, when searching for samples with the criterion
239         *
240         *      study.title contains 'nbic'
241         *
242         * this determination is more complex:
243         *
244         *      object.parent.getFieldValue( "title" )
245         *
246         *
247         * The other way around, when searching for studies with
248         *
249         *      sample.name equals 'sample 1'
250         *
251         * the value of the 'sample.name' property is a list:
252         *
253         *      object.samples*.getFieldValue( "name" )
254         * 
255         * The other search methods will handle the list and see whether any of the values
256         * matches the criterion.
257         *
258         * NB. The Criterion object has a convenience method to retrieve the field value on a
259         * specific (TemplateEntity) object: getFieldValue. This method also handles
260         * non-existing fields and casts the value to the correct type.
261         *
262         * This method should be overridden by all searches
263         *
264         * @see Criterion.getFieldValue()
265         *
266         * @return      Closure having 2 parameters: object and criterion
267         */
268        protected Closure valueCallback( String entity ) {
269                switch( entity ) {
270                        case "Study":
271                        case "Subject":
272                        case "Sample":
273                        case "Event":
274                        case "SamplingEvent":
275                        case "Assay":
276                                return { object, criterion -> return criterion.getFieldValue( object ); }
277                        default:
278                                return null;
279                }
280        }
281
282        /*****************************************************
283         *
284         * The other methods are helper functions for the execution of queries in subclasses
285         *
286         *****************************************************/
287
288        /**
289         * Returns a list of criteria targeted on the given entity
290         * @param entity        Entity to search criteria for
291         * @return                      List of criteria
292         */
293        protected List getEntityCriteria( String entity ) {
294                return criteria?.findAll { it.entity == entity }
295        }
296       
297       
298        /**
299         * Prepares a value from a template entity for comparison, by giving it a correct type
300         *
301         * @param value         Value of the field
302         * @param type          TemplateFieldType       Type of the specific field
303         * @return                      The value of the field in the correct entity
304         */
305        public static def prepare( def value, TemplateFieldType type ) {
306                if( value == null )
307                        return value
308
309                switch (type) {
310                        case TemplateFieldType.DATE:
311                                try {
312                                        return new SimpleDateFormat( "yyyy-MM-dd" ).parse( value.toString() )
313                                } catch( Exception e ) {
314                                        return value.toString();
315                                }
316                        case TemplateFieldType.RELTIME:
317                                try {
318                                        if( value instanceof Number ) {
319                                                return new RelTime( value );
320                                        } else if( value.toString().isNumber() ) {
321                                                return new RelTime( Long.parseLong( value.toString() ) )
322                                        } else {
323                                                return new RelTime( value );
324                                        }
325                                } catch( Exception e ) {
326                                        try {
327                                                return Long.parseLong( value )
328                                        } catch( Exception e2 ) {
329                                                return value.toString();
330                                        }
331                                }
332                        case TemplateFieldType.DOUBLE:
333                                try {
334                                        return Double.valueOf( value )
335                                } catch( Exception e ) {
336                                        return value.toString();
337                                }
338                        case TemplateFieldType.BOOLEAN:
339                                try {
340                                        return Boolean.valueOf( value )
341                                } catch( Exception e ) {
342                                        return value.toString();
343                                }
344                        case TemplateFieldType.LONG:
345                                try {
346                                        return Long.valueOf( value )
347                                } catch( Exception e ) {
348                                        return value.toString();
349                                }
350                        case TemplateFieldType.STRING:
351                        case TemplateFieldType.TEXT:
352                        case TemplateFieldType.STRINGLIST:
353                        case TemplateFieldType.TEMPLATE:
354                        case TemplateFieldType.MODULE:
355                        case TemplateFieldType.FILE:
356                        case TemplateFieldType.ONTOLOGYTERM:
357                        default:
358                                return value.toString();
359                }
360
361        }
362
363        /*****************************************************
364        *
365        * Methods for filtering lists based on specific (GSCF) criteria
366        *
367        *****************************************************/
368
369       
370        /**
371         * Filters a list with entities, based on the given criteria and a closure to check whether a criterion is matched
372         *
373         * @param entities      Original list with entities to check for these criteria
374         * @param criteria      List with criteria to match on
375         * @param check         Closure to see whether a specific entity matches a criterion. Gets two arguments:
376         *                                              element         The element to check
377         *                                              criterion       The criterion to check on.
378         *                                      Returns true if the criterion holds, false otherwise
379         * @return                      The filtered list of entities
380         */
381        protected List filterEntityList( List entities, List<Criterion> criteria, Closure check ) {
382                if( !entities || !criteria || criteria.size() == 0 ) {
383                        if( searchMode == SearchMode.and )
384                                return entities;
385                        else if( searchMode == SearchMode.or )
386                                return []
387                }
388
389                return entities.findAll { entity ->
390                        if( searchMode == SearchMode.and ) {
391                                for( criterion in criteria ) {
392                                        if( !check( entity, criterion ) ) {
393                                                return false;
394                                        }
395                                }
396                                return true;
397                        } else if( searchMode == SearchMode.or ) {
398                                for( criterion in criteria ) {
399                                        if( check( entity, criterion ) ) {
400                                                return true;
401                                        }
402                                }
403                                return false;
404                        }
405                }
406        }
407               
408        /**
409         * Filters the given list of studies on the study criteria
410         * @param studies               Original list of studies
411         * @param entity                Name of the entity to check the criteria for
412         * @param valueCallback Callback having a study and criterion as input, returning the value of the field to check on
413         * @return                              List with all studies that match the Criteria
414         */
415        protected List filterOnTemplateEntityCriteria( List studies, String entityName, Closure valueCallback ) {
416                def criteria = getEntityCriteria( entityName );
417
418                def checkCallback = { study, criterion ->
419                        def value = valueCallback( study, criterion );
420
421                        if( value == null ) {
422                                return false
423                        }
424
425                        if( value instanceof Collection ) {
426                                return criterion.matchAny( value )
427                        } else {
428                                return criterion.match( value );
429                        }
430                }
431
432                return filterEntityList( studies, criteria, checkCallback);
433        }
434
435        /**
436         * Filters the given list of studies on the study criteria
437         * @param studies       Original list of studies
438         * @return                      List with all studies that match the Study criteria
439         */
440        protected List filterOnStudyCriteria( List studies ) {
441                def entity = "Study"
442                return filterOnTemplateEntityCriteria(studies, entity, valueCallback( entity ) )
443        }
444
445        /**
446         * Filters the given list of studies on the subject criteria
447         * @param studies       Original list of studies
448         * @return                      List with all studies that match the Subject-criteria
449         */
450        protected List filterOnSubjectCriteria( List studies ) {
451                def entity = "Subject"
452                return filterOnTemplateEntityCriteria(studies, entity, valueCallback( entity ) )
453        }
454
455        /**
456         * Filters the given list of studies on the sample criteria
457         * @param studies       Original list of studies
458         * @return                      List with all studies that match the sample-criteria
459         */
460        protected List filterOnSampleCriteria( List studies ) {
461                def entity = "Sample"
462                return filterOnTemplateEntityCriteria(studies, entity, valueCallback( entity ) )
463        }
464
465        /**
466         * Filters the given list of studies on the event criteria
467         * @param studies       Original list of studies
468         * @return                      List with all studies that match the event-criteria
469         */
470        protected List filterOnEventCriteria( List studies ) {
471                def entity = "Event"
472                return filterOnTemplateEntityCriteria(studies, entity, valueCallback( entity ) )
473        }
474
475        /**
476         * Filters the given list of studies on the sampling event criteria
477         * @param studies       Original list of studies
478         * @return                      List with all studies that match the event-criteria
479         */
480        protected List filterOnSamplingEventCriteria( List studies ) {
481                def entity = "SamplingEvent"
482                return filterOnTemplateEntityCriteria(studies, entity, valueCallback( entity ) )
483        }
484
485        /**
486         * Filters the given list of studies on the assay criteria
487         * @param studies       Original list of studies
488         * @return                      List with all studies that match the assay-criteria
489         */
490        protected List filterOnAssayCriteria( List studies ) {
491                def entity = "Assay"
492                return filterOnTemplateEntityCriteria(studies, entity, valueCallback( entity ) )
493        }
494       
495       
496        /**
497         * Filters the given list of entities on criteria that mention all fields (e.g. search for studies with 'bacteria' in any field)
498         * @param objects       Original list of entities.
499         * @return                      List of all entities that match the given criteria
500         */
501        protected List filterOnAllFieldsCriteria( List objects ) {
502                def criteria = getEntityCriteria( "*" );
503               
504                // Find methods to determine a value for a criterion, based on all entities
505                def valueCallbacks = [:];
506                def entities = [ "Study", "Subject", "Sample", "Event", "SamplingEvent", "Assay" ];
507                entities.each {
508                        valueCallbacks[ it ] = valueCallback( it );
509                }
510               
511                // Create a closure that checks all entities
512                def checkCallback = { object, criterion ->
513                        def value = "";
514                        for( def entity in entities ) {
515                                value = valueCallbacks[ entity ]( object, criterion );
516                               
517                                if( value == null ) {
518                                        continue;
519                                }
520       
521                                if( value instanceof Collection ) {
522                                        if( criterion.matchAny( value ) )
523                                                return true;
524                                } else {
525                                        if( criterion.match( value ) )
526                                                return true;
527                                }
528                        }
529                       
530                        // If no match is found, return
531                        return false;
532                }
533
534                return filterEntityList( objects, criteria, checkCallback);
535        }
536       
537        /********************************************************************
538         *
539         * Methods for filtering object lists on module criteria
540         *
541         ********************************************************************/
542
543        /**
544         * Filters the given list of entities on the module criteria
545         * @param entities      Original list of entities. Entities should expose a giveUUID() method to give the token.
546         * @return                      List with all entities that match the module criteria
547         */
548        protected List filterOnModuleCriteria( List entities ) {
549                // An empty list can't be filtered more than is has been now
550                if( !entities || entities.size() == 0 )
551                        return [];
552
553                // Determine the moduleCommunicationService. Because this object
554                // is mocked in the tests, it can't be converted to a ApplicationContext object
555                def ctx = ApplicationHolder.getApplication().getMainContext();
556                def moduleCommunicationService = ctx.getBean("moduleCommunicationService");
557
558                switch( searchMode ) {
559                        case SearchMode.and:
560                                // Loop through all modules and check whether criteria have been given
561                                // for that module
562                                AssayModule.list().each { module ->
563                                        // Remove 'module' from module name
564                                        def moduleName = module.name.replace( 'module', '' ).trim()
565                                        def moduleCriteria = getEntityCriteria( moduleName );
566               
567                                        if( moduleCriteria && moduleCriteria.size() > 0 ) {
568                                                def callUrl = moduleCriteriaUrl( module );
569                                                def callArgs = moduleCriteriaArguments( module, entities, moduleCriteria );
570                                               
571                                                try {
572                                                        def json = moduleCommunicationService.callModuleMethod( module.url, callUrl, callArgs, "POST" );
573                                                        Closure checkClosure = moduleCriterionClosure( json );
574                                                        entities = filterEntityList( entities, moduleCriteria, checkClosure );
575                                                } catch( Exception e ) {
576                                                        //log.error( "Error while retrieving data from " + module.name + ": " + e.getMessage() )
577                                                        e.printStackTrace()
578                                                        throw e
579                                                }
580                                        }
581                                }
582               
583                                return entities;
584                        case SearchMode.or:
585                                def resultingEntities = []
586                               
587                                // Loop through all modules and check whether criteria have been given
588                                // for that module
589                                AssayModule.list().each { module ->
590                                        // Remove 'module' from module name
591                                        def moduleName = module.name.replace( 'module', '' ).trim()
592                                        def moduleCriteria = getEntityCriteria( moduleName );
593               
594                                        if( moduleCriteria && moduleCriteria.size() > 0 ) {
595                                                def callUrl = moduleCriteriaUrl( module );
596                                                def callArgs = moduleCriteriaArguments( module, entities, moduleCriteria );
597                                               
598                                                try {
599                                                        def json = moduleCommunicationService.callModuleMethod( module.url, callUrl, callArgs, "POST" );
600                                                        Closure checkClosure = moduleCriterionClosure( json );
601                                                       
602                                                        resultingEntities += filterEntityList( entities, moduleCriteria, checkClosure );
603                                                        resultingEntities = resultingEntities.unique();
604                                                       
605                                                } catch( Exception e ) {
606                                                        //log.error( "Error while retrieving data from " + module.name + ": " + e.getMessage() )
607                                                        e.printStackTrace()
608                                                        throw e
609                                                }
610                                        }
611                                }
612               
613                                println this.resultFields;
614                               
615                                return resultingEntities;
616                        default:
617                                return [];
618                }
619        }
620       
621        /**
622         * Returns a closure for determining the value of a module field
623         * @param json
624         * @return
625         */
626        protected Closure moduleCriterionClosure( def json ) {
627                return { entity, criterion ->
628                        // Find the value of the field in this sample. That value is still in the
629                        // JSON object
630                        def token = entity.giveUUID()
631                        def value
632                       
633                        if( criterion.field == '*' ) {
634                                // Collect the values from all fields
635                                value = [];
636                                json[ token ].each { field ->
637                                        if( field.value instanceof Collection ) {
638                                                field.value.each { value << it }
639                                        } else {
640                                                value << field.value;
641                                        }
642                                }
643                        } else {
644                                if( !json[ token ] || json[ token ][ criterion.field ] == null )
645                                        return false;
646       
647                                // Check whether a list or string is given
648                                value = json[ token ][ criterion.field ];
649       
650                                // Save the value of this entity for later use
651                                saveResultField( entity.id, criterion.entity + " " + criterion.field, value )
652       
653                                if( !( value instanceof Collection ) ) {
654                                        value = [ value ];
655                                }
656                        }
657
658                        // Convert numbers to a long or double in order to process them correctly
659                        def values = value.collect { val ->
660                                val = val.toString();
661                                if( val.isLong() ) {
662                                        val = Long.parseLong( val );
663                                } else if( val.isDouble() ) {
664                                        val = Double.parseDouble( val );
665                                }
666                                return val;
667                        }
668
669                        // Loop through all values and match any
670                        for( val in values ) {
671                                if( criterion.match( val ) )
672                                        return true;
673                        }
674
675                        return false;
676                }
677        }
678       
679        protected String moduleCriteriaUrl( module ) {
680                def callUrl = module.url + '/rest/getQueryableFieldData'
681                return callUrl;
682        }
683       
684        protected String moduleCriteriaArguments( module, entities, moduleCriteria ) {
685                // Retrieve the data from the module
686                def tokens = entities.collect { it.giveUUID() }.unique();
687                def fields = moduleCriteria.collect { it.field }.unique();
688       
689                def callUrl = 'entity=' + this.entity
690                tokens.sort().each { callUrl += "&tokens=" + it.encodeAsURL() }
691               
692                // If all fields are searched, all fields should be retrieved
693                if( fields.contains( '*' ) ) {
694                       
695                } else {
696                        fields.sort().each { callUrl += "&fields=" + it.encodeAsURL() }
697                }
698
699                return callUrl;
700        }
701
702        /*********************************************************************
703         *
704         * These methods are used for saving information about the search results and showing the information later on.
705         *
706         *********************************************************************/
707
708        /**
709         * Saves data about template entities to use later on. This data is copied to a special
710         * structure to make it compatible with data fetched from other modules.
711         * @see #saveResultField()
712         */
713        protected void saveResultFields() {
714                if( !results || !criteria )
715                        return
716
717                criteria.each { criterion ->
718                        if( criterion.field && criterion.field != '*' ) {
719                                def valueCallback = valueCallback( criterion.entity );
720                               
721                                if( valueCallback != null ) {
722                                        def name = criterion.entity + ' ' + criterion.field
723       
724                                        results.each { result ->
725                                                saveResultField( result.id, name, valueCallback( result, criterion ) );
726                                        }
727                                }
728                        }
729                }
730        }
731
732        /**
733         * Saves data about template entities to use later on. This data is copied to a special
734         * structure to make it compatible with data fetched from other modules.
735         * @param entities                      List of template entities to find data in
736         * @param criteria                      Criteria to search for
737         * @param valueCallback         Callback to retrieve a specific field from the entity
738         * @see #saveResultField()
739         */
740        protected void saveResultFields( entities, criteria, valueCallback ) {
741                for( criterion in criteria ) {
742                        for( entity in entities ) {
743                                if( criterion.field && criterion.field != '*' )
744                                        saveResultField( entity.id, criterion.entity + ' ' + criterion.field, valueCallback( entity, criterion ) )
745                        }
746                }
747        }
748
749
750        /**
751         * Saves a specific field of an object to use later on. Especially useful when looking up data from other modules.
752         * @param id            ID of the object
753         * @param fieldName     Field name that has been searched
754         * @param value         Value of the field
755         */
756        protected void saveResultField( id, fieldName, value ) {
757                if( resultFields[ id ] == null )
758                        resultFields[ id ] = [:]
759
760                // Handle special cases
761                if( value == null )
762                        value = "";
763               
764                if( fieldName == "*" )
765                        return;
766                       
767                if( value instanceof Collection ) {
768                        value = value.findAll { it != null }
769                }
770               
771                resultFields[ id ][ fieldName ] = value;
772        }
773
774        /**
775         * Removes all data from the result field map
776         */
777        protected void clearResultFields() {
778                resultFields = [:]
779        }
780
781        /**
782         * 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.
783         *
784         * 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
785         * query.)
786         * @return      Map with the entity id as a key, and a field-value map as value
787         */
788        public Map getShowableResultFields() {
789                def resultIds = getResults()*.id;
790                return getResultFields().findAll {
791                        resultIds.contains( it.key )
792                }
793        }
794       
795        /**
796         * Returns the field names that are found in the map with showable result fields
797         *
798         * @param fields        Map with showable result fields
799         * @see getShowableResultFields
800         * @return
801         */
802        public List getShowableResultFieldNames( fields ) {
803                return fields.values()*.keySet().flatten().unique();
804        }
805
806       
807        /************************************************************************
808         *
809         * Getters and setters
810         *
811         ************************************************************************/
812       
813        /**
814        * Returns a list of Criteria
815        */
816   public List getCriteria() { return criteria; }
817
818   /**
819        * Sets a new list of criteria
820        * @param c      List with criteria objects
821        */
822   public void setCriteria( List c ) { criteria = c; }
823
824   /**
825        * Adds a criterion to this query
826        * @param c      Criterion
827        */
828   public void addCriterion( Criterion c ) {
829           if( criteria )
830                   criteria << c;
831           else
832                   criteria = [c];
833   }
834
835   /**
836        * Retrieves the results found using this query. The result is empty is
837        * the query has not been executed yet.
838        */
839   public List getResults() { return results; }
840
841   /**
842        * Returns the results found using this query, filtered by a list of ids.
843        * @param selectedIds    List with ids of the entities you want to return.
844        * @return       A list with only those results for which the id is in the selectedIds
845        */
846   public List filterResults( List selectedTokens ) {
847           if( !selectedTokens || !results )
848                   return results
849
850           return results.findAll {
851                   selectedTokens.contains( it.giveUUID() )
852           }
853   }
854
855   /**
856        * Returns a list of fields for the results of this query. The fields returned are those
857        * fields that the query searched for.
858        */
859   public Map getResultFields() { return resultFields; }
860       
861        public String toString() {
862                return ( this.entity ? this.entity + " search" : "Search" ) + " " + this.id
863        }
864       
865        public boolean equals( Object o ) {
866                if( o == null )
867                        return false
868               
869                if( !( o instanceof Search ) ) 
870                        return false
871                       
872                Search s = (Search) o;
873               
874                return (        searchMode              == s.searchMode && 
875                                        entity                  == s.entity && 
876                                        criteria.size() == s.criteria.size() && 
877                                        s.criteria.containsAll( criteria ) && 
878                                        criteria.containsAll( s.criteria ) );
879        }
880}
Note: See TracBrowser for help on using the repository browser.