sqlite 使用TypeORM/SQL/NestJS查找循环乘积关系

q5iwbnjs  于 2023-04-30  发布在  SQLite
关注(0)|答案(1)|浏览(142)

目前,我有一个产品类如下:

@Entity()
export class Product {
  @PrimaryGeneratedColumn()
  id: number;

  @ManyToMany(() => Product, (product) => product.id, {
    onDelete: 'RESTRICT',
    onUpdate: 'CASCADE',
  })
  @JoinTable({ joinColumn: { name: 'product_id_1' } })
  sub_products: Product[];

  ...
}

当用户想要创建/更新产品时,我需要检查它们之间是否存在循环关系。
例如:
1.产品A,ID:1 { sub_products:[B] }
1.产品B,ID:2 { sub_products:[C] }
1.用户尝试更新产品C,ID为:3与{ sub_products:[A] } //应该防止这种情况
我的想法是:
1.通过查询数据库来递归构造一个图以获取下一个子数据。其中顶点是乘积,边表示父关系(来自:父,发送到:儿童)
1.从https://stackoverflow.com/a/53995651/13142405运行DFS循环检测算法
我的问题是:

  • 为了做(1),如果我只构造之前未访问的节点的顶点/边就足够了吗?也就是说,我将保留一组访问过的产品,如果我看到的下一个产品在这个集合中,我可以跳过构建它的顶点/边。
  • 用下面的测试来证明它的正确性就足够了吗?

测试:
1.无周期

1.平凡循环

1.树(注意4是如何被访问两次,但没有循环)

1.非平凡周期

fykwrbwg

fykwrbwg1#

解决方法:
1.构造未访问节点的顶点/边。
1.运行问题中提到的算法,稍微调整https://stackoverflow.com/a/53995651/13142405

export interface Edge<T> {
  from: T;
  to: T;
}

export class Graph<T> {
  adjLists: Map<T, Set<T>> = new Map<T, Set<T>>();

  copy(): Graph<T> {
    const g = new Graph<T>();
    g.adjLists = new Map([...this.adjLists]);
    return g;
  }

  // immutable
  merge(other: Graph<T>): Graph<T> {
    const copy = this.copy();
    for (const [key, value] of other.adjLists) {
      if (!copy.adjLists.has(key)) {
        copy.adjLists.set(key, value);
      } else {
        // handle collision
        const cAdjList = copy.adjLists.get(key);
        copy.adjLists.set(key, new Set([...cAdjList, ...value]));
      }
    }
    return copy;
  }

  addEdge({ from, to }: Edge<T>): Graph<T> {
    if (!this.adjLists.has(from)) this.adjLists.set(from, new Set<T>());
    this.adjLists.get(from).add(to);
    return this;
  }

  getCycles(): Edge<T>[] {
    const discovered: Set<T> = new Set();
    const finished: Set<T> = new Set();
    let cycles: Edge<T>[] = [];
    for (const u of [...this.adjLists.keys()]) {
      if (!discovered.has(u) && !finished.has(u)) {
        for (const cycle of getCyclesHelper<T>({
          G: this,
          u,
          discovered,
          finished,
        })) {
          cycles.push(cycle);
        }
      }
    }

    return cycles;
  }
}

function getCyclesHelper<T>({
  G,
  u,
  discovered,
  finished,
}: {
  G: Graph<T>;
  u: T;
  discovered: Set<T>;
  finished: Set<T>;
}): Edge<T>[] {
  discovered.add(u);
  let cycles: Edge<T>[] = [];
  for (const v of G.adjLists.get(u) ?? []) {
    if (discovered.has(v)) {
      const cycle: Edge<T> = {
        from: u,
        to: v,
      };
      cycles.push(cycle);
      break;
    }

    if (!finished.has(v)) {
      for (const cycle of getCyclesHelper<T>({
        G,
        u: v,
        discovered,
        finished,
      })) {
        cycles.push(cycle);
      }
    }
  }

  discovered.delete(u);
  finished.add(u);
  return cycles;
}

相关问题