1 | package dbnp.query |
---|
2 | |
---|
3 | import dbnp.studycapturing.* |
---|
4 | |
---|
5 | // TODO: Make use of the searchable-plugin possibilities instead of querying the database directly |
---|
6 | |
---|
7 | /** |
---|
8 | * Basic web interface for searching within studies |
---|
9 | * |
---|
10 | * @author Robert Horlings (robert@isdat.nl) |
---|
11 | */ |
---|
12 | class AdvancedQueryController { |
---|
13 | def entitiesToSearchFor = [ 'Study': 'Studies', 'Sample': 'Samples'] |
---|
14 | def index = { |
---|
15 | [entitiesToSearchFor: entitiesToSearchFor, searchableFields: getSearchableFields()] |
---|
16 | } |
---|
17 | |
---|
18 | /** |
---|
19 | * Searches for studies or samples based on the user parameters. |
---|
20 | * |
---|
21 | * @param entity The entity to search for ( 'Study' or 'Sample' ) |
---|
22 | * @param criteria HashMap with the values being hashmaps with field, operator and value. |
---|
23 | * [ 0: [ field: 'Study.name', operator: 'equals', value: 'term' ], 1: [..], .. ] |
---|
24 | */ |
---|
25 | def search = { |
---|
26 | if( !params.criteria ) { |
---|
27 | flash.error = "No criteria given to search for. Please try again."; |
---|
28 | redirect( action: 'index' ) |
---|
29 | } |
---|
30 | |
---|
31 | if( !params.entity || !entitiesToSearchFor*.key.contains( params.entity ) ) { |
---|
32 | flash.error = "No or incorrect entity given to search for. Please try again."; |
---|
33 | redirect( action: 'index', params: [ criteria: parseCriteria( params.criteria ) ] ) |
---|
34 | } |
---|
35 | |
---|
36 | // Create a search object and let it do the searching |
---|
37 | Search search; |
---|
38 | switch( params.entity ) { |
---|
39 | case "Study": search = new StudySearch(); break; |
---|
40 | case "Sample": search = new SampleSearch(); break; |
---|
41 | |
---|
42 | // This exception will only be thrown if the entitiesToSearchFor contains more entities than |
---|
43 | // mentioned in this switch structure. |
---|
44 | default: throw new Exception( "Can't search for entities of type " + params.entity ); |
---|
45 | } |
---|
46 | |
---|
47 | search.execute( parseCriteria( params.criteria ) ); |
---|
48 | |
---|
49 | render( view: search.getView(), model: [search: search] ); |
---|
50 | } |
---|
51 | |
---|
52 | /** |
---|
53 | * Returns a map of entities with the names of the fields the user can search on |
---|
54 | * @return |
---|
55 | */ |
---|
56 | protected def getSearchableFields() { |
---|
57 | def fields = [:]; |
---|
58 | |
---|
59 | getEntities().each { |
---|
60 | def entity = getEntity( 'dbnp.studycapturing.' + it ); |
---|
61 | |
---|
62 | if( entity ) { |
---|
63 | def domainFields = entity.giveDomainFields(); |
---|
64 | def templateFields = TemplateField.findAllByEntity( entity ) |
---|
65 | |
---|
66 | fields[ it ] = ( domainFields + templateFields ).collect { it.name }.unique().sort { a, b -> a[0].toUpperCase() + a[1..-1] <=> b[0].toUpperCase() + b[1..-1] }; |
---|
67 | } |
---|
68 | } |
---|
69 | |
---|
70 | return fields; |
---|
71 | } |
---|
72 | |
---|
73 | /** |
---|
74 | * Parses the criteria from the query form given by the user |
---|
75 | * @param c Data from the input form and had a form like |
---|
76 | * |
---|
77 | * [ |
---|
78 | * 0: [entityfield:a.b, operator: b, value: c], |
---|
79 | * 0.entityfield: a.b, |
---|
80 | * 0.operator: b, |
---|
81 | * 0.field: c |
---|
82 | * 1: [entityfield:f.q, operator: e, value: d], |
---|
83 | * 1.entityfield: f.q, |
---|
84 | * 1.operator: e, |
---|
85 | * 1.field: d |
---|
86 | * ] |
---|
87 | * |
---|
88 | * @return List with [entity: ..., field: ..., entityfield: ..., operator: ..., value: ...] tuples. |
---|
89 | */ |
---|
90 | protected List parseCriteria( def c ) { |
---|
91 | ArrayList list = []; |
---|
92 | |
---|
93 | // Loop through all keys of c and remove the non-numeric ones |
---|
94 | c.each { |
---|
95 | if( it.key ==~ /[0-9]+/ ) { |
---|
96 | def criterium = it.value; |
---|
97 | |
---|
98 | def field = criterium.entityfield?.split( /\./ ); |
---|
99 | |
---|
100 | if( field.size() > 1 ) { |
---|
101 | criterium.entity = field[0].toString(); |
---|
102 | criterium.field = field[1].toString(); |
---|
103 | } else { |
---|
104 | criterium.entity = null; |
---|
105 | criterium.field = field; |
---|
106 | } |
---|
107 | |
---|
108 | list << criterium; |
---|
109 | } |
---|
110 | } |
---|
111 | |
---|
112 | return list; |
---|
113 | } |
---|
114 | |
---|
115 | /** |
---|
116 | * Returns all entities for which criteria can be entered |
---|
117 | * @return |
---|
118 | */ |
---|
119 | protected def getEntities() { |
---|
120 | return [ 'Study', 'Subject', 'Sample', 'Event', 'SamplingEvent', 'Assay' ] |
---|
121 | } |
---|
122 | |
---|
123 | /** |
---|
124 | * Creates an object of the given entity. |
---|
125 | * |
---|
126 | * @return False if the entity is not a subclass of TemplateEntity |
---|
127 | */ |
---|
128 | protected def getEntity( entityName ) { |
---|
129 | // Find the templates |
---|
130 | def entity |
---|
131 | try { |
---|
132 | entity = Class.forName(entityName, true, this.getClass().getClassLoader()) |
---|
133 | |
---|
134 | // succes, is entity an instance of TemplateEntity? |
---|
135 | if (entity.superclass =~ /TemplateEntity$/ || entity.superclass.superclass =~ /TemplateEntity$/) { |
---|
136 | return entity; |
---|
137 | } else { |
---|
138 | return false; |
---|
139 | } |
---|
140 | } catch( ClassNotFoundException e ) { |
---|
141 | log.error "Class " + entityName + " not found: " + e.getMessage() |
---|
142 | return null; |
---|
143 | } |
---|
144 | |
---|
145 | } |
---|
146 | |
---|
147 | } |
---|