Closes #31, tag data infrastructure
New methods: Item.addTag(tag) Item.getTags() -- array of tagIDs Item.removeTag(tagID) Tags.getName(tagID) Tags.getID(tag) Tags.add(text) -- returns tagID of new tag Tags.purge() -- purge obsolete tags The last two are for use by Item.addTag() and Item.removeTag(), respectively, and probably don't need to be used elsewhere.
This commit is contained in:
parent
b75376c1c1
commit
52f7ed62d0
3 changed files with 173 additions and 31 deletions
|
@ -937,6 +937,48 @@ Scholar.Item.prototype.getNotes = function(){
|
|||
}
|
||||
|
||||
|
||||
//
|
||||
// Methods dealing with item tags
|
||||
//
|
||||
// save() is not required for tag functions
|
||||
//
|
||||
Scholar.Item.prototype.addTag = function(tag){
|
||||
if (!this.getID()){
|
||||
this.save();
|
||||
}
|
||||
|
||||
Scholar.DB.beginTransaction();
|
||||
var tagID = Scholar.Tags.getID(tag);
|
||||
if (!tagID){
|
||||
var tagID = Scholar.Tags.add(tag);
|
||||
}
|
||||
|
||||
var sql = "INSERT OR IGNORE INTO itemTags VALUES (?,?)";
|
||||
Scholar.DB.query(sql, [this.getID(), tagID]);
|
||||
|
||||
Scholar.DB.commitTransaction();
|
||||
Scholar.Notifier.trigger('modify', 'item', this.getID());
|
||||
}
|
||||
|
||||
Scholar.Item.prototype.getTags = function(){
|
||||
var sql = "SELECT tagID FROM itemTags WHERE itemID=" + this.getID();
|
||||
return Scholar.DB.columnQuery(sql);
|
||||
}
|
||||
|
||||
Scholar.Item.prototype.removeTag = function(tagID){
|
||||
if (!this.getID()){
|
||||
throw ('Cannot remove tag on unsaved item');
|
||||
}
|
||||
|
||||
Scholar.DB.beginTransaction();
|
||||
var sql = "DELETE FROM itemTags WHERE itemID=? AND tagID=?";
|
||||
Scholar.DB.query(sql, [this.getID(), tagID]);
|
||||
Scholar.Tags.purge();
|
||||
Scholar.DB.commitTransaction();
|
||||
Scholar.Notifier.trigger('modify', 'item', this.getID());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Delete item from database and clear from Scholar.Items internal array
|
||||
**/
|
||||
|
@ -1074,8 +1116,8 @@ Scholar.Item.prototype.toArray = function(){
|
|||
var note = Scholar.Items.get(notes[i]);
|
||||
arr['notes'].push({
|
||||
note: note.getNote(),
|
||||
tags: note.getTags(),
|
||||
// TODO
|
||||
tags: [],
|
||||
seeAlso: []
|
||||
});
|
||||
}
|
||||
|
@ -1090,8 +1132,8 @@ Scholar.Item.prototype.toArray = function(){
|
|||
}
|
||||
}
|
||||
|
||||
arr['tags'] = this.getTags();
|
||||
// TODO
|
||||
arr['tags'] = [];
|
||||
arr['seeAlso'] = [];
|
||||
|
||||
return arr;
|
||||
|
@ -1915,7 +1957,9 @@ Scholar.Collections = new function(){
|
|||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Same structure as Scholar.Tags -- make changes in both places if possible
|
||||
*/
|
||||
Scholar.Creators = new function(){
|
||||
var _creators = new Array; // indexed by first%%%last hash
|
||||
var _creatorsByID = new Array; // indexed by creatorID
|
||||
|
@ -1959,10 +2003,8 @@ Scholar.Creators = new function(){
|
|||
|
||||
var sql = 'SELECT creatorID FROM creators '
|
||||
+ 'WHERE firstName=? AND lastName=?';
|
||||
var params = [
|
||||
{'string': firstName}, {'string': lastName}
|
||||
];
|
||||
var creatorID = Scholar.DB.valueQuery(sql,params);
|
||||
var params = [{string: firstName}, {string: lastName}];
|
||||
var creatorID = Scholar.DB.valueQuery(sql, params);
|
||||
|
||||
if (creatorID){
|
||||
_creators[hash] = creatorID;
|
||||
|
@ -1982,15 +2024,9 @@ Scholar.Creators = new function(){
|
|||
|
||||
Scholar.DB.beginTransaction();
|
||||
|
||||
var sql = 'INSERT INTO creators '
|
||||
+ 'VALUES (?,?,?)';
|
||||
|
||||
var sql = 'INSERT INTO creators VALUES (?,?,?)';
|
||||
var rnd = Scholar.getRandomID('creators', 'creatorID');
|
||||
|
||||
var params = [
|
||||
{'int': rnd}, {'string': firstName}, {'string': lastName}
|
||||
];
|
||||
|
||||
var params = [{int: rnd}, {string: firstName}, {string: lastName}];
|
||||
Scholar.DB.query(sql, params);
|
||||
|
||||
Scholar.DB.commitTransaction();
|
||||
|
@ -2037,8 +2073,110 @@ Scholar.Creators = new function(){
|
|||
}
|
||||
|
||||
|
||||
/*
|
||||
* Same structure as Scholar.Creators -- make changes in both places if possible
|
||||
*/
|
||||
Scholar.Tags = new function(){
|
||||
var _tags = new Array; // indexed by tag text
|
||||
var _tagsByID = new Array; // indexed by tagID
|
||||
|
||||
this.getName = getName;
|
||||
this.getID = getID;
|
||||
this.add = add;
|
||||
this.purge = purge;
|
||||
|
||||
/*
|
||||
* Returns a tag for a given tagID
|
||||
*/
|
||||
function getName(tagID){
|
||||
if (_tagsByID[tagID]){
|
||||
return _tagsByID[tagID];
|
||||
}
|
||||
|
||||
var sql = 'SELECT tag FROM tags WHERE tagID=' + tagID;
|
||||
var result = Scholar.DB.valueQuery(sql);
|
||||
|
||||
if (!result){
|
||||
return false;
|
||||
}
|
||||
|
||||
_tagsByID[tagID] = result;
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Returns the tagID matching given tag
|
||||
*/
|
||||
function getID(tag){
|
||||
if (_tags[tag]){
|
||||
return _tags[tag];
|
||||
}
|
||||
|
||||
var sql = 'SELECT tagID FROM tags WHERE tag=?';
|
||||
var tagID = Scholar.DB.valueQuery(sql, [{string:tag}]);
|
||||
|
||||
if (tagID){
|
||||
_tags[tag] = tagID;
|
||||
}
|
||||
|
||||
return tagID;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Add a new tag to the database
|
||||
*
|
||||
* Returns new tagID
|
||||
*/
|
||||
function add(tag){
|
||||
Scholar.debug('Adding new tag', 4);
|
||||
|
||||
Scholar.DB.beginTransaction();
|
||||
|
||||
var sql = 'INSERT INTO tags VALUES (?,?)';
|
||||
var rnd = Scholar.getRandomID('tags', 'tagID');
|
||||
Scholar.DB.query(sql, [{int: rnd}, {string: tag}]);
|
||||
|
||||
Scholar.DB.commitTransaction();
|
||||
return rnd;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Delete obsolete tags from database and clear internal array entries
|
||||
*
|
||||
* Returns removed tagIDs on success
|
||||
*/
|
||||
function purge(){
|
||||
var sql = 'SELECT tagID FROM tags WHERE tagID NOT IN '
|
||||
+ '(SELECT tagID FROM itemTags);';
|
||||
var toDelete = Scholar.DB.columnQuery(sql);
|
||||
|
||||
if (!toDelete){
|
||||
return false;
|
||||
}
|
||||
|
||||
// Clear tag entries in internal array
|
||||
for (var i=0; i<toDelete.length; i++){
|
||||
var tag = this.getName(toDelete[i]);
|
||||
delete _tags[tag];
|
||||
delete _tagsByID[toDelete[i]];
|
||||
}
|
||||
|
||||
sql = 'DELETE FROM tags WHERE tagID NOT IN '
|
||||
+ '(SELECT tagID FROM itemTags);';
|
||||
var result = Scholar.DB.query(sql);
|
||||
|
||||
return toDelete;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Same structure as Scholar.ItemTypes -- make changes in both places if possible
|
||||
*/
|
||||
Scholar.CreatorTypes = new function(){
|
||||
var _types = new Array();
|
||||
var _typesLoaded;
|
||||
|
@ -2101,6 +2239,9 @@ Scholar.CreatorTypes = new function(){
|
|||
|
||||
|
||||
|
||||
/*
|
||||
* Same structure as Scholar.CreatorTypes -- make changes in both places if possible
|
||||
*/
|
||||
Scholar.ItemTypes = new function(){
|
||||
var _types = new Array();
|
||||
var _typesLoaded;
|
||||
|
|
|
@ -385,7 +385,7 @@ Scholar.Schema = new function(){
|
|||
//
|
||||
// Change this value to match the schema version
|
||||
//
|
||||
var toVersion = 25;
|
||||
var toVersion = 26;
|
||||
|
||||
if (toVersion != _getSchemaSQLVersion()){
|
||||
throw('Schema version does not match version in _migrateSchema()');
|
||||
|
@ -400,7 +400,9 @@ Scholar.Schema = new function(){
|
|||
// Each block performs the changes necessary to move from the
|
||||
// previous revision to that one.
|
||||
for (var i=parseInt(fromVersion) + 1; i<=toVersion; i++){
|
||||
if (i==25){
|
||||
if (i==26){
|
||||
Scholar.DB.query("DROP TABLE IF EXISTS keywords");
|
||||
Scholar.DB.query("DROP TABLE IF EXISTS itemKeywords");
|
||||
_initializeSchema();
|
||||
}
|
||||
}
|
||||
|
|
27
schema.sql
27
schema.sql
|
@ -1,4 +1,4 @@
|
|||
-- 25
|
||||
-- 26
|
||||
|
||||
DROP TABLE IF EXISTS version;
|
||||
CREATE TABLE version (
|
||||
|
@ -71,24 +71,23 @@
|
|||
DROP INDEX IF EXISTS itemNotes_sourceItemID;
|
||||
CREATE INDEX itemNotes_sourceItemID ON itemNotes(sourceItemID);
|
||||
|
||||
DROP TABLE IF EXISTS keywords;
|
||||
CREATE TABLE keywords (
|
||||
keywordID INTEGER PRIMARY KEY,
|
||||
keyword TEXT
|
||||
DROP TABLE IF EXISTS tags;
|
||||
CREATE TABLE tags (
|
||||
tagID INT,
|
||||
tag TEXT UNIQUE,
|
||||
PRIMARY KEY (tagID)
|
||||
);
|
||||
DROP INDEX IF EXISTS keyword;
|
||||
CREATE INDEX keyword ON keywords(keyword);
|
||||
|
||||
DROP TABLE IF EXISTS itemKeywords;
|
||||
CREATE TABLE itemKeywords (
|
||||
DROP TABLE IF EXISTS itemTags;
|
||||
CREATE TABLE itemTags (
|
||||
itemID INT,
|
||||
keywordID INT,
|
||||
PRIMARY KEY (itemID, keywordID),
|
||||
tagID INT,
|
||||
PRIMARY KEY (itemID, tagID),
|
||||
FOREIGN KEY (itemID) REFERENCES items(itemID),
|
||||
FOREIGN KEY (keywordID) REFERENCES keywords(keywordID)
|
||||
FOREIGN KEY (tagID) REFERENCES tags(tagID)
|
||||
);
|
||||
DROP INDEX IF EXISTS keywordID;
|
||||
CREATE INDEX keywordID ON itemKeywords(keywordID);
|
||||
DROP INDEX IF EXISTS itemTags_tagID;
|
||||
CREATE INDEX itemTags_tagID ON itemTags(tagID);
|
||||
|
||||
DROP TABLE IF EXISTS creators;
|
||||
CREATE TABLE creators (
|
||||
|
|
Loading…
Reference in a new issue