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

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

Changes in samplesearch test related to #313

  • Property svn:keywords set to Rev Author Date
File size: 10.9 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: 1521 $
12 * $Author: robert@isdat.nl $
13 * $Date: 2011-02-14 15:49:54 +0000 (ma, 14 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.list()
79                        if( studies )
80                                studies = studies.findAll { it.canRead( this.user ) };
81
82                        studies = filterStudiesOnStudyCriteria( studies );
83
84                        if( studies.size() == 0 ) {
85                                results = [];
86                                return;
87                        }
88
89                        def c = Sample.createCriteria()
90                        samples = c.list {
91                                'in'( 'parent', studies )
92                        }
93
94                        // Save data about the resulting studies in the
95                        // result fields array. The data that is now in the array
96                        // is saved based on the study id, not based on the sample id
97                        clearResultFields();
98                        saveResultFields( samples, getEntityCriteria( "Study" ), { sample, criterion ->
99                                return criterion.getFieldValue( sample.parent );
100                        });
101                } else {
102                        samples = Sample.list()
103                        if( samples )
104                                samples = samples.findAll { it.parent?.canRead( this.user ) }
105                }
106
107                samples = filterOnSubjectCriteria( samples );
108                samples = filterOnSampleCriteria( samples );
109                samples = filterOnEventCriteria( samples );
110                samples = filterOnSamplingEventCriteria( samples );
111                samples = filterOnAssayCriteria( samples );
112
113                samples = filterOnModuleCriteria( samples );
114
115                // Save matches
116                results = samples;
117        }
118
119        /**
120        * Searches for samples based on the given criteria. Only one of the criteria have to be satisfied and
121        * criteria for the different entities are satisfied as follows:
122        *
123        *               Sample.title = 'abc'
124        *                               Samples are returned from studies with title 'abc'
125        *
126        *               Subject.species = 'human'
127        *                               Samples are returned from subjects with species = 'human'
128        *
129        *               Sample.name = 'sample 1'
130        *                               Samples are returned with name = 'sample 1'
131        *
132        *               Event.startTime = '0s'
133        *                               Samples are returned from subjects that have had an event with start time = '0s'
134        *
135        *               SamplingEvent.startTime = '0s'
136        *                               Samples are returned that have originated from a sampling event with start time = '0s'
137        *
138        *               Assay.module = 'metagenomics'
139        *                               Samples are returned that have been processed in an assay with module = metagenomics
140        *
141        * When searching for more than one criterion per entity, these are taken separately. Searching for
142        *
143        *               Subject.species = 'human'
144        *               Subject.name = 'Jan'
145        *
146        *  will result in all samples from a human subject or a subject named 'Jan'. Samples from a mouse subject
147        *  named 'Jan' or a human subject named 'Kees' will also satisfy the criteria.
148        *
149        */
150   @Override
151   void executeOr() {
152           // We expect the sample criteria to be the most discriminative, and discard
153           // the most samples. (e.g. by searching on sample title of sample type). For
154           // that reason we first look through the list of studies. However, when the
155           // user didn't enter any sample criteria, this will be an extra step, but doesn't
156           // cost much time to process.
157           def samples = []
158           def allSamples = Sample.list().findAll { it.parent?.canRead( this.user ) }.toList();
159
160           // If no criteria are found, return all samples
161           if( !criteria || criteria.size() == 0 ) {
162                   results = allSamples
163                   return;
164           }
165           
166           samples = ( samples + filterSamplesOnStudyCriteria( allSamples - samples ) ).unique();
167           samples = ( samples + filterOnSubjectCriteria( allSamples - samples ) ).unique();
168           samples = ( samples + filterOnSampleCriteria( allSamples - samples ) ).unique();
169           samples = ( samples + filterOnEventCriteria( allSamples - samples ) ).unique();
170           samples = ( samples + filterOnSamplingEventCriteria( allSamples - samples ) ).unique();
171           samples = ( samples + filterOnAssayCriteria( allSamples - samples ) ).unique();
172           
173           samples = ( samples + filterOnModuleCriteria( allSamples - samples ) ).unique();
174           
175           // Save matches
176           results = samples;
177   }
178       
179        /**
180         * Filters the given list of studies on the study criteria
181         * @param studies       Original list of studies
182         * @return                      List with all samples that match the Study-criteria
183         */
184        protected List filterStudiesOnStudyCriteria( List studies ) {
185                return filterOnTemplateEntityCriteria(studies, "Study", { study, criterion -> return criterion.getFieldValue( study ) })
186        }
187       
188        /**
189        * Filters the given list of samples on the sample criteria
190        * @param samples        Original list of samples
191        * @return                       List with all samples that match the Study-criteria
192        */
193   protected List filterSamplesOnStudyCriteria( List samples ) {
194           return filterOnTemplateEntityCriteria(samples, "Study", { study, criterion -> 
195                   return criterion.getFieldValue( study.parent ) 
196           })
197   }
198
199
200        /**
201         * Filters the given list of samples on the subject criteria
202         * @param samples       Original list of samples
203         * @return                      List with all samples that match the Subject-criteria
204         */
205        protected List filterOnSubjectCriteria( List samples ) {
206                return filterOnTemplateEntityCriteria(samples, "Subject", { sample, criterion ->
207                        return criterion.getFieldValue( sample.parentSubject );
208                })
209        }
210
211        /**
212         * Filters the given list of samples on the sample criteria
213         * @param samples       Original list of samples
214         * @return                      List with all samples that match the sample-criteria
215         */
216        protected List filterOnSampleCriteria( List samples ) {
217                return filterOnTemplateEntityCriteria(samples, "Sample", { sample, criterion ->
218                        return criterion.getFieldValue( sample );
219                })
220        }
221
222        /**
223         * Filters the given list of samples on the event criteria
224         * @param samples       Original list of samples
225         * @return                      List with all samples that match the event-criteria
226         */
227        protected List filterOnEventCriteria( List samples ) {
228                return filterOnTemplateEntityCriteria(samples, "Event", { sample, criterion ->
229                        if( !sample || !sample.parentEventGroup || !sample.parentEventGroup.events || sample.parentEventGroup.events.size() == 0 )
230                                return null
231
232                        return criterion.getFieldValue( sample.parentEventGroup.events.toList() );
233                })
234        }
235
236        /**
237         * Filters the given list of samples on the sampling event criteria
238         * @param samples       Original list of samples
239         * @return                      List with all samples that match the event-criteria
240         */
241        protected List filterOnSamplingEventCriteria( List samples ) {
242                return filterOnTemplateEntityCriteria(samples, "SamplingEvent", { sample, criterion ->
243                        return criterion.getFieldValue( sample.parentEvent );
244                })
245        }
246
247        /**
248         * Filters the given list of samples on the assay criteria
249         * @param samples       Original list of samples
250         * @return                      List with all samples that match the assay-criteria
251         */
252        protected List filterOnAssayCriteria( List samples ) {
253                if( !samples?.size() )
254                        return [];
255
256                // There is no sample.assays property, so we have to look for assays another way: just find
257                // all assays that match the criteria
258                def criteria = getEntityCriteria( 'Assay' );
259               
260                if( getEntityCriteria( 'Assay' ).size() == 0 ) {
261                        if( searchMode == SearchMode.and )
262                                return samples
263                        else if( searchMode == SearchMode.or )
264                                return [];
265                }
266
267                def assays = filterEntityList( Assay.list(), criteria, { assay, criterion ->
268                        if( !assay )
269                                return false
270
271                        return criterion.matchEntity( assay );
272                });
273
274                // If no assays match these criteria, then no samples will match either
275                if( assays.size() == 0 )
276                        return [];
277
278                // Now filter the samples on whether they are attached to the filtered assays
279                def matchingSamples = samples.findAll { sample ->
280                        if( !sample.parent )
281                                return false;
282
283                        def studyAssays = assays.findAll { it.parent.equals( sample.parent ); }
284
285                        // See if this sample is present in any of the matching assays. If so,
286                        // this sample matches the criteria
287                        for( def assay in studyAssays ) {
288                                if( assay.samples?.contains( sample ) )
289                                        return true;
290                        }
291
292                        return false;
293                }
294               
295                // Save sample data for later use
296                println samples
297                println "Find values for " + matchingSamples + " and " + criteria
298                saveResultFields( matchingSamples, criteria, { sample, criterion ->
299                        println "Find value for " + sample + " and " + criterion
300                        def sampleAssays = Assay.findByParent( sample.parent ).findAll { it.samples?.contains( sample ) };
301                        if( sampleAssays && sampleAssays.size() > 0 )
302                                return sampleAssays.collect { criterion.getFieldValue( it ) }
303                        else
304                                return null
305                });
306       
307                return matchingSamples;
308        }
309
310        /**
311         * Returns the saved field data that could be shown on screen. This means, the data
312         * is filtered to show only data of the query results. Also, the study title and sample
313         * name are filtered out, in order to be able to show all data on the screen without
314         * checking further
315         *
316         * @return      Map with the entity id as a key, and a field-value map as value
317         */
318        public Map getShowableResultFields() {
319                Map showableFields = super.getShowableResultFields()
320                showableFields.each { sampleElement ->
321                        sampleElement.value = sampleElement.value.findAll { fieldElement ->
322                                fieldElement.key != "Study title" && fieldElement.key != "Sample name"
323                        }
324                }
325        }
326}
Note: See TracBrowser for help on using the repository browser.