]> gerrit.simantics Code Review - simantics/platform.git/blobdiff - bundles/org.simantics.db.layer0/src/org/simantics/db/layer0/adapter/impl/EntityInstances.java
Fixed index query regression in L0.Entity instance queries
[simantics/platform.git] / bundles / org.simantics.db.layer0 / src / org / simantics / db / layer0 / adapter / impl / EntityInstances.java
index 32a7d2a9f5b327edfe63a19ebd7b1045f28bfaef..07ddce7bd176e1fe2d8eca40819a0183ca3b64bf 100644 (file)
-/*******************************************************************************\r
- * Copyright (c) 2007, 2010 Association for Decentralized Information Management\r
- * in Industry THTH ry.\r
- * All rights reserved. This program and the accompanying materials\r
- * are made available under the terms of the Eclipse Public License v1.0\r
- * which accompanies this distribution, and is available at\r
- * http://www.eclipse.org/legal/epl-v10.html\r
- *\r
- * Contributors:\r
- *     VTT Technical Research Centre of Finland - initial API and implementation\r
- *******************************************************************************/\r
-package org.simantics.db.layer0.adapter.impl;\r
-\r
-import java.util.ArrayList;\r
-import java.util.Collection;\r
-import java.util.Collections;\r
-import java.util.List;\r
-import java.util.Set;\r
-\r
-import org.simantics.databoard.Bindings;\r
-import org.simantics.db.ReadGraph;\r
-import org.simantics.db.Resource;\r
-import org.simantics.db.common.primitiverequest.Adapter;\r
-import org.simantics.db.common.procedure.adapter.TransientCacheListener;\r
-import org.simantics.db.common.request.ObjectsWithType;\r
-import org.simantics.db.common.request.TernaryRead;\r
-import org.simantics.db.exception.DatabaseException;\r
-import org.simantics.db.layer0.adapter.Instances;\r
-import org.simantics.db.layer0.genericrelation.IndexQueries;\r
-import org.simantics.db.layer0.util.Layer0Utils;\r
-import org.simantics.db.request.ReadExt;\r
-import org.simantics.db.request.RequestFlags;\r
-import org.simantics.db.service.CollectionSupport;\r
-import org.simantics.layer0.Layer0;\r
-import org.simantics.operation.Layer0X;\r
-import org.simantics.scl.runtime.function.Function;\r
-\r
-import gnu.trove.set.hash.THashSet;\r
-\r
-/**\r
- * @author Antti Villberg\r
- * @author Tuukka Lehtonen\r
- */\r
-public class EntityInstances implements Instances {\r
-\r
-    private static final boolean TRACE_QUERIES = false;\r
-\r
-    private final Resource type;\r
-\r
-    public EntityInstances(Resource type) {\r
-        this.type = type;\r
-    }\r
-\r
-    @Override\r
-    public Collection<Resource> find(ReadGraph graph, Resource index) throws DatabaseException {\r
-        return find(graph, index, "");\r
-    }\r
-\r
-    /**\r
-     * A (cacheable) query to optimize single index queries for immutable\r
-     * indexes such as ontologies.\r
-     */\r
-    public static class QueryIndex extends TernaryRead<Resource, Resource, String, List<Resource>> implements ReadExt {\r
-\r
-        public QueryIndex(Resource index, Resource type, String filter) {\r
-            super(index, type, filter);\r
-        }\r
-\r
-        @Override\r
-        public List<Resource> perform(ReadGraph graph)\r
-                throws DatabaseException {\r
-            Resource type = parameter2;\r
-\r
-            Layer0 L0 = Layer0.getInstance(graph);\r
-            Layer0X L0X = Layer0X.getInstance(graph);\r
-            String typeName = graph.getRelatedValue(type, L0.HasName);\r
-            if (typeName.isEmpty())\r
-                return Collections.emptyList();\r
-\r
-            @SuppressWarnings({ "unchecked", "rawtypes" })\r
-            Function dependencyResources = graph.syncRequest(new Adapter(L0X.DependencyResources, Function.class), TransientCacheListener.<Function>instance());\r
-\r
-            StringBuilder filtersb = new StringBuilder();\r
-            filtersb.append("Types:*").append( IndexQueries.escape( typeName, true ) );\r
-            if (parameter3.length() > 0)\r
-                filtersb.append(" AND ").append( parameter3 );\r
-            String filter = filtersb.toString();\r
-\r
-            if (TRACE_QUERIES) {\r
-                System.out.println("EntityInstances.QueryIndex: finding " + filter + " from index " + graph.getPossibleURI(parameter));\r
-                //new Exception("EntityInstances: finding " + filter + " from index " + graph.getPossibleURI(parameter)).printStackTrace();\r
-            }\r
-            \r
-            @SuppressWarnings("unchecked")\r
-                       List<Resource> results = (List<Resource>)dependencyResources.apply(graph, parameter, filter);\r
-            if (results == null || results.isEmpty())\r
-                return Collections.emptyList();\r
-\r
-            if (TRACE_QUERIES)\r
-                System.out.println("  EntityInstances.QueryIndex: got " + results.size() + " results");\r
-\r
-//            // TreeSet to keep the results in deterministic order.\r
-//            Set<Resource> resultSet = new TreeSet<Resource>();\r
-//            for (Map<String, Object> entry : results) {\r
-//                Resource res = (Resource)entry.get("Resource");\r
-//                if (res != null && !resultSet.contains(res))\r
-//                    resultSet.add(res);\r
-//            }\r
-\r
-            CollectionSupport coll = graph.getService(CollectionSupport.class);\r
-            List<Resource> result = coll.createList();\r
-            \r
-            for (Resource res : Layer0Utils.sortByCluster(graph, results)) {\r
-                if (graph.isInstanceOf(res, type))\r
-                    result.add(res);\r
-            }\r
-\r
-            if (TRACE_QUERIES)\r
-                System.out.println("  EntityInstances.QueryIndex: got " + results.size() + " unique type-matching results");\r
-            \r
-            return result;\r
-\r
-        }\r
-        \r
-        @Override\r
-        public String toString() {\r
-               return "QueryIndex " + parameter + " " + parameter2 + " " + parameter3;\r
-        }\r
-\r
-               @Override\r
-               public boolean isImmutable(ReadGraph graph) throws DatabaseException {\r
-                       // TODO Auto-generated method stub\r
-                       return false;\r
-               }\r
-\r
-               @Override\r
-               public int getType() {\r
-                       return RequestFlags.IMMEDIATE_UPDATE;\r
-               }\r
-\r
-    }\r
-\r
-    private List<Resource> findRec(ReadGraph graph, Resource index, String filter, Set<Resource> visited) throws DatabaseException {\r
-\r
-       if(!visited.add(index)) return Collections.emptyList();\r
-\r
-       CollectionSupport coll = graph.getService(CollectionSupport.class);\r
-\r
-       List<Resource> indexResult = graph.syncRequest(new QueryIndex(index, type, filter), TransientCacheListener.<List<Resource>>instance());\r
-\r
-        Layer0 L0 = Layer0.getInstance(graph);\r
-        Collection<Resource> linkedRoots = graph.syncRequest(new ObjectsWithType(index, L0.IsLinkedTo, L0.IndexRoot));\r
-        if (linkedRoots.isEmpty())\r
-            return indexResult;\r
-\r
-        List<Resource> result = indexResult;\r
-        for (Resource dep : linkedRoots) {\r
-            Collection<Resource> linkedIndexResults = findRec(graph, dep, filter, visited);\r
-            if (linkedIndexResults.isEmpty())\r
-                continue;\r
-            if (result == indexResult) {\r
-               result = coll.createList();\r
-                result.addAll(indexResult);\r
-            } else {\r
-            }\r
-            result.addAll(linkedIndexResults);\r
-        }\r
-        \r
-        return result;\r
-        \r
-    }\r
-\r
-    @Override\r
-    public Collection<Resource> find(ReadGraph graph, Resource index, String filter) throws DatabaseException {\r
-       CollectionSupport coll = graph.getService(CollectionSupport.class);\r
-       \r
-       THashSet<Resource> visited = new THashSet<>();\r
-       List<Resource> rec = findRec(graph, index, filter, visited);\r
-       for(Resource global : Layer0Utils.listGlobalOntologies(graph)) {\r
-               if(!visited.add(global)) continue;\r
-               List<Resource> rs = graph.syncRequest(new QueryIndex(global, type, filter), TransientCacheListener.<List<Resource>>instance());\r
-               if(rec.isEmpty() && !rs.isEmpty()) {\r
-                       // TODO: rec could be an immutable empty list\r
-                       rec = new ArrayList<Resource>();\r
-               }\r
-               rec.addAll(rs);\r
-       }\r
-       Collection<Resource> result = coll.asSortedList(rec);\r
-       return result; \r
-    }\r
-    \r
-    @Override\r
-    public Collection<Resource> findByName(ReadGraph graph, Resource model,\r
-            String name) throws DatabaseException {\r
-        Layer0 L0 = Layer0.getInstance(graph);\r
-       CollectionSupport coll = graph.getService(CollectionSupport.class);\r
-        List<Resource> results = coll.createList();\r
-        for(Resource match : find(graph, model, name)) {\r
-            if(name.equals(graph.getPossibleRelatedValue(match, L0.HasName, Bindings.STRING))) results.add(match);\r
-        }\r
-        return results;\r
-    }\r
-\r
-}\r
+/*******************************************************************************
+ * Copyright (c) 2007, 2010 Association for Decentralized Information Management
+ * in Industry THTH ry.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     VTT Technical Research Centre of Finland - initial API and implementation
+ *******************************************************************************/
+package org.simantics.db.layer0.adapter.impl;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+
+import org.simantics.databoard.Bindings;
+import org.simantics.db.ReadGraph;
+import org.simantics.db.Resource;
+import org.simantics.db.common.primitiverequest.Adapter;
+import org.simantics.db.common.procedure.adapter.TransientCacheListener;
+import org.simantics.db.common.request.ObjectsWithType;
+import org.simantics.db.common.request.TernaryRead;
+import org.simantics.db.exception.DatabaseException;
+import org.simantics.db.layer0.adapter.Instances;
+import org.simantics.db.layer0.genericrelation.IndexQueries;
+import org.simantics.db.layer0.util.Layer0Utils;
+import org.simantics.db.request.ReadExt;
+import org.simantics.db.request.RequestFlags;
+import org.simantics.db.service.CollectionSupport;
+import org.simantics.layer0.Layer0;
+import org.simantics.operation.Layer0X;
+import org.simantics.scl.runtime.function.Function;
+
+import gnu.trove.set.hash.THashSet;
+
+/**
+ * @author Antti Villberg
+ * @author Tuukka Lehtonen
+ */
+public class EntityInstances implements Instances {
+
+    private static final boolean TRACE_QUERIES = false;
+
+    private final Resource type;
+
+    public EntityInstances(Resource type) {
+        this.type = type;
+    }
+
+    @Override
+    public Collection<Resource> find(ReadGraph graph, Resource index) throws DatabaseException {
+        return find(graph, index, "");
+    }
+
+    /**
+     * A (cacheable) query to optimize single index queries for immutable
+     * indexes such as ontologies.
+     */
+    public static class QueryIndex extends TernaryRead<Resource, Resource, String, List<Resource>> implements ReadExt {
+
+        public QueryIndex(Resource index, Resource type, String filter) {
+            super(index, type, filter);
+        }
+
+        @Override
+        public List<Resource> perform(ReadGraph graph)
+                throws DatabaseException {
+            Resource type = parameter2;
+            String filter = constructLuceneQuery(graph, type, parameter3);
+            if (filter == null)
+                return Collections.emptyList();
+
+            @SuppressWarnings({ "unchecked", "rawtypes" })
+            Function dependencyResources = graph.syncRequest(new Adapter(Layer0X.getInstance(graph).DependencyResources, Function.class), TransientCacheListener.<Function>instance());
+
+            if (TRACE_QUERIES) {
+                System.out.println("EntityInstances.QueryIndex: finding " + filter + " from index " + graph.getPossibleURI(parameter));
+                //new Exception("EntityInstances: finding " + filter + " from index " + graph.getPossibleURI(parameter)).printStackTrace();
+            }
+
+            @SuppressWarnings("unchecked")
+            List<Resource> results = (List<Resource>)dependencyResources.apply(graph, parameter, filter);
+            if (results == null || results.isEmpty())
+                return Collections.emptyList();
+
+            if (TRACE_QUERIES)
+                System.out.println("  EntityInstances.QueryIndex: got " + results.size() + " results");
+
+            // Optimize single result case.
+            if (results.size() == 1) {
+                Resource singleResult = results.get(0);
+                List<Resource> result = graph.isInstanceOf(singleResult, type) ? results : Collections.emptyList();
+                if (TRACE_QUERIES)
+                    System.out.println("  EntityInstances.QueryIndex: got " + results.size() + " unique type-matching results");
+                return result;
+            }
+
+            CollectionSupport coll = graph.getService(CollectionSupport.class);
+            List<Resource> result = coll.createList(results.size());
+            for (Resource res : Layer0Utils.sortByCluster(graph, results))
+                if (graph.isInstanceOf(res, type))
+                    result.add(res);
+
+            if (TRACE_QUERIES)
+                System.out.println("  EntityInstances.QueryIndex: got " + results.size() + " unique type-matching results");
+
+            return result;
+        }
+
+        private String constructLuceneQuery(ReadGraph graph, Resource type, String filter) throws DatabaseException {
+            Layer0 L0 = Layer0.getInstance(graph);
+            StringBuilder sb = new StringBuilder();
+            boolean emptyFilter = filter.isEmpty();
+            if (emptyFilter || !type.equals(L0.Entity)) {
+                String typeName = graph.getPossibleRelatedValue(type, L0.HasName, Bindings.STRING);
+                if (typeName == null || typeName.isEmpty())
+                    return null;
+                sb.append("Types:").append( IndexQueries.escape( typeName, true ) );
+            }
+            if (!emptyFilter) {
+                if (sb.length() > 0)
+                    sb.append(" AND ");
+                sb.append(filter);
+            }
+            return sb.toString();
+        }
+
+        @Override
+        public String toString() {
+               return "QueryIndex " + parameter + " " + parameter2 + " " + parameter3;
+        }
+
+        @Override
+        public boolean isImmutable(ReadGraph graph) throws DatabaseException {
+            return graph.isImmutable(parameter);
+        }
+
+               @Override
+               public int getType() {
+                       return RequestFlags.IMMEDIATE_UPDATE;
+               }
+
+    }
+
+    private List<Resource> findRec(ReadGraph graph, Resource index, String filter, Set<Resource> visited) throws DatabaseException {
+
+       if(!visited.add(index)) return Collections.emptyList();
+
+       CollectionSupport coll = graph.getService(CollectionSupport.class);
+
+       List<Resource> indexResult = graph.syncRequest(new QueryIndex(index, type, filter), TransientCacheListener.<List<Resource>>instance());
+
+        Layer0 L0 = Layer0.getInstance(graph);
+        Collection<Resource> linkedRoots = graph.syncRequest(new ObjectsWithType(index, L0.IsLinkedTo, L0.IndexRoot));
+        if (linkedRoots.isEmpty())
+            return indexResult;
+
+        List<Resource> result = indexResult;
+        for (Resource dep : linkedRoots) {
+            Collection<Resource> linkedIndexResults = findRec(graph, dep, filter, visited);
+            if (linkedIndexResults.isEmpty())
+                continue;
+            if (result == indexResult) {
+               result = coll.createList();
+                result.addAll(indexResult);
+            } else {
+            }
+            result.addAll(linkedIndexResults);
+        }
+        
+        return result;
+        
+    }
+
+    @Override
+    public Collection<Resource> find(ReadGraph graph, Resource index, String filter) throws DatabaseException {
+       CollectionSupport coll = graph.getService(CollectionSupport.class);
+       
+       THashSet<Resource> visited = new THashSet<>();
+       List<Resource> rec = findRec(graph, index, filter, visited);
+       for(Resource global : Layer0Utils.listGlobalOntologies(graph)) {
+               if(!visited.add(global)) continue;
+               List<Resource> rs = graph.syncRequest(new QueryIndex(global, type, filter), TransientCacheListener.<List<Resource>>instance());
+               if(rec.isEmpty() && !rs.isEmpty()) {
+                       // TODO: rec could be an immutable empty list
+                       rec = new ArrayList<Resource>();
+               }
+               rec.addAll(rs);
+       }
+       Collection<Resource> result = coll.asSortedList(rec);
+       return result; 
+    }
+    
+    @Override
+    public Collection<Resource> findByName(ReadGraph graph, Resource model,
+            String name) throws DatabaseException {
+        Layer0 L0 = Layer0.getInstance(graph);
+       CollectionSupport coll = graph.getService(CollectionSupport.class);
+        List<Resource> results = coll.createList();
+        for(Resource match : find(graph, model, name)) {
+            if(name.equals(graph.getPossibleRelatedValue(match, L0.HasName, Bindings.STRING))) results.add(match);
+        }
+        return results;
+    }
+
+}