diff --git a/graphql/schema/types/filters.graphql b/graphql/schema/types/filters.graphql index 894f30e50a5..efc3552dce1 100644 --- a/graphql/schema/types/filters.graphql +++ b/graphql/schema/types/filters.graphql @@ -48,6 +48,8 @@ input PerformerFilterType { aliases: StringCriterionInput """Filter by gender""" gender: GenderCriterionInput + """Filter to only include performers missing this property""" + is_missing: String } input SceneMarkerFilterType { diff --git a/pkg/models/querybuilder_performer.go b/pkg/models/querybuilder_performer.go index 84991c16137..1c4017271aa 100644 --- a/pkg/models/querybuilder_performer.go +++ b/pkg/models/querybuilder_performer.go @@ -158,6 +158,45 @@ func (qb *PerformerQueryBuilder) Query(performerFilter *PerformerFilterType, fin query.addArg(gender.Value.String()) } + if isMissingFilter := performerFilter.IsMissing; isMissingFilter != nil && *isMissingFilter != "" { + switch *isMissingFilter { + case "image": + query.addWhere("performers.image IS NULL") + case "url": + query.addWhere("performers.url IS NULL") + case "twitter": + query.addWhere("performers.twitter IS NULL") + case "instagram": + query.addWhere("performers.instagram IS NULL") + case "ethnicity": + query.addWhere("performers.ethnicity IS NULL") + case "country": + query.addWhere("performers.country IS NULL") + case "eye_color": + query.addWhere("performers.eye_color IS NULL") + case "height": + query.addWhere("performers.height IS NULL") + case "measurements": + query.addWhere("performers.measurements IS NULL") + case "fake_tits": + query.addWhere("performers.fake_tits IS NULL") + case "career_length": + query.addWhere("performers.career_length IS NULL") + case "tattoos": + query.addWhere("performers.tattoos IS NULL") + case "piercings": + query.addWhere("performers.piercings IS NULL") + case "aliases": + query.addWhere("performers.aliases IS NULL") + case "gender": + query.addWhere("performers.gender IS NULL") + case "scenes": + query.addWhere("scenes_join.scene_id IS NULL") + default: + query.addWhere("performers." + *isMissingFilter + " IS NULL") + } + } + handleStringCriterion(tableName+".ethnicity", performerFilter.Ethnicity, &query) handleStringCriterion(tableName+".country", performerFilter.Country, &query) handleStringCriterion(tableName+".eye_color", performerFilter.EyeColor, &query) diff --git a/ui/v2.5/src/models/list-filter/criteria/criterion.ts b/ui/v2.5/src/models/list-filter/criteria/criterion.ts index 752131b20f3..b95a9473814 100644 --- a/ui/v2.5/src/models/list-filter/criteria/criterion.ts +++ b/ui/v2.5/src/models/list-filter/criteria/criterion.ts @@ -12,7 +12,8 @@ export type CriterionType = | "duration" | "favorite" | "hasMarkers" - | "isMissing" + | "SceneIsMissing" + | "PerformerIsMissing" | "tags" | "sceneTags" | "performers" @@ -52,7 +53,9 @@ export abstract class Criterion { return "Favorite"; case "hasMarkers": return "Has Markers"; - case "isMissing": + case "SceneIsMissing": + return "Is Missing"; + case "PerformerIsMissing": return "Is Missing"; case "tags": return "Tags"; diff --git a/ui/v2.5/src/models/list-filter/criteria/is-missing.ts b/ui/v2.5/src/models/list-filter/criteria/is-missing.ts index 26419860d3f..1207094a39b 100644 --- a/ui/v2.5/src/models/list-filter/criteria/is-missing.ts +++ b/ui/v2.5/src/models/list-filter/criteria/is-missing.ts @@ -1,8 +1,8 @@ import { CriterionModifier } from "src/core/generated-graphql"; import { Criterion, CriterionType, ICriterionOption } from "./criterion"; -export class IsMissingCriterion extends Criterion { - public type: CriterionType = "isMissing"; +export class SceneIsMissingCriterion extends Criterion { + public type: CriterionType = "SceneIsMissing"; public parameterName: string = "is_missing"; public modifier = CriterionModifier.Equals; public modifierOptions = []; @@ -18,7 +18,38 @@ export class IsMissingCriterion extends Criterion { public value: string = ""; } -export class IsMissingCriterionOption implements ICriterionOption { - public label: string = Criterion.getLabel("isMissing"); - public value: CriterionType = "isMissing"; +export class SceneIsMissingCriterionOption implements ICriterionOption { + public label: string = Criterion.getLabel("SceneIsMissing"); + public value: CriterionType = "SceneIsMissing"; +} + +export class PerformerIsMissingCriterion extends Criterion { + public type: CriterionType = "PerformerIsMissing"; + public parameterName: string = "is_missing"; + public modifier = CriterionModifier.Equals; + public modifierOptions = []; + public options: string[] = [ + "image", + "url", + "twitter", + "instagram", + "ethnicity", + "country", + "eye_color", + "height", + "measurements", + "fake_tits", + "career_length", + "tattoos", + "piercings", + "aliases", + "gender", + "scenes" + ]; + public value: string = ""; +} + +export class PerformerIsMissingCriterionOption implements ICriterionOption { + public label: string = Criterion.getLabel("PerformerIsMissing"); + public value: CriterionType = "PerformerIsMissing"; } diff --git a/ui/v2.5/src/models/list-filter/criteria/utils.ts b/ui/v2.5/src/models/list-filter/criteria/utils.ts index afd265d05b3..811af34e0af 100644 --- a/ui/v2.5/src/models/list-filter/criteria/utils.ts +++ b/ui/v2.5/src/models/list-filter/criteria/utils.ts @@ -9,7 +9,7 @@ import { } from "./criterion"; import { FavoriteCriterion } from "./favorite"; import { HasMarkersCriterion } from "./has-markers"; -import { IsMissingCriterion } from "./is-missing"; +import {PerformerIsMissingCriterion, SceneIsMissingCriterion} from "./is-missing"; import { NoneCriterion } from "./none"; import { PerformersCriterion } from "./performers"; import { RatingCriterion } from "./rating"; @@ -35,8 +35,10 @@ export function makeCriteria(type: CriterionType = "none") { return new FavoriteCriterion(); case "hasMarkers": return new HasMarkersCriterion(); - case "isMissing": - return new IsMissingCriterion(); + case "SceneIsMissing": + return new SceneIsMissingCriterion(); + case "PerformerIsMissing": + return new PerformerIsMissingCriterion(); case "tags": return new TagsCriterion("tags"); case "sceneTags": @@ -47,7 +49,6 @@ export function makeCriteria(type: CriterionType = "none") { return new StudiosCriterion(); case "movies": return new MoviesCriterion(); - case "birth_year": return new NumberCriterion(type, type); case "age": { @@ -61,7 +62,7 @@ export function makeCriteria(type: CriterionType = "none") { ]; return ret; } - case "gender": + case "gender": return new GenderCriterion(); case "ethnicity": case "country": diff --git a/ui/v2.5/src/models/list-filter/filter.ts b/ui/v2.5/src/models/list-filter/filter.ts index 1fdb8ba67b5..4de49d0677d 100644 --- a/ui/v2.5/src/models/list-filter/filter.ts +++ b/ui/v2.5/src/models/list-filter/filter.ts @@ -25,8 +25,10 @@ import { HasMarkersCriterionOption } from "./criteria/has-markers"; import { - IsMissingCriterion, - IsMissingCriterionOption + PerformerIsMissingCriterion, + PerformerIsMissingCriterionOption, + SceneIsMissingCriterion, + SceneIsMissingCriterionOption } from "./criteria/is-missing"; import { NoneCriterionOption } from "./criteria/none"; import { @@ -115,7 +117,7 @@ export class ListFilterModel { new ResolutionCriterionOption(), ListFilterModel.createCriterionOption("duration"), new HasMarkersCriterionOption(), - new IsMissingCriterionOption(), + new SceneIsMissingCriterionOption(), new TagsCriterionOption(), new PerformersCriterionOption(), new StudiosCriterionOption(), @@ -145,13 +147,10 @@ export class ListFilterModel { new NoneCriterionOption(), new FavoriteCriterionOption(), new GenderCriterionOption(), + new PerformerIsMissingCriterionOption(), + ...numberCriteria.concat(stringCriteria).map(c => ListFilterModel.createCriterionOption(c)) ]; - this.criterionOptions = this.criterionOptions.concat( - numberCriteria.concat(stringCriteria).map(c => { - return ListFilterModel.createCriterionOption(c); - }) - ); break; } case FilterMode.Studios: @@ -381,8 +380,8 @@ export class ListFilterModel { case "hasMarkers": result.has_markers = (criterion as HasMarkersCriterion).value; break; - case "isMissing": - result.is_missing = (criterion as IsMissingCriterion).value; + case "SceneIsMissing": + result.is_missing = (criterion as SceneIsMissingCriterion).value; break; case "tags": { const tagsCrit = criterion as TagsCriterion; @@ -510,6 +509,8 @@ export class ListFilterModel { result.gender = { value: StashService.stringToGender(gCrit.value), modifier: gCrit.modifier }; break; } + case "PerformerIsMissing": + result.is_missing = (criterion as PerformerIsMissingCriterion).value; // no default } });