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
|
@ -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
|
* 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]);
|
var note = Scholar.Items.get(notes[i]);
|
||||||
arr['notes'].push({
|
arr['notes'].push({
|
||||||
note: note.getNote(),
|
note: note.getNote(),
|
||||||
|
tags: note.getTags(),
|
||||||
// TODO
|
// TODO
|
||||||
tags: [],
|
|
||||||
seeAlso: []
|
seeAlso: []
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -1090,8 +1132,8 @@ Scholar.Item.prototype.toArray = function(){
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
arr['tags'] = this.getTags();
|
||||||
// TODO
|
// TODO
|
||||||
arr['tags'] = [];
|
|
||||||
arr['seeAlso'] = [];
|
arr['seeAlso'] = [];
|
||||||
|
|
||||||
return arr;
|
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(){
|
Scholar.Creators = new function(){
|
||||||
var _creators = new Array; // indexed by first%%%last hash
|
var _creators = new Array; // indexed by first%%%last hash
|
||||||
var _creatorsByID = new Array; // indexed by creatorID
|
var _creatorsByID = new Array; // indexed by creatorID
|
||||||
|
@ -1959,9 +2003,7 @@ Scholar.Creators = new function(){
|
||||||
|
|
||||||
var sql = 'SELECT creatorID FROM creators '
|
var sql = 'SELECT creatorID FROM creators '
|
||||||
+ 'WHERE firstName=? AND lastName=?';
|
+ 'WHERE firstName=? AND lastName=?';
|
||||||
var params = [
|
var params = [{string: firstName}, {string: lastName}];
|
||||||
{'string': firstName}, {'string': lastName}
|
|
||||||
];
|
|
||||||
var creatorID = Scholar.DB.valueQuery(sql, params);
|
var creatorID = Scholar.DB.valueQuery(sql, params);
|
||||||
|
|
||||||
if (creatorID){
|
if (creatorID){
|
||||||
|
@ -1982,15 +2024,9 @@ Scholar.Creators = new function(){
|
||||||
|
|
||||||
Scholar.DB.beginTransaction();
|
Scholar.DB.beginTransaction();
|
||||||
|
|
||||||
var sql = 'INSERT INTO creators '
|
var sql = 'INSERT INTO creators VALUES (?,?,?)';
|
||||||
+ 'VALUES (?,?,?)';
|
|
||||||
|
|
||||||
var rnd = Scholar.getRandomID('creators', 'creatorID');
|
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.query(sql, params);
|
||||||
|
|
||||||
Scholar.DB.commitTransaction();
|
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(){
|
Scholar.CreatorTypes = new function(){
|
||||||
var _types = new Array();
|
var _types = new Array();
|
||||||
var _typesLoaded;
|
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(){
|
Scholar.ItemTypes = new function(){
|
||||||
var _types = new Array();
|
var _types = new Array();
|
||||||
var _typesLoaded;
|
var _typesLoaded;
|
||||||
|
|
|
@ -385,7 +385,7 @@ Scholar.Schema = new function(){
|
||||||
//
|
//
|
||||||
// Change this value to match the schema version
|
// Change this value to match the schema version
|
||||||
//
|
//
|
||||||
var toVersion = 25;
|
var toVersion = 26;
|
||||||
|
|
||||||
if (toVersion != _getSchemaSQLVersion()){
|
if (toVersion != _getSchemaSQLVersion()){
|
||||||
throw('Schema version does not match version in _migrateSchema()');
|
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
|
// Each block performs the changes necessary to move from the
|
||||||
// previous revision to that one.
|
// previous revision to that one.
|
||||||
for (var i=parseInt(fromVersion) + 1; i<=toVersion; i++){
|
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();
|
_initializeSchema();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
27
schema.sql
27
schema.sql
|
@ -1,4 +1,4 @@
|
||||||
-- 25
|
-- 26
|
||||||
|
|
||||||
DROP TABLE IF EXISTS version;
|
DROP TABLE IF EXISTS version;
|
||||||
CREATE TABLE version (
|
CREATE TABLE version (
|
||||||
|
@ -71,24 +71,23 @@
|
||||||
DROP INDEX IF EXISTS itemNotes_sourceItemID;
|
DROP INDEX IF EXISTS itemNotes_sourceItemID;
|
||||||
CREATE INDEX itemNotes_sourceItemID ON itemNotes(sourceItemID);
|
CREATE INDEX itemNotes_sourceItemID ON itemNotes(sourceItemID);
|
||||||
|
|
||||||
DROP TABLE IF EXISTS keywords;
|
DROP TABLE IF EXISTS tags;
|
||||||
CREATE TABLE keywords (
|
CREATE TABLE tags (
|
||||||
keywordID INTEGER PRIMARY KEY,
|
tagID INT,
|
||||||
keyword TEXT
|
tag TEXT UNIQUE,
|
||||||
|
PRIMARY KEY (tagID)
|
||||||
);
|
);
|
||||||
DROP INDEX IF EXISTS keyword;
|
|
||||||
CREATE INDEX keyword ON keywords(keyword);
|
|
||||||
|
|
||||||
DROP TABLE IF EXISTS itemKeywords;
|
DROP TABLE IF EXISTS itemTags;
|
||||||
CREATE TABLE itemKeywords (
|
CREATE TABLE itemTags (
|
||||||
itemID INT,
|
itemID INT,
|
||||||
keywordID INT,
|
tagID INT,
|
||||||
PRIMARY KEY (itemID, keywordID),
|
PRIMARY KEY (itemID, tagID),
|
||||||
FOREIGN KEY (itemID) REFERENCES items(itemID),
|
FOREIGN KEY (itemID) REFERENCES items(itemID),
|
||||||
FOREIGN KEY (keywordID) REFERENCES keywords(keywordID)
|
FOREIGN KEY (tagID) REFERENCES tags(tagID)
|
||||||
);
|
);
|
||||||
DROP INDEX IF EXISTS keywordID;
|
DROP INDEX IF EXISTS itemTags_tagID;
|
||||||
CREATE INDEX keywordID ON itemKeywords(keywordID);
|
CREATE INDEX itemTags_tagID ON itemTags(tagID);
|
||||||
|
|
||||||
DROP TABLE IF EXISTS creators;
|
DROP TABLE IF EXISTS creators;
|
||||||
CREATE TABLE creators (
|
CREATE TABLE creators (
|
||||||
|
|
Loading…
Reference in New Issue
Block a user