summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--idrop-web/grails-app/controllers/org/irods/mydrop/controller/MetadataSearchController.groovy202
-rw-r--r--idrop-web/grails-app/views/metadataSearch/index.gsp157
-rw-r--r--idrop-web/test/unit/org/irods/mydrop/controller/MetadataSearchControllerTests.groovy17
3 files changed, 376 insertions, 0 deletions
diff --git a/idrop-web/grails-app/controllers/org/irods/mydrop/controller/MetadataSearchController.groovy b/idrop-web/grails-app/controllers/org/irods/mydrop/controller/MetadataSearchController.groovy
new file mode 100644
index 0000000..4fe3012
--- /dev/null
+++ b/idrop-web/grails-app/controllers/org/irods/mydrop/controller/MetadataSearchController.groovy
@@ -0,0 +1,202 @@
+package org.irods.mydrop.controller
+
+import org.apache.commons.collections.FactoryUtils
+import org.apache.commons.collections.ListUtils
+import org.irods.jargon.core.connection.IRODSAccount
+import org.irods.jargon.core.pub.CollectionAO
+import org.irods.jargon.core.pub.CollectionAndDataObjectListAndSearchAO;
+import org.irods.jargon.core.pub.IRODSAccessObjectFactory
+import org.irods.jargon.core.query.AVUQueryElement
+import org.irods.jargon.core.query.AVUQueryOperatorEnum
+import org.irods.jargon.core.query.CollectionAndDataObjectListingEntry;
+import org.irods.jargon.core.query.MetaDataAndDomainData
+import org.irods.jargon.core.query.AVUQueryElement.AVUQueryPart
+import org.irods.jargon.core.pub.DataObjectAO;
+
+
+
+import java.util.ArrayList;
+
+class MetadataSearchController {
+
+ IRODSAccessObjectFactory irodsAccessObjectFactory
+ IRODSAccount irodsAccount
+
+ /**
+ * Interceptor grabs IRODSAccount from the SecurityContextHolder
+ */
+ def beforeInterceptor = [action:this.&auth]
+
+ def auth() {
+ if(!session["SPRING_SECURITY_CONTEXT"]) {
+ redirect(controller:"login", action:"login")
+ return false
+ }
+ irodsAccount = session["SPRING_SECURITY_CONTEXT"]
+ }
+
+
+ def afterInterceptor = {
+ log.debug("closing the session")
+ irodsAccessObjectFactory.closeSession()
+ }
+
+ def index = {
+
+ }
+
+ /**
+ * Search iRODS metadata
+ */
+ def search (MetadataSearchCommand metadataSearchCommand) {
+
+ CollectionAndDataObjectListAndSearchAO collectionAndDataObjectListAndSearchAO = irodsAccessObjectFactory.getCollectionAndDataObjectListAndSearchAO(irodsAccount)
+ DataObjectAO dataObjAO = irodsAccessObjectFactory.getDataObjectAO(irodsAccount) //dataObjects
+ CollectionAO collectionAO = irodsAccessObjectFactory.getCollectionAO(irodsAccount) //folders
+
+ List<CollectionAndDataObjectListingEntry> results = new ArrayList<CollectionAndDataObjectListingEntry>()
+ List<MetaDataAndDomainData> queryResults = new ArrayList<MetaDataAndDomainData>()
+ List<MetaDataAndDomainData> finalResults = new ArrayList<MetaDataAndDomainData>()
+
+ int counter = 0
+
+ for(AVUNode avuNode in metadataSearchCommand.AVUNodes) { //for every condition node do:
+ if(avuNode.andOr == "Every") { //if every condition has to be fulfilled
+ queryResults = connectedByAnd(avuNode, dataObjAO, collectionAO)
+ } else { //if at least one condition has to be fulfilled
+ queryResults = connectedByOr(avuNode, dataObjAO, collectionAO)
+ log.info(queryResults.toString())
+ }
+ log.info("OPERATOR:" + metadataSearchCommand.AVUOps[counter])
+ counter++
+ }
+
+
+
+ for (MetaDataAndDomainData data : queryResults){
+ results.add(collectionAndDataObjectListAndSearchAO.getCollectionAndDataObjectListingEntryAtGivenAbsolutePath(data.domainObjectUniqueName))
+ }
+
+
+ render(view:"/search/searchResult", model:[results:results])
+ }
+
+ private def connectedByAnd(AVUNode avuNode, DataObjectAO dataObjAO, CollectionAO collectionAO) {
+ List<MetaDataAndDomainData> queryRes1 = collectionAO.findMetadataValuesByMetadataQuery(createAVUQ(avuNode), true) //match folders names
+ List<MetaDataAndDomainData> queryRes2 = dataObjAO.findMetadataValuesByMetadataQuery(createAVUQ(avuNode)) //match dataObj names
+ List<MetaDataAndDomainData> queryResults = ListUtils.sum(queryRes1, queryRes2) //connect lists
+
+ queryResults
+ }
+
+ private def connectedByOr(AVUNode avuNode, DataObjectAO dataObjAO, CollectionAO collectionAO) {
+ List<AVUQueryElement> metadataQuery = new ArrayList<AVUQueryElement>()
+ List<MetaDataAndDomainData> finalResult = new ArrayList<MetaDataAndDomainData>()
+ List<MetaDataAndDomainData> queryRes1 = new ArrayList<MetaDataAndDomainData>()
+ List<MetaDataAndDomainData> queryRes2 = new ArrayList<MetaDataAndDomainData>()
+ List<MetaDataAndDomainData> queryResults = new ArrayList<MetaDataAndDomainData>()
+
+ for(AVUCommand avu in avuNode.AVUs){
+ metadataQuery.clear()
+ metadataQuery.add(AVUQueryElement.instanceForValueQuery(AVUQueryPart.ATTRIBUTE, AVUQueryOperatorEnum.EQUAL, avu.attribute));
+ metadataQuery.add(AVUQueryElement.instanceForValueQuery(AVUQueryPart.VALUE, chooseOperator(avu.operator), avu.value));
+
+ queryRes1 = collectionAO.findMetadataValuesByMetadataQuery(metadataQuery, true)
+ queryRes2 = dataObjAO.findMetadataValuesByMetadataQuery(metadataQuery)
+ queryResults = ListUtils.sum(queryRes1, queryRes2)
+
+ finalResult = sumOfLists(finalResult, queryResults)
+ }
+
+ finalResult //return list without duplicates
+
+ }
+
+ def createAVUQ(AVUNode avuNode) {
+
+ List<AVUQueryElement> metadataQuery = new ArrayList<AVUQueryElement>()
+
+ for(AVUCommand avu in avuNode.AVUs){
+ metadataQuery.add(AVUQueryElement.instanceForValueQuery(AVUQueryPart.ATTRIBUTE, AVUQueryOperatorEnum.EQUAL, avu.attribute));
+ metadataQuery.add(AVUQueryElement.instanceForValueQuery(AVUQueryPart.VALUE, chooseOperator(avu.operator), avu.value));
+ }
+
+ metadataQuery
+ }
+
+ //create one list from two ones without duplicates
+ private def sumOfLists(List<MetaDataAndDomainData> list1, List<MetaDataAndDomainData> list2) {
+
+ boolean alreadyEx = false
+
+ for(MetaDataAndDomainData elem1 in list1) {
+ alreadyEx = false
+ for(MetaDataAndDomainData elem2 in list2) {
+ if(elem1.getDomainObjectUniqueName() == elem2.getDomainObjectUniqueName())
+ alreadyEx = true
+ }
+ log.info("ATR:" + elem1.getAvuAttribute())
+ if(!alreadyEx) list2.add(elem1)
+ }
+ list2
+ }
+
+ def chooseOperator(String op) {
+ switch(op){
+ case "=":
+ AVUQueryOperatorEnum.EQUAL;
+ break
+ case ">":
+ AVUQueryOperatorEnum.GREATER_THAN;
+ break
+ case "<":
+ AVUQueryOperatorEnum.LESS_THAN;
+ break
+ case ">=":
+ AVUQueryOperatorEnum.GREATER_OR_EQUAL;
+ break
+ case "<=":
+ AVUQueryOperatorEnum.LESS_OR_EQUAL;
+ break
+ case "<>":
+ AVUQueryOperatorEnum.NOT_EQUAL;
+ break
+ case "like":
+ AVUQueryOperatorEnum.LIKE;
+ break
+ case "not like":
+
+ log.info("OP: notlike " + op)
+ AVUQueryOperatorEnum.NOT_LIKE;
+
+ }
+ }
+
+}
+
+
+
+class MetadataSearchCommand { //constists of the condition nodes and operators connecting them
+ List <AVUNode> AVUNodes = ListUtils.lazyList([], FactoryUtils.instantiateFactory(AVUNode))
+ List <String> AVUOps = ListUtils.lazyList([], FactoryUtils.instantiateFactory(String))
+
+}
+
+class AVUCommand {
+ String attribute
+ String operator //=, <, >, <=, >=, <>, like, not like
+ String value
+ String unit //unused
+
+ static constraints = {
+ attribute(blank:false)
+ value(blank:false)
+ }
+
+}
+
+class AVUNode {
+ List <AVUCommand> AVUs = ListUtils.lazyList([], FactoryUtils.instantiateFactory(AVUCommand))
+ String andOr //every condition fulfilled or at least one
+}
+
diff --git a/idrop-web/grails-app/views/metadataSearch/index.gsp b/idrop-web/grails-app/views/metadataSearch/index.gsp
new file mode 100644
index 0000000..5b05a91
--- /dev/null
+++ b/idrop-web/grails-app/views/metadataSearch/index.gsp
@@ -0,0 +1,157 @@
+<head>
+ <meta name="layout" content="mainNoSidebar" />
+<g:javascript library="mydrop/metadata"/>
+<g:javascript library="mydrop/metaFiltering"/>
+<g:javascript library="mydrop/search" />
+<g:javascript library="mydrop/dhtmlxcombo"/>
+<g:javascript library="mydrop/tag" />
+<g:javascript library="mydrop/home" />
+
+</head>
+
+
+
+<ul class="nav nav-tabs" id="searchTabs">
+ <li><a href="#metaQueryTab">Query</a></li>
+ <li><a href="#resultsTab">Results</a></li>
+</ul>
+
+<div class="tab-content">
+ <div class="tab-pane active" id="metaQueryTab">
+ <div class="row-fluid">
+ <div class="span2">
+ <div class="nav-header">Help</div>
+ <div class="metaHelp">
+ <p>
+ There'll be help
+ <br><br><br><br><br><br><br><br>
+ </p>
+ </div>
+
+ </div>
+ <div class="span9">
+ <g:form id="avuQuery" method="post" controller="metadataSearch"
+ action="search" class="">
+ <table class="filterMetaTable" id="filterTab1">
+ <thead>
+ <tr>
+ <th colspan="2">
+
+ </th>
+ <th colspan="2">
+ <input type="button" class="deleteNodeBtn" id="del" disabled value="X">
+ </th>
+ </tr>
+ <tr>
+ <th colspan="2">
+ <input type="radio" value="Every" name="AVUNodes[0].andOr" checked>
+ <label class="labelRadio"> Every condition fulfilled </label>
+ </th>
+ <th colspan="2">
+ <input type="radio" value="One" name="AVUNodes[0].andOr">
+ <label class="labelRadio"> At least one condition fulfilled</label>
+ </th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr id="filterRow0">
+ <!-- <td colspan="2">
+ <!-- <form class="formMeta">
+ <div class="singleFilter"> -->
+ <td> <div class="meta-header"> Attribute </div>
+ <div class="dhx_combo_box dhx_skyblue" style="width: 180px;">
+ <input name="attr" class="dhx_combo_input" type="text" autocomplete="off" style="width: 155px;">
+ <input type="hidden" name="" value="">
+ <input type="hidden" name="" value="false">
+ <img class="dhx_combo_img" src="<g:resource dir="imgs" file="combo_select_dhx_skyblue.gif" alt="file icon"/>"/>
+ </div>
+ </td>
+ <!-- </div>
+ <div class="singleFilter"> -->
+ <td>
+ <div class="meta-header"> Operator</div>
+ <select name="op" class="metaOperator">
+ <option> = </option>
+ <option> > </option>
+ <option> < </option>
+ <option> >= </option>
+ <option> <= </option>
+ <option> <> </option>
+ <option> like </option>
+ <option> not like </option>
+ </select>
+ </td>
+ <!--</div>
+ <div class="singleFilter"> -->
+ <td> <div class="meta-header"> Value </div>
+ <input name="val" type="text" autocomplete="off">
+ </td>
+ <!--</div>
+ <div class="singleFilter">-->
+ <td>
+ <br>
+ <input type='button' class="delConditionBtn" value='DELETE' disabled>
+ </td>
+ <!--</div>
+ </form>-->
+ <!-- </td> -->
+ </tr>
+ </tbody>
+ <tfoot>
+ <tr>
+ <td colspan ="2">
+ <input type="button" class="addConditionBtn" value="ADD CONDITION">
+ </td>
+ </tr>
+ </tfoot>
+ </table>
+
+ <div><input type="button" id="newNodeBtn" value="New conditions node"></div>
+ </g:form>
+ <div><button id="searchBtn" onclick="processMetadataQuery()">SEARCH</button></div>
+
+
+ </div>
+ </div>
+ </div>
+ <div class="tab-pane" id="resultsTab">
+ <div id="resultsTabInner">Search Results here</div>
+
+ </div>
+
+</div>
+
+<script>
+ $(document).ready(function() {
+
+ $.ajaxSetup({
+ cache : false
+ });
+ $("#topbarSearch").addClass("active");
+
+ $('#searchTabs a').click(function (e) {
+
+ e.preventDefault();
+ $(this).tab('show');
+ var state = {};
+ var tabId = this.hash
+ state["tab"] = tabId;
+ $.bbq.pushState(state);
+ });
+
+ $(window).bind( 'hashchange', function(e) {
+ processTagSearchStateChange( $.bbq.getState());
+ });
+
+ $(window).trigger( 'hashchange' );
+
+
+ });
+
+
+ //function addAVUTriplet() {
+
+
+ //}
+
+</script> \ No newline at end of file
diff --git a/idrop-web/test/unit/org/irods/mydrop/controller/MetadataSearchControllerTests.groovy b/idrop-web/test/unit/org/irods/mydrop/controller/MetadataSearchControllerTests.groovy
new file mode 100644
index 0000000..3a61185
--- /dev/null
+++ b/idrop-web/test/unit/org/irods/mydrop/controller/MetadataSearchControllerTests.groovy
@@ -0,0 +1,17 @@
+package org.irods.mydrop.controller
+
+
+
+import grails.test.mixin.*
+import org.junit.*
+
+/**
+ * See the API for {@link grails.test.mixin.web.ControllerUnitTestMixin} for usage instructions
+ */
+@TestFor(MetadataSearchController)
+class MetadataSearchControllerTests {
+
+ void testSomething() {
+ fail "Implement me"
+ }
+}