diff --git a/CHANGELOG.md b/CHANGELOG.md index 9997cd81461632727647bab467779089789dfb93..ea88e321261634711f9e084f318adf497672d66c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,6 +23,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed +* Select queries would originally only select the returned properties by their + names and would not check if a property is a subtype of a selected property. This + has changed now and select queries will also return subtypes of selected + properties. + ### Deprecated * `SERVER_SIDE_SCRIPTING_BIN_DIR` property is deprecated. diff --git a/src/doc/CaosDB-Query-Language.md b/src/doc/CaosDB-Query-Language.md index 47a890d716564eb72c70de4f9d39d1cd00d92157..07fbdbc310b8d22de4642f041918710e6e707488 100644 --- a/src/doc/CaosDB-Query-Language.md +++ b/src/doc/CaosDB-Query-Language.md @@ -351,6 +351,33 @@ Any result set can be filtered by logically combining POV filters or back refere * REFERENCE:: This one is tricky: `REFERENCE TO` expresses a the state of _having_ a reference property. `REFERENCED BY` expresses the state of _being_ referenced by another entity. * COUNT:: `COUNT` works like `FIND` but doesn't return the entities. + +## Select Queries + +In contrast to `FIND` queries, which always return the complete entity, there are `SELECT` queries which only return the entity with only those properties which are specified in the query. The syntax is very similar to `FIND` queries - just replace the `FIND` by `SELECT <comma separated list of selectors> FROM`: + +`SELECT p1, p2, p3 FROM Record ename` + +However, the `SELECT` query can also return properties of referenced entities and thereby are a means of joining entities together and return a custom view or projection: + +`SELECT Conductor.Last Name FROM Experiment` + +would return the conductor's last name, when `Conductor` is a reference property of `Experiment` and `Last Name` is a property of the `Conductor` records. + +### Selectors + +Selectors are strings of entity names which are separated by `.` (dot). E.g. `Conductor.Last Name` or `Conductor.Address` or even `Experiment.Conductor.Last name`. Selectors in a `SELECT` queries are separated by `,` (comma). E.g. `Conductor.First Name, Conductor.Last Name`. + +### Evaluation of Selectors + +The query will return all those properties which have the same name as +specified by the selector (case-insensitive). However, `SELECT` queries are +also capable of subtyping in the selectors: + +`SELECT Person FROM Experiment` would return all `Person` properties but all `Conductors` as well, if `Conductor` is a child of `Person`. + +Note: When a property `responsible` with data type `Person` exists, the above `SELECT` statement would not include records that use this property (since `responsible` is not a child of Person). + ## Versioning Since Caosdb 0.2 entities are optionally version controlled. The query language will be extended to include versioning in the future. A current minimal implementation introduces the `ANY VERSION OF` modifier which can be used to return all matching versions in the results of `COUNT`, `FIND`, and `SELECT` queries. diff --git a/src/main/java/org/caosdb/server/database/backend/transaction/IsSubType.java b/src/main/java/org/caosdb/server/database/backend/transaction/IsSubType.java index d75fb2c2453613c50915678d81f53f696589afd4..683903f396a186c8243b6bba20c0d19e29a61688 100644 --- a/src/main/java/org/caosdb/server/database/backend/transaction/IsSubType.java +++ b/src/main/java/org/caosdb/server/database/backend/transaction/IsSubType.java @@ -29,19 +29,36 @@ import org.caosdb.server.database.exceptions.TransactionException; public class IsSubType extends BackendTransaction { private final Integer child; - private final Integer parent; + private Integer parent = null; + private String parentName = null; public IsSubType(final Integer child, final Integer parent) { this.child = child; this.parent = parent; } - private boolean isSubType; + public IsSubType(Integer child, String parent) { + this.parentName = parent; + this.child = child; + } + + private Boolean isSubType = null; @Override public void execute() throws TransactionException { final IsSubTypeImpl t = getImplementation(IsSubTypeImpl.class); - this.isSubType = t.execute(this.child, this.parent); + + if (this.parent == null) { + this.isSubType = false; + for (Integer parent : execute(new GetIDByName(parentName, false)).getList()) { + this.isSubType = t.execute(this.child, parent); + if (this.isSubType) { + return; + } + } + } else { + this.isSubType = t.execute(this.child, this.parent); + } } public boolean isSubType() { diff --git a/src/main/java/org/caosdb/server/database/backend/transaction/RetrieveFullEntity.java b/src/main/java/org/caosdb/server/database/backend/transaction/RetrieveFullEntity.java index 38cd7073613e6477d013f8923aaecfc188a3c234..f9b6356adfcbe42760f272c4540c25489d060446 100644 --- a/src/main/java/org/caosdb/server/database/backend/transaction/RetrieveFullEntity.java +++ b/src/main/java/org/caosdb/server/database/backend/transaction/RetrieveFullEntity.java @@ -1,24 +1,20 @@ /* - * ** header v3.0 - * This file is a part of the CaosDB Project. + * ** header v3.0 This file is a part of the CaosDB Project. * - * Copyright (C) 2018 Research Group Biomedical Physics, - * Max-Planck-Institute for Dynamics and Self-Organization Göttingen - * Copyright (C) 2020 Timm Fitschen <t.fitschen@indiscale.com> - * Copyright (C) 2020 IndiScale GmbH <info@indiscale.com> + * Copyright (C) 2018 Research Group Biomedical Physics, Max-Planck-Institute for Dynamics and + * Self-Organization Göttingen Copyright (C) 2020 Timm Fitschen <t.fitschen@indiscale.com> Copyright + * (C) 2020 IndiScale GmbH <info@indiscale.com> * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. + * This program is free software: you can redistribute it and/or modify it under the terms of the + * GNU Affero General Public License as published by the Free Software Foundation, either version 3 + * of the License, or (at your option) any later version. * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without + * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Affero General Public License for more details. * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <https://www.gnu.org/licenses/>. + * You should have received a copy of the GNU Affero General Public License along with this program. + * If not, see <https://www.gnu.org/licenses/>. * * ** end header */ @@ -27,6 +23,7 @@ package org.caosdb.server.database.backend.transaction; import java.util.LinkedList; import java.util.List; import org.caosdb.server.database.BackendTransaction; +import org.caosdb.server.database.exceptions.EntityDoesNotExistException; import org.caosdb.server.datatype.ReferenceDatatype; import org.caosdb.server.datatype.ReferenceValue; import org.caosdb.server.entity.EntityInterface; @@ -124,10 +121,9 @@ public class RetrieveFullEntity extends BackendTransaction { */ public void retrieveSubEntities(EntityInterface e, List<Selection> selections) { for (final Selection s : selections) { + String propertyName = s.getSelector(); if (s.getSubselection() != null) { - String propertyName = s.getSelector(); - // Find matching (i.e. referencing) Properties for (Property p : e.getProperties()) { // get reference properties by name. @@ -142,10 +138,47 @@ public class RetrieveFullEntity extends BackendTransaction { ReferenceValue value = (ReferenceValue) p.getValue(); RetrieveEntity ref = new RetrieveEntity(value.getId()); - // recursion! (Only for the matching selections) + // recursion! (Only for the matching selections) retrieveFullEntity(ref, getSubSelects(selections, propertyName)); value.setEntity(ref, true); } + continue; + } + try { + boolean isSubtype = execute(new IsSubType(p.getId(), propertyName)).isSubType(); + if (isSubtype) { + if (p.getValue() != null) { + if (p.getDatatype() instanceof ReferenceDatatype) { + try { + p.parseValue(); + } catch (Message m) { + p.addError(m); + } + + ReferenceValue value = (ReferenceValue) p.getValue(); + RetrieveEntity ref = new RetrieveEntity(value.getId()); + // recursion! (Only for the matching selections) + retrieveFullEntity(ref, getSubSelects(selections, propertyName)); + value.setEntity(ref, true); + p.setName(propertyName); + } + } + } + } catch (EntityDoesNotExistException exc) { + // unknown parent name. + } + } + } else { + for (Property p : e.getProperties()) { + if (!propertyName.equalsIgnoreCase(p.getName())) { + try { + boolean isSubtype = execute(new IsSubType(p.getId(), propertyName)).isSubType(); + if (isSubtype) { + p.setName(propertyName); + } + } catch (EntityDoesNotExistException exc) { + // unknown parent name. + } } } } diff --git a/src/main/java/org/caosdb/server/jobs/Job.java b/src/main/java/org/caosdb/server/jobs/Job.java index b3bfda36bedbabfa3eac2eada3938c731041178d..5d8dd5d98a00960561c6b97f8e1f4fa118eafd0b 100644 --- a/src/main/java/org/caosdb/server/jobs/Job.java +++ b/src/main/java/org/caosdb/server/jobs/Job.java @@ -57,12 +57,10 @@ import org.caosdb.server.utils.Observer; import org.caosdb.server.utils.ServerMessages; import org.reflections.Reflections; - /** * This is a Job. * * @todo Describe me. - * */ public abstract class Job extends AbstractObservable implements Observer { private Transaction<? extends TransactionContainer> transaction = null;