searches.js (6042B)
1 /* 2 ***** BEGIN LICENSE BLOCK ***** 3 4 Copyright © 2006-2016 Center for History and New Media 5 George Mason University, Fairfax, Virginia, USA 6 https://zotero.org 7 8 This file is part of Zotero. 9 10 Zotero is free software: you can redistribute it and/or modify 11 it under the terms of the GNU Affero General Public License as published by 12 the Free Software Foundation, either version 3 of the License, or 13 (at your option) any later version. 14 15 Zotero is distributed in the hope that it will be useful, 16 but WITHOUT ANY WARRANTY; without even the implied warranty of 17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 GNU Affero General Public License for more details. 19 20 You should have received a copy of the GNU Affero General Public License 21 along with Zotero. If not, see <http://www.gnu.org/licenses/>. 22 23 ***** END LICENSE BLOCK ***** 24 */ 25 26 Zotero.Searches = function() { 27 this.constructor = null; 28 29 this._ZDO_object = 'search'; 30 this._ZDO_id = 'savedSearchID'; 31 this._ZDO_table = 'savedSearches'; 32 33 this._primaryDataSQLParts = { 34 savedSearchID: "O.savedSearchID", 35 name: "O.savedSearchName AS name", 36 libraryID: "O.libraryID", 37 key: "O.key", 38 version: "O.version", 39 synced: "O.synced" 40 } 41 42 this._primaryDataSQLFrom = "FROM savedSearches O"; 43 44 this.init = Zotero.Promise.coroutine(function* () { 45 yield Zotero.DataObjects.prototype.init.apply(this); 46 yield Zotero.SearchConditions.init(); 47 }); 48 49 50 this.getByLibrary = function (libraryID) { 51 var searches = []; 52 for (let id in this._objectCache) { 53 let s = this._objectCache[id]; 54 if (s.libraryID == libraryID) { 55 searches.push(s); 56 } 57 } 58 59 // Do proper collation sort 60 var collation = Zotero.getLocaleCollation(); 61 searches.sort(function (a, b) { 62 return collation.compareString(1, a.name, b.name); 63 }); 64 return searches; 65 }; 66 67 68 /** 69 * Returns an array of Zotero.Search objects, ordered by name 70 * 71 * @param {Integer} [libraryID] 72 */ 73 this.getAll = Zotero.Promise.coroutine(function* (libraryID) { 74 var sql = "SELECT savedSearchID FROM savedSearches WHERE libraryID=?"; 75 var ids = yield Zotero.DB.columnQueryAsync(sql, libraryID); 76 if (!ids.length) { 77 return [] 78 } 79 80 var searches = this.get(ids); 81 // Do proper collation sort 82 var collation = Zotero.getLocaleCollation(); 83 searches.sort(function (a, b) { 84 return collation.compareString(1, a.name, b.name); 85 }); 86 return searches; 87 }); 88 89 90 this.getPrimaryDataSQL = function () { 91 // This should be the same as the query in Zotero.Search.loadPrimaryData(), 92 // just without a specific savedSearchID 93 return "SELECT " 94 + Object.keys(this._primaryDataSQLParts).map(key => this._primaryDataSQLParts[key]).join(", ") + " " 95 + "FROM savedSearches O WHERE 1"; 96 } 97 98 99 this.conditionEquals = function (data1, data2) { 100 return data1.condition === data2.condition 101 && data1.operator === data2.operator 102 && data1.value === data2.value; 103 }, 104 105 106 this._loadConditions = Zotero.Promise.coroutine(function* (libraryID, ids, idSQL) { 107 var sql = "SELECT savedSearchID, searchConditionID, condition, operator, value, required " 108 + "FROM savedSearches LEFT JOIN savedSearchConditions USING (savedSearchID) " 109 + "WHERE libraryID=?" + idSQL 110 + "ORDER BY savedSearchID, searchConditionID"; 111 var params = [libraryID]; 112 var lastID = null; 113 var rows = []; 114 var setRows = function (searchID, rows) { 115 var search = this._objectCache[searchID]; 116 if (!search) { 117 throw new Error("Search " + searchID + " not found"); 118 } 119 120 search._conditions = {}; 121 122 if (rows.length) { 123 search._maxSearchConditionID = rows[rows.length - 1].searchConditionID; 124 } 125 126 // Reindex conditions, in case they're not contiguous in the DB 127 for (let i = 0; i < rows.length; i++) { 128 let condition = rows[i]; 129 130 // Parse "condition[/mode]" 131 let [conditionName, mode] = Zotero.SearchConditions.parseCondition(condition.condition); 132 133 // Not sure how this can happen, but prevent an error if it does 134 if (condition.value === null) { 135 condition.value = ''; 136 } 137 138 let cond = Zotero.SearchConditions.get(conditionName); 139 if (!cond || cond.noLoad) { 140 Zotero.debug("Invalid saved search condition '" + conditionName + "' -- skipping", 2); 141 continue; 142 } 143 144 // Convert itemTypeID to itemType 145 // 146 // TEMP: This can be removed at some point 147 if (conditionName == 'itemTypeID') { 148 conditionName = 'itemType'; 149 condition.value = Zotero.ItemTypes.getName(condition.value); 150 } 151 // Parse old-style collection/savedSearch conditions ('0_ABCD2345' -> 'ABCD2345') 152 else if (conditionName == 'collection' || conditionName == 'savedSearch') { 153 if (condition.value.includes('_')) { 154 let [_, objKey] = condition.value.split('_'); 155 condition.value = objKey; 156 } 157 } 158 159 search._conditions[i] = { 160 id: i, 161 condition: conditionName, 162 mode: mode, 163 operator: condition.operator, 164 value: condition.value, 165 required: !!condition.required 166 }; 167 } 168 search._loaded.conditions = true; 169 search._clearChanged('conditions'); 170 }.bind(this); 171 172 yield Zotero.DB.queryAsync( 173 sql, 174 params, 175 { 176 noCache: ids.length != 1, 177 onRow: function (row) { 178 let searchID = row.getResultByIndex(0); 179 180 if (lastID && searchID != lastID) { 181 setRows(lastID, rows); 182 rows = []; 183 } 184 185 lastID = searchID; 186 let searchConditionID = row.getResultByIndex(1); 187 // No conditions 188 if (searchConditionID === null) { 189 return; 190 } 191 rows.push({ 192 searchConditionID, 193 condition: row.getResultByIndex(2), 194 operator: row.getResultByIndex(3), 195 value: row.getResultByIndex(4), 196 required: row.getResultByIndex(5) 197 }); 198 }.bind(this) 199 } 200 ); 201 if (lastID) { 202 setRows(lastID, rows); 203 } 204 }); 205 206 Zotero.DataObjects.call(this); 207 208 return this; 209 }.bind(Object.create(Zotero.DataObjects.prototype))();