TYPEORM/Postgres: one-directional foreign key - typeorm

I have an entity that represents a wallet transaction. This Entity needs to be generic so I have it declared in a package and then reuse it in different services. I will explain what I am trying to do on an example.
Each service that uses the wallet transaction base class will have tables, that refer to it. For instance, I have a shop service, that has a list of transactions and table of sales and a table of returns. Another service, let's call it payments, will have the same wallet transaction table in its own database but the tables that will refer to it will be different - for instance deposits and payouts.
The common thing here is that in the wallet transaction table, there is a column with an id that references something in a separate table. Explained with code:
Generic Transaction table
export abstract class TransactionEntity<TransactionType> {
#PrimaryGeneratedColumn('identity', {
type: 'bigint',
generatedIdentity: 'ALWAYS',
primaryKeyConstraintName: PK_TRANSACTION_ID,
})
id!: number;
#Unique(UQ_WALLET_TRANSACTION_ID, ['walletTransactionId'])
#Column({ type: 'bigint', nullable: true })
walletTransactionId!: number;
#Column('text')
userId!: string;
#Column('bigint')
walletId!: number;
#Column({ type: 'text', nullable: true })
currency!: string;
#Column({
type: 'decimal',
precision: 24,
scale: 6,
})
amount!: number;
#Column('text')
status!: ServiceTransactionStatus;
#Column('text')
transactionType!: TransactionType;
#Column('bigint')
referenceId!: number; // reference to local tables with relevant information
}
In service that needs to reuse the transaction
Deposit = 'deposit',
Refund = 'refund',
}
#Entity()
export class WalletTransaction extends TransactionEntity<WalletTransactionType> {}
Specific table that needs to refer to the transaction table
export class Deposit {
#PrimaryGeneratedColumn('identity', {
type: 'bigint',
generatedIdentity: 'ALWAYS',
primaryKeyConstraintName: 'pk_deposit_id',
})
id!: number;
#Index('idx_deposit_user_id')
#Column('text')
userId!: string;
#Column('text')
someData!: string;
...
#OneToMany(() => WalletTransaction, (walletTransaction) => walletTransaction.referenceId)
#JoinColumn({ foreignKeyConstraintName: 'fk_deposit_wallet_transaction' })
walletTransactions!: Relation<WalletTransaction[]>;
}
The problem I have is that it does not seem to be possible to do a one way OneToMany in TypeORM so no migration gets generated and I don't get the reference_id column.
As far as I have found, TypeORM does not allow to have a single-directional relation (i.e. you can't have OneToMany refering to a table without the corresponding ManyToOne in the original table.
I would like to be able for multiple tables to have a reference to the common transaction table and I would like to have a foreign key that ensures each record in for instance the deposit table has a corresponding record in the WalletTransaction table. I know how to do it in postgres if I manually create a migration to set this up
I have tried to define everything as above but TypeORM does not then pick up on the connection between the reference_id and a corresponding table.
I would be OK with adding a column to the wallet_transaction table to refer to a specific table to say where the foreign key has the ManyToOne target (deposit table, payout table). Maybe there is some way to use compound keys
I have tried to google around but I have found only a few articles that say that one direction connection like this is not possible in Typeorm. They were all older date but the TypeORM documentation basically assumes that the only way to connect 2 tables is having both way OtM/MtO specified.

Related

Realtime Database: Query Multiple Nodes

I have the following structure in my RTDB (I'm using typescript interface notation to communicate the structure):
interface MyDB {
customers: {
[id: string]: {
firstName: string;
lastName: string;
};
};
projects: {
[id: string]: {
created: string;
customerId: string;
phase: string;
};
};
}
Given that I have two "tables" or document nodes, I'm not certain what the correct format for getting a project, as well as it's associated customer, should be.
I was thinking this:
db.ref('projects').once(projects => {
const customers = db.ref('customers').once(customers => {
const project = projects[SOME_PROJECT_ID];
const customer = customers[project.customerId];
// Proceed to do cool stuff with our customer and project...
});
});
Now, there are plenty of ways to express this. To be honest I did it this way in this example for simplicity, but I would actually not serialize the db.ref calls - I would put them in a combined observable and have them go out in parallel but that doesn't really matter because the inner code wouldn't change.
My question is -- is this how it is expected that one handle multi-document lookups that need to be joined in realtime database, or is there a "better" more "RTDB-y" way of doing it?
The issue I see here is the understanding I have is that we're selecting ALL projects and ALL customers. If I want to only get customers that have associated projects, is there a more efficient way to do that? I have seen that you might want to track project id's on each customer and do a filter there. But, I'm not sure the best way to track multiple project IDs (as a string with some kind of separater, or is there an array search function, etc?)
Thanks
Firebase Realtime Database doesn't offer any type of SQL-like join. If you have two locations to read, it requires two queries. How you do those two queries is entirely up to you. The database and its SDK is not opinionated about how you do that. If what you have works, then go with it.

How can we configure a TypeORM ViewEntity's ViewColumn to be of JSON type?

I am creating a #ViewEntity() with TypeORM on MySQL in which I directly select a JSON column. The view is correct and just a normal SQL view.
The column definition for the view class looks as such:
#ViewColumn() document: Estimate;
where Estimate is an interface specifying the data shape of the JSON, although I have tried Estimate | Object as well. The entities retrieved by the Repository are always having the document property as a string, evidently the ORM is not parsing the JSON. Therefore I am having to do some annoying JSON.parse() and mutating the retrieved record before responding to requests.
ViewColumnOptions only takes a property of name, so I cannot specify { type: 'json' } as I would on a conventional #Entity()'s #Column(). Are JSON columns in TypeORM views even implemented? I have been unable to find results in the docs nor on the github issues.
This is not well-covered in the documentation.
Turns out you can just use a #Column() decorator within a view, so my solution for the above is to specify my view's column definition as
#Column({ type: 'json' }) document: Estimate;
instead of as
#ViewColumn() document: Estimate; in the original question.
Furthermore, for anyone who may stumble upon this later, if you want to do relations against your view, you must decorate at least one column in your view as #PrimaryColumn() in order for TypeORM to formulate the SQL properly. Again, this is not covered in the docs; you could be forgiven after having read them for believing that only #ViewColumn() is valid within a #ViewEntity().
Your relations inside the #ViewEntity() will need to look as such for an example Customer relation:
#ManyToOne(() => Customer)
#JoinColumn({ name: 'customerId' })
customer: Customer;
#ViewColumn() customerId: string;
In the above I have selected in my view's SQL customerId alias and you can see I must specify that both as the name of the #JoinColumn() as well as a #ViewColumn() after the property declaration of the related entity.

Rails Neo4j How to add new field in existing database

class Invitation
include Neo4j::ActiveNode
property :email
end
This is Neo node Invitation node I can see it in the graph database.
- If I add some new field in it, for existing nodes it will not reflect in the graph database
- If I create a new node I can see it in graph database
So the question is if I have to add one field suppose
property :valid, type: Boolean, default: true
How can I add this so that I can see it in existing nodes in graph database???, same like we do in active record migrations
I have added field in Node
property :vish, type: Boolean, default: true
So when query
Invitation.where(vish: true).count ==> result 0
If I add new node Invitation.create(email: 'urvishh#gmail.com')
Then run query
Invitation.where(vish: true).count ==> result 1
This is exact issue I am getting
The short answer will be No: there is no way to Search for undeclared property values in persisted nodes.
Edited:
They added a Migration like behaviour for the gem that might fit your current needs.
http://neo4jrb.readthedocs.io/en/latest/Migrations.html
Discovery answer:
Nodes should be considered as documents that stores properties inside them. What we are dealing here is with an implementation of the Cypher Query and the Neo4j::ActiveNode that not only ignores the default values for properties.
This could be easily tested:
class User
include Neo4j::ActiveNode
property :name, type: String
property :email, type: String, default: 'example#example.com'
end
Then create two nodes:
User.create(name: 'John', email: 'john#cena.com'
User.create(name: 'John')
We try to search for undeclared property values:
> User.where(title: 'Mr')
=> #<QueryProxy CYPHER: "MATCH (result_user:`User`) WHERE (result_user.title = {result_user_title})">
We effectively call Cyper and get results, this means that the property declaration in model is not used at all in the Neo4j::ActiveNode#where
It means is only used to set and get properties, but ignored by the finders.
There might be workarounds, that are actually missing implementations in the Neo4j gem:
You can search by array of values in the Cyper connector, but is not parsing properly the values:
User.where(another_field: nil).count
CYPHER 39ms MATCH (result_user:`User`) WHERE (result_user.another_field IS NULL) RETURN count(result_user) AS result_user
=> 100
User.where(another_field: ['Something', nil]).count
CYPHER 12ms MATCH (result_user:`User`) WHERE (result_user.another_field IN {result_user_another_field}) RETURN count(result_user) AS result_user | {:result_user_another_field=>["Something", nil]}
=> 0
As you can see in the last query, nil is passed literally. So you can't do this.
I've opened an Issue in the repository in your behalf, and linked this question in order to get a solution.

Breezejs non scalar complex type not updating properly when refetched from server

We are running into an issue with updating non scalar complex type.
Our simplified metadata looks like this
Table
shortName: 'Table',
defaultResourceName: "/table",
dataProperties: {
name: { max: 50, required: true },
description: { max: 500},
columns: {complexType: 'Column', isScalar: false}
...
}
Column
shortName: 'Column',
isComplexType: true,
dataProperties: {
name: { max: 50, required: true },
description: { max: 500 },
...
}
Our backend is document based nosqldb.
In the UI, we display all the tables and its columns in a tree like structure. User can click on a Table name or column and edit it's properties in a property editor. Table and Column have separate editors, even though internally they are part of the same object.
Initially when we display the tree we only fetch limited no. of dataproperties of the json, only the ones that are required to be displayed in the tree. But when the user clicks on a particular table or a column we fetch the entire table (columns are complex types) from the server and update the cache to display them in the editor.
We are using knockout as the model library so all the properties are knockout observables. We are passing the same breeze entity objects to viewmodels of these editors as well as the tree.
If the user clicks on a table in the tree, and edits name or description in the editor, we are seeing table name in the tree is also changing as it should, since they are both the same observable. But when I click on a column name in the tree, and edit the column name in the editor, I do not see the change getting reflected in the tree.
What I think is happening here is, when I am re-querying the same object from the server to display in the editor, table name which a simple property is being updated with the new value from the server, but column name which is part of complex object, is actually getting replaced with new values from server, since each complex object in the array itself is being replaced. So it seems like tree and editor has different obsrevables.
Funny thing is, if after clicking on a particular column and having it displayed in the editor, I move to a different module (we are using spa), and then come back to the original module again, then if I click on the same column again and update the name, this time the change is getting reflected in the tree. But not the first time.
Could this be a bug, or am I missing something? Is there a workaround?
We are using breeze js 1.5.6
Thanks!

Querying data from Firebase using multiple filters

I have a Firebase array called products that contains items like this one:
JhMIecX5K47gt0VaoQN: {
brand: "Esprit"
category: "Clothes",
gender: "Boys",
name: "Pants",
}
Is it possible to query products from this array using multiple filters with the Firebase API. For example I might want to filter by brand and category (all "Pants" by "Esprit"). So far I've tried ordering by child key and then limiting the start and end of this ordering, but I can't figure out how to apply more filters.
I'm using the iOS SDK.
Firebase can only order/filter on one property (or value) at a time. If you call a orderBy... method multiple times in a single query it will raise an error to indicate this is not allowed.
Actually, in Firebase, when you order by a specific key, there is another index involved : push IDs (if you use them) They are almost perfectly ordered chronologically, so you order by field XXX plus Time.
More details here
In your case, this is not about time, so the only solution is to use additional composite indexes :
JhMIecX5K47gt0VaoQN: {
brand: "Esprit"
category: "Clothes",
brandcategoryIndex: "EspritClothes",
categorybrandIndex: "ClothesEsprit",
brandnameIndex: "EspritPants",
namebrandIndex: "PantsEsprit",
gender: "Boys",
name: "Pants",
}
In the above example, you can query by :
Category > Brand (and the other way)
Name > Brand (and the other way)
Just make sure you add those fields to your Firebase index in the rules section, and that you maintain them anytime the items are modified.
(this process of adding a cost of redundant data at the benefit of performance, which is forced by Firebase in this case, is called denormalization)

Resources