source: trunk/src/groovy/dbnp/query/SampleSearch.groovy @ 1501

Last change on this file since 1501 was 1501, checked in by robert@…, 9 years ago
  • Number of seconds for the rest controller to keep data in cache is now a configuration option
  • After searching, it is possible to choose which action to perform on the search results.
  • Property svn:keywords set to Rev Author Date
File size: 10.8 KB
Line 
1/**
2 * SampleSearch Domain Class
3 *
4 * This class provides querying capabilities for searching for samples
5 *
6 * @author  Robert Horlings (robert@isdat.nl)
7 * @since       20110118
8 * @package     dbnp.query
9 *
10 * Revision information:
11 * $Rev: 1501 $
12 * $Author: robert@isdat.nl $
13 * $Date: 2011-02-07 15:07:54 +0000 (ma, 07 feb 2011) $
14 */
15package dbnp.query
16
17import java.util.Map;
18
19import dbnp.studycapturing.*
20import org.dbnp.gdt.*
21import org.apache.commons.logging.LogFactory;
22
23class SampleSearch extends Search {
24        private static final log = LogFactory.getLog(this);
25       
26        public SampleSearch() {
27                super();
28                               
29                this.entity = "Sample";
30        }
31
32        /**
33         * Searches for samples based on the given criteria. All criteria have to be satisfied and
34         * criteria for the different entities are satisfied as follows:
35         *
36         *              Sample.title = 'abc'           
37         *                              Only samples are returned from studies with title 'abc'
38         *             
39         *              Subject.species = 'human'
40         *                              Only samples are returned from subjects with species = 'human' 
41         *
42         *              Sample.name = 'sample 1'
43         *                              Only samples are returned with name = 'sample 1'
44         *
45         *              Event.startTime = '0s'
46         *                              Only samples are returned from subjects that have had an event with start time = '0s' 
47         *
48         *              SamplingEvent.startTime = '0s'
49         *                              Only samples are returned that have originated from a sampling event with start time = '0s' 
50         *
51         *              Assay.module = 'metagenomics'
52         *                              Only samples are returned that have been processed in an assay with module = metagenomics 
53         *
54         * When searching for more than one criterion per entity, these are taken combined. Searching for
55         *
56         *              Subject.species = 'human'
57         *              Subject.name = 'Jan'
58         *
59         *  will result in all samples from a human subject named 'Jan'. Samples from a mouse subject
60         *  named 'Jan' or a human subject named 'Kees' won't satisfy the criteria.
61         *     
62         */
63        @Override
64        void executeAnd() {
65                // If no criteria are found, return all samples
66                if( !criteria || criteria.size() == 0 ) {
67                        results = Sample.list().findAll { it.parent?.canRead( this.user ) };
68                        return;
69                }
70
71                // We expect the sample criteria to be the most discriminative, and discard
72                // the most samples. (e.g. by searching on sample title of sample type). For
73                // that reason we first look through the list of studies. However, when the
74                // user didn't enter any sample criteria, this will be an extra step, but doesn't
75                // cost much time to process.
76                def samples = []
77                if( getEntityCriteria( 'Study' ).size() > 0 ) {
78                        def studies = Study.findAll().findAll { it.canRead( this.user ) };
79
80                        studies = filterStudiesOnStudyCriteria( studies );
81
82                        if( studies.size() == 0 ) {
83                                results = [];
84                                return;
85                        }
86
87                        def c = Sample.createCriteria()
88                        samples = c.list {
89                                'in'( 'parent', studies )
90                        }
91
92                        // Save data about the resulting studies in the
93                        // result fields array. The data that is now in the array
94                        // is saved based on the study id, not based on the sample id
95                        clearResultFields();
96                        saveResultFields( samples, getEntityCriteria( "Study" ), { sample, criterion ->
97                                return criterion.getFieldValue( sample.parent );
98                        });
99                } else {
100                        samples = Sample.findAll().findAll { it.parent?.canRead( this.user ) }
101                }
102
103                samples = filterOnSubjectCriteria( samples );
104                samples = filterOnSampleCriteria( samples );
105                samples = filterOnEventCriteria( samples );
106                samples = filterOnSamplingEventCriteria( samples );
107                samples = filterOnAssayCriteria( samples );
108
109                samples = filterOnModuleCriteria( samples );
110
111                // Save matches
112                results = samples;
113        }
114
115        /**
116        * Searches for samples based on the given criteria. Only one of the criteria have to be satisfied and
117        * criteria for the different entities are satisfied as follows:
118        *
119        *               Sample.title = 'abc'
120        *                               Samples are returned from studies with title 'abc'
121        *
122        *               Subject.species = 'human'
123        *                               Samples are returned from subjects with species = 'human'
124        *
125        *               Sample.name = 'sample 1'
126        *                               Samples are returned with name = 'sample 1'
127        *
128        *               Event.startTime = '0s'
129        *                               Samples are returned from subjects that have had an event with start time = '0s'
130        *
131        *               SamplingEvent.startTime = '0s'
132        *                               Samples are returned that have originated from a sampling event with start time = '0s'
133        *
134        *               Assay.module = 'metagenomics'
135        *                               Samples are returned that have been processed in an assay with module = metagenomics
136        *
137        * When searching for more than one criterion per entity, these are taken separately. Searching for
138        *
139        *               Subject.species = 'human'
140        *               Subject.name = 'Jan'
141        *
142        *  will result in all samples from a human subject or a subject named 'Jan'. Samples from a mouse subject
143        *  named 'Jan' or a human subject named 'Kees' will also satisfy the criteria.
144        *
145        */
146   @Override
147   void executeOr() {
148           // We expect the sample criteria to be the most discriminative, and discard
149           // the most samples. (e.g. by searching on sample title of sample type). For
150           // that reason we first look through the list of studies. However, when the
151           // user didn't enter any sample criteria, this will be an extra step, but doesn't
152           // cost much time to process.
153           def samples = []
154           def allSamples = Sample.list().findAll { it.parent?.canRead( this.user ) }.toList();
155
156           // If no criteria are found, return all samples
157           if( !criteria || criteria.size() == 0 ) {
158                   results = allSamples
159                   return;
160           }
161           
162           samples = ( samples + filterSamplesOnStudyCriteria( allSamples - samples ) ).unique();
163           samples = ( samples + filterOnSubjectCriteria( allSamples - samples ) ).unique();
164           samples = ( samples + filterOnSampleCriteria( allSamples - samples ) ).unique();
165           samples = ( samples + filterOnEventCriteria( allSamples - samples ) ).unique();
166           samples = ( samples + filterOnSamplingEventCriteria( allSamples - samples ) ).unique();
167           samples = ( samples + filterOnAssayCriteria( allSamples - samples ) ).unique();
168           
169           samples = ( samples + filterOnModuleCriteria( allSamples - samples ) ).unique();
170           
171           // Save matches
172           results = samples;
173   }
174       
175        /**
176         * Filters the given list of studies on the study criteria
177         * @param studies       Original list of studies
178         * @return                      List with all samples that match the Study-criteria
179         */
180        protected List filterStudiesOnStudyCriteria( List studies ) {
181                return filterOnTemplateEntityCriteria(studies, "Study", { study, criterion -> return criterion.getFieldValue( study ) })
182        }
183       
184        /**
185        * Filters the given list of samples on the sample criteria
186        * @param samples        Original list of samples
187        * @return                       List with all samples that match the Study-criteria
188        */
189   protected List filterSamplesOnStudyCriteria( List samples ) {
190           return filterOnTemplateEntityCriteria(samples, "Study", { study, criterion -> 
191                   return criterion.getFieldValue( study.parent ) 
192           })
193   }
194
195
196        /**
197         * Filters the given list of samples on the subject criteria
198         * @param samples       Original list of samples
199         * @return                      List with all samples that match the Subject-criteria
200         */
201        protected List filterOnSubjectCriteria( List samples ) {
202                return filterOnTemplateEntityCriteria(samples, "Subject", { sample, criterion ->
203                        return criterion.getFieldValue( sample.parentSubject );
204                })
205        }
206
207        /**
208         * Filters the given list of samples on the sample criteria
209         * @param samples       Original list of samples
210         * @return                      List with all samples that match the sample-criteria
211         */
212        protected List filterOnSampleCriteria( List samples ) {
213                return filterOnTemplateEntityCriteria(samples, "Sample", { sample, criterion ->
214                        return criterion.getFieldValue( sample );
215                })
216        }
217
218        /**
219         * Filters the given list of samples on the event criteria
220         * @param samples       Original list of samples
221         * @return                      List with all samples that match the event-criteria
222         */
223        protected List filterOnEventCriteria( List samples ) {
224                return filterOnTemplateEntityCriteria(samples, "Event", { sample, criterion ->
225                        if( !sample || !sample.parentEventGroup || !sample.parentEventGroup.events || sample.parentEventGroup.events.size() == 0 )
226                                return null
227
228                        return criterion.getFieldValue( sample.parentEventGroup.events.toList() );
229                })
230        }
231
232        /**
233         * Filters the given list of samples on the sampling event criteria
234         * @param samples       Original list of samples
235         * @return                      List with all samples that match the event-criteria
236         */
237        protected List filterOnSamplingEventCriteria( List samples ) {
238                return filterOnTemplateEntityCriteria(samples, "SamplingEvent", { sample, criterion ->
239                        return criterion.getFieldValue( sample.parentEvent );
240                })
241        }
242
243        /**
244         * Filters the given list of samples on the assay criteria
245         * @param samples       Original list of samples
246         * @return                      List with all samples that match the assay-criteria
247         */
248        protected List filterOnAssayCriteria( List samples ) {
249                if( !samples?.size() )
250                        return [];
251
252                // There is no sample.assays property, so we have to look for assays another way: just find
253                // all assays that match the criteria
254                def criteria = getEntityCriteria( 'Assay' );
255               
256                if( getEntityCriteria( 'Assay' ).size() == 0 ) {
257                        if( searchMode == SearchMode.and )
258                                return samples
259                        else if( searchMode == SearchMode.or )
260                                return [];
261                }
262
263                def assays = filterEntityList( Assay.list(), criteria, { assay, criterion ->
264                        if( !assay )
265                                return false
266
267                        return criterion.matchEntity( assay );
268                });
269
270                // If no assays match these criteria, then no samples will match either
271                if( assays.size() == 0 )
272                        return [];
273
274                // Now filter the samples on whether they are attached to the filtered assays
275                def matchingSamples = samples.findAll { sample ->
276                        if( !sample.parent )
277                                return false;
278
279                        def studyAssays = assays.findAll { it.parent.equals( sample.parent ); }
280
281                        // See if this sample is present in any of the matching assays. If so,
282                        // this sample matches the criteria
283                        for( def assay in studyAssays ) {
284                                if( assay.samples?.contains( sample ) )
285                                        return true;
286                        }
287
288                        return false;
289                }
290               
291                // Save sample data for later use
292                println samples
293                println "Find values for " + matchingSamples + " and " + criteria
294                saveResultFields( matchingSamples, criteria, { sample, criterion ->
295                        println "Find value for " + sample + " and " + criterion
296                        def sampleAssays = Assay.findByParent( sample.parent ).findAll { it.samples?.contains( sample ) };
297                        if( sampleAssays && sampleAssays.size() > 0 )
298                                return sampleAssays.collect { criterion.getFieldValue( it ) }
299                        else
300                                return null
301                });
302       
303                return matchingSamples;
304        }
305
306        /**
307         * Returns the saved field data that could be shown on screen. This means, the data
308         * is filtered to show only data of the query results. Also, the study title and sample
309         * name are filtered out, in order to be able to show all data on the screen without
310         * checking further
311         *
312         * @return      Map with the entity id as a key, and a field-value map as value
313         */
314        public Map getShowableResultFields() {
315                Map showableFields = super.getShowableResultFields()
316                showableFields.each { sampleElement ->
317                        sampleElement.value = sampleElement.value.findAll { fieldElement ->
318                                fieldElement.key != "Study title" && fieldElement.key != "Sample name"
319                        }
320                }
321        }
322}
Note: See TracBrowser for help on using the repository browser.