Filter improvement exploration

Changed the rating filter to allow for more than just an equality check.  This progresses #29.
This commit is contained in:
Stash Dev
2019-03-24 15:11:58 -07:00
parent c1f1a6ccff
commit b1db98bd1f
22 changed files with 339 additions and 54 deletions

File diff suppressed because one or more lines are too long

View File

@@ -10858,6 +10858,35 @@ func (e *executableSchema) GenerateMetadataInputMiddleware(ctx context.Context,
return obj, nil return obj, nil
} }
func UnmarshalIntCriterionInput(v interface{}) (IntCriterionInput, error) {
var it IntCriterionInput
var asMap = v.(map[string]interface{})
for k, v := range asMap {
switch k {
case "value":
var err error
it.Value, err = graphql.UnmarshalInt(v)
if err != nil {
return it, err
}
case "modifier":
var err error
err = (&it.Modifier).UnmarshalGQL(v)
if err != nil {
return it, err
}
}
}
return it, nil
}
func (e *executableSchema) IntCriterionInputMiddleware(ctx context.Context, obj *IntCriterionInput) (*IntCriterionInput, error) {
return obj, nil
}
func UnmarshalPerformerCreateInput(v interface{}) (PerformerCreateInput, error) { func UnmarshalPerformerCreateInput(v interface{}) (PerformerCreateInput, error) {
var it PerformerCreateInput var it PerformerCreateInput
var asMap = v.(map[string]interface{}) var asMap = v.(map[string]interface{})
@@ -11303,9 +11332,9 @@ func UnmarshalSceneFilterType(v interface{}) (SceneFilterType, error) {
switch k { switch k {
case "rating": case "rating":
var err error var err error
var ptr1 int var ptr1 IntCriterionInput
if v != nil { if v != nil {
ptr1, err = graphql.UnmarshalInt(v) ptr1, err = UnmarshalIntCriterionInput(v)
it.Rating = &ptr1 it.Rating = &ptr1
} }
@@ -11392,6 +11421,14 @@ func UnmarshalSceneFilterType(v interface{}) (SceneFilterType, error) {
func (e *executableSchema) SceneFilterTypeMiddleware(ctx context.Context, obj *SceneFilterType) (*SceneFilterType, error) { func (e *executableSchema) SceneFilterTypeMiddleware(ctx context.Context, obj *SceneFilterType) (*SceneFilterType, error) {
if obj.Rating != nil {
var err error
obj.Rating, err = e.IntCriterionInputMiddleware(ctx, obj.Rating)
if err != nil {
return nil, err
}
}
return obj, nil return obj, nil
} }
@@ -12301,7 +12338,7 @@ input SceneMarkerFilterType {
input SceneFilterType { input SceneFilterType {
"""Filter by rating""" """Filter by rating"""
rating: Int rating: IntCriterionInput
"""Filter by resolution""" """Filter by resolution"""
resolution: ResolutionEnum resolution: ResolutionEnum
"""Filter to only include scenes which have markers. ` + "`" + `true` + "`" + ` or ` + "`" + `false` + "`" + `""" """Filter to only include scenes which have markers. ` + "`" + `true` + "`" + ` or ` + "`" + `false` + "`" + `"""
@@ -12316,6 +12353,28 @@ input SceneFilterType {
performer_id: ID performer_id: ID
} }
enum CriterionModifier {
"""="""
EQUALS,
"""!="""
NOT_EQUALS,
""">"""
GREATER_THAN,
"""<"""
LESS_THAN,
"""IS NULL"""
IS_NULL,
"""IS NOT NULL"""
NOT_NULL,
INCLUDES,
EXCLUDES,
}
input IntCriterionInput {
value: Int!
modifier: CriterionModifier!
}
####################################### #######################################
# Config # Config
####################################### #######################################

View File

@@ -77,6 +77,11 @@ type GenerateMetadataInput struct {
Transcodes bool `json:"transcodes"` Transcodes bool `json:"transcodes"`
} }
type IntCriterionInput struct {
Value int `json:"value"`
Modifier CriterionModifier `json:"modifier"`
}
type MarkerStringsResultType struct { type MarkerStringsResultType struct {
Count int `json:"count"` Count int `json:"count"`
ID string `json:"id"` ID string `json:"id"`
@@ -144,7 +149,7 @@ type SceneFileType struct {
type SceneFilterType struct { type SceneFilterType struct {
// Filter by rating // Filter by rating
Rating *int `json:"rating"` Rating *IntCriterionInput `json:"rating"`
// Filter by resolution // Filter by resolution
Resolution *ResolutionEnum `json:"resolution"` Resolution *ResolutionEnum `json:"resolution"`
// Filter to only include scenes which have markers. `true` or `false` // Filter to only include scenes which have markers. `true` or `false`
@@ -270,6 +275,65 @@ type TagUpdateInput struct {
Name string `json:"name"` Name string `json:"name"`
} }
type CriterionModifier string
const (
// =
CriterionModifierEquals CriterionModifier = "EQUALS"
// !=
CriterionModifierNotEquals CriterionModifier = "NOT_EQUALS"
// >
CriterionModifierGreaterThan CriterionModifier = "GREATER_THAN"
// <
CriterionModifierLessThan CriterionModifier = "LESS_THAN"
// IS NULL
CriterionModifierIsNull CriterionModifier = "IS_NULL"
// IS NOT NULL
CriterionModifierNotNull CriterionModifier = "NOT_NULL"
CriterionModifierIncludes CriterionModifier = "INCLUDES"
CriterionModifierExcludes CriterionModifier = "EXCLUDES"
)
var AllCriterionModifier = []CriterionModifier{
CriterionModifierEquals,
CriterionModifierNotEquals,
CriterionModifierGreaterThan,
CriterionModifierLessThan,
CriterionModifierIsNull,
CriterionModifierNotNull,
CriterionModifierIncludes,
CriterionModifierExcludes,
}
func (e CriterionModifier) IsValid() bool {
switch e {
case CriterionModifierEquals, CriterionModifierNotEquals, CriterionModifierGreaterThan, CriterionModifierLessThan, CriterionModifierIsNull, CriterionModifierNotNull, CriterionModifierIncludes, CriterionModifierExcludes:
return true
}
return false
}
func (e CriterionModifier) String() string {
return string(e)
}
func (e *CriterionModifier) UnmarshalGQL(v interface{}) error {
str, ok := v.(string)
if !ok {
return fmt.Errorf("enums must be strings")
}
*e = CriterionModifier(str)
if !e.IsValid() {
return fmt.Errorf("%s is not a valid CriterionModifier", str)
}
return nil
}
func (e CriterionModifier) MarshalGQL(w io.Writer) {
fmt.Fprint(w, strconv.Quote(e.String()))
}
type ResolutionEnum string type ResolutionEnum string
const ( const (

View File

@@ -164,8 +164,11 @@ func (qb *SceneQueryBuilder) Query(sceneFilter *SceneFilterType, findFilter *Fin
} }
if rating := sceneFilter.Rating; rating != nil { if rating := sceneFilter.Rating; rating != nil {
whereClauses = append(whereClauses, "rating = ?") clause, count := getIntCriterionWhereClause("rating", *sceneFilter.Rating)
args = append(args, *sceneFilter.Rating) whereClauses = append(whereClauses, clause)
if count == 1 {
args = append(args, sceneFilter.Rating.Value)
}
} }
if resolutionFilter := sceneFilter.Resolution; resolutionFilter != nil { if resolutionFilter := sceneFilter.Resolution; resolutionFilter != nil {

View File

@@ -5,6 +5,7 @@ import (
"fmt" "fmt"
"github.com/jmoiron/sqlx" "github.com/jmoiron/sqlx"
"github.com/stashapp/stash/pkg/database" "github.com/stashapp/stash/pkg/database"
"github.com/stashapp/stash/pkg/logger"
"reflect" "reflect"
"strconv" "strconv"
"strings" "strings"
@@ -104,6 +105,48 @@ func getInBinding(length int) string {
return "(" + bindings + ")" return "(" + bindings + ")"
} }
func getCriterionModifierBinding(criterionModifier CriterionModifier, value interface{}) (string, int) {
var length int
switch x := value.(type) {
case []string:
length = len(x)
case []int:
length = len(x)
default:
length = 1
logger.Debugf("unsupported type: %T\n", x)
}
if modifier := criterionModifier.String(); criterionModifier.IsValid() {
switch modifier {
case "EQUALS":
return "= ?", 1
case "NOT_EQUALS":
return "!= ?", 1
case "GREATER_THAN":
return "> ?", 1
case "LESS_THAN":
return "< ?", 1
case "IS_NULL":
return "IS NULL", 0
case "NOT_NULL":
return "IS NOT NULL", 0
case "INCLUDES":
return "IN "+getInBinding(length), length // TODO?
case "EXCLUDES":
return "NOT IN "+getInBinding(length), length // TODO?
default:
logger.Errorf("todo")
return "= ?", 1 // TODO
}
}
return "= ?", 1 // TODO
}
func getIntCriterionWhereClause(column string, input IntCriterionInput) (string, int) {
binding, count := getCriterionModifierBinding(input.Modifier, input.Value)
return column+" "+binding, count
}
func runIdsQuery(query string, args []interface{}) ([]int, error) { func runIdsQuery(query string, args []interface{}) ([]int, error) {
var result []struct { var result []struct {
Int int `db:"id"` Int int `db:"id"`

View File

@@ -357,7 +357,7 @@ input SceneMarkerFilterType {
input SceneFilterType { input SceneFilterType {
"""Filter by rating""" """Filter by rating"""
rating: Int rating: IntCriterionInput
"""Filter by resolution""" """Filter by resolution"""
resolution: ResolutionEnum resolution: ResolutionEnum
"""Filter to only include scenes which have markers. `true` or `false`""" """Filter to only include scenes which have markers. `true` or `false`"""
@@ -372,6 +372,28 @@ input SceneFilterType {
performer_id: ID performer_id: ID
} }
enum CriterionModifier {
"""="""
EQUALS,
"""!="""
NOT_EQUALS,
""">"""
GREATER_THAN,
"""<"""
LESS_THAN,
"""IS NULL"""
IS_NULL,
"""IS NOT NULL"""
NOT_NULL,
INCLUDES,
EXCLUDES,
}
input IntCriterionInput {
value: Int!
modifier: CriterionModifier!
}
####################################### #######################################
# Config # Config
####################################### #######################################

View File

@@ -8,6 +8,7 @@ import {
import _ from "lodash"; import _ from "lodash";
import React, { FunctionComponent, useEffect, useRef, useState } from "react"; import React, { FunctionComponent, useEffect, useRef, useState } from "react";
import { isArray } from "util"; import { isArray } from "util";
import { CriterionModifier } from "../../core/generated-graphql";
import { Criterion, CriterionType } from "../../models/list-filter/criteria/criterion"; import { Criterion, CriterionType } from "../../models/list-filter/criteria/criterion";
import { NoneCriterion } from "../../models/list-filter/criteria/none"; import { NoneCriterion } from "../../models/list-filter/criteria/none";
import { PerformersCriterion } from "../../models/list-filter/criteria/performers"; import { PerformersCriterion } from "../../models/list-filter/criteria/performers";
@@ -18,7 +19,7 @@ import { ListFilterModel } from "../../models/list-filter/filter";
import { FilterMultiSelect } from "../select/FilterMultiSelect"; import { FilterMultiSelect } from "../select/FilterMultiSelect";
interface IAddFilterProps { interface IAddFilterProps {
onAddCriterion: (criterion: Criterion) => void; onAddCriterion: (criterion: Criterion, oldId?: string) => void;
onCancel: () => void; onCancel: () => void;
filter: ListFilterModel; filter: ListFilterModel;
editingCriterion?: Criterion; editingCriterion?: Criterion;
@@ -43,6 +44,12 @@ export const AddFilter: FunctionComponent<IAddFilterProps> = (props: IAddFilterP
setCriterion(newCriterion); setCriterion(newCriterion);
} }
function onChangedModifierSelect(event: React.ChangeEvent<HTMLSelectElement>) {
const newCriterion = _.cloneDeep(criterion);
newCriterion.modifier = event.target.value as any;
setCriterion(newCriterion);
}
function onChangedSingleSelect(event: React.ChangeEvent<HTMLSelectElement>) { function onChangedSingleSelect(event: React.ChangeEvent<HTMLSelectElement>) {
const newCriterion = _.cloneDeep(criterion); const newCriterion = _.cloneDeep(criterion);
newCriterion.value = event.target.value; newCriterion.value = event.target.value;
@@ -54,7 +61,8 @@ export const AddFilter: FunctionComponent<IAddFilterProps> = (props: IAddFilterP
const value = singleValueSelect.current.props.defaultValue; const value = singleValueSelect.current.props.defaultValue;
if (value === undefined || value === "" || typeof value === "number") { criterion.value = criterion.options[0]; } if (value === undefined || value === "" || typeof value === "number") { criterion.value = criterion.options[0]; }
} }
props.onAddCriterion(criterion); const oldId = !!props.editingCriterion ? props.editingCriterion.getId() : undefined;
props.onAddCriterion(criterion, oldId);
onToggle(); onToggle();
} }
@@ -69,7 +77,25 @@ export const AddFilter: FunctionComponent<IAddFilterProps> = (props: IAddFilterP
const maybeRenderFilterPopoverContents = () => { const maybeRenderFilterPopoverContents = () => {
if (criterion.type === "none") { return; } if (criterion.type === "none") { return; }
function renderModifier() {
if (criterion.modifierOptions.length === 0) { return; }
return (
<div>
<HTMLSelect
options={criterion.modifierOptions}
onChange={onChangedModifierSelect}
defaultValue={criterion.modifier}
/>
</div>
);
}
function renderSelect() { function renderSelect() {
// Hide the value select if the modifier is "IsNull" or "NotNull"
if (criterion.modifier === CriterionModifier.IsNull || criterion.modifier === CriterionModifier.NotNull) {
return;
}
if (isArray(criterion.value)) { if (isArray(criterion.value)) {
let type: "performers" | "studios" | "tags" | "" = ""; let type: "performers" | "studios" | "tags" | "" = "";
if (criterion instanceof PerformersCriterion) { if (criterion instanceof PerformersCriterion) {
@@ -103,7 +129,12 @@ export const AddFilter: FunctionComponent<IAddFilterProps> = (props: IAddFilterP
); );
} }
} }
return <FormGroup>{renderSelect()}</FormGroup>; return (
<FormGroup>
{renderModifier()}
{renderSelect()}
</FormGroup>
);
}; };
function maybeRenderFilterSelect() { function maybeRenderFilterSelect() {

View File

@@ -23,7 +23,7 @@ interface IListFilterProps {
onChangeSortDirection: (sortDirection: "asc" | "desc") => void; onChangeSortDirection: (sortDirection: "asc" | "desc") => void;
onChangeSortBy: (sortBy: string) => void; onChangeSortBy: (sortBy: string) => void;
onChangeDisplayMode: (displayMode: DisplayMode) => void; onChangeDisplayMode: (displayMode: DisplayMode) => void;
onAddCriterion: (criterion: Criterion) => void; onAddCriterion: (criterion: Criterion, oldId?: string) => void;
onRemoveCriterion: (criterion: Criterion) => void; onRemoveCriterion: (criterion: Criterion) => void;
filter: ListFilterModel; filter: ListFilterModel;
} }
@@ -67,8 +67,8 @@ export const ListFilter: FunctionComponent<IListFilterProps> = (props: IListFilt
props.onChangeDisplayMode(displayMode); props.onChangeDisplayMode(displayMode);
} }
function onAddCriterion(criterion: Criterion) { function onAddCriterion(criterion: Criterion, oldId?: string) {
props.onAddCriterion(criterion); props.onAddCriterion(criterion, oldId);
} }
function onCancelAddCriterion() { function onCancelAddCriterion() {
@@ -122,7 +122,7 @@ export const ListFilter: FunctionComponent<IListFilterProps> = (props: IListFilt
function renderFilterTags() { function renderFilterTags() {
return props.filter.criteria.map((criterion) => ( return props.filter.criteria.map((criterion) => (
<Tag <Tag
key={criterion.type} key={criterion.getId()}
className="tag-item" className="tag-item"
itemID={criterion.getId()} itemID={criterion.getId()}
interactive={true} interactive={true}

View File

@@ -1,10 +1,10 @@
/* tslint:disable */ /* tslint:disable */
// Generated in 2019-03-24T09:16:39-07:00 // Generated in 2019-03-24T13:29:34-07:00
export type Maybe<T> = T | undefined; export type Maybe<T> = T | undefined;
export interface SceneFilterType { export interface SceneFilterType {
/** Filter by rating */ /** Filter by rating */
rating?: Maybe<number>; rating?: Maybe<IntCriterionInput>;
/** Filter by resolution */ /** Filter by resolution */
resolution?: Maybe<ResolutionEnum>; resolution?: Maybe<ResolutionEnum>;
/** Filter to only include scenes which have markers. `true` or `false` */ /** Filter to only include scenes which have markers. `true` or `false` */
@@ -19,6 +19,12 @@ export interface SceneFilterType {
performer_id?: Maybe<string>; performer_id?: Maybe<string>;
} }
export interface IntCriterionInput {
value: number;
modifier: CriterionModifier;
}
export interface FindFilterType { export interface FindFilterType {
q?: Maybe<string>; q?: Maybe<string>;
@@ -222,6 +228,17 @@ export interface ConfigGeneralInput {
generatedPath?: Maybe<string>; generatedPath?: Maybe<string>;
} }
export enum CriterionModifier {
Equals = "EQUALS",
NotEquals = "NOT_EQUALS",
GreaterThan = "GREATER_THAN",
LessThan = "LESS_THAN",
IsNull = "IS_NULL",
NotNull = "NOT_NULL",
Includes = "INCLUDES",
Excludes = "EXCLUDES"
}
export enum ResolutionEnum { export enum ResolutionEnum {
Low = "LOW", Low = "LOW",
Standard = "STANDARD", Standard = "STANDARD",

View File

@@ -117,14 +117,26 @@ export class ListHook {
setFilter(newFilter); setFilter(newFilter);
} }
function onAddCriterion(criterion: Criterion) { function onAddCriterion(criterion: Criterion, oldId?: string) {
const newFilter = _.cloneDeep(filter); const newFilter = _.cloneDeep(filter);
const existingIndex = newFilter.criteria.findIndex((c) => c.getId() === criterion.getId());
// Find if we are editing an existing criteria, then modify that. Or create a new one.
const existingIndex = newFilter.criteria.findIndex((c) => {
// If we modified an existing criterion, then look for the old id.
const id = !!oldId ? oldId : criterion.getId();
return c.getId() === id;
});
if (existingIndex === -1) { if (existingIndex === -1) {
newFilter.criteria.push(criterion); newFilter.criteria.push(criterion);
} else { } else {
newFilter.criteria[existingIndex] = criterion; newFilter.criteria[existingIndex] = criterion;
} }
// Remove duplicate modifiers
newFilter.criteria = newFilter.criteria.filter((obj, pos, arr) => {
return arr.map((mapObj: any) => mapObj.getId()).indexOf(obj.getId()) === pos;
});
newFilter.currentPage = 1; newFilter.currentPage = 1;
setFilter(newFilter); setFilter(newFilter);
} }

View File

@@ -1,5 +1,6 @@
import { isArray } from "util"; import { isArray } from "util";
import { ILabeledId } from "../types"; import { CriterionModifier } from "../../../core/generated-graphql";
import { ILabeledId, ILabeledValue } from "../types";
export type CriterionType = export type CriterionType =
"none" | "none" |
@@ -13,15 +14,6 @@ export type CriterionType =
"performers" | "performers" |
"studios"; "studios";
export enum CriterionModifier {
Equals,
NotEquals,
GreaterThan,
LessThan,
Inclusive,
Exclusive,
}
export abstract class Criterion<Option = any, Value = any> { export abstract class Criterion<Option = any, Value = any> {
public static getLabel(type: CriterionType = "none"): string { public static getLabel(type: CriterionType = "none"): string {
switch (type) { switch (type) {
@@ -38,9 +30,23 @@ export abstract class Criterion<Option = any, Value = any> {
} }
} }
public static getModifierOption(modifier: CriterionModifier = CriterionModifier.Equals): ILabeledValue {
switch (modifier) {
case CriterionModifier.Equals: return {value: CriterionModifier.Equals, label: "Equals"};
case CriterionModifier.NotEquals: return {value: CriterionModifier.NotEquals, label: "Not Equals"};
case CriterionModifier.GreaterThan: return {value: CriterionModifier.GreaterThan, label: "Greater Than"};
case CriterionModifier.LessThan: return {value: CriterionModifier.LessThan, label: "Less Than"};
case CriterionModifier.IsNull: return {value: CriterionModifier.IsNull, label: "Is NULL"};
case CriterionModifier.NotNull: return {value: CriterionModifier.NotNull, label: "Not NULL"};
case CriterionModifier.Includes: return {value: CriterionModifier.Includes, label: "Includes"};
case CriterionModifier.Excludes: return {value: CriterionModifier.Excludes, label: "Excludes"};
}
}
public abstract type: CriterionType; public abstract type: CriterionType;
public abstract parameterName: string; public abstract parameterName: string;
public abstract modifier: CriterionModifier; public abstract modifier: CriterionModifier;
public abstract modifierOptions: ILabeledValue[];
public abstract options: Option[]; public abstract options: Option[];
public abstract value: Value; public abstract value: Value;
@@ -51,13 +57,17 @@ export abstract class Criterion<Option = any, Value = any> {
case CriterionModifier.NotEquals: modifierString = "is not"; break; case CriterionModifier.NotEquals: modifierString = "is not"; break;
case CriterionModifier.GreaterThan: modifierString = "is greater than"; break; case CriterionModifier.GreaterThan: modifierString = "is greater than"; break;
case CriterionModifier.LessThan: modifierString = "is less than"; break; case CriterionModifier.LessThan: modifierString = "is less than"; break;
case CriterionModifier.Inclusive: modifierString = "includes"; break; case CriterionModifier.IsNull: modifierString = "is null"; break;
case CriterionModifier.Exclusive: modifierString = "exculdes"; break; case CriterionModifier.NotNull: modifierString = "is not null"; break;
case CriterionModifier.Includes: modifierString = "includes"; break;
case CriterionModifier.Excludes: modifierString = "exculdes"; break;
default: modifierString = ""; default: modifierString = "";
} }
let valueString: string; let valueString: string;
if (isArray(this.value) && this.value.length > 0) { if (this.modifier === CriterionModifier.IsNull || this.modifier === CriterionModifier.NotNull) {
valueString = "";
} else if (isArray(this.value) && this.value.length > 0) {
let items = this.value; let items = this.value;
if ((this.value as ILabeledId[])[0].label) { if ((this.value as ILabeledId[])[0].label) {
items = this.value.map((item) => item.label) as any; items = this.value.map((item) => item.label) as any;

View File

@@ -1,6 +1,6 @@
import { CriterionModifier } from "../../../core/generated-graphql";
import { import {
Criterion, Criterion,
CriterionModifier,
CriterionType, CriterionType,
ICriterionOption, ICriterionOption,
} from "./criterion"; } from "./criterion";
@@ -9,6 +9,7 @@ export class FavoriteCriterion extends Criterion<string, string> {
public type: CriterionType = "favorite"; public type: CriterionType = "favorite";
public parameterName: string = "filter_favorites"; public parameterName: string = "filter_favorites";
public modifier = CriterionModifier.Equals; public modifier = CriterionModifier.Equals;
public modifierOptions = [];
public options: string[] = [true.toString(), false.toString()]; public options: string[] = [true.toString(), false.toString()];
public value: string = ""; public value: string = "";
} }

View File

@@ -1,6 +1,6 @@
import { CriterionModifier } from "../../../core/generated-graphql";
import { import {
Criterion, Criterion,
CriterionModifier,
CriterionType, CriterionType,
ICriterionOption, ICriterionOption,
} from "./criterion"; } from "./criterion";
@@ -9,6 +9,7 @@ export class HasMarkersCriterion extends Criterion<string, string> {
public type: CriterionType = "hasMarkers"; public type: CriterionType = "hasMarkers";
public parameterName: string = "has_markers"; public parameterName: string = "has_markers";
public modifier = CriterionModifier.Equals; public modifier = CriterionModifier.Equals;
public modifierOptions = [];
public options: string[] = [true.toString(), false.toString()]; public options: string[] = [true.toString(), false.toString()];
public value: string = ""; public value: string = "";
} }

View File

@@ -1,6 +1,6 @@
import { CriterionModifier } from "../../../core/generated-graphql";
import { import {
Criterion, Criterion,
CriterionModifier,
CriterionType, CriterionType,
ICriterionOption, ICriterionOption,
} from "./criterion"; } from "./criterion";
@@ -9,6 +9,7 @@ export class IsMissingCriterion extends Criterion<string, string> {
public type: CriterionType = "isMissing"; public type: CriterionType = "isMissing";
public parameterName: string = "is_missing"; public parameterName: string = "is_missing";
public modifier = CriterionModifier.Equals; public modifier = CriterionModifier.Equals;
public modifierOptions = [];
public options: string[] = ["title", "url", "date", "gallery", "studio", "performers"]; public options: string[] = ["title", "url", "date", "gallery", "studio", "performers"];
public value: string = ""; public value: string = "";
} }

View File

@@ -1,6 +1,6 @@
import { CriterionModifier } from "../../../core/generated-graphql";
import { import {
Criterion, Criterion,
CriterionModifier,
CriterionType, CriterionType,
ICriterionOption, ICriterionOption,
} from "./criterion"; } from "./criterion";
@@ -9,6 +9,7 @@ export class NoneCriterion extends Criterion<any, any> {
public type: CriterionType = "none"; public type: CriterionType = "none";
public parameterName: string = ""; public parameterName: string = "";
public modifier = CriterionModifier.Equals; public modifier = CriterionModifier.Equals;
public modifierOptions = [];
public options: any; public options: any;
public value: any; public value: any;
} }

View File

@@ -1,7 +1,7 @@
import { CriterionModifier } from "../../../core/generated-graphql";
import { ILabeledId } from "../types"; import { ILabeledId } from "../types";
import { import {
Criterion, Criterion,
CriterionModifier,
CriterionType, CriterionType,
ICriterionOption, ICriterionOption,
} from "./criterion"; } from "./criterion";
@@ -16,6 +16,7 @@ export class PerformersCriterion extends Criterion<IOptionType, ILabeledId[]> {
public type: CriterionType = "performers"; public type: CriterionType = "performers";
public parameterName: string = "performers"; public parameterName: string = "performers";
public modifier = CriterionModifier.Equals; public modifier = CriterionModifier.Equals;
public modifierOptions = [];
public options: IOptionType[] = []; public options: IOptionType[] = [];
public value: ILabeledId[] = []; public value: ILabeledId[] = [];
} }

View File

@@ -1,6 +1,6 @@
import { CriterionModifier } from "../../../core/generated-graphql";
import { import {
Criterion, Criterion,
CriterionModifier,
CriterionType, CriterionType,
ICriterionOption, ICriterionOption,
} from "./criterion"; } from "./criterion";
@@ -9,6 +9,14 @@ export class RatingCriterion extends Criterion<number, number> { // TODO <number
public type: CriterionType = "rating"; public type: CriterionType = "rating";
public parameterName: string = "rating"; public parameterName: string = "rating";
public modifier = CriterionModifier.Equals; public modifier = CriterionModifier.Equals;
public modifierOptions = [
Criterion.getModifierOption(CriterionModifier.Equals),
Criterion.getModifierOption(CriterionModifier.NotEquals),
Criterion.getModifierOption(CriterionModifier.GreaterThan),
Criterion.getModifierOption(CriterionModifier.LessThan),
Criterion.getModifierOption(CriterionModifier.IsNull),
Criterion.getModifierOption(CriterionModifier.NotNull),
];
public options: number[] = [1, 2, 3, 4, 5]; public options: number[] = [1, 2, 3, 4, 5];
public value: number = 0; public value: number = 0;
} }

View File

@@ -1,6 +1,6 @@
import { CriterionModifier } from "../../../core/generated-graphql";
import { import {
Criterion, Criterion,
CriterionModifier,
CriterionType, CriterionType,
ICriterionOption, ICriterionOption,
} from "./criterion"; } from "./criterion";
@@ -9,6 +9,7 @@ export class ResolutionCriterion extends Criterion<string, string> { // TODO <st
public type: CriterionType = "resolution"; public type: CriterionType = "resolution";
public parameterName: string = "resolution"; public parameterName: string = "resolution";
public modifier = CriterionModifier.Equals; public modifier = CriterionModifier.Equals;
public modifierOptions = [];
public options: string[] = ["240p", "480p", "720p", "1080p", "4k"]; public options: string[] = ["240p", "480p", "720p", "1080p", "4k"];
public value: string = ""; public value: string = "";
} }

View File

@@ -1,7 +1,7 @@
import { CriterionModifier } from "../../../core/generated-graphql";
import { ILabeledId } from "../types"; import { ILabeledId } from "../types";
import { import {
Criterion, Criterion,
CriterionModifier,
CriterionType, CriterionType,
ICriterionOption, ICriterionOption,
} from "./criterion"; } from "./criterion";
@@ -16,6 +16,7 @@ export class StudiosCriterion extends Criterion<IOptionType, ILabeledId[]> {
public type: CriterionType = "studios"; public type: CriterionType = "studios";
public parameterName: string = "studios"; public parameterName: string = "studios";
public modifier = CriterionModifier.Equals; public modifier = CriterionModifier.Equals;
public modifierOptions = [];
public options: IOptionType[] = []; public options: IOptionType[] = [];
public value: ILabeledId[] = []; public value: ILabeledId[] = [];
} }

View File

@@ -1,8 +1,8 @@
import * as GQL from "../../../core/generated-graphql"; import * as GQL from "../../../core/generated-graphql";
import { CriterionModifier } from "../../../core/generated-graphql";
import { ILabeledId } from "../types"; import { ILabeledId } from "../types";
import { import {
Criterion, Criterion,
CriterionModifier,
CriterionType, CriterionType,
ICriterionOption, ICriterionOption,
} from "./criterion"; } from "./criterion";
@@ -11,6 +11,7 @@ export class TagsCriterion extends Criterion<GQL.AllTagsForFilterAllTags, ILabel
public type: CriterionType; public type: CriterionType;
public parameterName: string; public parameterName: string;
public modifier = CriterionModifier.Equals; public modifier = CriterionModifier.Equals;
public modifierOptions = [];
public options: GQL.AllTagsForFilterAllTags[] = []; public options: GQL.AllTagsForFilterAllTags[] = [];
public value: ILabeledId[] = []; public value: ILabeledId[] = [];

View File

@@ -157,6 +157,7 @@ export class ListFilterModel {
const encodedCriterion = JSON.parse(jsonString); const encodedCriterion = JSON.parse(jsonString);
const criterion = makeCriteria(encodedCriterion.type); const criterion = makeCriteria(encodedCriterion.type);
criterion.value = encodedCriterion.value; criterion.value = encodedCriterion.value;
criterion.modifier = encodedCriterion.modifier;
this.criteria.push(criterion); this.criteria.push(criterion);
} }
} }
@@ -168,6 +169,7 @@ export class ListFilterModel {
const encodedCriterion: any = {}; const encodedCriterion: any = {};
encodedCriterion.type = criterion.type; encodedCriterion.type = criterion.type;
encodedCriterion.value = criterion.value; encodedCriterion.value = criterion.value;
encodedCriterion.modifier = criterion.modifier;
const jsonCriterion = JSON.stringify(encodedCriterion); const jsonCriterion = JSON.stringify(encodedCriterion);
encodedCriteria.push(jsonCriterion); encodedCriteria.push(jsonCriterion);
}); });
@@ -200,7 +202,8 @@ export class ListFilterModel {
this.criteria.forEach((criterion) => { this.criteria.forEach((criterion) => {
switch (criterion.type) { switch (criterion.type) {
case "rating": case "rating":
result.rating = (criterion as RatingCriterion).value; const crit = criterion as RatingCriterion;
result.rating = { value: crit.value, modifier: crit.modifier };
break; break;
case "resolution": { case "resolution": {
switch ((criterion as ResolutionCriterion).value) { switch ((criterion as ResolutionCriterion).value) {

View File

@@ -16,3 +16,8 @@ export interface ILabeledId {
id: string; id: string;
label: string; label: string;
} }
export interface ILabeledValue {
label: string;
value: string;
}