diff --git a/src/main.js b/src/main.js
index 5cfd0b2..d01ebc5 100644
--- a/src/main.js
+++ b/src/main.js
@@ -8,7 +8,7 @@ import { SearchBar } from './searchbar.js';
import { InternalErrorPage } from './error.js';
import { LoadingPage } from './loading.js';
import { ProvideDB } from './database.js';
-import { UserQuery, user_query_from_search_string, maybe_image_query, do_image_query, maybe_album_query, do_album_query, maybe_tag_query, do_tag_query, ConstFilter, ResultTypeEnum } from './queries.js';
+import { UserQuery, user_query_from_search_string, maybe_image_query, do_image_query, maybe_album_query, do_album_query, maybe_tag_query, do_tag_query, ConstFilter } from './queries.js';
import { Browser } from './browser.js';
import { UserQueryWidget } from './userquerywidget.js';
import { ResultsView } from './resultsview.js';
@@ -53,8 +53,7 @@ export function LoadedMainPage(props) {
useEffect(() => {
// Single-fire effect to start retrieving the albums and tags lists.
var blank_user_query = new UserQuery();
- blank_user_query.album_filter = new ConstFilter(ResultTypeEnum.ALBUM, true);
- blank_user_query.tag_filter = new ConstFilter(ResultTypeEnum.TAG, true);
+ blank_user_query.filter = new ConstFilter(true);
var sql_album_query = maybe_album_query(blank_user_query);
var sql_tag_query = maybe_tag_query(blank_user_query);
@@ -109,13 +108,13 @@ export function LoadedMainPage(props) {
}
function onNewQuery(q) {
- 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);
+ var do_update = !_.isEqual(q.filter, gallery_user_query.filter);
setGalleryUserQuery(q);
- if (do_update_photos) { updatePhotos(q); }
- if (do_update_albums) { updateAlbums(q); }
- if (do_update_tags) { updateTags(q); }
+ if (do_update) {
+ updatePhotos(q);
+ updateAlbums(q);
+ updateTags(q);
+ }
}
function onSearch(q) {
diff --git a/src/queries.js b/src/queries.js
index ee71562..a51b3ce 100644
--- a/src/queries.js
+++ b/src/queries.js
@@ -89,16 +89,15 @@ export const MatchTypeEnum = {
MATCH_TAG_NAME_EQUALS: 10, // Match on the local tag name (excluding parents)
}
+/*
export const ResultTypeEnum = {
IMAGE: 1,
ALBUM: 2,
TAG: 3,
}
+*/
export class ResultFilter {
- constructor(type) { this.result_type = type; }
-
- result_type = ResultTypeEnum.IMAGE;
to_sql_where() { return "(1=1)"; }
is_true() { return false; }
@@ -108,7 +107,7 @@ export class ResultFilter {
}
export class ConstFilter extends ResultFilter {
- constructor(type, val) { super(type); this.constval = val; }
+ constructor(val) { super(); this.constval = val; }
constval = true; // True lets everything through, false rejects everything
to_sql_where() {
@@ -121,12 +120,12 @@ export class ConstFilter extends ResultFilter {
}
export class NegationFilter extends ResultFilter {
- constructor(rtype, body) {
- super(rtype);
+ constructor(body) {
+ super();
this.body = body;
}
- body = new ConstFilter(this.return_type, false);
+ body = new ConstFilter(false);
to_sql_where() {
return "NOT (" + this.body.to_sql_where() + ")";
@@ -137,10 +136,10 @@ export class NegationFilter extends ResultFilter {
simplify() {
var f = this.body.simplify();
if (f.is_true()) {
- return new ConstFilter(this.result_type, false);
+ return new ConstFilter(false);
}
if (f.is_false()) {
- return new ConstFilter(this.result_type, true);
+ return new ConstFilter(true);
}
if (f.is_negation()) {
return f.body;
@@ -155,8 +154,8 @@ export const TimeFilterTypeEnum = {
}
export class TimeFilter extends ResultFilter {
- constructor(rtype, time, type) {
- super(rtype);
+ constructor(time, type) {
+ super();
this.time = time;
this.type = type;
}
@@ -186,8 +185,8 @@ export const ImageTypeEnum = {
}
export class ImageTypeFilter extends ResultFilter {
- constructor(rtype, type) {
- super(rtype);
+ constructor(type) {
+ super();
this.type = type;
}
@@ -202,8 +201,8 @@ export class ImageTypeFilter extends ResultFilter {
}
export class MatchingFilter extends ResultFilter {
- constructor(rtype, from, mtype) {
- super(rtype);
+ constructor(from, mtype) {
+ super();
this.match_from = from;
this.match_type = mtype;
}
@@ -258,14 +257,16 @@ export const LogicalOperatorEnum = {
}
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;
+ constructor(operands, op) { super(); this.operands = operands; this.operator = op; }
+ operands = 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 operand_wheres = [];
+ for (let i = 0; i < this.operands.length; i++) {
+ operand_wheres.push(this.operands[i].to_sql_where());
+ }
+
var operator_str = "";
if (this.operator === LogicalOperatorEnum.AND) {
operator_str = " AND ";
@@ -274,30 +275,51 @@ export class LogicalOperatorFilter extends ResultFilter {
} else {
throw new Error('Unsupported logical operator: ' + this.operator);
}
- return "(" + where1 + operator_str + where2 + ")";
+ var retstr = "(" + operand_wheres[0];
+ for (let i = 1; i < this.operands.length; i++) {
+ retstr += operator_str + operand_wheres[i];
+ }
+ retstr += ")";
+ return retstr;
}
simplify() {
- var a = this.sub_filter_a.simplify();
- var b = this.sub_filter_b.simplify();
+ var simple = [];
+ for (let i = 0; i < this.operands.length; i++) {
+ simple.push(this.operands[i].simplify());
+ }
if (this.operator === LogicalOperatorEnum.OR) {
- if (a.is_true() || b.is_true()) {
- return new ConstFilter(this.return_type, true);
+ for (let i = simple.length - 1; i >= 0; i--) {
+ if (simple[i].is_true()) {
+ return new ConstFilter(true);
+ }
+ if (simple[i].is_false()) {
+ // Remove this element, because OR FALSE means nothing
+ simple.splice(i, 1);
+ }
}
- if (a.is_false()) { return b; }
- if (b.is_false()) { return a; }
}
if (this.operator === LogicalOperatorEnum.AND) {
- if (a.is_false() || b.is_false()) {
- return new ConstFilter(this.return_type, false);
+ for (let i = simple.length - 1; i >= 0; i--) {
+ if (simple[i].is_false()) {
+ return new ConstFilter(false);
+ }
+ if (simple[i].is_true()) {
+ // Remove this element, because AND TRUE means nothing
+ simple.splice(i, 1);
+ }
}
- if (a.is_true()) { return b; }
- if (b.is_true()) { return a; }
}
- return this;
+ if(simple.length == 0) {
+ return null;
+ } else if(simple.length == 1) {
+ return simple[0];
+ }
+
+ return new LogicalOperatorFilter(simple, this.operator);
}
}
@@ -306,8 +328,8 @@ export class LocationFilter extends ResultFilter {
// to a location and filters objects outside said location.
// Locations are directly stored from Nominatim API responses,
// which have GeoJSON and other metadata.
- constructor(rtype, geo_area) {
- super(rtype);
+ constructor(geo_area) {
+ super();
this.geo_area = geo_area;
}
@@ -327,9 +349,7 @@ export class LocationFilter extends ResultFilter {
}
export class UserQuery {
- image_filter = new ConstFilter(ResultTypeEnum.IMAGE, false);
- album_filter = new ConstFilter(ResultTypeEnum.ALBUM, false);
- tag_filter = new ConstFilter(ResultTypeEnum.TAG, false);
+ filter = new ConstFilter(false);
}
// This query will return database entries with the fields "id", "uniqueHash", "relativePath" (of the album) and "name" for each matching image.
@@ -378,24 +398,24 @@ export function tag_query_with_where(maybe_where) {
export function maybe_image_query(user_query, database) {
var where = false;
- if (user_query.image_filter) {
- where = "WHERE " + user_query.image_filter.to_sql_where();
+ if (user_query.filter) {
+ where = "WHERE " + user_query.filter.to_sql_where();
}
return image_query_with_where(where, database);
}
export function maybe_album_query(user_query, database) {
var where = false;
- if (user_query.album_filter) {
- where = "WHERE " + user_query.album_filter.to_sql_where();
+ if (user_query.filter) {
+ where = "WHERE " + user_query.filter.to_sql_where();
}
return album_query_with_where(where, database);
}
export function maybe_tag_query(user_query) {
var where = false;
- if (user_query.tag_filter) {
- where = "WHERE " + user_query.tag_filter.to_sql_where();
+ if (user_query.filter) {
+ where = "WHERE " + user_query.filter.to_sql_where();
}
return tag_query_with_where(where);
}
@@ -408,51 +428,31 @@ export function filter_is_const_false(filter) {
return false;
}
-function filter_from_text_segment(result_type, segment) {
+function filter_from_text_segment(segment) {
var filter = false;
- var name_filter;
-
- if (result_type === ResultTypeEnum.IMAGE) {
- // Option 1: match on image name
- name_filter = new MatchingFilter(
- result_type,
- segment['text'],
- MatchTypeEnum.MATCH_IMAGE_NAME_NATURAL
- );
-
- // Option 2: match on album path (TODO: need natural matching)
- var album_filter = new MatchingFilter(
- result_type,
- segment['text'],
- MatchTypeEnum.MATCH_ALBUM_NAME_EQUALS_OR_CHILD
- );
-
- filter = new LogicalOperatorFilter(result_type, name_filter, album_filter, LogicalOperatorEnum.OR);
- if (segment['negated']) {
- filter = new NegationFilter(result_type, filter);
- }
- } else if (result_type === ResultTypeEnum.ALBUM) {
- // TODO: We need a natural matcher for album names
- name_filter = new MatchingFilter(
- result_type,
- segment['text'],
- 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) {
- // TODO: We need a natural matcher for tag names
- name_filter = new MatchingFilter(
- result_type,
- segment['text'],
- MatchTypeEnum.MATCH_TAG_NAME_EQUALS_OR_CHILD
- );
- filter = name_filter;
- if (segment['negated']) {
- filter = new NegationFilter(result_type, filter);
- }
+
+ // Option 1: match on image name
+ var image_filter = new MatchingFilter(
+ segment['text'],
+ MatchTypeEnum.MATCH_IMAGE_NAME_NATURAL
+ );
+
+ // Option 2: match on album path (TODO: need natural matching)
+ var album_filter = new MatchingFilter(
+ segment['text'],
+ MatchTypeEnum.MATCH_ALBUM_EQUALS_OR_CHILD
+ );
+
+ // Option 3: match on tag name
+ // TODO: We need a natural matcher for tag names
+ var tag_filter = new MatchingFilter(
+ segment['text'],
+ MatchTypeEnum.MATCH_TAG_EQUALS_OR_CHILD
+ );
+
+ filter = new LogicalOperatorFilter([image_filter, album_filter, tag_filter], LogicalOperatorEnum.OR);
+ if (segment['negated']) {
+ filter = new NegationFilter(filter);
}
return filter.simplify();
@@ -468,12 +468,8 @@ export function user_query_from_search_string(search_string) {
var r = new UserQuery();
texts.forEach(text => {
- r.image_filter = new LogicalOperatorFilter(ResultTypeEnum.IMAGE, r.image_filter,
- filter_from_text_segment(ResultTypeEnum.IMAGE, text), LogicalOperatorEnum.AND).simplify();
- r.album_filter = new LogicalOperatorFilter(ResultTypeEnum.ALBUM, r.album_filter,
- filter_from_text_segment(ResultTypeEnum.ALBUM, text), LogicalOperatorEnum.AND).simplify();
- r.tag_filter = new LogicalOperatorFilter(ResultTypeEnum.TAG, r.tag_filter,
- filter_from_text_segment(ResultTypeEnum.TAG, text), LogicalOperatorEnum.AND).simplify();
+ r.filter = new LogicalOperatorFilter([r.filter, filter_from_text_segment(text)],
+ LogicalOperatorEnum.OR).simplify();
});
return r;
@@ -481,21 +477,8 @@ 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 =
+ r.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();
@@ -504,24 +487,10 @@ export function user_query_from_browsed_album(album_path) {
export function user_query_from_browsed_tag(tag_path) {
var r = new UserQuery();
- r.image_filter =
- new MatchingFilter(
- ResultTypeEnum.IMAGE,
- tag_path,
- MatchTypeEnum.MATCH_TAG_EQUALS_OR_CHILD,
- false).simplify();
- r.album_filter =
+ r.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 725392b..fcb2c1d 100644
--- a/src/userquerywidget.js
+++ b/src/userquerywidget.js
@@ -17,6 +17,7 @@ import Select from '@material-ui/core/Select';
import TextField from '@material-ui/core/TextField';
import ScheduleIcon from '@material-ui/icons/Schedule';
import VideocamIcon from '@material-ui/icons/Videocam';
+import LocationOnIcon from '@material-ui/icons/LocationOn';
import { MuiPickersUtilsProvider, DateTimePicker } from "@material-ui/pickers";
import DateFnsUtils from '@date-io/date-fns';
@@ -30,7 +31,7 @@ import { SearchBar } from './searchbar.js';
import {
filter_is_const_false, ConstFilter, LogicalOperatorFilter, MatchingFilter,
- ResultTypeEnum, LogicalOperatorEnum, MatchTypeEnum, NegationFilter, TimeFilterTypeEnum,
+ LogicalOperatorEnum, MatchTypeEnum, NegationFilter, TimeFilterTypeEnum,
TimeFilter, ImageTypeFilter, ImageTypeEnum, LocationFilter
} from './queries.js'
import { Typography } from '@material-ui/core';
@@ -82,7 +83,6 @@ export function EditLocationFilterExpression(props) {
setGeoLayers([layer]);
// Update the proposed polygon
- console.log("Updated proposal:", result);
setProposal(result);
}
@@ -94,7 +94,6 @@ export function EditLocationFilterExpression(props) {
// Do a geometry query.
query_geometry(query)
.then(result => {
- console.log("Query result:", result);
if (result) {
updateProposal(result);
}
@@ -327,15 +326,15 @@ export function EditFilterExpressionDialog(props) {
const handleTypeChange = e => {
var val = e.target.value;
if (val == FilterTypeEnum.CONST) {
- setFilter(new ConstFilter(filter.result_type, true));
+ setFilter(new ConstFilter(true));
} else if (val == FilterTypeEnum.MATCHING) {
- setFilter(new MatchingFilter(filter.result_type, "", MatchTypeEnum.MATCH_IMAGE_NAME_NATURAL));
+ setFilter(new MatchingFilter("", MatchTypeEnum.MATCH_IMAGE_NAME_NATURAL));
} else if (val == FilterTypeEnum.TIME) {
- setFilter(new TimeFilter(filter.result_type, Date.now(), TimeFilterTypeEnum.AFTER));
+ setFilter(new TimeFilter(Date.now(), TimeFilterTypeEnum.AFTER));
} else if (val == FilterTypeEnum.IMAGETYPE) {
- setFilter(new ImageTypeFilter(filter.result_type, ImageTypeEnum.PHOTO));
+ setFilter(new ImageTypeFilter(ImageTypeEnum.PHOTO));
} else if (val == FilterTypeEnum.LOCATION) {
- setFilter(new LocationFilter(filter.result_type, [[0.0, 0.0]]));
+ setFilter(new LocationFilter([[0.0, 0.0]]));
} else {
throw new Error('Unsupported filter type: ' + val);
}
@@ -521,24 +520,14 @@ export function LogicalOperatorFilterExpressionControl(props) {
}
var _ = require('lodash');
- const handleAChanged = (new_a) => {
- if (new_a == null) {
- onChange(expr.sub_filter_b);
- return;
- }
-
+ const handleOpChanged = (idx, new_op) => {
var new_me = _.cloneDeep(expr);
- new_me.sub_filter_a = new_a;
- onChange(new_me);
- }
- const handleBChanged = (new_b) => {
- if (new_b == null) {
- onChange(expr.sub_filter_a);
- return;
- }
- var new_me = _.cloneDeep(expr);
- new_me.sub_filter_b = new_b;
+ if (new_op == null) {
+ new_me.operands.splice(idx, 1);
+ } else {
+ new_me.operands[idx] = new_op;
+ }
onChange(new_me);
}
@@ -546,16 +535,19 @@ export function LogicalOperatorFilterExpressionControl(props) {
<>
-
-
-
-
-
-
-
-
-
-
+ {expr.operands.map((operand, idx) => {
+ const handleChange = new_op => {
+ handleOpChanged(idx, new_op);
+ };
+
+ return (<>
+
+
+
+
+
+ >);
+ })}
>
- )
+ );
}
export function ConstFilterExpressionControl(props) {
@@ -677,7 +669,7 @@ export function ImageTypeFilterExpressionControl(props) {
export function LocationFilterExpressionControl(props) {
const classes = useStyles();
- const { expr, onClick, onChange } = props;
+ const { expr, onClick } = props;
return (
}
>
- LOCATION
+ {expr.geo_area.display_name}
);
}
@@ -763,7 +756,7 @@ export function FilterExpressionControl(props) {
const handleNegation = () => {
handleCloseMenu();
- var new_filter = new NegationFilter(expr.result_type, expr);
+ var new_filter = new NegationFilter(expr);
onChange(new_filter.simplify());
}
@@ -818,18 +811,7 @@ export function FilterExpressionControl(props) {
export function FilterControl(props) {
const classes = useStyles();
- const { filter, onChange, resultType, resultTypeString } = props;
-
- const enabled = !filter_is_const_false(filter);
-
- function handleResultToggled() {
- if (enabled) {
- onChange(new ConstFilter(resultType, false));
- }
- else {
- onChange(new ConstFilter(resultType, true));
- }
- }
+ const { filter, onChange } = props;
// The root node may in principle never be removed,
// except if it is a transformation node (then the body becomes the new root).
@@ -838,16 +820,6 @@ export function FilterControl(props) {
return (
<>
-
- }
- label={resultTypeString + ':'}
- />
>
@@ -856,55 +828,17 @@ export function FilterControl(props) {
export function UserQueryWidget(props) {
const { userQuery, onChange } = props;
- const classes = useStyles();
-
var _ = require('lodash');
- function handleImageFilterChange(filter) {
- var q = _.cloneDeep(userQuery);
- q.image_filter = filter;
- onChange(q);
- }
- function handleAlbumFilterChange(filter) {
- var q = _.cloneDeep(userQuery);
- q.album_filter = filter;
- onChange(q);
- }
- function handleTagFilterChange(filter) {
+ function handleFilterChange(filter) {
var q = _.cloneDeep(userQuery);
- q.tag_filter = filter;
+ q.filter = filter;
onChange(q);
}
return (
- <>
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- >
+
);
}
\ No newline at end of file