001/* 002 * (C) Copyright 2008-2010 Nuxeo SA (http://nuxeo.com/) and others. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 * 016 * Contributors: 017 * Florent Guillaume 018 */ 019 020package org.nuxeo.ecm.platform.tag; 021 022import java.util.List; 023 024import org.nuxeo.ecm.core.query.QueryFilter; 025import org.nuxeo.ecm.core.query.QueryParseException; 026import org.nuxeo.ecm.core.storage.sql.ColumnType; 027import org.nuxeo.ecm.core.storage.sql.Model; 028import org.nuxeo.ecm.core.storage.sql.Session.PathResolver; 029import org.nuxeo.ecm.core.storage.sql.jdbc.NXQLQueryMaker; 030import org.nuxeo.ecm.core.storage.sql.jdbc.SQLInfo; 031import org.nuxeo.ecm.core.storage.sql.jdbc.db.Column; 032import org.nuxeo.ecm.core.storage.sql.jdbc.db.Join; 033import org.nuxeo.ecm.core.storage.sql.jdbc.db.Select; 034import org.nuxeo.ecm.core.storage.sql.jdbc.db.Table; 035 036/** 037 * Query Maker specialized for tagging queries that need joins. 038 */ 039public class TagQueryMaker extends NXQLQueryMaker { 040 041 /** The NXTAG query type. */ 042 public static final String NXTAG = "NXTAG"; 043 044 public static final String SCHEMA_TAG = "tag"; 045 046 public static final String SCHEMA_RELATION = "relation"; 047 048 public static final String PROPERTY_SOURCE = "source"; 049 050 public static final String PROPERTY_TARGET = "target"; 051 052 /** 053 * Makes sure the Tag table is joined with the relation target instead of the hierarchy id. 054 */ 055 public static final String TAG_IS_TARGET = "TAGISTARGET: "; 056 057 /** 058 * Adds a COUNT() on the relation source, to count documents. 059 */ 060 public static final String COUNT_SOURCE = "COUNTSOURCE: "; 061 062 protected String type; 063 064 protected Table relationTable; 065 066 protected Column firstSelectedColumn; 067 068 @Override 069 public String getName() { 070 return NXTAG; 071 } 072 073 @Override 074 public boolean accepts(String queryType) { 075 return queryType.equals(NXTAG); 076 } 077 078 @Override 079 public Query buildQuery(SQLInfo sqlInfo, Model model, PathResolver pathResolver, String query, 080 QueryFilter queryFilter, Object... params) { 081 if (query.startsWith(TAG_IS_TARGET)) { 082 type = TAG_IS_TARGET; 083 } else if (query.startsWith(COUNT_SOURCE)) { 084 type = COUNT_SOURCE; 085 // SELECT "TAG"."LABEL" AS "_C1", 086 // COUNT(DISTINCT "RELATION"."SOURCE") AS "_C2" 087 // FROM "HIERARCHY" 088 // JOIN "RELATION" ON "HIERARCHY"."ID" = "RELATION"."ID" 089 // JOIN "DUBLINCORE" ON "HIERARCHY"."ID" = "DUBLINCORE"."ID" 090 // JOIN "TAG" ON "RELATION"."TARGET" = "TAG"."ID" 091 // WHERE "HIERARCHY"."PRIMARYTYPE" IN ('Tagging') 092 // AND ("RELATION"."SOURCE" = '47c4c0f7...') -- or IN () 093 // AND ("DUBLINCORE"."CREATOR" = 'Administrator') 094 // GROUP BY "_C1" 095 } else { 096 throw new QueryParseException("Bad query: " + query); 097 } 098 query = query.substring(type.length()); 099 return super.buildQuery(sqlInfo, model, pathResolver, query, queryFilter, params); 100 } 101 102 /** 103 * Adds an initial join on the Relation table, and records it. 104 */ 105 @Override 106 protected void fixInitialJoins() { 107 relationTable = getFragmentTable(dataHierTable, SCHEMA_RELATION, SCHEMA_RELATION, -1, false); 108 } 109 110 /** 111 * Patches the Tag join to join on the relation target instead of the hierarchy id. 112 */ 113 @Override 114 protected void addJoin(int kind, String alias, Table table, String column, Table contextTable, 115 String contextColumn, String name, int index, String primaryType) { 116 if (table.getKey().equals(SCHEMA_TAG)) { 117 kind = Join.INNER; 118 contextTable = relationTable; 119 contextColumn = PROPERTY_TARGET; 120 } 121 super.addJoin(kind, alias, table, column, contextTable, contextColumn, name, index, null); 122 } 123 124 @Override 125 protected String getSelectColName(Column col) { 126 String name = super.getSelectColName(col); 127 if (firstSelectedColumn == null) { 128 firstSelectedColumn = col; 129 } 130 if (type == COUNT_SOURCE && col.getTable().getKey().equals(SCHEMA_RELATION) 131 && col.getKey().equals(PROPERTY_SOURCE)) { 132 name = String.format("COUNT(DISTINCT %s)", name); 133 } 134 return name; 135 } 136 137 @Override 138 protected void fixWhatColumns(List<Column> whatColumns) { 139 if (type == COUNT_SOURCE) { 140 // 2nd col is a COUNT -> different type 141 Column targetCol = whatColumns.remove(1); 142 Column countCol = new Column(targetCol.getTable(), null, ColumnType.INTEGER, null); 143 whatColumns.add(countCol); 144 } 145 } 146 147 @Override 148 protected void fixSelect(Select select) { 149 if (type == COUNT_SOURCE) { 150 // add a GROUP BY on first col 151 String name; 152 if (dialect.needsOriginalColumnInGroupBy()) { 153 name = firstSelectedColumn.getFullQuotedName(); 154 } else { 155 name = dialect.openQuote() + COL_ALIAS_PREFIX + "1" + dialect.closeQuote(); 156 } 157 select.setGroupBy(name); 158 } 159 } 160 161}