From e127a136881490b0ebda322bc80883d954194ad1 Mon Sep 17 00:00:00 2001 From: pkupczyk <pkupczyk> Date: Tue, 10 Nov 2015 11:38:55 +0000 Subject: [PATCH] SSDM-2636 : V3 AS API - sort search results by property values SVN: 35020 --- .../collection}/AlphanumComparator.java | 2 +- .../collection}/AlphanumComparatorTest.java | 4 +- .../openbis-v3-api-test/html/index.html | 4 + .../test/lib/naturalsort/js/naturalSort.js | 56 +++++++++ .../html/test/test-search.js | 39 +++--- .../web/server/resultset/ColumnSortUtils.java | 1 + .../api/v3/dto/entity/dataset/DataSet.js | 4 + .../v3/dto/entity/experiment/Experiment.js | 4 + .../api/v3/dto/entity/material/Material.js | 4 + .../api/v3/dto/entity/sample/Sample.js | 4 + .../api/v3/dto/entity/space/Space.js | 7 ++ .../fetchoptions/sort/EntitySortOptions.js | 11 +- .../systemtest/api/v3/SearchMaterialTest.java | 35 ++++++ .../systemtest/api/v3/SearchSampleTest.java | 117 +++++++++++++----- .../fetchoptions/sort/EntitySortOptions.java | 35 +++++- .../v3/dto/fetchoptions/sort/SortAndPage.java | 17 +-- .../v3/dto/fetchoptions/sort/SortOptions.java | 19 +-- .../sort/comparator/AbstractComparator.java | 7 +- .../comparator/AbstractStringComparator.java | 35 ++++++ .../sort/comparator/CodeComparator.java | 2 +- .../sort/comparator/PropertyComparator.java | 51 ++++++++ .../dto/fetchoptions/tag/TagSortOptions.java | 15 ++- 22 files changed, 386 insertions(+), 87 deletions(-) rename {openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/server/resultset => common/source/java/ch/systemsx/cisd/common/collection}/AlphanumComparator.java (98%) rename {openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/client/web/server/resultset => common/sourceTest/java/ch/systemsx/cisd/common/collection}/AlphanumComparatorTest.java (95%) create mode 100644 js-test/servers/common/core-plugins/tests/1/as/webapps/openbis-v3-api-test/html/test/lib/naturalsort/js/naturalSort.js create mode 100644 openbis_api/source/java/ch/ethz/sis/openbis/generic/shared/api/v3/dto/fetchoptions/sort/comparator/AbstractStringComparator.java create mode 100644 openbis_api/source/java/ch/ethz/sis/openbis/generic/shared/api/v3/dto/fetchoptions/sort/comparator/PropertyComparator.java diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/server/resultset/AlphanumComparator.java b/common/source/java/ch/systemsx/cisd/common/collection/AlphanumComparator.java similarity index 98% rename from openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/server/resultset/AlphanumComparator.java rename to common/source/java/ch/systemsx/cisd/common/collection/AlphanumComparator.java index 75fad5e9d1d..9e0973d06cf 100644 --- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/server/resultset/AlphanumComparator.java +++ b/common/source/java/ch/systemsx/cisd/common/collection/AlphanumComparator.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package ch.systemsx.cisd.openbis.generic.client.web.server.resultset; +package ch.systemsx.cisd.common.collection; /* * The Alphanum Algorithm is an improved sorting algorithm for strings diff --git a/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/client/web/server/resultset/AlphanumComparatorTest.java b/common/sourceTest/java/ch/systemsx/cisd/common/collection/AlphanumComparatorTest.java similarity index 95% rename from openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/client/web/server/resultset/AlphanumComparatorTest.java rename to common/sourceTest/java/ch/systemsx/cisd/common/collection/AlphanumComparatorTest.java index f7f4ed5493e..88cf7e554f8 100644 --- a/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/client/web/server/resultset/AlphanumComparatorTest.java +++ b/common/sourceTest/java/ch/systemsx/cisd/common/collection/AlphanumComparatorTest.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package ch.systemsx.cisd.openbis.generic.client.web.server.resultset; +package ch.systemsx.cisd.common.collection; import java.util.Arrays; @@ -22,6 +22,8 @@ import org.testng.AssertJUnit; import org.testng.annotations.DataProvider; import org.testng.annotations.Test; +import ch.systemsx.cisd.common.collection.AlphanumComparator; + /** * @author Piotr Buczek */ diff --git a/js-test/servers/common/core-plugins/tests/1/as/webapps/openbis-v3-api-test/html/index.html b/js-test/servers/common/core-plugins/tests/1/as/webapps/openbis-v3-api-test/html/index.html index 770f479f270..96e47bafbe5 100644 --- a/js-test/servers/common/core-plugins/tests/1/as/webapps/openbis-v3-api-test/html/index.html +++ b/js-test/servers/common/core-plugins/tests/1/as/webapps/openbis-v3-api-test/html/index.html @@ -13,12 +13,16 @@ require.paths["test"] = testPath; require.paths["test/qunit"] = testPath + "/lib/qunit/js/qunit"; require.paths["test/qunit-report"] = testPath + "/lib/qunit/js/qunit-reporter-junit"; + require.paths["test/naturalsort"] = testPath + "/lib/naturalsort/js/naturalSort"; require.urlArgs = 'now=' + Date.now(); require.shim["test/qunit-report"] = { deps : [ 'test/qunit' ] }; + require.shim["test/naturalsort"] = { + exports : "naturalSort" + }; </script> <script src="/openbis/resources/api/v3/require.js"></script> diff --git a/js-test/servers/common/core-plugins/tests/1/as/webapps/openbis-v3-api-test/html/test/lib/naturalsort/js/naturalSort.js b/js-test/servers/common/core-plugins/tests/1/as/webapps/openbis-v3-api-test/html/test/lib/naturalsort/js/naturalSort.js new file mode 100644 index 00000000000..f6dc3951c13 --- /dev/null +++ b/js-test/servers/common/core-plugins/tests/1/as/webapps/openbis-v3-api-test/html/test/lib/naturalsort/js/naturalSort.js @@ -0,0 +1,56 @@ +/** +* +* Natural Sort algorithm for Javascript - Version 0.7 - Released under MIT license +* Author: Jim Palmer (based on chunking idea from Dave Koelle) +* +* @param a first string to compare +* @param b second string to compare +* @return 1 if a > b, 0 if a == b, -1 if a < b +* +* @see http://www.overset.com/2008/09/01/javascript-natural-sort-algorithm-with-unicode-support/ +*/ +naturalSort = function(a, b) { + var re = /(^-?[0-9]+(\.?[0-9]*)[df]?e?[0-9]?$|^0x[0-9a-f]+$|[0-9]+)/gi, + sre = /(^[ ]*|[ ]*$)/g, + dre = /(^([\w ]+,?[\w ]+)?[\w ]+,?[\w ]+\d+:\d+(:\d+)?[\w]?|^\d{1,4}[\/\-]\d{1,4}[\/\-]\d{1,4}|^\w+, \w+ \d+, \d{4})/, + hre = /^0x[0-9a-f]+$/i, + ore = /^0/, + i = function(s) { + return naturalSort.insensitive && ('' + s).toLowerCase() || '' + s; + }, + // convert all to strings strip whitespace + x = i(a).replace(sre, '') || '', + y = i(b).replace(sre, '') || '', + // chunk/tokenize + xN = x.replace(re, '\0$1\0').replace(/\0$/, '').replace(/^\0/,'').split('\0'), + yN = y.replace(re, '\0$1\0').replace(/\0$/, '').replace(/^\0/,'').split('\0'), + // numeric, hex or date detection + xD = parseInt(x.match(hre)) || (xN.length != 1 && x.match(dre) && Date.parse(x)), + yD = parseInt(y.match(hre)) || xD && y.match(dre) && Date.parse(y) || null, oFxNcL, oFyNcL; + // first try and sort Hex codes or Dates + if (yD) + if (xD < yD) + return -1; + else if (xD > yD) + return 1; + // natural sorting through split numeric strings and default strings + for (var cLoc = 0, numS = Math.max(xN.length, yN.length); cLoc < numS; cLoc++) { + // find floats not starting with '0', string or 0 if not defined (Clint Priest) + oFxNcL = !(xN[cLoc] || '').match(ore) && parseFloat(xN[cLoc]) || xN[cLoc] || 0; + oFyNcL = !(yN[cLoc] || '').match(ore) && parseFloat(yN[cLoc]) || yN[cLoc] || 0; + // handle numeric vs string comparison - number < string - (Kyle Adams) + if (isNaN(oFxNcL) !== isNaN(oFyNcL)) { + return (isNaN(oFxNcL)) ? 1 : -1; + } + // rely on string comparison if different types - i.e. '02' < 2 != '02' < '2' + else if (typeof oFxNcL !== typeof oFyNcL) { + oFxNcL += ''; + oFyNcL += ''; + } + if (oFxNcL < oFyNcL) + return -1; + if (oFxNcL > oFyNcL) + return 1; + } +return 0; +}; diff --git a/js-test/servers/common/core-plugins/tests/1/as/webapps/openbis-v3-api-test/html/test/test-search.js b/js-test/servers/common/core-plugins/tests/1/as/webapps/openbis-v3-api-test/html/test/test-search.js index 9635d9ed063..33ca24c6785 100644 --- a/js-test/servers/common/core-plugins/tests/1/as/webapps/openbis-v3-api-test/html/test/test-search.js +++ b/js-test/servers/common/core-plugins/tests/1/as/webapps/openbis-v3-api-test/html/test/test-search.js @@ -1,4 +1,4 @@ -define([ 'jquery', 'underscore', 'openbis', 'test/common' ], function($, _, openbis, common) { +define([ 'jquery', 'underscore', 'openbis', 'test/common', 'test/naturalsort' ], function($, _, openbis, common, naturalsort) { return function() { QUnit.module("Search tests"); @@ -26,7 +26,7 @@ define([ 'jquery', 'underscore', 'openbis', 'test/common' ], function($, _, open }); } - var testSearchWithPagingAndSorting = function(c, fSearch, fetchOptions, fieldName) { + var testSearchWithPagingAndSorting = function(c, fSearch, fetchOptions, fieldName, fieldParameters) { c.start(); fetchOptions.from(null).count(null); @@ -40,17 +40,12 @@ define([ 'jquery', 'underscore', 'openbis', 'test/common' ], function($, _, open c.ok("Sorting by " + fieldName); + var fieldGetterName = "get" + fieldName.substr(0, 1).toUpperCase() + fieldName.substr(1); + objects.sort(function(o1, o2) { - var v1 = o1[fieldName]; - var v2 = o2[fieldName]; - - if (v1 > v2) { - return 1; - } else if (v1 < v2) { - return -1; - } else { - return 0; - } + var v1 = o1[fieldGetterName](fieldParameters); + var v2 = o2[fieldGetterName](fieldParameters); + return naturalsort(v1, v2); }); var codes = objects.map(function(object) { @@ -63,12 +58,12 @@ define([ 'jquery', 'underscore', 'openbis', 'test/common' ], function($, _, open var secondFromEnd = codes[codes.length - 2]; fetchOptions.from(1).count(1); - fetchOptions.sortBy()[fieldName](); + fetchOptions.sortBy()[fieldName](fieldParameters); return fSearch(facade).then(function(results) { c.ok("Got results ASC"); c.assertObjectsWithValues(results.getObjects(), "code", [ secondFromStart ]); - fetchOptions.sortBy()[fieldName]().desc(); + fetchOptions.sortBy()[fieldName](fieldParameters).desc(); return fSearch(facade).then(function(results) { c.ok("Got results DESC"); @@ -375,6 +370,22 @@ define([ 'jquery', 'underscore', 'openbis', 'test/common' ], function($, _, open }, fo); }); + QUnit.test("searchDataSets() with sorting by property", function(assert) { + var c = new common(assert); + + var criteria = new c.DataSetSearchCriteria(); + criteria.withOrOperator(); + criteria.withPermId().thatEquals("20130412142543232-197"); + criteria.withPermId().thatEquals("20130412142205843-196"); + criteria.withPermId().thatEquals("20130412142942295-198"); + + var fo = c.createDataSetFetchOptions(); + + testSearchWithPagingAndSorting(c, function(facade) { + return facade.searchDataSets(criteria, fo); + }, fo, "property", "RESOLUTION"); + }); + QUnit.test("searchDataSets() withoutSample", function(assert) { var c = new common(assert); diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/server/resultset/ColumnSortUtils.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/server/resultset/ColumnSortUtils.java index 90ebb4b1235..fabdda2e8b1 100644 --- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/server/resultset/ColumnSortUtils.java +++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/server/resultset/ColumnSortUtils.java @@ -20,6 +20,7 @@ import java.util.Comparator; import org.apache.commons.collections.ComparatorUtils; +import ch.systemsx.cisd.common.collection.AlphanumComparator; import ch.systemsx.cisd.openbis.generic.shared.basic.GridRowModel; import ch.systemsx.cisd.openbis.generic.shared.basic.IColumnDefinition; import ch.systemsx.cisd.openbis.generic.shared.basic.dto.SortInfo.SortDir; diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/public/resources/api/v3/dto/entity/dataset/DataSet.js b/openbis/source/java/ch/systemsx/cisd/openbis/public/resources/api/v3/dto/entity/dataset/DataSet.js index 68b41d60db4..6bb98dc9e48 100644 --- a/openbis/source/java/ch/systemsx/cisd/openbis/public/resources/api/v3/dto/entity/dataset/DataSet.js +++ b/openbis/source/java/ch/systemsx/cisd/openbis/public/resources/api/v3/dto/entity/dataset/DataSet.js @@ -198,6 +198,10 @@ define([ "stjs", "util/Exceptions" ], function(stjs, exceptions) { prototype.setSample = function(sample) { this.sample = sample; }; + prototype.getProperty = function(propertyName) { + var properties = this.getProperties(); + return properties ? properties[propertyName] : null; + }; prototype.getProperties = function() { if (this.getFetchOptions().hasProperties()) { return this.properties; diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/public/resources/api/v3/dto/entity/experiment/Experiment.js b/openbis/source/java/ch/systemsx/cisd/openbis/public/resources/api/v3/dto/entity/experiment/Experiment.js index 77bb229989e..f8cec13ee9e 100644 --- a/openbis/source/java/ch/systemsx/cisd/openbis/public/resources/api/v3/dto/entity/experiment/Experiment.js +++ b/openbis/source/java/ch/systemsx/cisd/openbis/public/resources/api/v3/dto/entity/experiment/Experiment.js @@ -111,6 +111,10 @@ define([ "stjs", "util/Exceptions" ], function(stjs, exceptions) { prototype.setHistory = function(history) { this.history = history; }; + prototype.getProperty = function(propertyName) { + var properties = this.getProperties(); + return properties ? properties[propertyName] : null; + }; prototype.getProperties = function() { if (this.getFetchOptions().hasProperties()) { return this.properties; diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/public/resources/api/v3/dto/entity/material/Material.js b/openbis/source/java/ch/systemsx/cisd/openbis/public/resources/api/v3/dto/entity/material/Material.js index 8579fc6655d..63cc5be0d02 100644 --- a/openbis/source/java/ch/systemsx/cisd/openbis/public/resources/api/v3/dto/entity/material/Material.js +++ b/openbis/source/java/ch/systemsx/cisd/openbis/public/resources/api/v3/dto/entity/material/Material.js @@ -79,6 +79,10 @@ define([ "stjs", "util/Exceptions" ], function(stjs, exceptions) { prototype.setModificationDate = function(modificationDate) { this.modificationDate = modificationDate; }; + prototype.getProperty = function(propertyName) { + var properties = this.getProperties(); + return properties ? properties[propertyName] : null; + }; prototype.getProperties = function() { if (this.getFetchOptions().hasProperties()) { return this.properties; diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/public/resources/api/v3/dto/entity/sample/Sample.js b/openbis/source/java/ch/systemsx/cisd/openbis/public/resources/api/v3/dto/entity/sample/Sample.js index 83658842b24..073646fce52 100644 --- a/openbis/source/java/ch/systemsx/cisd/openbis/public/resources/api/v3/dto/entity/sample/Sample.js +++ b/openbis/source/java/ch/systemsx/cisd/openbis/public/resources/api/v3/dto/entity/sample/Sample.js @@ -106,6 +106,10 @@ define([ "stjs", "util/Exceptions" ], function(stjs, exceptions) { prototype.setExperiment = function(experiment) { this.experiment = experiment; }; + prototype.getProperty = function(propertyName) { + var properties = this.getProperties(); + return properties ? properties[propertyName] : null; + }; prototype.getProperties = function() { if (this.getFetchOptions().hasProperties()) { return this.properties; diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/public/resources/api/v3/dto/entity/space/Space.js b/openbis/source/java/ch/systemsx/cisd/openbis/public/resources/api/v3/dto/entity/space/Space.js index 2218a8d7b28..98d8f73cd74 100644 --- a/openbis/source/java/ch/systemsx/cisd/openbis/public/resources/api/v3/dto/entity/space/Space.js +++ b/openbis/source/java/ch/systemsx/cisd/openbis/public/resources/api/v3/dto/entity/space/Space.js @@ -13,6 +13,7 @@ define([ "stjs", "util/Exceptions" ], function(stjs, exceptions) { prototype.code = null; prototype.description = null; prototype.registrationDate = null; + prototype.modificationDate = null; prototype.registrator = null; prototype.samples = null; prototype.projects = null; @@ -47,6 +48,12 @@ define([ "stjs", "util/Exceptions" ], function(stjs, exceptions) { prototype.setRegistrationDate = function(registrationDate) { this.registrationDate = registrationDate; }; + prototype.getModificationDate = function() { + return this.modificationDate; + }; + prototype.setModificationDate = function(modificationDate) { + this.modificationDate = modificationDate; + }; prototype.getRegistrator = function() { if (this.getFetchOptions().hasRegistrator()) { return this.registrator; diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/public/resources/api/v3/dto/fetchoptions/sort/EntitySortOptions.js b/openbis/source/java/ch/systemsx/cisd/openbis/public/resources/api/v3/dto/fetchoptions/sort/EntitySortOptions.js index 665d45d821c..015ebf585ac 100644 --- a/openbis/source/java/ch/systemsx/cisd/openbis/public/resources/api/v3/dto/fetchoptions/sort/EntitySortOptions.js +++ b/openbis/source/java/ch/systemsx/cisd/openbis/public/resources/api/v3/dto/fetchoptions/sort/EntitySortOptions.js @@ -6,7 +6,8 @@ define([ "require", "stjs", "dto/fetchoptions/sort/SortOptions" ], function(requ var fields = { CODE : "CODE", REGISTRATION_DATE : "REGISTRATION_DATE", - MODIFICATION_DATE : "MODIFICATION_DATE" + MODIFICATION_DATE : "MODIFICATION_DATE", + PROPERTY : "PROPERTY" }; stjs.extend(EntitySortOptions, SortOptions, [ SortOptions ], function(constructor, prototype) { @@ -18,6 +19,14 @@ define([ "require", "stjs", "dto/fetchoptions/sort/SortOptions" ], function(requ prototype.getCode = function() { return this.getSorting(fields.CODE); }; + + prototype.property = function(propertyName) { + return this.getOrCreateSorting(fields.PROPERTY + propertyName); + }; + prototype.getProperty = function(propertyName) { + return this.getSorting(fields.PROPERTY + propertyName); + }; + prototype.registrationDate = function() { return this.getOrCreateSorting(fields.REGISTRATION_DATE); }; diff --git a/openbis/sourceTest/java/ch/ethz/sis/openbis/systemtest/api/v3/SearchMaterialTest.java b/openbis/sourceTest/java/ch/ethz/sis/openbis/systemtest/api/v3/SearchMaterialTest.java index f912eacd6b1..92a3dcb0667 100644 --- a/openbis/sourceTest/java/ch/ethz/sis/openbis/systemtest/api/v3/SearchMaterialTest.java +++ b/openbis/sourceTest/java/ch/ethz/sis/openbis/systemtest/api/v3/SearchMaterialTest.java @@ -236,6 +236,41 @@ public class SearchMaterialTest extends AbstractTest testSearch(TEST_USER, criteria, new MaterialPermId("SRM_1", "SELF_REF"), new MaterialPermId("SRM_1A", "SELF_REF")); } + @Test + public void testSearchWithSortingByPropertyWithFloatValues() + { + String sessionToken = v3api.login(TEST_USER, PASSWORD); + + MaterialSearchCriteria criteria = new MaterialSearchCriteria(); + criteria.withOrOperator(); + criteria.withId().thatEquals(new MaterialPermId("GFP", "CONTROL")); + criteria.withId().thatEquals(new MaterialPermId("SCRAM", "CONTROL")); + criteria.withId().thatEquals(new MaterialPermId("XXXXX-ALL", "CONTROL")); + criteria.withId().thatEquals(new MaterialPermId("X-NO-DESC", "CONTROL")); + criteria.withId().thatEquals(new MaterialPermId("X-NO-SIZE", "CONTROL")); + + MaterialFetchOptions fo = new MaterialFetchOptions(); + fo.withProperties(); + + fo.sortBy().property("VOLUME").asc(); + List<Material> materials1 = v3api.searchMaterials(sessionToken, criteria, fo).getObjects(); + + assertEquals(materials1.get(0).getProperties().get("VOLUME"), "2.2"); + assertEquals(materials1.get(1).getProperties().get("VOLUME"), "3.0"); + assertEquals(materials1.get(2).getProperties().get("VOLUME"), "22.22"); + assertEquals(materials1.get(3).getProperties().get("VOLUME"), "99.99"); + assertEquals(materials1.get(4).getProperties().get("VOLUME"), "123"); + + fo.sortBy().property("VOLUME").desc(); + List<Material> materials2 = v3api.searchMaterials(sessionToken, criteria, fo).getObjects(); + + assertEquals(materials2.get(0).getProperties().get("VOLUME"), "123"); + assertEquals(materials2.get(1).getProperties().get("VOLUME"), "99.99"); + assertEquals(materials2.get(2).getProperties().get("VOLUME"), "22.22"); + assertEquals(materials2.get(3).getProperties().get("VOLUME"), "3.0"); + assertEquals(materials2.get(4).getProperties().get("VOLUME"), "2.2"); + } + @Test public void testSearchWithAndOperator() { diff --git a/openbis/sourceTest/java/ch/ethz/sis/openbis/systemtest/api/v3/SearchSampleTest.java b/openbis/sourceTest/java/ch/ethz/sis/openbis/systemtest/api/v3/SearchSampleTest.java index 7540181d162..c6a3f12373a 100644 --- a/openbis/sourceTest/java/ch/ethz/sis/openbis/systemtest/api/v3/SearchSampleTest.java +++ b/openbis/sourceTest/java/ch/ethz/sis/openbis/systemtest/api/v3/SearchSampleTest.java @@ -621,6 +621,67 @@ public class SearchSampleTest extends AbstractSampleTest v3api.logout(sessionToken); } + @Test + public void testSearchWithSortingByPropertyWithTextValues() + { + SampleSearchCriteria criteria = new SampleSearchCriteria(); + criteria.withOrOperator(); + criteria.withPermId().thatEquals("200902091219327-1025"); + criteria.withPermId().thatEquals("200902091225616-1027"); + criteria.withPermId().thatEquals("200902091250077-1026"); + + String sessionToken = v3api.login(TEST_USER, PASSWORD); + + SampleFetchOptions fo = new SampleFetchOptions(); + fo.withProperties(); + + fo.sortBy().property("COMMENT").asc(); + List<Sample> samples1 = search(sessionToken, criteria, fo); + assertEquals(samples1.get(0).getProperties().get("COMMENT"), "extremely simple stuff"); + assertEquals(samples1.get(1).getProperties().get("COMMENT"), "stuff like others"); + assertEquals(samples1.get(2).getProperties().get("COMMENT"), "very advanced stuff"); + + fo.sortBy().property("COMMENT").desc(); + List<Sample> samples2 = search(sessionToken, criteria, fo); + assertEquals(samples2.get(0).getProperties().get("COMMENT"), "very advanced stuff"); + assertEquals(samples2.get(1).getProperties().get("COMMENT"), "stuff like others"); + assertEquals(samples2.get(2).getProperties().get("COMMENT"), "extremely simple stuff"); + + v3api.logout(sessionToken); + } + + @Test + public void testSearchWithSortingByPropertyWithIntegerValues() + { + SampleSearchCriteria criteria = new SampleSearchCriteria(); + criteria.withOrOperator(); + criteria.withPermId().thatEquals("200902091219327-1025"); + criteria.withPermId().thatEquals("200902091225616-1027"); + criteria.withPermId().thatEquals("200902091250077-1026"); + criteria.withPermId().thatEquals("200811050946559-981"); + + String sessionToken = v3api.login(TEST_USER, PASSWORD); + + SampleFetchOptions fo = new SampleFetchOptions(); + fo.withProperties(); + + fo.sortBy().property("SIZE").asc(); + List<Sample> samples1 = search(sessionToken, criteria, fo); + assertEquals(samples1.get(0).getProperties().get("SIZE"), "123"); + assertEquals(samples1.get(1).getProperties().get("SIZE"), "321"); + assertEquals(samples1.get(2).getProperties().get("SIZE"), "666"); + assertEquals(samples1.get(3).getProperties().get("SIZE"), "4711"); + + fo.sortBy().property("SIZE").desc(); + List<Sample> samples2 = search(sessionToken, criteria, fo); + assertEquals(samples2.get(0).getProperties().get("SIZE"), "4711"); + assertEquals(samples2.get(1).getProperties().get("SIZE"), "666"); + assertEquals(samples2.get(2).getProperties().get("SIZE"), "321"); + assertEquals(samples2.get(3).getProperties().get("SIZE"), "123"); + + v3api.logout(sessionToken); + } + @Test public void testSearchWithSortingByRegistrationDate() { @@ -840,76 +901,64 @@ public class SearchSampleTest extends AbstractSampleTest v3api.logout(sessionToken); } - - private String getCodePropertyPairs(List<Sample> samples, String propertyCode) { - StringBuilder builder = new StringBuilder(); - builder.append("SEARCH TEST RESULT: "); - for(Sample sample : samples) { - builder.append(sample.getCode() + ":" + sample.getProperties().get(propertyCode) + " "); - } - - return builder.toString(); - } - @Test public void testSearchNumeric() { - //SIZE: 4711 CODE: 3VCP7 - //SIZE: 123 CODE: CP-TEST-1 - //SIZE: 321 CODE: CP-TEST-2 - //SIZE: 666 CODE: CP-TEST-3 - + // SIZE: 4711 CODE: 3VCP7 + // SIZE: 123 CODE: CP-TEST-1 + // SIZE: 321 CODE: CP-TEST-2 + // SIZE: 666 CODE: CP-TEST-3 + String sessionToken = v3api.login(TEST_USER, PASSWORD); SampleFetchOptions sortByCodeFO = new SampleFetchOptions(); sortByCodeFO.sortBy().code().asc(); sortByCodeFO.withProperties(); - - //Greater or Equals - Giving integer as real + + // Greater or Equals - Giving integer as real SampleSearchCriteria criteriaGOE = new SampleSearchCriteria(); criteriaGOE.withNumberProperty("SIZE").thatIsGreaterThanOrEqualTo(321.0); List<Sample> samplesGOE = search(sessionToken, criteriaGOE, sortByCodeFO); assertSampleIdentifiersInOrder(samplesGOE, "/CISD/3VCP7", "/CISD/CP-TEST-2", "/CISD/CP-TEST-3"); - - //Greater - Giving integer as real + + // Greater - Giving integer as real SampleSearchCriteria criteriaG = new SampleSearchCriteria(); criteriaG.withNumberProperty("SIZE").thatIsGreaterThan(321.0); List<Sample> samplesG = search(sessionToken, criteriaG, sortByCodeFO); assertSampleIdentifiersInOrder(samplesG, "/CISD/3VCP7", "/CISD/CP-TEST-3"); - - //Equals As Text - Real + + // Equals As Text - Real SampleSearchCriteria criteriaETxt2 = new SampleSearchCriteria(); criteriaETxt2.withProperty("SIZE").thatEquals("666.0"); List<Sample> samplesETxt2 = search(sessionToken, criteriaETxt2, sortByCodeFO); assertSampleIdentifiersInOrder(samplesETxt2, "/CISD/CP-TEST-3"); - - //Equals As Text - Integer + + // Equals As Text - Integer SampleSearchCriteria criteriaETxt = new SampleSearchCriteria(); criteriaETxt.withProperty("SIZE").thatEquals("666"); List<Sample> samplesETxt = search(sessionToken, criteriaETxt, sortByCodeFO); assertSampleIdentifiersInOrder(samplesETxt, "/CISD/CP-TEST-3"); - - //Equals + + // Equals SampleSearchCriteria criteriaE = new SampleSearchCriteria(); criteriaE.withNumberProperty("SIZE").thatEquals(666); List<Sample> samplesE = search(sessionToken, criteriaE, sortByCodeFO); assertSampleIdentifiersInOrder(samplesE, "/CISD/CP-TEST-3"); - - //Less + + // Less SampleSearchCriteria criteriaL = new SampleSearchCriteria(); criteriaL.withNumberProperty("SIZE").thatIsLessThan(666); List<Sample> samplesL = search(sessionToken, criteriaL, sortByCodeFO); assertSampleIdentifiersInOrder(samplesL, "/CISD/CP-TEST-1", "/CISD/CP-TEST-2"); - - //Less or Equals + + // Less or Equals SampleSearchCriteria criteriaLOE = new SampleSearchCriteria(); criteriaLOE.withNumberProperty("SIZE").thatIsLessThanOrEqualTo(321); List<Sample> samplesLOE = search(sessionToken, criteriaLOE, sortByCodeFO); assertSampleIdentifiersInOrder(samplesLOE, "/CISD/CP-TEST-1", "/CISD/CP-TEST-2"); - + v3api.logout(sessionToken); } - private void testSearch(String user, SampleSearchCriteria criteria, String... expectedIdentifiers) { String sessionToken = v3api.login(user, PASSWORD); @@ -925,12 +974,12 @@ public class SearchSampleTest extends AbstractSampleTest assertEquals(samples.size(), expectedCount); v3api.logout(sessionToken); } - + private List<Sample> search(String sessionToken, SampleSearchCriteria criteria, SampleFetchOptions fetchOptions) { SearchResult<Sample> searchResult = v3api.searchSamples(sessionToken, criteria, fetchOptions); return searchResult.getObjects(); } - + } diff --git a/openbis_api/source/java/ch/ethz/sis/openbis/generic/shared/api/v3/dto/fetchoptions/sort/EntitySortOptions.java b/openbis_api/source/java/ch/ethz/sis/openbis/generic/shared/api/v3/dto/fetchoptions/sort/EntitySortOptions.java index f94435a6c7a..2e1c5ee2478 100644 --- a/openbis_api/source/java/ch/ethz/sis/openbis/generic/shared/api/v3/dto/fetchoptions/sort/EntitySortOptions.java +++ b/openbis_api/source/java/ch/ethz/sis/openbis/generic/shared/api/v3/dto/fetchoptions/sort/EntitySortOptions.java @@ -18,16 +18,17 @@ package ch.ethz.sis.openbis.generic.shared.api.v3.dto.fetchoptions.sort; import static ch.ethz.sis.openbis.generic.shared.api.v3.dto.fetchoptions.sort.comparator.CodeComparator.CODE; import static ch.ethz.sis.openbis.generic.shared.api.v3.dto.fetchoptions.sort.comparator.ModificationDateComparator.MODIFICATION_DATE; +import static ch.ethz.sis.openbis.generic.shared.api.v3.dto.fetchoptions.sort.comparator.PropertyComparator.PROPERTY; import static ch.ethz.sis.openbis.generic.shared.api.v3.dto.fetchoptions.sort.comparator.RegistrationDateComparator.REGISTRATION_DATE; import java.util.Comparator; -import java.util.Map; import ch.ethz.sis.openbis.generic.shared.api.v3.dto.entity.interfaces.ICodeHolder; import ch.ethz.sis.openbis.generic.shared.api.v3.dto.entity.interfaces.IModificationDateHolder; import ch.ethz.sis.openbis.generic.shared.api.v3.dto.entity.interfaces.IRegistrationDateHolder; import ch.ethz.sis.openbis.generic.shared.api.v3.dto.fetchoptions.sort.comparator.CodeComparator; import ch.ethz.sis.openbis.generic.shared.api.v3.dto.fetchoptions.sort.comparator.ModificationDateComparator; +import ch.ethz.sis.openbis.generic.shared.api.v3.dto.fetchoptions.sort.comparator.PropertyComparator; import ch.ethz.sis.openbis.generic.shared.api.v3.dto.fetchoptions.sort.comparator.RegistrationDateComparator; import ch.systemsx.cisd.base.annotation.JsonObject; @@ -50,6 +51,16 @@ public class EntitySortOptions<OBJECT extends ICodeHolder & IRegistrationDateHol return getSorting(CODE); } + public SortOrder property(String propertyName) + { + return getOrCreateSorting(PROPERTY + propertyName); + } + + public SortOrder getProperty(String propertyName) + { + return getSorting(PROPERTY + propertyName); + } + public SortOrder registrationDate() { return getOrCreateSorting(REGISTRATION_DATE); @@ -71,11 +82,23 @@ public class EntitySortOptions<OBJECT extends ICodeHolder & IRegistrationDateHol } @Override - public void addComparators(Map<String, Comparator<OBJECT>> map) + public Comparator<OBJECT> getComparator(String field) { - map.put(CODE, new CodeComparator<OBJECT>()); - map.put(REGISTRATION_DATE, new RegistrationDateComparator<OBJECT>()); - map.put(MODIFICATION_DATE, new ModificationDateComparator<OBJECT>()); + if (CODE.equals(field)) + { + return new CodeComparator<OBJECT>(); + } else if (REGISTRATION_DATE.equals(field)) + { + return new RegistrationDateComparator<OBJECT>(); + } else if (MODIFICATION_DATE.equals(field)) + { + return new ModificationDateComparator<OBJECT>(); + } else if (field.startsWith(PROPERTY)) + { + return new PropertyComparator<OBJECT>(field.substring(PROPERTY.length())); + } else + { + return null; + } } - } diff --git a/openbis_api/source/java/ch/ethz/sis/openbis/generic/shared/api/v3/dto/fetchoptions/sort/SortAndPage.java b/openbis_api/source/java/ch/ethz/sis/openbis/generic/shared/api/v3/dto/fetchoptions/sort/SortAndPage.java index 7125b0472e4..8bb07cd91eb 100644 --- a/openbis_api/source/java/ch/ethz/sis/openbis/generic/shared/api/v3/dto/fetchoptions/sort/SortAndPage.java +++ b/openbis_api/source/java/ch/ethz/sis/openbis/generic/shared/api/v3/dto/fetchoptions/sort/SortAndPage.java @@ -216,15 +216,18 @@ public class SortAndPage int index = 0; for (Sorting sorting : sortings) { - Comparator aComparator = sortBy.getComparator(sorting.getField()); - if (aComparator == null) + if (sorting.getField() != null) { - throw new IllegalArgumentException("Comparator for field " + sorting.getField() + " not found"); - } + Comparator aComparator = sortBy.getComparator(sorting.getField()); + if (aComparator == null) + { + throw new IllegalArgumentException("Comparator for field " + sorting.getField() + " not found"); + } - comparators[index] = aComparator; - directions[index] = sorting.getOrder().isAsc() ? 1 : -1; - index++; + comparators[index] = aComparator; + directions[index] = sorting.getOrder().isAsc() ? 1 : -1; + index++; + } } return new Comparator() diff --git a/openbis_api/source/java/ch/ethz/sis/openbis/generic/shared/api/v3/dto/fetchoptions/sort/SortOptions.java b/openbis_api/source/java/ch/ethz/sis/openbis/generic/shared/api/v3/dto/fetchoptions/sort/SortOptions.java index f2a170ca158..3ed31b2e4e5 100644 --- a/openbis_api/source/java/ch/ethz/sis/openbis/generic/shared/api/v3/dto/fetchoptions/sort/SortOptions.java +++ b/openbis_api/source/java/ch/ethz/sis/openbis/generic/shared/api/v3/dto/fetchoptions/sort/SortOptions.java @@ -18,10 +18,8 @@ package ch.ethz.sis.openbis.generic.shared.api.v3.dto.fetchoptions.sort; import java.io.Serializable; import java.util.Comparator; -import java.util.HashMap; import java.util.LinkedList; import java.util.List; -import java.util.Map; import ch.systemsx.cisd.base.annotation.JsonObject; @@ -34,24 +32,11 @@ public abstract class SortOptions<OBJECT> implements Serializable private static final long serialVersionUID = 1L; - private transient Map<String, Comparator<OBJECT>> comparators; - private List<Sorting> sortings = new LinkedList<>(); - protected void addComparators(Map<String, Comparator<OBJECT>> map) - { - } - - public final Comparator<OBJECT> getComparator(String field) + public Comparator<OBJECT> getComparator(String field) { - if (comparators == null) - { - Map<String, Comparator<OBJECT>> map = new HashMap<String, Comparator<OBJECT>>(); - addComparators(map); - comparators = map; - } - - return comparators.get(field); + return null; } protected SortOrder getOrCreateSorting(String field) diff --git a/openbis_api/source/java/ch/ethz/sis/openbis/generic/shared/api/v3/dto/fetchoptions/sort/comparator/AbstractComparator.java b/openbis_api/source/java/ch/ethz/sis/openbis/generic/shared/api/v3/dto/fetchoptions/sort/comparator/AbstractComparator.java index abab698dbf3..23a9b7ea079 100644 --- a/openbis_api/source/java/ch/ethz/sis/openbis/generic/shared/api/v3/dto/fetchoptions/sort/comparator/AbstractComparator.java +++ b/openbis_api/source/java/ch/ethz/sis/openbis/generic/shared/api/v3/dto/fetchoptions/sort/comparator/AbstractComparator.java @@ -32,7 +32,7 @@ public abstract class AbstractComparator<OBJECT, VALUE extends Comparable<VALUE> if (value1 != null && value2 != null) { - return value1.compareTo(value2); + return doCompare(value1, value2); } else if (value1 != null) { return 1; @@ -45,6 +45,11 @@ public abstract class AbstractComparator<OBJECT, VALUE extends Comparable<VALUE> } } + protected int doCompare(VALUE value1, VALUE value2) + { + return value1.compareTo(value2); + } + protected abstract VALUE getValue(OBJECT o); } diff --git a/openbis_api/source/java/ch/ethz/sis/openbis/generic/shared/api/v3/dto/fetchoptions/sort/comparator/AbstractStringComparator.java b/openbis_api/source/java/ch/ethz/sis/openbis/generic/shared/api/v3/dto/fetchoptions/sort/comparator/AbstractStringComparator.java new file mode 100644 index 00000000000..6b2f2d5cf1d --- /dev/null +++ b/openbis_api/source/java/ch/ethz/sis/openbis/generic/shared/api/v3/dto/fetchoptions/sort/comparator/AbstractStringComparator.java @@ -0,0 +1,35 @@ +/* + * Copyright 2015 ETH Zuerich, CISD + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package ch.ethz.sis.openbis.generic.shared.api.v3.dto.fetchoptions.sort.comparator; + +import ch.systemsx.cisd.common.collection.AlphanumComparator; + +/** + * @author pkupczyk + */ +public abstract class AbstractStringComparator<OBJECT> extends AbstractComparator<OBJECT, String> +{ + + private AlphanumComparator alphanumComparator = new AlphanumComparator(); + + @Override + protected int doCompare(String value1, String value2) + { + return alphanumComparator.compare(value1, value2); + } + +} diff --git a/openbis_api/source/java/ch/ethz/sis/openbis/generic/shared/api/v3/dto/fetchoptions/sort/comparator/CodeComparator.java b/openbis_api/source/java/ch/ethz/sis/openbis/generic/shared/api/v3/dto/fetchoptions/sort/comparator/CodeComparator.java index f4bdd276445..2791277619e 100644 --- a/openbis_api/source/java/ch/ethz/sis/openbis/generic/shared/api/v3/dto/fetchoptions/sort/comparator/CodeComparator.java +++ b/openbis_api/source/java/ch/ethz/sis/openbis/generic/shared/api/v3/dto/fetchoptions/sort/comparator/CodeComparator.java @@ -21,7 +21,7 @@ import ch.ethz.sis.openbis.generic.shared.api.v3.dto.entity.interfaces.ICodeHold /** * @author pkupczyk */ -public class CodeComparator<OBJECT extends ICodeHolder> extends AbstractComparator<OBJECT, String> +public class CodeComparator<OBJECT extends ICodeHolder> extends AbstractStringComparator<OBJECT> { public static final String CODE = "CODE"; diff --git a/openbis_api/source/java/ch/ethz/sis/openbis/generic/shared/api/v3/dto/fetchoptions/sort/comparator/PropertyComparator.java b/openbis_api/source/java/ch/ethz/sis/openbis/generic/shared/api/v3/dto/fetchoptions/sort/comparator/PropertyComparator.java new file mode 100644 index 00000000000..b3ae8647d02 --- /dev/null +++ b/openbis_api/source/java/ch/ethz/sis/openbis/generic/shared/api/v3/dto/fetchoptions/sort/comparator/PropertyComparator.java @@ -0,0 +1,51 @@ +/* + * Copyright 2015 ETH Zuerich, CISD + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package ch.ethz.sis.openbis.generic.shared.api.v3.dto.fetchoptions.sort.comparator; + +import java.util.Map; + +import ch.ethz.sis.openbis.generic.shared.api.v3.dto.entity.interfaces.IPropertiesHolder; + +/** + * @author pkupczyk + */ +public class PropertyComparator<OBJECT> extends AbstractStringComparator<OBJECT> +{ + + public static final String PROPERTY = "PROPERTY"; + + private String propertyName; + + public PropertyComparator(String propertyName) + { + this.propertyName = propertyName; + } + + @Override + protected String getValue(OBJECT o) + { + if (o instanceof IPropertiesHolder) + { + Map<String, String> properties = ((IPropertiesHolder) o).getProperties(); + return properties != null ? properties.get(propertyName) : null; + } else + { + throw new IllegalArgumentException("Object " + o + " does not implement " + IPropertiesHolder.class + + " interface therefore cannot be sorted by a property value."); + } + } +} diff --git a/openbis_api/source/java/ch/ethz/sis/openbis/generic/shared/api/v3/dto/fetchoptions/tag/TagSortOptions.java b/openbis_api/source/java/ch/ethz/sis/openbis/generic/shared/api/v3/dto/fetchoptions/tag/TagSortOptions.java index 9a227166207..d2ede6f23cf 100644 --- a/openbis_api/source/java/ch/ethz/sis/openbis/generic/shared/api/v3/dto/fetchoptions/tag/TagSortOptions.java +++ b/openbis_api/source/java/ch/ethz/sis/openbis/generic/shared/api/v3/dto/fetchoptions/tag/TagSortOptions.java @@ -20,7 +20,6 @@ import static ch.ethz.sis.openbis.generic.shared.api.v3.dto.fetchoptions.sort.co import static ch.ethz.sis.openbis.generic.shared.api.v3.dto.fetchoptions.sort.comparator.RegistrationDateComparator.REGISTRATION_DATE; import java.util.Comparator; -import java.util.Map; import ch.ethz.sis.openbis.generic.shared.api.v3.dto.entity.tag.Tag; import ch.ethz.sis.openbis.generic.shared.api.v3.dto.fetchoptions.sort.SortOptions; @@ -59,9 +58,17 @@ public class TagSortOptions extends SortOptions<Tag> } @Override - public void addComparators(Map<String, Comparator<Tag>> map) + public Comparator<Tag> getComparator(String field) { - map.put(CODE, new CodeComparator<Tag>()); - map.put(REGISTRATION_DATE, new RegistrationDateComparator<Tag>()); + if (CODE.equals(field)) + { + return new CodeComparator<Tag>(); + } else if (REGISTRATION_DATE.equals(field)) + { + return new RegistrationDateComparator<Tag>(); + } else + { + return null; + } } } -- GitLab