return home.getGroupbySubnode (kstr, rel);
return home.getGroupbySubnode (kstr);
    private Node getNodeByRelation (DbWrapper db, DbTxn txn, Node home, String kstr, Relation rel) throws Exception {	Node node = null;	if (rel != null && rel.virtual && home.getState() != INode.VIRTUAL) {	    Key k = home.getKey ().getVirtualKey (kstr);	    node = (Node) cache.get (k);	    if (node != null && node.getState() != INode.INVALID) {	        if (rel.prototype != null && !rel.prototype.equals (node.getString ("prototype", false)))	            node.setString ("prototype", rel.prototype);	        return node;	    }	    // if subnodes are stored in embedded db we have to actually assign it the virtual node,	    // otherwise it and its subnodes will be lost across restarts.	    if (rel.other == null || (!rel.other.isRelational() && !home.getDbMapping().isRelational())) {	        node = (Node) home.createNode (rel.propname);	        if (rel.prototype != null)	            node.setString ("prototype", rel.prototype);	    } else {	        node = new Node (home, kstr, safe, rel.prototype);	    }	    if (rel.prototype != null) {	        node.setDbMapping (app.getDbMapping (rel.prototype));	    } else {	        // make a db mapping good enough that the virtual node finds its subnodes	        DbMapping dbm = new DbMapping ();	        dbm.setSubnodeMapping (rel.other);	        dbm.setSubnodeRelation (rel.getVirtualSubnodeRelation());	        dbm.setPropertyMapping (rel.other);	        dbm.setPropertyRelation (rel.getVirtualPropertyRelation());	        node.setDbMapping (dbm);	    }	} else if (rel != null && rel.groupby != null) {	    return home.getGroupbySubnode (kstr, rel);	} else if (rel == null || rel.other == null || !rel.other.isRelational ()) {	    node = db.getNode (txn, kstr);	    node.nmgr = safe;	    node.setDbMapping (rel.other);	    return node;	} else {	    TableDataSet tds = null;	    try {	        DbMapping dbm = rel.other;	        tds = new TableDataSet (dbm.getConnection (), dbm.getSchema (), dbm.getKeyDef ());	        StringBuffer where = new StringBuffer ();	        where.append (rel.getRemoteField ());	        where.append (" = '");	        where.append (escape(kstr));	        where.append ("'");	        // Additionally filter properties through subnode relation?	        if (rel.subnodesAreProperties) {	            String homeid = home.getState() == Node.VIRTUAL ? home.parentID : home.getID ();	            // first check for dynamic subrel from node	            String nodesubrel = home.getSubnodeRelation();	            if (nodesubrel != null && nodesubrel.trim().length() > 5) {	                where.append (" and ");	                where.append (nodesubrel.trim().substring(5).trim());	            } else {	                Relation subrel = home.getDbMapping().getSubnodeRelation ();	                if (subrel != null) {	                    if (subrel.getFilter () != null)	                        subrel = subrel.getFilter ();	                    if (subrel != null && subrel.direction == Relation.BACKWARD) {	                        where.append (" and ");	                        where.append (subrel.remoteField);	                        where.append (" = '");	                        where.append (homeid);	                        where.append ("'");	                    }	                }	            }	        }		        tds.where (where.toString ());	        if (logSql)	            IServer.getLogger().log ("### getNodeByRelation: "+tds.getSelectString());	        tds.fetchRecords ();	        if (tds.size () == 0)	            return null;	        if (tds.size () > 1)	            throw new RuntimeException ("More than one value returned by query.");	        Record rec = tds.getRecord (0);	        node = new Node (rel.other, rec, safe);	        // Check if node is already cached with primary Key.	        if (!rel.usesPrimaryKey()) {	            Key pk = node.getKey();	            Node existing = (Node) cache.get (pk);	            if (existing != null && existing.getState() != Node.INVALID) {	                node = existing;	            }	        }	    } finally {	        if (tds != null) {	            tds.close ();	        }	    }	}	return node;    }