mirror of
https://github.com/stashapp/stash.git
synced 2025-12-07 17:02:38 +01:00
More performer filter criteria (#179)
* Add new performer filter criteria to UI * Add backend support for new performer criteria
This commit is contained in:
parent
c0911f1626
commit
3c089dd97c
7 changed files with 478 additions and 43 deletions
|
|
@ -22,6 +22,30 @@ enum ResolutionEnum {
|
|||
input PerformerFilterType {
|
||||
"""Filter by favorite"""
|
||||
filter_favorites: Boolean
|
||||
"""Filter by birth year"""
|
||||
birth_year: IntCriterionInput
|
||||
"""Filter by age"""
|
||||
age: IntCriterionInput
|
||||
"""Filter by ethnicity"""
|
||||
ethnicity: StringCriterionInput
|
||||
"""Filter by country"""
|
||||
country: StringCriterionInput
|
||||
"""Filter by eye color"""
|
||||
eye_color: StringCriterionInput
|
||||
"""Filter by height"""
|
||||
height: StringCriterionInput
|
||||
"""Filter by measurements"""
|
||||
measurements: StringCriterionInput
|
||||
"""Filter by fake tits value"""
|
||||
fake_tits: StringCriterionInput
|
||||
"""Filter by career length"""
|
||||
career_length: StringCriterionInput
|
||||
"""Filter by tattoos"""
|
||||
tattoos: StringCriterionInput
|
||||
"""Filter by piercings"""
|
||||
piercings: StringCriterionInput
|
||||
"""Filter by aliases"""
|
||||
aliases: StringCriterionInput
|
||||
}
|
||||
|
||||
input SceneMarkerFilterType {
|
||||
|
|
@ -71,6 +95,11 @@ enum CriterionModifier {
|
|||
EXCLUDES,
|
||||
}
|
||||
|
||||
input StringCriterionInput {
|
||||
value: String!
|
||||
modifier: CriterionModifier!
|
||||
}
|
||||
|
||||
input IntCriterionInput {
|
||||
value: Int!
|
||||
modifier: CriterionModifier!
|
||||
|
|
|
|||
|
|
@ -2,6 +2,8 @@ package models
|
|||
|
||||
import (
|
||||
"database/sql"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/jmoiron/sqlx"
|
||||
"github.com/stashapp/stash/pkg/database"
|
||||
|
|
@ -111,30 +113,60 @@ func (qb *PerformerQueryBuilder) Query(performerFilter *PerformerFilterType, fin
|
|||
findFilter = &FindFilterType{}
|
||||
}
|
||||
|
||||
var whereClauses []string
|
||||
var havingClauses []string
|
||||
var args []interface{}
|
||||
body := selectDistinctIDs("performers")
|
||||
body += `
|
||||
query := queryBuilder{
|
||||
tableName: "performers",
|
||||
}
|
||||
|
||||
query.body = selectDistinctIDs("performers")
|
||||
query.body += `
|
||||
left join performers_scenes as scenes_join on scenes_join.performer_id = performers.id
|
||||
left join scenes on scenes_join.scene_id = scenes.id
|
||||
`
|
||||
|
||||
if q := findFilter.Q; q != nil && *q != "" {
|
||||
searchColumns := []string{"performers.name", "performers.checksum", "performers.birthdate", "performers.ethnicity"}
|
||||
whereClauses = append(whereClauses, getSearch(searchColumns, *q))
|
||||
clause, thisArgs := getSearchBinding(searchColumns, *q, false)
|
||||
query.addWhere(clause)
|
||||
query.addArg(thisArgs...)
|
||||
}
|
||||
|
||||
if favoritesFilter := performerFilter.FilterFavorites; favoritesFilter != nil {
|
||||
var favStr string
|
||||
if *favoritesFilter == true {
|
||||
whereClauses = append(whereClauses, "performers.favorite = 1")
|
||||
favStr = "1"
|
||||
} else {
|
||||
whereClauses = append(whereClauses, "performers.favorite = 0")
|
||||
favStr = "0"
|
||||
}
|
||||
query.addWhere("performers.favorite = " + favStr)
|
||||
}
|
||||
|
||||
sortAndPagination := qb.getPerformerSort(findFilter) + getPagination(findFilter)
|
||||
idsResult, countResult := executeFindQuery("performers", body, args, sortAndPagination, whereClauses, havingClauses)
|
||||
if birthYear := performerFilter.BirthYear; birthYear != nil {
|
||||
clauses, thisArgs := getBirthYearFilterClause(birthYear.Modifier, birthYear.Value)
|
||||
query.addWhere(clauses...)
|
||||
query.addArg(thisArgs...)
|
||||
}
|
||||
|
||||
if age := performerFilter.Age; age != nil {
|
||||
clauses, thisArgs := getAgeFilterClause(age.Modifier, age.Value)
|
||||
query.addWhere(clauses...)
|
||||
query.addArg(thisArgs...)
|
||||
}
|
||||
|
||||
handleStringCriterion("ethnicity", performerFilter.Ethnicity, &query)
|
||||
handleStringCriterion("country", performerFilter.Country, &query)
|
||||
handleStringCriterion("eye_color", performerFilter.EyeColor, &query)
|
||||
handleStringCriterion("height", performerFilter.Height, &query)
|
||||
handleStringCriterion("measurements", performerFilter.Measurements, &query)
|
||||
handleStringCriterion("fake_tits", performerFilter.FakeTits, &query)
|
||||
handleStringCriterion("career_length", performerFilter.CareerLength, &query)
|
||||
handleStringCriterion("tattoos", performerFilter.Tattoos, &query)
|
||||
handleStringCriterion("piercings", performerFilter.Piercings, &query)
|
||||
|
||||
// TODO - need better handling of aliases
|
||||
handleStringCriterion("aliases", performerFilter.Aliases, &query)
|
||||
|
||||
query.sortAndPagination = qb.getPerformerSort(findFilter) + getPagination(findFilter)
|
||||
idsResult, countResult := query.executeFind()
|
||||
|
||||
var performers []*Performer
|
||||
for _, id := range idsResult {
|
||||
|
|
@ -145,6 +177,98 @@ func (qb *PerformerQueryBuilder) Query(performerFilter *PerformerFilterType, fin
|
|||
return performers, countResult
|
||||
}
|
||||
|
||||
func handleStringCriterion(column string, value *StringCriterionInput, query *queryBuilder) {
|
||||
if value != nil {
|
||||
if modifier := value.Modifier.String(); value.Modifier.IsValid() {
|
||||
switch modifier {
|
||||
case "EQUALS":
|
||||
clause, thisArgs := getSearchBinding([]string{column}, value.Value, false)
|
||||
query.addWhere(clause)
|
||||
query.addArg(thisArgs...)
|
||||
case "NOT_EQUALS":
|
||||
clause, thisArgs := getSearchBinding([]string{column}, value.Value, true)
|
||||
query.addWhere(clause)
|
||||
query.addArg(thisArgs...)
|
||||
case "IS_NULL":
|
||||
query.addWhere(column + " IS NULL")
|
||||
case "NOT_NULL":
|
||||
query.addWhere(column + " IS NOT NULL")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func getBirthYearFilterClause(criterionModifier CriterionModifier, value int) ([]string, []interface{}) {
|
||||
var clauses []string
|
||||
var args []interface{}
|
||||
|
||||
yearStr := strconv.Itoa(value)
|
||||
startOfYear := yearStr + "-01-01"
|
||||
endOfYear := yearStr + "-12-31"
|
||||
|
||||
if modifier := criterionModifier.String(); criterionModifier.IsValid() {
|
||||
switch modifier {
|
||||
case "EQUALS":
|
||||
// between yyyy-01-01 and yyyy-12-31
|
||||
clauses = append(clauses, "performers.birthdate >= ?")
|
||||
clauses = append(clauses, "performers.birthdate <= ?")
|
||||
args = append(args, startOfYear)
|
||||
args = append(args, endOfYear)
|
||||
case "NOT_EQUALS":
|
||||
// outside of yyyy-01-01 to yyyy-12-31
|
||||
clauses = append(clauses, "performers.birthdate < ? OR performers.birthdate > ?")
|
||||
args = append(args, startOfYear)
|
||||
args = append(args, endOfYear)
|
||||
case "GREATER_THAN":
|
||||
// > yyyy-12-31
|
||||
clauses = append(clauses, "performers.birthdate > ?")
|
||||
args = append(args, endOfYear)
|
||||
case "LESS_THAN":
|
||||
// < yyyy-01-01
|
||||
clauses = append(clauses, "performers.birthdate < ?")
|
||||
args = append(args, startOfYear)
|
||||
}
|
||||
}
|
||||
|
||||
return clauses, args
|
||||
}
|
||||
|
||||
func getAgeFilterClause(criterionModifier CriterionModifier, value int) ([]string, []interface{}) {
|
||||
var clauses []string
|
||||
var args []interface{}
|
||||
|
||||
// get the date at which performer would turn the age specified
|
||||
dt := time.Now()
|
||||
birthDate := dt.AddDate(-value-1, 0, 0)
|
||||
yearAfter := birthDate.AddDate(1, 0, 0)
|
||||
|
||||
if modifier := criterionModifier.String(); criterionModifier.IsValid() {
|
||||
switch modifier {
|
||||
case "EQUALS":
|
||||
// between birthDate and yearAfter
|
||||
clauses = append(clauses, "performers.birthdate >= ?")
|
||||
clauses = append(clauses, "performers.birthdate < ?")
|
||||
args = append(args, birthDate)
|
||||
args = append(args, yearAfter)
|
||||
case "NOT_EQUALS":
|
||||
// outside of birthDate and yearAfter
|
||||
clauses = append(clauses, "performers.birthdate < ? OR performers.birthdate >= ?")
|
||||
args = append(args, birthDate)
|
||||
args = append(args, yearAfter)
|
||||
case "GREATER_THAN":
|
||||
// < birthDate
|
||||
clauses = append(clauses, "performers.birthdate < ?")
|
||||
args = append(args, birthDate)
|
||||
case "LESS_THAN":
|
||||
// > yearAfter
|
||||
clauses = append(clauses, "performers.birthdate >= ?")
|
||||
args = append(args, yearAfter)
|
||||
}
|
||||
}
|
||||
|
||||
return clauses, args
|
||||
}
|
||||
|
||||
func (qb *PerformerQueryBuilder) getPerformerSort(findFilter *FindFilterType) string {
|
||||
var sort string
|
||||
var direction string
|
||||
|
|
|
|||
|
|
@ -13,6 +13,33 @@ import (
|
|||
"github.com/stashapp/stash/pkg/logger"
|
||||
)
|
||||
|
||||
type queryBuilder struct {
|
||||
tableName string
|
||||
body string
|
||||
|
||||
whereClauses []string
|
||||
havingClauses []string
|
||||
args []interface{}
|
||||
|
||||
sortAndPagination string
|
||||
}
|
||||
|
||||
func (qb queryBuilder) executeFind() ([]int, int) {
|
||||
return executeFindQuery(qb.tableName, qb.body, qb.args, qb.sortAndPagination, qb.whereClauses, qb.havingClauses)
|
||||
}
|
||||
|
||||
func (qb *queryBuilder) addWhere(clauses ...string) {
|
||||
qb.whereClauses = append(qb.whereClauses, clauses...)
|
||||
}
|
||||
|
||||
func (qb *queryBuilder) addHaving(clauses ...string) {
|
||||
qb.havingClauses = append(qb.havingClauses, clauses...)
|
||||
}
|
||||
|
||||
func (qb *queryBuilder) addArg(args ...interface{}) {
|
||||
qb.args = append(qb.args, args...)
|
||||
}
|
||||
|
||||
var randomSortFloat = rand.Float64()
|
||||
|
||||
func selectAll(tableName string) string {
|
||||
|
|
@ -92,6 +119,7 @@ func getSort(sort string, direction string, tableName string) string {
|
|||
}
|
||||
|
||||
func getSearch(columns []string, q string) string {
|
||||
// TODO - susceptible to SQL injection
|
||||
var likeClauses []string
|
||||
queryWords := strings.Split(q, " ")
|
||||
trimmedQuery := strings.Trim(q, "\"")
|
||||
|
|
@ -113,6 +141,39 @@ func getSearch(columns []string, q string) string {
|
|||
return "(" + likes + ")"
|
||||
}
|
||||
|
||||
func getSearchBinding(columns []string, q string, not bool) (string, []interface{}) {
|
||||
var likeClauses []string
|
||||
var args []interface{}
|
||||
|
||||
notStr := ""
|
||||
binaryType := " OR "
|
||||
if not {
|
||||
notStr = " NOT "
|
||||
binaryType = " AND "
|
||||
}
|
||||
|
||||
queryWords := strings.Split(q, " ")
|
||||
trimmedQuery := strings.Trim(q, "\"")
|
||||
if trimmedQuery == q {
|
||||
// Search for any word
|
||||
for _, word := range queryWords {
|
||||
for _, column := range columns {
|
||||
likeClauses = append(likeClauses, column+notStr+" LIKE ?")
|
||||
args = append(args, "%"+word+"%")
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Search the exact query
|
||||
for _, column := range columns {
|
||||
likeClauses = append(likeClauses, column+notStr+" LIKE ?")
|
||||
args = append(args, "%"+trimmedQuery+"%")
|
||||
}
|
||||
}
|
||||
likes := strings.Join(likeClauses, binaryType)
|
||||
|
||||
return "(" + likes + ")", args
|
||||
}
|
||||
|
||||
func getInBinding(length int) string {
|
||||
bindings := strings.Repeat("?, ", length)
|
||||
bindings = strings.TrimRight(bindings, ", ")
|
||||
|
|
@ -128,22 +189,11 @@ func getCriterionModifierBinding(criterionModifier CriterionModifier, value inte
|
|||
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 "EQUALS", "NOT_EQUALS", "GREATER_THAN", "LESS_THAN", "IS_NULL", "NOT_NULL":
|
||||
return getSimpleCriterionClause(criterionModifier, "?")
|
||||
case "INCLUDES":
|
||||
return "IN " + getInBinding(length), length // TODO?
|
||||
case "EXCLUDES":
|
||||
|
|
@ -156,6 +206,30 @@ func getCriterionModifierBinding(criterionModifier CriterionModifier, value inte
|
|||
return "= ?", 1 // TODO
|
||||
}
|
||||
|
||||
func getSimpleCriterionClause(criterionModifier CriterionModifier, rhs string) (string, int) {
|
||||
if modifier := criterionModifier.String(); criterionModifier.IsValid() {
|
||||
switch modifier {
|
||||
case "EQUALS":
|
||||
return "= " + rhs, 1
|
||||
case "NOT_EQUALS":
|
||||
return "!= " + rhs, 1
|
||||
case "GREATER_THAN":
|
||||
return "> " + rhs, 1
|
||||
case "LESS_THAN":
|
||||
return "< " + rhs, 1
|
||||
case "IS_NULL":
|
||||
return "IS NULL", 0
|
||||
case "NOT_NULL":
|
||||
return "IS NOT NULL", 0
|
||||
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
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import {
|
|||
Dialog,
|
||||
FormGroup,
|
||||
HTMLSelect,
|
||||
InputGroup,
|
||||
} from "@blueprintjs/core";
|
||||
import _ from "lodash";
|
||||
import React, { FunctionComponent, useEffect, useRef, useState } from "react";
|
||||
|
|
@ -31,6 +32,8 @@ export const AddFilter: FunctionComponent<IAddFilterProps> = (props: IAddFilterP
|
|||
const [isOpen, setIsOpen] = useState(false);
|
||||
const [criterion, setCriterion] = useState<Criterion<any, any>>(new NoneCriterion());
|
||||
|
||||
const valueStage = useRef<any>(criterion.value);
|
||||
|
||||
// Configure if we are editing an existing criterion
|
||||
useEffect(() => {
|
||||
if (!props.editingCriterion) { return; }
|
||||
|
|
@ -56,10 +59,26 @@ export const AddFilter: FunctionComponent<IAddFilterProps> = (props: IAddFilterP
|
|||
setCriterion(newCriterion);
|
||||
}
|
||||
|
||||
function onChangedInput(event: React.ChangeEvent<HTMLInputElement>) {
|
||||
valueStage.current = event.target.value;
|
||||
}
|
||||
|
||||
function onBlurInput() {
|
||||
const newCriterion = _.cloneDeep(criterion);
|
||||
newCriterion.value = valueStage.current;
|
||||
setCriterion(newCriterion);
|
||||
}
|
||||
|
||||
function onAddFilter() {
|
||||
if (!isArray(criterion.value) && !!singleValueSelect.current) {
|
||||
const value = singleValueSelect.current.props.defaultValue;
|
||||
if (value === undefined || value === "" || typeof value === "number") { criterion.value = criterion.options[0]; }
|
||||
if (criterion.options && (value === undefined || value === "" || typeof value === "number")) {
|
||||
criterion.value = criterion.options[0];
|
||||
} else if (typeof value === "number" && value === undefined) {
|
||||
criterion.value = 0;
|
||||
} else if (value === undefined) {
|
||||
criterion.value = "";
|
||||
}
|
||||
}
|
||||
const oldId = !!props.editingCriterion ? props.editingCriterion.getId() : undefined;
|
||||
props.onAddCriterion(criterion, oldId);
|
||||
|
|
@ -119,14 +138,25 @@ export const AddFilter: FunctionComponent<IAddFilterProps> = (props: IAddFilterP
|
|||
);
|
||||
}
|
||||
} else {
|
||||
return (
|
||||
<HTMLSelect
|
||||
ref={singleValueSelect}
|
||||
options={criterion.options}
|
||||
onChange={onChangedSingleSelect}
|
||||
defaultValue={criterion.value}
|
||||
/>
|
||||
);
|
||||
if (criterion.options) {
|
||||
return (
|
||||
<HTMLSelect
|
||||
ref={singleValueSelect}
|
||||
options={criterion.options}
|
||||
onChange={onChangedSingleSelect}
|
||||
defaultValue={criterion.value}
|
||||
/>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<InputGroup
|
||||
type={criterion.inputType}
|
||||
onChange={onChangedInput}
|
||||
onBlur={onBlurInput}
|
||||
defaultValue={criterion.value ? criterion.value : ""}
|
||||
/>
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
return (
|
||||
|
|
|
|||
|
|
@ -12,7 +12,19 @@ export type CriterionType =
|
|||
"tags" |
|
||||
"sceneTags" |
|
||||
"performers" |
|
||||
"studios";
|
||||
"studios" |
|
||||
"birth_year" |
|
||||
"age" |
|
||||
"ethnicity" |
|
||||
"country" |
|
||||
"eye_color" |
|
||||
"height" |
|
||||
"measurements" |
|
||||
"fake_tits" |
|
||||
"career_length" |
|
||||
"tattoos" |
|
||||
"piercings" |
|
||||
"aliases";
|
||||
|
||||
export abstract class Criterion<Option = any, Value = any> {
|
||||
public static getLabel(type: CriterionType = "none"): string {
|
||||
|
|
@ -27,6 +39,18 @@ export abstract class Criterion<Option = any, Value = any> {
|
|||
case "sceneTags": return "Scene Tags";
|
||||
case "performers": return "Performers";
|
||||
case "studios": return "Studios";
|
||||
case "birth_year": return "Birth Year";
|
||||
case "age": return "Age";
|
||||
case "ethnicity": return "Ethnicity";
|
||||
case "country": return "Country";
|
||||
case "eye_color": return "Eye Color";
|
||||
case "height": return "Height";
|
||||
case "measurements": return "Measurements";
|
||||
case "fake_tits": return "Fake Tits";
|
||||
case "career_length": return "Career Length";
|
||||
case "tattoos": return "Tattoos";
|
||||
case "piercings": return "Piercings";
|
||||
case "aliases": return "Aliases";
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -48,8 +72,9 @@ export abstract class Criterion<Option = any, Value = any> {
|
|||
public abstract parameterName: string;
|
||||
public abstract modifier: CriterionModifier;
|
||||
public abstract modifierOptions: ILabeledValue[];
|
||||
public abstract options: Option[];
|
||||
public abstract options: Option[] | undefined;
|
||||
public abstract value: Value;
|
||||
public inputType: "number" | "text" | undefined;
|
||||
|
||||
public getLabel(): string {
|
||||
let modifierString: string;
|
||||
|
|
@ -102,3 +127,71 @@ export interface ICriterionOption {
|
|||
label: string;
|
||||
value: CriterionType;
|
||||
}
|
||||
|
||||
export class CriterionOption implements ICriterionOption {
|
||||
public label: string;
|
||||
public value: CriterionType;
|
||||
|
||||
constructor(label : string, value : CriterionType) {
|
||||
this.label = label;
|
||||
this.value = value;
|
||||
}
|
||||
}
|
||||
|
||||
export class StringCriterion extends Criterion<string, string> {
|
||||
public type: CriterionType;
|
||||
public parameterName: string;
|
||||
public modifier = CriterionModifier.Equals;
|
||||
public modifierOptions = [
|
||||
Criterion.getModifierOption(CriterionModifier.Equals),
|
||||
Criterion.getModifierOption(CriterionModifier.NotEquals),
|
||||
Criterion.getModifierOption(CriterionModifier.IsNull),
|
||||
Criterion.getModifierOption(CriterionModifier.NotNull),
|
||||
];
|
||||
public options: string[] | undefined;
|
||||
public value: string = "";
|
||||
|
||||
constructor(type : CriterionType, parameterName?: string, options? : string[]) {
|
||||
super();
|
||||
|
||||
this.type = type;
|
||||
this.options = options;
|
||||
this.inputType = "text";
|
||||
|
||||
if (!!parameterName) {
|
||||
this.parameterName = parameterName;
|
||||
} else {
|
||||
this.parameterName = type;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class NumberCriterion extends Criterion<number, number> {
|
||||
public type: CriterionType;
|
||||
public parameterName: string;
|
||||
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[] | undefined;
|
||||
public value: number = 0;
|
||||
|
||||
constructor(type : CriterionType, parameterName?: string, options? : number[]) {
|
||||
super();
|
||||
|
||||
this.type = type;
|
||||
this.options = options;
|
||||
this.inputType = "number";
|
||||
|
||||
if (!!parameterName) {
|
||||
this.parameterName = parameterName;
|
||||
} else {
|
||||
this.parameterName = type;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,12 +1,7 @@
|
|||
import { QueryHookResult } from "react-apollo-hooks";
|
||||
import {
|
||||
AllPerformersForFilterQuery,
|
||||
AllPerformersForFilterVariables,
|
||||
AllTagsForFilterQuery,
|
||||
AllTagsForFilterVariables,
|
||||
CriterionModifier,
|
||||
} from "../../../core/generated-graphql";
|
||||
import { StashService } from "../../../core/StashService";
|
||||
import { Criterion, CriterionType } from "./criterion";
|
||||
import { Criterion, CriterionType, StringCriterion, NumberCriterion } from "./criterion";
|
||||
import { FavoriteCriterion } from "./favorite";
|
||||
import { HasMarkersCriterion } from "./has-markers";
|
||||
import { IsMissingCriterion } from "./is-missing";
|
||||
|
|
@ -29,5 +24,28 @@ export function makeCriteria(type: CriterionType = "none") {
|
|||
case "sceneTags": return new TagsCriterion("sceneTags");
|
||||
case "performers": return new PerformersCriterion();
|
||||
case "studios": return new StudiosCriterion();
|
||||
|
||||
case "birth_year":
|
||||
case "age":
|
||||
var ret = new NumberCriterion(type, type);
|
||||
// null/not null doesn't make sense for these criteria
|
||||
ret.modifierOptions = [
|
||||
Criterion.getModifierOption(CriterionModifier.Equals),
|
||||
Criterion.getModifierOption(CriterionModifier.NotEquals),
|
||||
Criterion.getModifierOption(CriterionModifier.GreaterThan),
|
||||
Criterion.getModifierOption(CriterionModifier.LessThan)
|
||||
];
|
||||
return ret;
|
||||
case "ethnicity":
|
||||
case "country":
|
||||
case "eye_color":
|
||||
case "height":
|
||||
case "measurements":
|
||||
case "fake_tits":
|
||||
case "career_length":
|
||||
case "tattoos":
|
||||
case "piercings":
|
||||
case "aliases":
|
||||
return new StringCriterion(type, type);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import {
|
|||
SceneMarkerFilterType,
|
||||
SortDirectionEnum,
|
||||
} from "../../core/generated-graphql";
|
||||
import { Criterion, ICriterionOption } from "./criteria/criterion";
|
||||
import { Criterion, ICriterionOption, CriterionType, CriterionOption, NumberCriterion, StringCriterion } from "./criteria/criterion";
|
||||
import { FavoriteCriterion, FavoriteCriterionOption } from "./criteria/favorite";
|
||||
import { HasMarkersCriterion, HasMarkersCriterionOption } from "./criteria/has-markers";
|
||||
import { IsMissingCriterion, IsMissingCriterionOption } from "./criteria/is-missing";
|
||||
|
|
@ -75,10 +75,29 @@ export class ListFilterModel {
|
|||
DisplayMode.Grid,
|
||||
DisplayMode.List,
|
||||
];
|
||||
|
||||
var numberCriteria : CriterionType[] = ["birth_year", "age"];
|
||||
var stringCriteria : CriterionType[] = [
|
||||
"ethnicity",
|
||||
"country",
|
||||
"eye_color",
|
||||
"height",
|
||||
"measurements",
|
||||
"fake_tits",
|
||||
"career_length",
|
||||
"tattoos",
|
||||
"piercings",
|
||||
"aliases"
|
||||
];
|
||||
|
||||
this.criterionOptions = [
|
||||
new NoneCriterionOption(),
|
||||
new FavoriteCriterionOption(),
|
||||
new FavoriteCriterionOption()
|
||||
];
|
||||
|
||||
this.criterionOptions = this.criterionOptions.concat(numberCriteria.concat(stringCriteria).map((c) => {
|
||||
return new CriterionOption(Criterion.getLabel(c), c);
|
||||
}));
|
||||
break;
|
||||
case FilterMode.Studios:
|
||||
if (!!this.sortBy === false) { this.sortBy = "name"; }
|
||||
|
|
@ -245,6 +264,54 @@ export class ListFilterModel {
|
|||
case "favorite":
|
||||
result.filter_favorites = (criterion as FavoriteCriterion).value === "true";
|
||||
break;
|
||||
case "birth_year":
|
||||
const byCrit = criterion as NumberCriterion;
|
||||
result.birth_year = { value: byCrit.value, modifier: byCrit.modifier };
|
||||
break;
|
||||
case "age":
|
||||
const ageCrit = criterion as NumberCriterion;
|
||||
result.age = { value: ageCrit.value, modifier: ageCrit.modifier };
|
||||
break;
|
||||
case "ethnicity":
|
||||
const ethCrit = criterion as StringCriterion;
|
||||
result.ethnicity = { value: ethCrit.value, modifier: ethCrit.modifier };
|
||||
break;
|
||||
case "country":
|
||||
const cntryCrit = criterion as StringCriterion;
|
||||
result.country = { value: cntryCrit.value, modifier: cntryCrit.modifier };
|
||||
break;
|
||||
case "eye_color":
|
||||
const ecCrit = criterion as StringCriterion;
|
||||
result.eye_color = { value: ecCrit.value, modifier: ecCrit.modifier };
|
||||
break;
|
||||
case "height":
|
||||
const hCrit = criterion as StringCriterion;
|
||||
result.height = { value: hCrit.value, modifier: hCrit.modifier };
|
||||
break;
|
||||
case "measurements":
|
||||
const mCrit = criterion as StringCriterion;
|
||||
result.measurements = { value: mCrit.value, modifier: mCrit.modifier };
|
||||
break;
|
||||
case "fake_tits":
|
||||
const ftCrit = criterion as StringCriterion;
|
||||
result.fake_tits = { value: ftCrit.value, modifier: ftCrit.modifier };
|
||||
break;
|
||||
case "career_length":
|
||||
const clCrit = criterion as StringCriterion;
|
||||
result.career_length = { value: clCrit.value, modifier: clCrit.modifier };
|
||||
break;
|
||||
case "tattoos":
|
||||
const tCrit = criterion as StringCriterion;
|
||||
result.tattoos = { value: tCrit.value, modifier: tCrit.modifier };
|
||||
break;
|
||||
case "piercings":
|
||||
const pCrit = criterion as StringCriterion;
|
||||
result.piercings = { value: pCrit.value, modifier: pCrit.modifier };
|
||||
break;
|
||||
case "aliases":
|
||||
const aCrit = criterion as StringCriterion;
|
||||
result.aliases = { value: aCrit.value, modifier: aCrit.modifier };
|
||||
break;
|
||||
}
|
||||
});
|
||||
return result;
|
||||
|
|
|
|||
Loading…
Reference in a new issue