Changeset 1526


Ignore:
Timestamp:
Feb 16, 2011, 11:12:14 AM (6 years ago)
Author:
robert@…
Message:

Added assay search and improved query form

Location:
trunk
Files:
2 added
14 edited

Legend:

Unmodified
Added
Removed
  • trunk/grails-app/controllers/dbnp/query/AdvancedQueryController.groovy

    r1524 r1526  
    1515        def authenticationService
    1616
    17         def entitiesToSearchFor = [ 'Study': 'Studies', 'Sample': 'Samples']
     17        def entitiesToSearchFor = [ 'Study': 'Studies', 'Sample': 'Samples', 'Assay': 'Assays']
    1818
    1919        /**
     
    198198
    199199                if( queryIds.size() == 0 ) {
    200                         flash.error = "Incorrect search ID given to show"
    201                         redirect( action: "index" );
     200                        flash.error = "Incorrect search ID given to search in"
     201                        redirect( action: "list" );
    202202                        return
    203203                }
     
    287287                        case "Study":   return "studyresults";  break;
    288288                        case "Sample":  return "sampleresults"; break;
     289                        case "Assay":   return "assayresults";  break;
    289290                        default:                return "results"; break;
    290291                }
     
    298299                        case "Study":   return new StudySearch();
    299300                        case "Sample":  return new SampleSearch();
    300 
     301                        case "Assay":   return new AssaySearch();
     302                       
    301303                        // This exception will only be thrown if the entitiesToSearchFor contains more entities than
    302304                        // mentioned in this switch structure.
     
    375377                // Loop through all keys of c and remove the non-numeric ones
    376378                for( c in formCriteria ) {
    377                         if( c.key ==~ /[0-9]+/ ) {
     379                        if( c.key ==~ /[0-9]+/ && c.value.entityfield ) {
    378380                                def formCriterion = c.value;
    379381
     
    478480
    479481                // First check whether a search with the same criteria is already present
    480                 def previousSearch = retrieveSearchByCriteria( s.getCriteria(), s.searchMode );
     482                def previousSearch = retrieveSearch( s );
    481483
    482484                def id
     
    496498        /**
    497499         * Retrieves a search from session with the same criteria as given
    498          * @param criteria      List of criteria to search for
     500         * @param s                     Search that is used as an example to search for
    499501         * @return                      Search that has this criteria, or null if no such search is found.
    500502         */
    501         protected Search retrieveSearchByCriteria( List criteria, SearchMode searchMode = SearchMode.and ) {
     503        protected Search retrieveSearch( Search s ) {
    502504                if( !session.queries )
    503505                        return null
    504506
    505                 if( !criteria )
    506                         return null
    507 
    508507                for( query in session.queries ) {
    509                         def key = query.key;
    510508                        def value = query.value;
    511509
    512                         if( value.searchMode == searchMode && value.criteria && value.criteria.containsAll( criteria ) && criteria.containsAll( value.criteria ) ) {
    513                                 return value;
    514                         }
     510                        if( s.equals( value ) )
     511                                return value
    515512                }
    516513
  • trunk/grails-app/domain/dbnp/studycapturing/Assay.groovy

    r1457 r1526  
    7474       
    7575        /**
     76         * Basic equals method to check whether objects are equals, by comparing the ids
     77         * @param o             Object to compare with
     78         * @return              True iff the id of the given Study is equal to the id of this Study
     79         */
     80        public boolean equals( Object o ) {
     81                if( o == null )
     82                        return false;
     83
     84                if( !( o instanceof Assay ) )
     85                        return false
     86
     87                Assay s = (Assay) o;
     88
     89                return this.id == s.id
     90        }
     91       
     92        /**
    7693         * Returns the UUID of this sample and generates one if needed
    7794         */
  • trunk/grails-app/views/advancedQuery/_criteria.gsp

    r1512 r1526  
    11<%@ page import="dbnp.query.*" %>
    2 <ul id="criteria">
    3         <g:each in="${criteria}" var="criterion" status="j">
    4                 <li>
    5                         <span class="entityfield">${criterion.entityField().toLowerCase()}</span>
    6                         <span class="operator">${criterion.operator}</span>
    7                         <span class="value">
    8                                 <g:if test="${criterion.value != null && criterion.value instanceof Search}">
    9                                         <g:link action="show" id="${criterion.value.id}">${criterion.value}</g:link>
     2<g:if test="${criteria}">
     3        with
     4        <ul id="criteria">
     5                <g:each in="${criteria}" var="criterion" status="j">
     6                        <li>
     7                                <span class="entityfield">${criterion.entityField().toLowerCase()}</span>
     8                                <span class="operator">${criterion.operator}</span>
     9                                <span class="value">
     10                                        <g:if test="${criterion.value != null && criterion.value instanceof Search}">
     11                                                <g:link action="show" id="${criterion.value.id}">${criterion.value}</g:link>
     12                                        </g:if>
     13                                        <g:else>
     14                                                ${criterion.value}
     15                                        </g:else>
     16                                </span>
     17                                <g:if test="${j < criteria.size() -1}">
     18                                        <g:if test="${search.searchMode == SearchMode.and}">and</g:if>
     19                                        <g:if test="${search.searchMode == SearchMode.or}">or</g:if>
    1020                                </g:if>
    11                                 <g:else>
    12                                         ${criterion.value}
    13                                 </g:else>
    14                         </span>
    15                         <g:if test="${j < criteria.size() -1}">
    16                                 <g:if test="${search.searchMode == SearchMode.and}">and</g:if>
    17                                 <g:if test="${search.searchMode == SearchMode.or}">or</g:if>
    18                         </g:if>
    19                 </li>
    20         </g:each>
    21 </ul>
     21                        </li>
     22                </g:each>
     23        </ul>
     24</g:if>
     25<g:else>
     26        without criteria.
     27</g:else>
  • trunk/grails-app/views/advancedQuery/index.gsp

    r1512 r1526  
    3030                                        showCriterium("${criterion.entityField().encodeAsJavaScript()}", "${criterion.value.toString().encodeAsJavaScript()}", "${criterion.operator.toString().encodeAsJavaScript()}");
    3131                                </g:each>
    32                                 showHideNoCriteriaItem();
    3332                        });
    3433                </g:if>
     
    5150
    5251<div id="searchForm">
    53         <form id="input_criteria">
    54         <h2>Add criterium</h2>
    55         <p class="explanation">
    56                 N.B. Comparing numerical values is done without taking into
    57                 account the units. E.g. a weight of 1 kg equals 1 grams.
    58         </p>
    59         <label for="field">Field</label>
    60                 <select name="field" id="queryFieldSelect">
    61                         <option value=""></option>
    62                         <g:each in="${searchableFields}" var="entity">
    63                                 <optgroup label="${entity.key}">
    64                                         <g:each in="${entity.value}" var="field">
    65                                                 <option value="${entity.key}.${field}">
    66                                                         ${field[0].toUpperCase() + field[1..-1]}
    67                                                 </option>
    68                                         </g:each>
    69                                 </optgroup>
    70                         </g:each>
    71                 </select>
    72                
    73         <label for="value">Comparison</label>
    74                 <select name="operator">
    75                         <option value="equals">Equals</option>
    76                         <option value="contains">Contains</option>
    77                         <option value="&gt;=">Greater than or equals</option>
    78                         <option value="&gt;">Greater than</option>
    79                         <option value="&lt;">Lower than</option>
    80                         <option value="&lt;=">Lower than or equals</option>
    81                 </select>
    82        
    83         <label for="value">Value</label>
    84                 <input class='text' type="text" name="value" />
    85        
    86         <input class="button" type="button" onClick="addCriterium();" value="Add" />
    87         </form>
    8852        <g:form action="search" method="get">
    89                 <label for="entity">Search for</label><g:select from="${entitiesToSearchFor}" optionKey="key" optionValue="value" name="entity" /><br />
    90                 <label for="entity">Searchtype</label><g:select from="${searchModes}" name="operator" /><br />
    91                 <label for="criteria">Criteria</label>
     53
     54                <h3><span class="nummer">1</span>Select criteria</h3>
     55                <p class="explanation">
     56                        N.B. Comparing numerical values is done without taking into
     57                        account the units. E.g. a weight of 1 kg equals 1 grams.
     58                </p>
    9259                <ul id="criteria">
    93                         <li class="emptyList">No criteria added. Use the form on the right to specify criteria to search on.</li>
     60                        <li class="titlerow">
     61                                <span class="entityfield">
     62                                        Field
     63                                </span>
     64                                <span class="operator">
     65                                        Operator
     66                                </span>
     67                                <span class="value">
     68                                        Value
     69                                </span>
     70                        </li>
     71                        <li class="newCriterion">
     72                                <span class="entityfield">
     73                                        <select name="criteria.0.entityfield" id="queryFieldSelect">
     74                                                <option value=""></option>
     75                                                <g:each in="${searchableFields}" var="entity">
     76                                                        <optgroup label="${entity.key}">
     77                                                                <g:each in="${entity.value}" var="field">
     78                                                                        <option value="${entity.key}.${field}">
     79                                                                                ${field[0].toUpperCase() + field[1..-1]}
     80                                                                        </option>
     81                                                                </g:each>
     82                                                        </optgroup>
     83                                                </g:each>
     84                                        </select>
     85                                </span>
     86                                <span class="operator">
     87                                        <select id="operator" name="criteria.0.operator">
     88                                                <option value="equals">Equals</option>
     89                                                <option value="contains">Contains</option>
     90                                                <option value="&gt;=">&gt;=</option>
     91                                                <option value="&gt;">&gt;</option>
     92                                                <option value="&lt;">&lt;</option>
     93                                                <option value="&lt;=">&lt;=</option>
     94                                        </select>
     95                                </span>
     96                                <span class="value">
     97                                        <input class='text' type="text" id="value" name="criteria.0.value" />
     98                                </span>
     99                                <span class="addButton">
     100                                        <a href="#" onClick="addCriterion(); return false;">
     101                                                <img src="${fam.icon( name: 'add' )}" border="0">
     102                                        </a>
     103                                </span>
     104                        </li>
    94105                </ul>
    95106               
    96                 <input type="submit" value="Run query" class="submitcriteria" disabled="disabled" />
     107                <div id="searchMode">
     108                        <h3><span class="nummer">1b</span>Search mode</h3>
     109                        <p>
     110                                Choose how to combine the given criteria:<br />
     111                                <g:select from="${searchModes}" name="operator" />
     112                        </p>
     113                </div>
     114               
     115                <h3><span class="nummer">2</span>Output type</h3>
     116                <p>
     117                        Choose the type of output:<br />
     118                        <g:select from="${entitiesToSearchFor}" optionKey="key" optionValue="value" name="entity" />
     119                </p>
     120
     121                <h3><span class="nummer">3</span>Run query</h3>
     122                <p>
     123                        <input type="submit" value="Run query" class="submitcriteria" />
     124                </p>
    97125        </g:form>
    98126       
  • trunk/grails-app/views/advancedQuery/list.gsp

    r1524 r1526  
    5656                                <td>${search.entity}</td>
    5757                                <td>
    58                                         <g:each in="${search.getCriteria()}" var="criterion" status="j">
    59                                                 <g:if test="${j > 0}">, </g:if>
     58                                        <g:set var="criteria" value="${search.getCriteria()}" />
     59                                        <g:each in="${criteria}" var="criterion" status="j">
    6060                                                <span class="entityfield">${criterion.entityField()}</span>
    6161                                                <span class="operator">${criterion.operator}</span>
     
    6868                                                        </g:else>
    6969                                                </span>
     70                                                <g:if test="${j < criteria.size() -1}">
     71                                                        <g:if test="${search.searchMode == SearchMode.and}">and</g:if>
     72                                                        <g:if test="${search.searchMode == SearchMode.or}">or</g:if>
     73                                                </g:if>                                         
    7074                                        </g:each>
    7175                                </td>
  • trunk/grails-app/views/advancedQuery/results.gsp

    r1524 r1526  
    1212
    1313<div class="searchoptions">
    14         ${search.getNumResults()} <g:if test="${search.getNumResults() == 1}">result</g:if><g:else>results</g:else> found with
     14        ${search.getNumResults()} <g:if test="${search.getNumResults() == 1}">result</g:if><g:else>results</g:else> found
    1515        <g:render template="criteria" model="[criteria: search.getCriteria()]" />
    1616</div>
     
    4949                                                        if( fieldValue ) {
    5050                                                                if( fieldValue instanceof Collection ) {
    51                                                                         fieldValue = fieldValue.collect { it.toString() }.findAll { it }.join( ', ' );
     51                                                                        fieldValue = fieldValue.collect { it.toString() }.findAll { it }.unique().join( ', ' );
    5252                                                                } else {
    5353                                                                        fieldValue = fieldValue.toString();
  • trunk/grails-app/views/advancedQuery/sampleresults.gsp

    r1524 r1526  
    1212
    1313<div class="searchoptions">
    14         ${search.getNumResults()} <g:if test="${search.getNumResults() == 1}">sample</g:if><g:else>samples</g:else> found with
     14        ${search.getNumResults()} <g:if test="${search.getNumResults() == 1}">sample</g:if><g:else>samples</g:else> found
    1515        <g:render template="criteria" model="[criteria: search.getCriteria()]" />
    1616</div>
     
    5050                                                        if( fieldValue ) {
    5151                                                                if( fieldValue instanceof Collection ) {
    52                                                                         fieldValue = fieldValue.collect { it.toString() }.findAll { it }.join( ', ' );
     52                                                                        fieldValue = fieldValue.collect { it.toString() }.findAll { it }.unique().join( ', ' );
    5353                                                                } else {
    5454                                                                        fieldValue = fieldValue.toString();
  • trunk/grails-app/views/advancedQuery/studyresults.gsp

    r1524 r1526  
    1212
    1313<div class="searchoptions">
    14         ${search.getNumResults()} <g:if test="${search.getNumResults() == 1}">study</g:if><g:else>studies</g:else> found with
     14        ${search.getNumResults()} <g:if test="${search.getNumResults() == 1}">study</g:if><g:else>studies</g:else> found
    1515        <g:render template="criteria" model="[criteria: search.getCriteria()]" />
    1616</div>
     
    8686                                                        if( fieldValue ) {
    8787                                                                if( fieldValue instanceof Collection ) {
    88                                                                         fieldValue = fieldValue.collect { it.toString() }.findAll { it }.join( ', ' );
     88                                                                        fieldValue = fieldValue.collect { it.toString() }.findAll { it }.unique().join( ', ' );
    8989                                                                } else {
    9090                                                                        fieldValue = fieldValue.toString();
  • trunk/grails-app/views/study/show_assays.gsp

    r1490 r1526  
    3333                  <td>
    3434                        <g:if test="${assay.module.openInFrame == null || assay.module.openInFrame == Boolean.TRUE}">
    35                   <jumpbar:link frameSource="${assay.module.url}/assay/showByToken/${assay.giveUUID()}" pageTitle="Metabolomics Module">
     35                  <jumpbar:link frameSource="${assay.module.url}/assay/showByToken/${assay.giveUUID()}" pageTitle="${assay.module.name}">
    3636                                view
    3737                          </jumpbar:link>
  • trunk/src/groovy/dbnp/query/SampleSearch.groovy

    r1524 r1526  
    7171                }
    7272
    73                 // We expect the sample criteria to be the most discriminative, and discard
    74                 // the most samples. (e.g. by searching on sample title of sample type). For
     73                // We expect the study criteria to be the most discriminative, and discard
     74                // the most samples. (e.g. by searching on study title or study type). For
    7575                // that reason we first look through the list of studies. However, when the
    76                 // user didn't enter any sample criteria, this will be an extra step, but doesn't
     76                // user didn't enter any study criteria, this will be an extra step, but doesn't
    7777                // cost much time to process.
    7878                def samples = []
     
    152152        @Override
    153153        void executeOr() {
    154                 // We expect the sample criteria to be the most discriminative, and discard
    155                 // the most samples. (e.g. by searching on sample title of sample type). For
    156                 // that reason we first look through the list of studies. However, when the
    157                 // user didn't enter any sample criteria, this will be an extra step, but doesn't
    158                 // cost much time to process.
    159                 def samples = []
    160154                def allSamples = Sample.list().findAll { it.parent?.canRead( this.user ) }.toList();
    161 
    162                 // If no criteria are found, return all samples
    163                 if( !criteria || criteria.size() == 0 ) {
    164                         results = allSamples
    165                         return;
    166                 }
    167 
    168                 samples = ( samples + filterOnStudyCriteria( allSamples - samples ) ).unique();
    169                 samples = ( samples + filterOnSubjectCriteria( allSamples - samples ) ).unique();
    170                 samples = ( samples + filterOnSampleCriteria( allSamples - samples ) ).unique();
    171                 samples = ( samples + filterOnEventCriteria( allSamples - samples ) ).unique();
    172                 samples = ( samples + filterOnSamplingEventCriteria( allSamples - samples ) ).unique();
    173                 samples = ( samples + filterOnAssayCriteria( allSamples - samples ) ).unique();
    174 
    175                 samples = ( samples + filterOnModuleCriteria( allSamples - samples ) ).unique();
    176 
    177                 // Save matches
    178                 results = samples;
     155                executeOr( allSamples );
    179156        }
    180157
     
    203180                                                return null
    204181
    205                                         return criterion.getFieldValue( sample.parentEventGroup.events.toList() );
     182                                        return sample.parentEventGroup.events?.collect { criterion.getFieldValue( it ) };
    206183                                }
    207184                        case "SamplingEvent":
     
    258235                });
    259236
     237                println "Matching assays: " + assays
     238       
    260239                // If no assays match these criteria, then no samples will match either
    261240                if( assays.size() == 0 )
     
    268247
    269248                        def studyAssays = assays.findAll { it.parent.equals( sample.parent ); }
    270 
     249                       
     250                        println "Assays for " + sample + " (based on study): " + studyAssays
     251                       
    271252                        // See if this sample is present in any of the matching assays. If so,
    272253                        // this sample matches the criteria
     
    274255                                if( assay.samples?.contains( sample ) )
    275256                                        return true;
     257                               
     258                                println "Assay " + assay + " with samples " + assay.samples + " doesn't contain " + sample;
    276259                        }
    277260
  • trunk/src/groovy/dbnp/query/Search.groovy

    r1524 r1526  
    7171
    7272        /**
    73          * Returns a list of Criteria
    74          */
    75         public List getCriteria() { return criteria; }
    76 
    77         /**
    78          * Sets a new list of criteria
    79          * @param c     List with criteria objects
    80          */
    81         public void setCriteria( List c ) { criteria = c; }
    82 
    83         /**
    84          * Adds a criterion to this query
    85          * @param c     Criterion
    86          */
    87         public void addCriterion( Criterion c ) {
    88                 if( criteria )
    89                         criteria << c;
    90                 else
    91                         criteria = [c];
    92         }
    93 
    94         /**
    95          * Retrieves the results found using this query. The result is empty is
    96          * the query has not been executed yet.
    97          */
    98         public List getResults() { return results; }
    99 
    100         /**
    101          * Returns the results found using this query, filtered by a list of ids.
    102          * @param selectedIds   List with ids of the entities you want to return.
    103          * @return      A list with only those results for which the id is in the selectedIds
    104          */
    105         public List filterResults( List selectedIds ) {
    106                 if( !selectedIds || !results )
    107                         return results
    108 
    109                 return results.findAll {
    110                         selectedIds.contains( it.id )
    111                 }
    112         }
    113 
    114         /**
    115          * Returns a list of fields for the results of this query. The fields returned are those
    116          * fields that the query searched for.
    117          */
    118         public Map getResultFields() { return resultFields; }
    119 
    120         /**
    12173         * Constructor of this search object. Sets the user field to the
    12274         * currently logged in user
     
    179131         * subclasses searching for a specific entity
    180132         */
    181         public void executeAnd() {
     133        protected void executeAnd() {
    182134
    183135        }
     
    187139         * subclasses searching for a specific entity
    188140         */
    189         public void executeOr() {
    190 
    191         }
    192 
     141        protected void executeOr() {
     142
     143        }
     144
     145        /**
     146         * Default implementation of an inclusive (AND) search. Can be called by subclasses in order
     147         * to simplify searches.
     148         *
     149         * Filters the list of objects on study, subject, sample, event, samplingevent and assaycriteria,
     150         * based on the closures defined in valueCallback. Afterwards, the objects are filtered on module
     151         * criteria
     152         *
     153         * @param objects       List of objects to search in
     154         */
     155        protected void executeAnd( List objects ) {
     156                // If no criteria are found, return all studies
     157                if( !criteria || criteria.size() == 0 ) {
     158                        results = objects;
     159                        return;
     160                }
     161
     162                // Perform filters
     163                objects = filterOnStudyCriteria( objects );
     164                objects = filterOnSubjectCriteria( objects );
     165                objects = filterOnSampleCriteria( objects );
     166                objects = filterOnEventCriteria( objects );
     167                objects = filterOnSamplingEventCriteria( objects );
     168                objects = filterOnAssayCriteria( objects );
     169
     170                objects = filterOnModuleCriteria( objects );
     171
     172                // Save matches
     173                results = objects;
     174        }
     175
     176        /**
     177        * Default implementation of an exclusive (OR) search. Can be called by subclasses in order
     178        * to simplify searches.
     179        *
     180        * Filters the list of objects on study, subject, sample, event, samplingevent and assaycriteria,
     181        * based on the closures defined in valueCallback. Afterwards, the objects are filtered on module
     182        * criteria
     183        *
     184        * @param allObjects     List of objects to search in
     185        */
     186   protected void executeOr( List allObjects ) {
     187                // If no criteria are found, return all studies
     188                if( !criteria || criteria.size() == 0 ) {
     189                        results = allObjects;
     190                        return;
     191                }
     192
     193                // Perform filters on those objects not yet found by other criteria
     194                def objects = []
     195                objects = ( objects + filterOnStudyCriteria( allObjects - objects ) ).unique();
     196                objects = ( objects + filterOnSubjectCriteria( allObjects - objects ) ).unique();
     197                objects = ( objects + filterOnSampleCriteria( allObjects - objects ) ).unique();
     198                objects = ( objects + filterOnEventCriteria( allObjects - objects ) ).unique();
     199                objects = ( objects + filterOnSamplingEventCriteria( allObjects - objects ) ).unique();
     200                objects = ( objects + filterOnAssayCriteria( allObjects - objects ) ).unique();
     201               
     202                // All objects (including the ones already found by another criterion) are sent to
     203                // be filtered on module criteria, in order to have the module give data about all
     204                // objects (for showing purposes later on)
     205                objects = ( objects + filterOnModuleCriteria( allObjects ) ).unique();
     206               
     207                // Save matches
     208                results = objects;
     209   }
     210
     211               
    193212        /************************************************************************
    194213         *
    195          * These methods are used in querying and should be overridden by subclasses
     214         * These methods are used in querying and can be overridden by subclasses
    196215         * in order to provide custom searching
    197216         *
    198          */
     217         ************************************************************************/
    199218
    200219        /**
     
    270289                return criteria?.findAll { it.entity == entity }
    271290        }
    272 
    273         /**
    274          * Filters a list with entities, based on the given criteria and a closure to check whether a criterion is matched
    275          *
    276          * @param entities      Original list with entities to check for these criteria
    277          * @param criteria      List with criteria to match on
    278          * @param check         Closure to see whether a specific entity matches a criterion. Gets two arguments:
    279          *                                              element         The element to check
    280          *                                              criterion       The criterion to check on.
    281          *                                      Returns true if the criterion holds, false otherwise
    282          * @return                      The filtered list of entities
    283          */
    284         protected List filterEntityList( List entities, List<Criterion> criteria, Closure check ) {
    285                 if( !entities || !criteria || criteria.size() == 0 ) {
    286                         if( searchMode == SearchMode.and )
    287                                 return entities;
    288                         else if( searchMode == SearchMode.or )
    289                                 return []
    290                 }
    291 
    292                 return entities.findAll { entity ->
    293                         if( searchMode == SearchMode.and ) {
    294                                 for( criterion in criteria ) {
    295                                         if( !check( entity, criterion ) ) {
    296                                                 return false;
    297                                         }
    298                                 }
    299                                 return true;
    300                         } else if( searchMode == SearchMode.or ) {
    301                                 for( criterion in criteria ) {
    302                                         if( check( entity, criterion ) ) {
    303                                                 return true;
    304                                         }
    305                                 }
    306                                 return false;
    307                         }
    308                 }
    309         }
    310 
     291       
     292       
    311293        /**
    312294         * Prepares a value from a template entity for comparison, by giving it a correct type
    313295         *
    314          * @param value         Value of the field 
     296         * @param value         Value of the field
    315297         * @param type          TemplateFieldType       Type of the specific field
    316298         * @return                      The value of the field in the correct entity
     
    374356        }
    375357
     358        /*****************************************************
     359        *
     360        * Methods for filtering lists based on specific (GSCF) criteria
     361        *
     362        *****************************************************/
     363
     364       
     365        /**
     366         * Filters a list with entities, based on the given criteria and a closure to check whether a criterion is matched
     367         *
     368         * @param entities      Original list with entities to check for these criteria
     369         * @param criteria      List with criteria to match on
     370         * @param check         Closure to see whether a specific entity matches a criterion. Gets two arguments:
     371         *                                              element         The element to check
     372         *                                              criterion       The criterion to check on.
     373         *                                      Returns true if the criterion holds, false otherwise
     374         * @return                      The filtered list of entities
     375         */
     376        protected List filterEntityList( List entities, List<Criterion> criteria, Closure check ) {
     377                if( !entities || !criteria || criteria.size() == 0 ) {
     378                        if( searchMode == SearchMode.and )
     379                                return entities;
     380                        else if( searchMode == SearchMode.or )
     381                                return []
     382                }
     383
     384                return entities.findAll { entity ->
     385                        if( searchMode == SearchMode.and ) {
     386                                for( criterion in criteria ) {
     387                                        if( !check( entity, criterion ) ) {
     388                                                return false;
     389                                        }
     390                                }
     391                                return true;
     392                        } else if( searchMode == SearchMode.or ) {
     393                                for( criterion in criteria ) {
     394                                        if( check( entity, criterion ) ) {
     395                                                return true;
     396                                        }
     397                                }
     398                                return false;
     399                        }
     400                }
     401        }
     402               
    376403        /**
    377404         * Filters the given list of studies on the study criteria
     
    460487                return filterOnTemplateEntityCriteria(studies, entity, valueCallback( entity ) )
    461488        }
     489       
     490        /********************************************************************
     491         *
     492         * Methods for filtering object lists on module criteria
     493         *
     494         ********************************************************************/
    462495
    463496        /**
     
    690723        }
    691724
     725       
     726        /************************************************************************
     727         *
     728         * Getters and setters
     729         *
     730         ************************************************************************/
     731       
     732        /**
     733        * Returns a list of Criteria
     734        */
     735   public List getCriteria() { return criteria; }
     736
     737   /**
     738        * Sets a new list of criteria
     739        * @param c      List with criteria objects
     740        */
     741   public void setCriteria( List c ) { criteria = c; }
     742
     743   /**
     744        * Adds a criterion to this query
     745        * @param c      Criterion
     746        */
     747   public void addCriterion( Criterion c ) {
     748           if( criteria )
     749                   criteria << c;
     750           else
     751                   criteria = [c];
     752   }
     753
     754   /**
     755        * Retrieves the results found using this query. The result is empty is
     756        * the query has not been executed yet.
     757        */
     758   public List getResults() { return results; }
     759
     760   /**
     761        * Returns the results found using this query, filtered by a list of ids.
     762        * @param selectedIds    List with ids of the entities you want to return.
     763        * @return       A list with only those results for which the id is in the selectedIds
     764        */
     765   public List filterResults( List selectedIds ) {
     766           if( !selectedIds || !results )
     767                   return results
     768
     769           return results.findAll {
     770                   selectedIds.contains( it.id )
     771           }
     772   }
     773
     774   /**
     775        * Returns a list of fields for the results of this query. The fields returned are those
     776        * fields that the query searched for.
     777        */
     778   public Map getResultFields() { return resultFields; }
     779       
    692780        public String toString() {
    693781                return ( this.entity ? this.entity + " search" : "Search" ) + " " + this.id
    694782        }
     783       
     784        public boolean equals( Object o ) {
     785                if( o == null )
     786                        return false
     787               
     788                if( !( o instanceof Search ) ) 
     789                        return false
     790                       
     791                Search s = (Search) o;
     792               
     793                return (        searchMode              == s.searchMode &&
     794                                        entity                  == s.entity &&
     795                                        criteria.size() == s.criteria.size() &&
     796                                        s.criteria.containsAll( criteria ) &&
     797                                        criteria.containsAll( s.criteria ) );
     798        }
    695799}
  • trunk/src/groovy/dbnp/query/StudySearch.groovy

    r1524 r1526  
    6565         */
    6666        @Override
    67         void executeAnd() {
     67        protected void executeAnd() {
    6868                def studies = Study.list().findAll { it.canRead( this.user ) };
    6969
    70                 // If no criteria are found, return all studies
    71                 if( !criteria || criteria.size() == 0 ) {
    72                         results = studies;
    73                         return;
    74                 }
    75 
    76                 // Perform filters
    77                 studies = filterOnStudyCriteria( studies );
    78                 studies = filterOnSubjectCriteria( studies );
    79                 studies = filterOnSampleCriteria( studies );
    80                 studies = filterOnEventCriteria( studies );
    81                 studies = filterOnSamplingEventCriteria( studies );
    82                 studies = filterOnAssayCriteria( studies );
    83 
    84                 studies = filterOnModuleCriteria( studies );
    85 
    86                 // Save matches
    87                 results = studies;
     70                executeAnd( studies );
    8871        }
    8972
     
    121104         */
    122105        @Override
    123         void executeOr() {
     106        protected void executeOr() {
    124107                def allStudies = Study.list().findAll { it.canRead( this.user ) };
    125 
    126                 // If no criteria are found, return all studies
    127                 if( !criteria || criteria.size() == 0 ) {
    128                         results = allStudies;
    129                         return;
    130                 }
    131 
    132                 // Perform filters
    133                 def studies = []
    134                 studies = ( studies + filterOnStudyCriteria( allStudies - studies ) ).unique();
    135                 studies = ( studies + filterOnSubjectCriteria( allStudies - studies ) ).unique();
    136                 studies = ( studies + filterOnSampleCriteria( allStudies - studies ) ).unique();
    137                 studies = ( studies + filterOnEventCriteria( allStudies - studies ) ).unique();
    138                 studies = ( studies + filterOnSamplingEventCriteria( allStudies - studies ) ).unique();
    139                 studies = ( studies + filterOnAssayCriteria( allStudies - studies ) ).unique();
    140                
    141                 studies = ( studies + filterOnModuleCriteria( allStudies - studies ) ).unique();
    142                
    143                 // Save matches
    144                 results = studies;
     108                executeOr( allStudies );
    145109        }
    146110
  • trunk/web-app/css/advancedQuery.css

    r1524 r1526  
    11label { display: inline-block; zoom: 1; *display: inline; width: 110px; margin-top: 10px;  }
    22
    3 #searchForm { position: relative; border: 1px solid #666666; padding: 5px 10px; margin: 10px 0; }
     3#searchForm { position: relative; margin: 10px 0; font-size: 11px; }
     4#searchForm h3 { font-size: 13px; }
     5#searchForm h3 .nummer { display: inline-block; zoom: 1; *display: inline; width: 25px; }
    46
    5 #searchForm ul#criteria { margin-left: 110px; padding-left: 0px; margin-top: -19px; list-style-type: none;  }
    6 #searchForm ul#criteria li { margin: 2px 0; padding-left: 0; }
     7#searchForm p { margin-left: 25px; }
     8
     9#searchForm ul#criteria { margin-left: 25px; padding-left: 0px; list-style-type: none;  }
     10#searchForm ul#criteria li { margin: 2px 0; padding: 2px 0; }
    711#searchForm ul#criteria li span,
    812#searchForm ul#criteria li a { display: inline-block; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; margin-right: 5px; }
    9 #searchForm ul#criteria li .entityfield { width: 180px; }
     13#searchForm ul#criteria li .entityfield { width: 200px; }
    1014#searchForm ul#criteria li .operator { width: 100px; }
    1115#searchForm ul#criteria li .value { width: 240px; }
     16#searchForm ul#criteria li .addButton { margin: 4px 0; }
     17#searchForm ul#criteria li input, #searchForm ul#criteria li select { font-size: 11px; }
    1218
    13 #searchForm ul#criteria li.emptyList { color: #666; }
    14 #searchForm ul#criteria li.emptyList:hover { cursor: default; }
     19#searchForm ul#criteria li.titlerow { font-weight: bold; border-bottom: 1px solid #aaa; }
    1520
    16 #searchForm ul#criteria li:hover { cursor: pointer; }
    17 #searchForm ul#criteria li:hover span { text-decoration: line-through; }
    18 
    19 #input_criteria { display: block; float: right; width: 260px; border-left: 1px solid #666; padding: 10px; position: relative; height: 100%; }
    20 #input_criteria h2 { margin-top: 2px; margin-bottom: 8px; font-weight: bold; }
    21 #input_criteria label { width: 80px; margin-top: 8px; }
    22 #input_criteria input.text, #input_criteria select { width: 165px; }
    23 #input_criteria input.button {  margin-top: 8px; }
    24 #input_criteria .explanation { font-size: 10px; }
     21#searchMode { display: none; }
    2522
    2623.ui-menu-item .entity { color: #666; font-style: italic; }
  • trunk/web-app/js/advancedQuery.js

    r1482 r1526  
    88        // By replacing it with javascript, users without javascript will still be able to use the select
    99        $( '#queryFieldSelect' ).after( $( '<input type="text" class="text" id="queryFieldText">' ));
    10         $( '#queryFieldText' ).after( $( '<input type="hidden" name="field" id="queryField"></span>' ));
     10        $( '#queryFieldText' ).after( $( '<input type="hidden" name="criteria.0.entityfield" id="queryField"></span>' ));
    1111        $( '#queryFieldSelect' ).remove();
    1212
     
    4040
    4141// Is used to keep track of a unique ID for all criteria.
    42 var criteriumId = 0;
     42// ID = 0 is used for the input fields, in order to have them sent as well
     43// when the user clicks 'run query'
     44var criteriumId = 1;
    4345
    4446/**
    4547 * Adds a criteria to the list of search criteria
    4648 */
    47 function addCriterium() {
    48         var field_descriptor = $( '#input_criteria [name=field]' ).val();
    49         var value = $( '#input_criteria input[name=value]' ).val();
    50         var operator = $( '#input_criteria select[name=operator]' ).val();
     49function addCriterion() {
     50        var field_descriptor = $( '#searchForm #queryField' ).val();
     51        var value = $( '#searchForm input#value' ).val();
     52        var operator = $( '#searchForm select#operator' ).val();
    5153       
    5254        // Show the title and a remove button
    5355        showCriterium(field_descriptor, value, operator);
    54         showHideNoCriteriaItem();
     56        toggleSearchMode();
    5557       
    5658        // Clear the input form
    57         $( '#input_criteria #queryFieldText' ).val( '' );
    58         $( '#input_criteria [name=field]' ).val( '' );
    59         $( '#input_criteria select[name=operator]' ).val( 'equals' );
    60         $( '#input_criteria input[name=value]' ).val( '' );
    61 }
    62 
    63 function showHideNoCriteriaItem() {
    64         remainingCriteria = $( '#criteria' ).children().length - 1; // -1 because one element is the 'empty' item
    65 
    66         if( remainingCriteria == 0 ) {
    67                 // Show the 'none box'
    68                 $('#criteria .emptyList').show();
    69                 $('.submitcriteria' ).attr( 'disabled', 'disabled' );
    70         } else {
    71                 // Hide the 'none box'
    72                 $('#criteria .emptyList').hide();
    73                 $('.submitcriteria' ).attr( 'disabled', '' );
    74         }       
     59        $( '#searchForm #queryFieldText' ).val( '' );
     60        $( '#searchForm #queryField' ).val( '' );
     61        $( '#searchForm select#operator' ).val( 'equals' );
     62        $( '#searchForm input#value' ).val( '' );
    7563}
    7664
     
    8068function removeCriterium(element) {
    8169        element.remove();
    82         showHideNoCriteriaItem();
     70        toggleSearchMode();
    8371}
     72
     73function toggleSearchMode() {
     74        if( $('#criteria' ).children( 'li' ) - 2 == 0 ) {
     75                $( '#searchMode' ).hide();
     76        } else {
     77                $( '#searchMode' ).show();
     78        }
     79}
     80
    8481
    8582/**
     
    9895                valueSpan = createCriteriumElement( 'value', value );
    9996       
     97        var input = $( '<a href="#" onClick="return false;"><img src="../plugins/famfamfam-1.0.1/images/icons/delete.png" border="0"></a>' );
     98        input.bind( 'click', function() {
     99                if( confirm( "Are you sure you want to remove this criterium?" ) ) {
     100                        removeCriterium( $(this).closest( 'li' ) );
     101                        return false;
     102                }
     103        });
     104        var span = $( '<span></span>' );
     105        span.append( "\n" ).append( input );
     106       
    100107        // Increase the criteriumID to ensure a unique number every time
    101108        criteriumId++;
     
    103110        // Append them to a list item
    104111        var li = $( '<li></li>' );
    105         li.append( fieldSpan ).append( operatorSpan ).append( valueSpan );
     112        li.append( fieldSpan ).append( "\n" ).append( operatorSpan ).append( "\n" ).append( valueSpan ).append( "\n" ).append( span );
    106113       
    107         li.bind( 'click', function() {
    108                 if( confirm( "Are you sure you want to remove this criterium?" ) ) {
    109                         removeCriterium( $(this) );
    110                         return false;
    111                 }
    112         });
    113114
    114         $('#criteria').append(li);
     115        $('#criteria .newCriterion').before(li);
    115116}
    116117
Note: See TracChangeset for help on using the changeset viewer.