Given two classes:
export class Person {
public name: string;
public lastName: string;
public address: Address;
}
export class Address {
public city: string;
public street: string;
}
How would one query to see the changes to a Person address field? For example if you would like to check where some one lived in the year 2000
If you use MongoDB to hold your world state, you can perform so called Mango queries to retrieve all records that satisfy your request. If you write your application in Node.js, you could use the sqltomango package to convert SQL queries to Mango.
All these options however only apply on the current state data, not historical. To see how an object has been modified, or to see if any object at any point in time matched your search criteria, you can iterate through the historical blocks and check the transactions contained in them.
Related
Im new to dynamoDB and im trying to build an ecommerce store. I have a table with a user, product and order.
My access patterns are:
get all products in a users order
I can then use this for a similar issue with the users cart. But im not sure how. My user to order relationship is one to many and my product to order relationship is many to many.
My data looks like this:
type Variant = {
size: Sizes;
quantity: number;
price: number;
}
type OrderProduct = {
id: string;
orderId: string;
product: Product;
status: string;
trackingId: string;
}
export type Product = {
id: string;
name: string;
description: string;
category: string;
createdAt: string;
variants: Variant[];
}
export type Order = {
id: string;
userId: string;
products: OrderProduct[];
createdAt: string;
}
export type User = {
id: string;
name: string;
address: string;
}
Ive seen this on aws for many to many relationships: aws many to many relationships
But this doesnt really explain how to do a one to many and then many to many query. Any advice and help with the query would be great!
DynamoDB only allows you to query by partition key (and ranged key), or to query by indexes.
If you have different tables, you cannot do a join query. You might need to create a global secondary index and then do a query on that.
So, for instance, if your Product had a secondary index over a field called "order_id", you coud do:
const documentClient = new AWS.DynamoDB.DocumentClient();
const orderId = 1234; // the real order id
const options = {
TableName: 'Product',
IndexName: 'OrderIdIndex',
KeyConditionExpression: 'order_id = :order_id',
ExpressionAttributeValues: {
':order_id': orderId
}
};
const response = await documentClient.query(options)
Keep in mind that this example is modifying your original structure.
You might need to add that new index and attribute
Edit
Keep in mind that there might be some delay for the index propagation. For example, if you insert a new Product, and you immediately want to search using the Index by order_id, DynamoDB might tell you that there is no product (because its propagating the data). If that small delay is not acceptable, you might prefer to first query the Order, and then query each product by Id (you could use batchGet if needed)
You do not do relationship queries in Dynamo. It is not a Relational Database, it is a document database.
This means most importantly, your normal way of storing data in multiple tables and usually by some whatever unique auto incrimented identifier in an SQL is a terrible way to do it in a dynamo
Instead, you need to store your data based on your access patterns - and this may feel very weird coming from SQL! You may even feel like you are duplicating data at times.
Since a Dynamo query requires you to know what the Partition Key is in order to query (you cannot do a search or a conditional on the PK) then the PK needs to be what you have to start your query.
so with your access pattern described, your PK must be the user. Then, a separate entry for each item in their cart would be the way to proceed - basically something like:
(EDIT: you can switch User for OrderID very easily too of course)
PK: User
SK: ITEM#123456123
PK: User
SK: ITEM#123491239
PK: User
SK: Item#113322
and maybe even a
PK: User
SK: META
with attribiutes like "total items" or "login time" or "sales offered" or whatever else needs to be tracked.
then if you query against the PK of USER, you get back a list of all their items. They remove an item, you remove the SK document associated with that item. They increase the amount, then you increase that items quantity attribute. ect.
This is in effect a One to Many relationship: One (the PK of User) and Many (SK's prefixed with ITEM#) - you can then do a query of PK=User, SK (beginsWith) ITEM# to retrieve all the items of a user.
But as you may be able to see, this can get very complex very fast if you are trying to do many different relationships - dynamo is not built for that. If you need to do anything deeper than a single relationship like this or need to be able to dynamically decide the relationships/queries at run time, then Dyanmo is not the solution, SQL is.
Using TestORM to connect to an already created table. The caveat is that it, along with most tables in my firm, has a dot in the name: dbo.Application
My entity file:
import { Entity, Column, PrimaryGeneratedColumn } from 'typeorm'
#Entity({name: 'Application'}) // fixed
export class Application {
#PrimaryGeneratedColumn()
ApplicationID: number;
#Column()
ApplicationName: string;
#Column()
LastUpdate: number;
}
EDIT
Okay, I see the issue. Without {name: 'Application'} it tries to create the table 'application', not 'Application'
However, I have a new error and I cannot figure it out. It looks like TestORM is trying to drop the ApplicationName column, which is type varchar(100). Do it and string not jive?
For the record, DB columns are:
ApplicationID(PK, int, not null)
ApplicationName(varchar(100),null)
LastUpdate(datetime,null)
EDIT again:
Setting synchronize: false in the connection prevents it from dropping the column. Still curious why it tried it before but this is livable.
In order to create objects with unusual characters in the name, enclose the name in square brackets, like this: [My-Table!]. However in your case you probably don't want to do it as dbo is not a part of table name, but a name of a schema, as #sean-lange correctly pointed out.
Okay, so I'm starting to dig into graphql a little bit, and I've built an api using koa, type-graphql, and sequelize-typescript. Everything works pretty well.... I managed to get a query working, and even managed to optimize a little bit by using graphql-fields to filter the columns I query in the database... However when I've aliased a field name, I can't seem to get the mapped name.....
For example, given the following ObjectType/Sequelize Model....
export interface IDepartment {
departmentId: number;
name: string;
description: string;
}
#ObjectType()
#Table({ underscored: true })
export class Department extends Model<Department> implements IDepartment {
#Field({ name: 'id' })
#PrimaryKey
#Column({ field: 'department_id'})
public departmentId: number;
#Field()
#Length({ max: 100 })
#Column
name: string;
#Field()
#Length({ max: 100 })
#AllowNull
#Column
description: string;
}
and sample query....
query {
department(name: "Test Dept") {
id
name,
description
}
}
sample resolver...
async department(#Arg('name') name: string, #Info() info: GraphQLResolveInfo) {
return Department.findOne({
where: { name }
});
}
This works just fine.... but when I do
async department(#Arg('name') name: string, #Info() info: GraphQLResolveInfo) {
let fields = Object.keys(getFields(info))
return Department.findOne({
attributes: fields,
where: { name }
});
}
(getFields is graphql-fields), it fails because the query specified field name id, which is what graphql-fields returns, but the column name is department_id (sequelize model name departmentId).
I've gone through the schema with a fine tooth comb, using the introspectionFromSchema function to see a detailed copy of my schema, but nowhere is there a mention of departmentId or department_id.... However I know it's out there somewhere because when I exclude the attributes field from my sequelize query, even though sequelize returns departmentId as the property name, when I return it from my resolver and it reaches the client, the property name is id.
Any help would be appreciated.... I'm trying to optimize everything by only fetching requested properties and not the entire object. I could always store the maps as separate constants and use those in my #Field definition, but I want to do that as a last resort, however if I can I'm trying to keep the code as lean as possible....
Thank you all in advance.
Unfortunately, the name option was introduced mostly to support resolvers inheritance. Using this for mapping the schema field names is a kinda undocumented feature so it's doesn't provide any mapping or exposing mapping metadata.
Using the name option for input or args types will be even worse - it will result in no access to the fields and the properties being undefined.
For now my recommendation is to just keep it simple and don't map the field names until a proper fix arrives.
In datamodel.graphql
type Ride {
rideId: String
productId: String
passenger: Passenger
origin: Origin
destination: Destination
dateTime: DateTime
feedback: String
}
type Passenger {
id: ID! #unique
firstName: String
lastName: String
}
type Destination {
# The unique ID of the destination.
id: ID! #unique
latitude: Float
longitude: Float
address: String
}
type Origin {
id: ID! #unique
latitude: Float
longitude: Float
address: String
}
type Report {
productId: String
passenger: Passenger
description: String
}
I deployed this data model and generates a MySql Db, auto queries, mutations with this.
It creates a "Ride", "Passenger", "Report" and "Origin" table in MySql. But it didn't create any column for passenger, origin, destination in "Ride" table.
It separates creates a relation table for this like _PassengerToRide, _OriginToRide, and _DestinationToRide.
Lack of relation establishment in "Ride" table, I couldn't get the details from Passenger, Origin and Destination tables when I query "rides()". Is this the correct way to define the datamodel.graphql. (edited)
Based on your description, this query should "just work":
query {
rides {
feedback
passenger {
id
}
origin {
id
}
destination {
id
}
}
}
Prisma uses the relation table approach you mentioned to keep track if relations between two nodes, for example table _OriginToRide relates to relation #relation(name: "OriginToRide") from your datamodel.
You don't have to change anything on the SQL level to connect the relations afterwards.
Note: The above applies to Prisma database connectors with activated migrations. If your connector doesn't do migrations, different approaches to represent relations are supported. The respective datamodel to support this can be generated by introspecting your existing database schema.
My research suggests no. Here is a quick example of what I'd like to do.
Given 3 tables: Company, Product, Lookup...
Company has many Products. Products have one Company. Lookup table serves as an enum/constant for hardcoded values. I.e. state/country names, application specific naming conventions, etc.
Here are the models in sequelize-typescript (though the question still fully relates to sequelize js):
// COMPANY
#Table
export default class Company extends Model<Company> {
#PrimaryKey
#Column
Oid:number;
#Column
Name:string;
#Column
Address:string;
#HasMany(() => Product)
products: Product[];
}
// PRODUCT
#Table
export default class Product extends Model<Product>{
#PrimaryKey
#Column
Oid: number;
#ForeignKey(() => Company)
#Column
companyOid: number;
#BelongsTo(() => Company)
company: Company;
#Column
Price: number;
#Column
Name: string;
//#ForeignKey(() => Lookup) //attempt #1
//#Column({references: {model: "Lookup", key: "Oid"}}) //attempt #2
#Column
LkpStateOid: number;
}
// LOOKUP
#Table
export default class Lookup extends Model<Lookup> {
#PrimaryKey
//#BelongsTo(() => Product) // do not want to hardcode Product, as it could be any table
#Column
Oid:number;
#Column
Value:number; // where the value represents the hardcoded state, county, etc., ie "CA"
}
The issue here is that there is no "real" relationship between Product.lkpStateOid and Lookup.oid, except that one references the other in order to obtain the Lookup.value. In any sql variant, this is not an issue- just a simple join on the tables. In sequelize, however, the relationship between the tables must be known before I can get any of the associated data in a query. This is what I'd like to do:
const companyInfo = await Db.Company.find({
include: [{
model: Product,
include: [{
model: Lookup,
attributes: ["value"]
}]
}]
})
The first include is no problem. The second, nested include is not successful. I've tried a number of different annotations on the table, but the secondary issue (even if i could successfully "fake" a relationship in order to be able to associate the data) is that I do not want to hardcode which table the Lookup table belongsTo because the Lookup table could contain values that are needed in any number of other tables. Is there a way to accomplish this goal?
This link seems close to what I'd like (and is commented as an idea in the above table): http://docs.sequelizejs.com/manual/tutorial/associations.html#enforcing-a-foreign-key-reference-without-constraints
Any help would be appreciated. Thanks!
Yes, it is possible, because Sequelize accepts literal sequel queries in the form of sequelize.query(). You can totally bypass the include[] syntax (I haven't tested this yet, but it is my understanding). Just put a raw SQL query with a JOIN statement inside sequelize.query().
http://docs.sequelizejs.com/manual/tutorial/raw-queries.html