diff options
Diffstat (limited to 'idrop-web')
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" + } +} |