Vincit / objection.js

An SQL-friendly ORM for Node.js

Home Page:https://vincit.github.io/objection.js

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Cross-schema relations / graph upserts

davidjamesstone opened this issue · comments

I'd like to start by saying a big thank you to @koskimas for supporting this library over the years. It's one of the two or three goto node libraries I use pretty much everywhere, along with hapi and knex. It has a fantastic design, powerful and is very intuitive to use. Like a lot of others, I was sad to read on the future of Objection but completely understand. Time is precious and maintaining a library of this complexity (for nothing) is unsustainable. Best of luck in whatever you're choosing to do with your time now.

We're continuing to use Objection for now but we'll keep an eye on developments, hopefully it won't be abandoned.

Cross-schema graph method support

I have been working on a project that uses a PG db with 2 schema with FK's between them.

I have read this issue and tried the various ways/hacks to get cross-schema withGraphFetched, upsertGraph etc. to work when there are two schemas involved. Nothing seems to work (at least in v3x), I'm not even sure any of the suggested approaches have ever worked in older versions of Objection TBH.

So - I'm here asking if anyone has got cross-schema relations to work in Objection??

Here's a short example of what I would like to be able to do. I have two schemas: A and B. Schema A contains a model/table called Post and schema B has Tags. There's a many-to-many join table in A called postTags.

import Knex from 'knex'
import { Model, QueryBuilder } from 'objection'
import * as knexfile from '../knexfile.mjs'

const knex = Knex(knexfile)
const schemaAName = 'A'
const schemaBName = 'B'

Model.knex(knex)

class SchemaQueryBuilderA extends QueryBuilder {
  constructor(modelClass) {
    super(modelClass)
    this.withSchema(schemaAName)
  }
}

export class BaseModelA extends Model {
  static query() {
    // https://github.com/Vincit/objection.js/issues/85#issuecomment-185183032
    return super.query().withSchema(schemaAName)
  }

  static relatedQuery() {
    // https://github.com/Vincit/objection.js/issues/85#issuecomment-185183032
    return super.relatedQuery().withSchema(schemaAName)
  }

  static get QueryBuilder() {
    return SchemaQueryBuilderA
  }

  static get RelatedQueryBuilder() {
    return SchemaQueryBuilderA
  }
}


class SchemaQueryBuilderB extends QueryBuilder {
  constructor(modelClass) {
    super(modelClass)
    this.withSchema(schemaBName)
  }
}

export class BaseModelB extends Model {
  static query() {
    // https://github.com/Vincit/objection.js/issues/85#issuecomment-185183032
    return super.query().withSchema(schemaAName)
  }

  static relatedQuery() {
    // https://github.com/Vincit/objection.js/issues/85#issuecomment-185183032
    return super.relatedQuery().withSchema(schemaAName)
  }

  static get QueryBuilder() {
    return SchemaQueryBuilderB
  }

  static get RelatedQueryBuilder() {
    return SchemaQueryBuilderB
  }
}

// Tag Model
class Tag extends BaseModelB {
  static tableName = 'tag'

  static get relationMappings() {
    return {
      posts: {
        relation: BaseModel.ManyToManyRelation,
        modelClass: Post,
        join: {
          from: 'tag.id',
          through: {
            from: 'postTag.tagId',
            to: 'postTag.postId'
          },
          to: 'post.id'
        }
      }
    }
  }
}

// Post Model
class Post extends BaseModelA {
  static tableName = 'post'

  static get relationMappings() {
    return {
      tags: {
        relation: BaseModel.ManyToManyRelation,
        modelClass: Tag,
        join: {
          from: 'post.id',
          through: {
            from: 'postTag.postId',
            to: 'postTag.tagId'
          },
          to: 'tag.id'
        }
      }
    }
  }
}

This approach implements a custom query builder in both BaseModels, overriding the static QueryBuilder and RelatedQueryBuilder methods. The custom builder simply calls knex's withSchema. The base classes also override the query() and relatedQuery() methods.

I'd then like to call methods on e.g. Post and fetch related tags like:

Post.query().where('id', id).withGraphFetched('[tags]')

Or be able to upsert Post with tags using the updateGraph methods.

This doesn't seem to work and I'd like to know if anyone has had more success? Objection generates SQL but does not qualify the tables with the correct schema name. It always uses the schema that started the query chain.

Hopefully, someone can tell me categorically that this doesn't actually work, contrary to some of the comments on other issues.

I also tried the approach of encoding the schema name into the relations like this (B.tag.id)

static get relationMappings() {
  return {
    tags: {
      relation: BaseModel.ManyToManyRelation,
      modelClass: Tag,
      join: {
        from: 'post.id',
        through: {
          from: 'postTag.postId',
          to: 'postTag.tagId'
        },
        to: 'B.tag.id'
      }
    }
  }
}

I wasn't expecting this to work and it didn't.

Any help or pointers on cross-schema graph/fetches would be greatly received - even if just to confirm that it isn't supported.

Thanks!

@davidjamesstone could you try a custom query builder that adds support back for the defaultSchema field? You can find an example of this here: #2147 (comment), see DefaultSchemaQueryBuilder and BaseModel

I'm closing this in the absence of a response to my suggestion. I'm happy to reopen if the suggestion does not work.