Delete orphaned tags

TG-109 #ready-for-test

Signed-off-by: trivernis <trivernis@protonmail.com>
pull/18/head
trivernis 3 years ago
parent a94cc48a5c
commit 179dcf0d4e
Signed by: Trivernis
GPG Key ID: DFFFCC2C7A02DB45

@ -1,9 +1,10 @@
use sea_orm::prelude::*; use sea_orm::prelude::*;
use sea_orm::sea_query::Query;
use sea_orm::ActiveValue::Set; use sea_orm::ActiveValue::Set;
use sea_orm::{DatabaseTransaction, TransactionTrait}; use sea_orm::{DatabaseTransaction, TransactionTrait};
use mediarepo_core::error::RepoResult; use mediarepo_core::error::RepoResult;
use mediarepo_database::entities::content_descriptor_tag; use mediarepo_database::entities::{content_descriptor_tag, namespace, tag};
use crate::dao::tag::TagDao; use crate::dao::tag::TagDao;
@ -41,11 +42,15 @@ impl TagDao {
#[tracing::instrument(level = "debug", skip(self))] #[tracing::instrument(level = "debug", skip(self))]
pub async fn remove_mappings(&self, cd_ids: Vec<i64>, tag_ids: Vec<i64>) -> RepoResult<()> { pub async fn remove_mappings(&self, cd_ids: Vec<i64>, tag_ids: Vec<i64>) -> RepoResult<()> {
let trx = self.ctx.db.begin().await?;
content_descriptor_tag::Entity::delete_many() content_descriptor_tag::Entity::delete_many()
.filter(content_descriptor_tag::Column::CdId.is_in(cd_ids)) .filter(content_descriptor_tag::Column::CdId.is_in(cd_ids))
.filter(content_descriptor_tag::Column::TagId.is_in(tag_ids)) .filter(content_descriptor_tag::Column::TagId.is_in(tag_ids))
.exec(&self.ctx.db) .exec(&trx)
.await?; .await?;
delete_orphans(&trx).await?;
trx.commit().await?;
Ok(()) Ok(())
} }
@ -66,3 +71,34 @@ async fn get_existing_mappings(
.collect(); .collect();
Ok(existing_mappings) Ok(existing_mappings)
} }
/// Deletes orphaned tag entries and namespaces from the database
async fn delete_orphans(trx: &DatabaseTransaction) -> RepoResult<()> {
tag::Entity::delete_many()
.filter(
tag::Column::Id.not_in_subquery(
Query::select()
.column(content_descriptor_tag::Column::TagId)
.from(content_descriptor_tag::Entity)
.group_by_col(content_descriptor_tag::Column::TagId)
.to_owned(),
),
)
.exec(trx)
.await?;
namespace::Entity::delete_many()
.filter(
namespace::Column::Id.not_in_subquery(
Query::select()
.column(tag::Column::NamespaceId)
.from(tag::Entity)
.group_by_col(tag::Column::NamespaceId)
.to_owned(),
),
)
.exec(trx)
.await?;
Ok(())
}

@ -18,7 +18,7 @@
<div class="tag-input" fxFlex="200px"> <div class="tag-input" fxFlex="200px">
<div class="tag-input-field"> <div class="tag-input-field">
<app-tag-input (tagAdded)="this.editTag($event)" [allowInvalid]="true" <app-tag-input (tagAdded)="this.editTag($event)" [allowInvalid]="true"
[availableTags]="this.allTags"></app-tag-input> [availableTags]="(this.allTags | async) ?? []"></app-tag-input>
<button class="add-tag-button" mat-icon-button> <button class="add-tag-button" mat-icon-button>
<ng-icon *ngIf="editMode === 'Toggle'" name="mat-change-circle"></ng-icon> <ng-icon *ngIf="editMode === 'Toggle'" name="mat-change-circle"></ng-icon>
<ng-icon *ngIf="editMode === 'Add'" name="mat-add-circle"></ng-icon> <ng-icon *ngIf="editMode === 'Add'" name="mat-add-circle"></ng-icon>

@ -16,6 +16,7 @@ import {CdkVirtualScrollViewport} from "@angular/cdk/scrolling";
import {TagService} from "../../../../services/tag/tag.service"; import {TagService} from "../../../../services/tag/tag.service";
import {LoggingService} from "../../../../services/logging/logging.service"; import {LoggingService} from "../../../../services/logging/logging.service";
import {BusyIndicatorComponent} from "../../app-common/busy-indicator/busy-indicator.component"; import {BusyIndicatorComponent} from "../../app-common/busy-indicator/busy-indicator.component";
import {Observable} from "rxjs";
@Component({ @Component({
selector: "app-tag-edit", selector: "app-tag-edit",
@ -32,7 +33,7 @@ export class TagEditComponent implements AfterViewInit, OnChanges {
@ViewChild(BusyIndicatorComponent) busyIndicator!: BusyIndicatorComponent; @ViewChild(BusyIndicatorComponent) busyIndicator!: BusyIndicatorComponent;
public tags: Tag[] = []; public tags: Tag[] = [];
public allTags: Tag[] = []; public allTags: Observable<Tag[]>;
public editMode: string = "Toggle"; public editMode: string = "Toggle";
private fileTags: { [key: number]: Tag[] } = {}; private fileTags: { [key: number]: Tag[] } = {};
@ -41,10 +42,10 @@ export class TagEditComponent implements AfterViewInit, OnChanges {
private logger: LoggingService, private logger: LoggingService,
private tagService: TagService, private tagService: TagService,
) { ) {
this.allTags = tagService.tags.asObservable();
} }
async ngAfterViewInit() { async ngAfterViewInit() {
this.tagService.tags.subscribe(tags => this.allTags = tags);
await this.tagService.loadTags(); await this.tagService.loadTags();
await this.tagService.loadNamespaces(); await this.tagService.loadNamespaces();
await this.loadFileTags(); await this.loadFileTags();
@ -58,12 +59,12 @@ export class TagEditComponent implements AfterViewInit, OnChanges {
public async editTag(tag: string): Promise<void> { public async editTag(tag: string): Promise<void> {
if (tag.length > 0) { if (tag.length > 0) {
let tagInstance = this.allTags.find( let tagInstance = this.tagService.tags.value.find(
t => t.getNormalizedOutput() === tag); t => t.getNormalizedOutput() === tag);
if (!tagInstance) { if (!tagInstance) {
tagInstance = (await this.tagService.createTags([tag]))[0]; tagInstance = (await this.tagService.createTags([tag]))[0];
this.allTags.push(tagInstance); this.tagService.tags.next([...this.tagService.tags.value, tagInstance]);
} }
this.changeDetector.markForCheck(); this.changeDetector.markForCheck();
switch (this.editMode) { switch (this.editMode) {
@ -95,11 +96,13 @@ export class TagEditComponent implements AfterViewInit, OnChanges {
file.id, file.id,
addedTags, removedTags addedTags, removedTags
); );
if (addedTags.length > 0) { if (addedTags.length > 0) {
await this.tagService.loadTags(); await this.tagService.loadTags();
await this.tagService.loadNamespaces();
} }
} }
await this.tagService.loadTags();
await this.tagService.loadNamespaces();
this.mapFileTagsToTagList(); this.mapFileTagsToTagList();
const index = this.tags.indexOf(tag); const index = this.tags.indexOf(tag);
if (index >= 0) { if (index >= 0) {
@ -142,6 +145,8 @@ export class TagEditComponent implements AfterViewInit, OnChanges {
); );
} }
} }
await this.tagService.loadTags();
await this.tagService.loadNamespaces();
this.mapFileTagsToTagList(); this.mapFileTagsToTagList();
}); });
this.tagEditEvent.emit(this); this.tagEditEvent.emit(this);

Loading…
Cancel
Save