Addresses #171, Add more conditions to advanced search architecture
- Implemented 'collectionID' and 'savedSearchID' conditions (a.k.a. search within a search) and removed special 'context' condition. Per my conversation with Dan, the 'recursive' flag is now a global flag that applies to all specified collectionIDs, which is less than ideal but probably better than the alternatives (e.g. having condition-specific recursive checkboxes). It does mean, however, that a "Search subfolders" checkbox is irrelevant if there are no collectionID conditions and should probably be greyed out until applicable. Another side effect is that it's no longer possible to do an ANY search and return results only within a specific folder (though it can now be done by putting the ANY conditions in a subsearch). Since ANY searches are always annoying in this regard, what I might do is add a way to mark particular conditions as required even in ANY mode, which would allow for quite a lot of flexibility... Note also that while 'collectionID' and 'savedSearchID' are standard conditions, they should probably be combined into a single condition on the interface side (like playlists and smart playlists under just 'Playlist' in iTunes). - Now skips invalid/obsolete saved conditions in load()
This commit is contained in:
parent
4fe960d190
commit
3a410c5acd
1 changed files with 130 additions and 76 deletions
|
@ -47,6 +47,12 @@ Scholar.Search.prototype.load = function(savedSearchID){
|
|||
+ "WHERE savedSearchID=" + savedSearchID + " ORDER BY searchConditionID");
|
||||
|
||||
for (var i in conditions){
|
||||
if (!Scholar.SearchConditions.get(conditions[i]['condition'])){
|
||||
Scholar.debug("Invalid saved search condition '"
|
||||
+ conditions[i]['condition'] + "' -- skipping", 2);
|
||||
continue;
|
||||
}
|
||||
|
||||
this._conditions[conditions[i]['searchConditionID']] = {
|
||||
id: conditions[i]['searchConditionID'],
|
||||
condition: conditions[i]['condition'],
|
||||
|
@ -193,11 +199,18 @@ Scholar.Search.prototype.getSQL = function(){
|
|||
if (!this._sql){
|
||||
this._buildQuery();
|
||||
}
|
||||
|
||||
return this._sql;
|
||||
}
|
||||
|
||||
|
||||
Scholar.Search.prototype.getSQLParams = function(){
|
||||
if (!this._sql){
|
||||
this._buildQuery();
|
||||
}
|
||||
return this._sqlParams;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Build the SQL query for the search
|
||||
*/
|
||||
|
@ -231,11 +244,6 @@ Scholar.Search.prototype._buildQuery = function(){
|
|||
// Handle special conditions
|
||||
else {
|
||||
switch (data['name']){
|
||||
// Collection to search in
|
||||
case 'context':
|
||||
var parentCollectionID = this._conditions[i]['value'];
|
||||
continue;
|
||||
|
||||
// Search subfolders
|
||||
case 'recursive':
|
||||
var recursive = this._conditions[i]['operator']=='true';
|
||||
|
@ -263,25 +271,27 @@ Scholar.Search.prototype._buildQuery = function(){
|
|||
var binOp = ' AND ';
|
||||
}
|
||||
|
||||
for (i in tables){
|
||||
for (var i in tables){
|
||||
for (var j in tables[i]){
|
||||
var openParens = 0;
|
||||
var skipOperators = false;
|
||||
|
||||
//
|
||||
// Special table handling
|
||||
//
|
||||
switch (i){
|
||||
case 'items':
|
||||
case 'savedSearches':
|
||||
break;
|
||||
default:
|
||||
sql += 'itemID IN (SELECT itemID FROM ' + i + ' WHERE (';
|
||||
var openParens = 2;
|
||||
openParens = 2;
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Special field handling
|
||||
// Special condition handling
|
||||
//
|
||||
|
||||
// For itemData fields, include fieldID
|
||||
switch (tables[i][j]['name']){
|
||||
case 'field':
|
||||
sql += 'fieldID=? AND ';
|
||||
|
@ -289,10 +299,57 @@ Scholar.Search.prototype._buildQuery = function(){
|
|||
Scholar.ItemFields.getID(tables[i][j]['alias'])
|
||||
);
|
||||
break;
|
||||
|
||||
case 'collectionID':
|
||||
sql += "collectionID ";
|
||||
if (tables[i][j]['operator']=='isNot'){
|
||||
sql += "NOT ";
|
||||
}
|
||||
// Add given collection id
|
||||
sql += "IN (?,";
|
||||
sqlParams.push({int:tables[i][j]['value']});
|
||||
|
||||
// And descendents if recursive search
|
||||
if (recursive){
|
||||
var col = Scholar.Collections.get(tables[i][j]['value']);
|
||||
var descendents = col.getDescendents(false, 'collection');
|
||||
if (descendents){
|
||||
for (var k in descendents){
|
||||
sql += '?,';
|
||||
sqlParams.push(descendents[k]['id']);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Strip final comma
|
||||
sql = sql.substring(0, sql.length-1) + ")";
|
||||
|
||||
skipOperators = true;
|
||||
break;
|
||||
|
||||
case 'savedSearchID':
|
||||
sql += "itemID ";
|
||||
if (tables[i][j]['operator']=='isNot'){
|
||||
sql += "NOT ";
|
||||
}
|
||||
sql += "IN (";
|
||||
var search = new Scholar.Search();
|
||||
search.load(tables[i][j]['value']);
|
||||
sql += search.getSQL();
|
||||
var subpar = search.getSQLParams();
|
||||
for (var k in subpar){
|
||||
sqlParams.push(subpar[k]);
|
||||
}
|
||||
sql += ")";
|
||||
|
||||
skipOperators = true;
|
||||
break;
|
||||
|
||||
case 'tag':
|
||||
sql += "tagID IN (SELECT tagID FROM tags WHERE ";
|
||||
openParens++;
|
||||
break;
|
||||
|
||||
case 'creator':
|
||||
sql += "creatorID IN (SELECT creatorID FROM creators "
|
||||
+ "WHERE ";
|
||||
|
@ -300,49 +357,51 @@ Scholar.Search.prototype._buildQuery = function(){
|
|||
break;
|
||||
}
|
||||
|
||||
sql += tables[i][j]['field'];
|
||||
switch (tables[i][j]['operator']){
|
||||
case 'contains':
|
||||
sql += ' LIKE ?';
|
||||
sqlParams.push('%' + tables[i][j]['value'] + '%');
|
||||
break;
|
||||
if (!skipOperators){
|
||||
sql += tables[i][j]['field'];
|
||||
switch (tables[i][j]['operator']){
|
||||
case 'contains':
|
||||
sql += ' LIKE ?';
|
||||
sqlParams.push('%' + tables[i][j]['value'] + '%');
|
||||
break;
|
||||
|
||||
case 'doesNotContain':
|
||||
sql += ' NOT LIKE ?';
|
||||
sqlParams.push('%' + tables[i][j]['value'] + '%');
|
||||
break;
|
||||
case 'doesNotContain':
|
||||
sql += ' NOT LIKE ?';
|
||||
sqlParams.push('%' + tables[i][j]['value'] + '%');
|
||||
break;
|
||||
|
||||
case 'is':
|
||||
sql += '=?';
|
||||
sqlParams.push(tables[i][j]['value']);
|
||||
break;
|
||||
case 'is':
|
||||
sql += '=?';
|
||||
sqlParams.push(tables[i][j]['value']);
|
||||
break;
|
||||
|
||||
case 'isNot':
|
||||
sql += '!=?';
|
||||
sqlParams.push(tables[i][j]['value']);
|
||||
break;
|
||||
case 'isNot':
|
||||
sql += '!=?';
|
||||
sqlParams.push(tables[i][j]['value']);
|
||||
break;
|
||||
|
||||
case 'greaterThan':
|
||||
sql += '>?';
|
||||
sqlParams.push({int:tables[i][j]['value']});
|
||||
break;
|
||||
case 'greaterThan':
|
||||
sql += '>?';
|
||||
sqlParams.push({int:tables[i][j]['value']});
|
||||
break;
|
||||
|
||||
case 'lessThan':
|
||||
sql += '<?';
|
||||
sqlParams.push({int:tables[i][j]['value']});
|
||||
break;
|
||||
case 'lessThan':
|
||||
sql += '<?';
|
||||
sqlParams.push({int:tables[i][j]['value']});
|
||||
break;
|
||||
|
||||
case 'isBefore':
|
||||
// TODO
|
||||
break;
|
||||
case 'isBefore':
|
||||
// TODO
|
||||
break;
|
||||
|
||||
case 'isAfter':
|
||||
// TODO
|
||||
break;
|
||||
case 'isAfter':
|
||||
// TODO
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Close open parentheses
|
||||
for (k=openParens; k>0; k--){
|
||||
for (var k=openParens; k>0; k--){
|
||||
sql += ')';
|
||||
}
|
||||
|
||||
|
@ -353,28 +412,6 @@ Scholar.Search.prototype._buildQuery = function(){
|
|||
sql = sql.substring(0, sql.length-binOp.length);
|
||||
}
|
||||
|
||||
if (parentCollectionID){
|
||||
sql = "SELECT itemID FROM (" + sql + ") WHERE itemID IN "
|
||||
+ "(SELECT itemID FROM collectionItems WHERE collectionID IN ("
|
||||
|
||||
sql += "?,";
|
||||
sqlParams.push({int:parentCollectionID});
|
||||
|
||||
if (recursive){
|
||||
var col = Scholar.Collections.get(parentCollectionID);
|
||||
var descendents = col.getDescendents(false, 'collection');
|
||||
if (descendents){
|
||||
for (var i in descendents){
|
||||
sql += '?,';
|
||||
sqlParams.push(descendents[i]['id']);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Strip final comma
|
||||
sql = sql.substring(0, sql.length-1) + "))";
|
||||
}
|
||||
|
||||
this._sql = sql;
|
||||
this._sqlParams = sqlParams.length ? sqlParams : null;
|
||||
}
|
||||
|
@ -451,11 +488,6 @@ Scholar.SearchConditions = new function(){
|
|||
// Special conditions
|
||||
//
|
||||
|
||||
// Context (i.e. collection id to search within)
|
||||
{
|
||||
name: 'context'
|
||||
},
|
||||
|
||||
// Search recursively
|
||||
{
|
||||
name: 'recursive',
|
||||
|
@ -478,6 +510,28 @@ Scholar.SearchConditions = new function(){
|
|||
// Standard conditions
|
||||
//
|
||||
|
||||
// Collection id to search within
|
||||
{
|
||||
name: 'collectionID',
|
||||
operators: {
|
||||
is: true,
|
||||
isNot: true
|
||||
},
|
||||
table: 'collectionItems',
|
||||
field: 'collectionID'
|
||||
},
|
||||
|
||||
// Saved search to search within
|
||||
{
|
||||
name: 'savedSearchID',
|
||||
operators: {
|
||||
is: true,
|
||||
isNot: true
|
||||
},
|
||||
table: 'savedSearches',
|
||||
field: 'savedSearchID'
|
||||
},
|
||||
|
||||
{
|
||||
name: 'title',
|
||||
operators: {
|
||||
|
|
Loading…
Add table
Reference in a new issue