import { create_photo, create_album } from './media.js'; export function escape_regex(string) { return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); // $& means the whole matched string } export function do_image_query(query, database, collection_path, collection_thumbs_path) { return new Promise(function (resolve, reject) { var queries = []; queries.push(query); database.queries_async(queries).then(res => { var photos = []; if (res && Array.isArray(res)) { res.forEach(row => { var imagepath = process.env.PUBLIC_URL + collection_path + "/" + row["relativePath"] + "/" + row["name"]; var thumbpath = process.env.PUBLIC_URL + collection_thumbs_path + "/" + row["uniqueHash"] + ".jpg"; photos.push(create_photo(row["id"], row["name"], imagepath, thumbpath)); }); } resolve(photos); }); }); } export function do_album_query(query, database) { return new Promise(function (resolve, reject) { var queries = []; queries.push(query); database.queries_async(queries).then(res => { var albums = []; if (res && Array.isArray(res)) { res.forEach(row => { var album_name = new String(row["relativePath"]).substring(row["relativePath"].lastIndexOf('/') + 1); albums.push(create_album(row["id"], album_name, row["relativePath"])); }); } resolve(albums); }); }); } export const MatchTypeEnum = { MATCH_EQUALS: 1, MATCH_REGEXP: 2, }; export const MatchAgainstEnum = { MATCH_RESULT_NAME: 1, // Match on the name of whatever object type we are querying for MATCH_IMAGE_NAME: 2, // Match on the name of the relevant image, if any MATCH_ALBUM_NAME: 3, // Match on the name of the relevant album, if any } export const ResultTypeEnum = { IMAGE: 1, ALBUM: 2, } export class ResultFilter { constructor(type) { this.result_type = type; } result_type = ResultTypeEnum.IMAGE; to_sql_where() { return "(1=1)"; } } export class ConstFilter extends ResultFilter { constructor(type, val) { super(type); this.constval = val; } constval = true; // True lets everything through, false rejects everything to_sql_where() { return this.constval ? "(1=1)" : "(1=0)"; } } export class MatchingFilter extends ResultFilter { constructor(rtype, against, from, mtype, negate) { super(rtype); this.match_against = against; this.match_from = from; this.match_type = mtype; this.negate = negate; } // What string to match against, e.g. "Image.name" (field) or "\"Image.name\"" (literal) match_against = ""; // optional string used in the filtering match_from = ""; // How to use the matching string match_type = MatchTypeEnum.MATCH_EQUALS; // If true, negates the filter negate = false; to_sql_where() { var match_type_str = false; if(this.match_type == MatchTypeEnum.MATCH_EQUALS) { match_type_str = "="; } else if(this.match_type == MatchTypeEnum.MATCH_REGEXP) { match_type_str = " REGEXP "; } else { throw new Error('Unsupported match type: ' + this.match_type); } return "(" + (this.negate ? "NOT " : "") + this.match_against + match_type_str + this.match_from + ")"; } } export const LogicalOperatorEnum = { AND: 1, OR: 2, } export class LogicalOperatorFilter extends ResultFilter { constructor(type, a, b, op) { super(type); this.sub_filter_a = a; this.sub_filter_b = b; this.operator = op; } sub_filter_a = false; sub_filter_b = false; operator = LogicalOperatorEnum.AND; to_sql_where() { var where1 = this.sub_filter_a.to_sql_where(); var where2 = this.sub_filter_b.to_sql_where(); var operator_str = ""; if(this.operator == LogicalOperatorEnum.AND) { operator_str = " AND "; } else if(this.operator == LogicalOperatorEnum.OR) { operator_str = " OR "; } else { throw new Error('Unsupported logical operator: ' + this.operator); } return "(" + where1 + operator_str + where2 + ")"; } } export class UserQuery { image_filter = new ConstFilter(ResultTypeEnum.IMAGE, true); album_filter = new ConstFilter(ResultTypeEnum.ALBUM, true); } // This query will return database entries with the fields "id", "uniqueHash", "relativePath" (of the album) and "name" for each matching image. export function image_query_with_where(maybe_where) { return "SELECT Images.id, Images.name, Images.uniqueHash, Albums.relativePath FROM Images INNER JOIN Albums ON Images.album=Albums.id " + (maybe_where ? maybe_where : "") + ";"; } // This query will return database entries with the fields "id" and "relativePath" for each matching album. export function album_query_with_where(maybe_where) { return "SELECT Albums.id, Albums.relativePath FROM Albums " + (maybe_where ? maybe_where : "") + ";"; } export function maybe_image_query(user_query) { var where = false; if(user_query.image_filter) { where = "WHERE " + user_query.image_filter.to_sql_where(); } return image_query_with_where(where); } export function maybe_album_query(user_query) { var where = false; if(user_query.album_filter) { where = "WHERE " + user_query.album_filter.to_sql_where(); } return album_query_with_where(where); } function filter_from_text_segment(result_type, segment) { var match_type = false; var match_text = false; var match_against = false; if(result_type == ResultTypeEnum.IMAGE) { match_type = MatchTypeEnum.MATCH_EQUALS; match_text = "\"" + segment['text'] + "\""; match_against = "Images.name"; } else if(result_type == ResultTypeEnum.ALBUM) { // Match against the album "name", which is the basename of its relative path. match_type = MatchTypeEnum.MATCH_REGEXP; match_text = "\"" + '\/(.*\/)*' + escape_regex(segment['text']) + "\""; match_against = "Albums.relativePath"; } return new MatchingFilter(result_type, match_against,match_text, match_type, segment['negated']); } export function user_query_from_search_string(search_string) { const parser = require('search-string'); var parsed = parser.parse(search_string); var conditions = parsed.getParsedQuery(); var texts = parsed.getTextSegments(); var r = new UserQuery(); texts.forEach(text => { console.log(text); r.image_filter = new LogicalOperatorFilter(ResultTypeEnum.IMAGE, r.image_filter, filter_from_text_segment(ResultTypeEnum.IMAGE, text), LogicalOperatorEnum.AND); r.album_filter = new LogicalOperatorFilter(ResultTypeEnum.ALBUM, r.album_filter, filter_from_text_segment(ResultTypeEnum.ALBUM, text), LogicalOperatorEnum.AND); }); return r; } export function user_query_from_album_path(album_path) { var r = new UserQuery(); var match_type = MatchTypeEnum.MATCH_EQUALS; var match_text = "\"" + album_path + "\""; var match_against = "Albums.relativePath"; r.image_filter = new MatchingFilter(ResultTypeEnum.ALBUM, match_against, match_text, match_type, false); r.album_filter = new ConstFilter(ResultTypeEnum.ALBUM, false); return r; }