diff --git a/src/browser.js b/src/browser.js index a9e972e..487aedd 100644 --- a/src/browser.js +++ b/src/browser.js @@ -22,25 +22,25 @@ class NavTreeItem { } } -export function split_relative_path(path) { +export function split_path(path) { var r = path.split("/"); - r.shift(); + if(path[0] === '/') { r.shift(); } return r; } export function insert_into_album_tree(treebaseitem, treeitem) { - var parts = split_relative_path(treeitem.data); + var parts = split_path(treeitem.data); var current_item = treebaseitem; for (var i = 0; i < parts.length; i++) { var part = parts[i]; var subitem = false; - var required_relative_path = (current_item.data === "/") ? + var required_path = (current_item.data === "/") ? current_item.data + part : current_item.data + "/" + part; for (var j = 0; j < current_item.children.length; j++) { var child = current_item.children[j]; - if (child.data === required_relative_path) { + if (child.data === required_path) { subitem = child; break; } @@ -48,7 +48,7 @@ export function insert_into_album_tree(treebaseitem, treeitem) { if (!subitem) { var new_sub = new NavTreeItem( part, - required_relative_path, + required_path, [] ); current_item.children.push(new_sub); @@ -75,7 +75,33 @@ export function build_albums_tree(all_db_albums) { } export function insert_into_tag_tree(treebaseitem, treeitem) { - treebaseitem.children.push(treeitem); + var parts = split_path(treeitem.data); + var current_item = treebaseitem; + for (var i = 0; i < parts.length; i++) { + var part = parts[i]; + var subitem = false; + var required_path = (current_item.data === "") ? + current_item.data + part : + current_item.data + "/" + part; + + for (var j = 0; j < current_item.children.length; j++) { + var child = current_item.children[j]; + if (child.data === required_path) { + subitem = child; + break; + } + } + if (!subitem) { + var new_sub = new NavTreeItem( + part, + required_path, + [] + ); + current_item.children.push(new_sub); + subitem = new_sub; + } + current_item = subitem; + }; } export function build_tags_tree(all_db_tags) { @@ -84,11 +110,12 @@ export function build_tags_tree(all_db_tags) { var tag = all_db_tags[i]; var item = new NavTreeItem( tag.state.name, - tag.state.name, + tag.state.fullname, [] ); insert_into_tag_tree(tree, item); }; + return tree; } diff --git a/src/database.js b/src/database.js index d0b289a..437b783 100644 --- a/src/database.js +++ b/src/database.js @@ -4,9 +4,19 @@ import NodeEnvironment from 'jest-environment-node'; export async function sqljs_async_queries(sqljs_object, queries) { //var t0 = performance.now(); for (let i = 0; i < (queries.length - 1); i++) { - sqljs_object.exec(queries[i]); + console.log("Query: ", queries[i]); + try { + sqljs_object.exec(queries[i]); + } catch (e) { + throw e; + } + } + console.log("Query: ", queries[queries.length - 1]); + try { + var r = sqljs_object.exec(queries[queries.length - 1]); + } catch (e) { + throw e; } - var r = sqljs_object.exec(queries[queries.length - 1]); //console.log("Queries took ", (performance.now() - t0), " ms."); //console.log("Query result for ", queries[queries.length - 1], ": ", r); return r; @@ -87,7 +97,9 @@ export async function add_full_tag_info(db) { db.exec(query); }); - var r = db.exec("SELECT * FROM Tags;"); + console.log(db.exec("PRAGMA table_info([Tags]);")); + + return db; } export function ProvideDB(props) { @@ -98,10 +110,10 @@ export function ProvideDB(props) { useEffect(() => { fetch_sqljs_db_from_sqlite(db_url) .then(db => { - add_full_tag_info(db).then(() => { - add_custom_functions(db); + add_full_tag_info(db).then((newdb) => { + add_custom_functions(newdb); setError(false); - setDb(db); + setDb(newdb); }) }) .catch(error => { setError(error); }); @@ -112,5 +124,9 @@ export function ProvideDB(props) { db_error: error, }; + if (db != null) { + console.log("Provided DB tags schema:", db.exec("PRAGMA table_info([Tags]);")); + } + return children({ ...child_props }); } \ No newline at end of file diff --git a/src/main.js b/src/main.js index bbb5b57..10a1704 100644 --- a/src/main.js +++ b/src/main.js @@ -109,7 +109,6 @@ export function LoadedMainPage(props) { } function onNewQuery(q) { - console.log(q.image_filter, " ?= ", gallery_user_query.image_filter); var do_update_photos = !_.isEqual(q.image_filter, gallery_user_query.image_filter); var do_update_albums = !_.isEqual(q.album_filter, gallery_user_query.album_filter); var do_update_tags = !_.isEqual(q.tag_filter, gallery_user_query.tag_filter); diff --git a/src/media.js b/src/media.js index d1951d2..8b54922 100644 --- a/src/media.js +++ b/src/media.js @@ -24,7 +24,8 @@ export class Album extends Media { export class Tag extends Media { state = { id: false, - name: false + name: false, // This is the name of the leaf only, like "Sander" + fullname: false, // this is the full "path" to the tag, like "People/Sander" } } @@ -51,10 +52,11 @@ export function create_album(maybe_id, maybe_name, maybe_relative_path) { return a; } -export function create_tag(maybe_id, maybe_name) { +export function create_tag(maybe_id, maybe_name, maybe_fullname) { var t = new Tag(); if (maybe_id) { t.state.id = maybe_id; } if (maybe_name) { t.state.name = maybe_name; } + if (maybe_fullname) { t.state.fullname = maybe_fullname } return t; } diff --git a/src/queries.js b/src/queries.js index 521138e..a743b32 100644 --- a/src/queries.js +++ b/src/queries.js @@ -11,6 +11,7 @@ export function do_image_query(query, database, collection_path, collection_thum var queries = []; queries.push(query); sqljs_async_queries(database, queries).then(res => { + console.log("response: ", res); var photos = []; if (res && Array.isArray(res) && res.length > 0) { var cols = res[0].columns; @@ -30,7 +31,8 @@ export function do_image_query(query, database, collection_path, collection_thum }); } resolve(photos); - }); + }) + .catch(err => { throw err; }); }); } @@ -58,13 +60,14 @@ export function do_tag_query(query, database) { return new Promise(function (resolve, reject) { var queries = []; queries.push(query); + console.log("Provided DB tags schema before query:", database.exec("PRAGMA table_info([Tags]);")); sqljs_async_queries(database, queries).then(res => { var tags = []; if (res && Array.isArray(res) && res.length > 0) { var cols = res[0].columns; var data = res[0].values; data.forEach(row => { - tags.push(create_tag(row[cols.indexOf("id")], row[cols.indexOf("fullname")])); + tags.push(create_tag(row[cols.indexOf("id")], row[cols.indexOf("name")], row[cols.indexOf("fullname")])); }); } resolve(tags); @@ -79,7 +82,10 @@ export const MatchTypeEnum = { MATCH_ALBUM_EQUALS_OR_CHILD: 4, // Match on the full name (relative path) of the relevant album, if any, or any of its children MATCH_ALBUM_NATURAL: 5, MATCH_ALBUM_NAME_EQUALS: 6, // Match on the local album name (excluding parents) - MATCH_TAG_EQUALS: 7, // Match on the name of the relevant tag(s), if any + MATCH_TAG_EQUALS: 7, // Match on the full name (relative path) of the relevant tag, if any + MATCH_TAG_EQUALS_OR_CHILD: 8, // Match on the full name (path) of the relevant tag, if any, or any of its children + MATCH_TAG_NATURAL: 9, + MATCH_TAG_NAME_EQUALS: 10, // Match on the local tag name (excluding parents) } export const ResultTypeEnum = { @@ -164,7 +170,7 @@ export class MatchingFilter extends ResultFilter { + escape_regex(this.match_from) + '.*"))'; } else if (this.match_type == MatchTypeEnum.MATCH_ALBUM_EQUALS) { - return '(Albums.relativePath = "' + this.match_from + '")'; + return '(Albums.relativePath="' + this.match_from + '")'; } else if (this.match_type == MatchTypeEnum.MATCH_ALBUM_EQUALS_OR_CHILD) { return 'REGEXP(Albums.relativePath, "' + escape_regex(this.match_from) @@ -176,7 +182,17 @@ export class MatchingFilter extends ResultFilter { + escape_regex(this.match_from) + '(\/[^\/]+)*")'; } else if (this.match_type == MatchTypeEnum.MATCH_TAG_EQUALS) { - return '(Tags.name="' + this.match_from + '")'; + return '(Tags.fullname="' + this.match_from + '")'; + } else if (this.match_type == MatchTypeEnum.MATCH_TAG_EQUALS_OR_CHILD) { + return '(Tags.fullname NOT NULL AND REGEXP(Tags.fullname, "' + + escape_regex(this.match_from) + + '(\/[^\/]+)*"))'; + } else if (this.match_type == MatchTypeEnum.MATCH_TAG_NATURAL) { + throw new Error("Natural matching on tag names is not yet supported."); + } else if (this.match_type == MatchTypeEnum.MATCH_TAG_NAME_EQUALS) { + return 'REGEXP(Tags.fullname, "\/(.*\/)*' + + escape_regex(this.match_from) + + '(\/[^\/]+)*")'; } console.log(this); @@ -314,7 +330,7 @@ function filter_from_text_segment(result_type, segment) { var album_filter = new MatchingFilter( result_type, segment['text'], - MatchTypeEnum.MATCH_ALBUM_NAME_EQUALS + MatchTypeEnum.MATCH_ALBUM_NAME_EQUALS_OR_CHILD ); filter = new LogicalOperatorFilter(result_type, name_filter, album_filter, LogicalOperatorEnum.OR); @@ -326,18 +342,18 @@ function filter_from_text_segment(result_type, segment) { name_filter = new MatchingFilter( result_type, segment['text'], - MatchTypeEnum.MATCH_ALBUM_NAME_EQUALS + MatchTypeEnum.MATCH_ALBUM_NAME_EQUALS_OR_CHILD ); filter = name_filter; if (segment['negated']) { filter = new NegationFilter(result_type, filter); } } else if (result_type === ResultTypeEnum.TAG) { - // Match against the tag name. + // TODO: We need a natural matcher for tag names name_filter = new MatchingFilter( result_type, segment['text'], - MatchTypeEnum.MATCH_TAG_EQUALS + MatchTypeEnum.MATCH_TAG_NAME_EQUALS_OR_CHILD ); filter = name_filter; if (segment['negated']) { @@ -371,26 +387,51 @@ export function user_query_from_search_string(search_string) { export function user_query_from_browsed_album(album_path) { var r = new UserQuery(); - r.image_filter = new MatchingFilter( - ResultTypeEnum.IMAGE, - album_path, - MatchTypeEnum.MATCH_ALBUM_EQUALS_OR_CHILD, - false).simplify(); - r.album_filter = new ConstFilter(ResultTypeEnum.ALBUM, false).simplify(); - r.tag_filter = new ConstFilter(ResultTypeEnum.TAG, false).simplify(); - + r.image_filter = + new MatchingFilter( + ResultTypeEnum.IMAGE, + album_path, + MatchTypeEnum.MATCH_ALBUM_EQUALS_OR_CHILD, + false).simplify(); + /* + r.album_filter = + new MatchingFilter( + ResultTypeEnum.ALBUM, + album_path, + MatchTypeEnum.MATCH_ALBUM_EQUALS_OR_CHILD, + false).simplify(); + r.tag_filter = + new MatchingFilter( + ResultTypeEnum.TAG, + album_path, + MatchTypeEnum.MATCH_ALBUM_EQUALS_OR_CHILD, + false).simplify(); + */ return r; } -export function user_query_from_browsed_tag(name) { +export function user_query_from_browsed_tag(tag_path) { var r = new UserQuery(); - r.image_filter = new MatchingFilter( - ResultTypeEnum.IMAGE, - name, - MatchTypeEnum.MATCH_TAG_EQUALS, - false).simplify(); - r.album_filter = new ConstFilter(ResultTypeEnum.ALBUM, false).simplify(); - r.tag_filter = new ConstFilter(ResultTypeEnum.TAG, false).simplify(); + r.image_filter = + new MatchingFilter( + ResultTypeEnum.IMAGE, + tag_path, + MatchTypeEnum.MATCH_TAG_EQUALS_OR_CHILD, + false).simplify(); + /* + r.album_filter = + new MatchingFilter( + ResultTypeEnum.ALBUM, + tag_path, + MatchTypeEnum.MATCH_TAG_EQUALS_OR_CHILD, + false).simplify(); + r.tag_filter = + new MatchingFilter( + ResultTypeEnum.TAG, + tag_path, + MatchTypeEnum.MATCH_TAG_EQUALS_OR_CHILD, + false).simplify(); + */ return r; } \ No newline at end of file diff --git a/src/userquerywidget.js b/src/userquerywidget.js index cf18280..55e0993 100644 --- a/src/userquerywidget.js +++ b/src/userquerywidget.js @@ -138,6 +138,9 @@ export function EditMatchingFilterExpression(props) { Album Natural Album Name Equals Tag Equals + Tag Equals Or Child + Tag Natural + Tag Name Equals @@ -311,6 +314,8 @@ export function MatchingFilterExpressionControl(props) { .replace(/"$/g, ''); if (expr.match_type === MatchTypeEnum.MATCH_TAG_EQUALS) { return + } else if (expr.match_type === MatchTypeEnum.MATCH_TAG_EQUALS_OR_CHILD) { + return } else if (expr.match_type === MatchTypeEnum.MATCH_ALBUM_EQUALS) { return } else if (expr.match_type === MatchTypeEnum.MATCH_ALBUM_EQUALS_OR_CHILD) {