Exception when updating bigquery schema - ruby

I tried insert to bigquery with schema:
require 'gcloud'
gc = Gcloud.new 'PROJECT_ID'
bq = gc.bigquery
ds = bq.dataset 'MY_DATASET'
t = ds.create_table 'MY_TABLE'
t.schema = { fields: [ { name: 'Name', type: 'STRING' } ] }
t.insert [{'name' => 'test1'}]
As expected, my terminal console showed the output error:
[{"reason"=>"invalid", "location"=>"name",
"debugInfo"=>"generic::not_found: no such field.", "message"=>"no such
field."}]
When I tried to update schema to insert the key Name:
t.schema = {
fields: [
{ name: 'Name', type: 'STRING' },
{ name: 'name', type: 'STRING' }
]
}
Displayed the exception:
Gcloud::Bigquery::ApiError: Field name already exists in schema
Any suggestion how can I solve this? This is a BigQuery bug?

When inserting data (via streaming, at least, which t.insert appears to use), field names are case-sensitive. So if you updated to 'Name', it should work with your schema.
t.insert [{'Name' => 'test1'}]
However, within queries, field names are case-insensitive, so it's invalid for a table to have field names that differ only by case: they'd be indistinguishable. This leads to your second error with "Field name already exists in schema".
This is admittedly pretty confusing. I'll look into whether we can make case-sensitivity more predictable for users.

Related

keystonejs form a multi-column unique constraint

How to form a unique constraint with multiple fields in keystonejs?
const Redemption = list({
access: allowAll,
fields: {
program: relationship({ ref: 'Program', many: false }),
type: text({ label: 'Type', validation: { isRequired: true }, isIndexed: 'unique' }),
name: text({ label: 'name', validation: { isRequired: true }, isIndexed: 'unique' }),
},
//TODO: validation to check that program, type, name form a unique constraint
})
The best way I can think to do this currently is by adding another field to the list and concatenating your other values into it using a hook. This lets you enforces uniqueness across these three values (combine) at the DB-level.
The list config (and hook) might look like this:
const Redemption = list({
access: allowAll,
fields: {
program: relationship({ ref: 'Program', many: false }),
type: text({ validation: { isRequired: true } }),
name: text({ validation: { isRequired: true } }),
compoundKey: text({
isIndexed: 'unique',
ui: {
createView: { fieldMode: 'hidden' },
itemView: { fieldMode: 'read' },
listView: { fieldMode: 'hidden' },
},
graphql: { omit: ['create', 'update'] },
}),
},
hooks: {
resolveInput: async ({ item, resolvedData }) => {
const program = resolvedData.program?.connect.id || ( item ? item?.programId : 'none');
const type = resolvedData.type || item?.type;
const name = resolvedData.name || item?.name;
resolvedData.compoundKey = `${program}-${type}-${name}`;
return resolvedData;
},
}
});
Few things to note here:
I've removed the isIndexed: 'unique' config for the main three fields. If I understand the problem you're trying to solve correctly, you actually don't want these values (on their own) to be distinct.
I've also remove the label config from your example. The label defaults to the field key so, in your example, that config is redundant.
As you can see, I've added the compoundKey field to store our composite values:
The ui settings make the field appear as uneditable in the UI
The graphql settings block updates on the API too (you could do the same thing with access control but I think just omitting the field is a bit cleaner)
And of course the unique index, which will be enforced by the DB
I've used a resolveInput hook as it lets you modify data before it's saved. To account for both create and update operations we need to consult both the resolvedData and item arguments - resolvedData gives us new/updated values (but undefined for any fields not being updated) and item give us the existing values in the DB. By combining values from both we can build the correct compound key each time and add it to the returned object.
And it works! When creating a redemption we'll be prompted for the 3 main fields (the compound key is hidden):
And the compound key is correctly set from the values entered:
Editing any of the values also updates the compound key:
Note that the compound key field is read-only for clarity.
And if we check the resultant DB structure, we can see our unique constraint being enforced:
CREATE TABLE "Redemption" (
id text PRIMARY KEY,
program text REFERENCES "Program"(id) ON DELETE SET NULL ON UPDATE CASCADE,
type text NOT NULL DEFAULT ''::text,
name text NOT NULL DEFAULT ''::text,
"compoundKey" text NOT NULL DEFAULT ''::text
);
CREATE UNIQUE INDEX "Redemption_pkey" ON "Redemption"(id text_ops);
CREATE INDEX "Redemption_program_idx" ON "Redemption"(program text_ops);
CREATE UNIQUE INDEX "Redemption_compoundKey_key" ON "Redemption"("compoundKey" text_ops);
Attempting to violate the constraint will produce an error:
If you wanted to customise this behaviour you could implement a validateInput hook and return a custom ValidationFailureError message.

Is there a way to auto increment a number field in RxDB?

I've got a simple schema:
export default {
title: 'hash schema',
version: 0,
primaryKey: 'hash',
type: 'object',
keyCompression: true,
properties: {
uuid: { type: 'string' },
id: { type: 'number' }
}
}
I want to have a table with a string field uuid as a primary key and I want to map it to an unique number that is automatically increased.
Is there a way to do so?
"automatically incrementing" doesn't sound very UUIDish to me. UUIDs are supposed to be random and unpredictable. You leave yourself vulnerable to the German Tank Problem
Nevertheless, you can indicate a string should be a UUID in JSON Schema by using "format": "uuid". It is only available in implementations supporting specification version draft2019-09 or later.

Prisma Not Returning Created Related Records

i want to create a new graphql api and i have an issue that i am struggling to fix.
the code is open source and can be found at: https://github.com/glitr-io/glitr-api
i want to create a mutation to create a record with relations... it seems the record is created correctly with all the expected relations, (when checking directly into the database), but the value returned by the create<YourTableName> method, is missing all the relations.
... so so i get an error on the api because "Cannot return null for non-nullable field Meme.author.". i am unable to figure out what could be wrong in my code.
the resolver looks like the following:
...
const newMeme = await ctx.prisma.createMeme({
author: {
connect: { id: userId },
},
memeItems: {
create: memeItems.map(({
type,
meta,
value,
style,
tags = []
}) => ({
type,
meta,
value,
style,
tags: {
create: tags.map(({ name = '' }) => (
{
name
}
))
}
}))
},
tags: {
create: tags.map(({ name = '' }) => (
{
name
}
))
}
});
console.log('newMeme', newMeme);
...
that value of newMeme in the console.log here (which what is returned in this resolver) is:
newMeme {
id: 'ck351j0f9pqa90919f52fx67w',
createdAt: '2019-11-18T23:08:46.437Z',
updatedAt: '2019-11-18T23:08:46.437Z',
}
where those fields returned are the auto-generated fields. so i get an error for a following mutation because i tried to get the author:
mutation{
meme(
memeItems: [{
type: TEXT
meta: "test1-meta"
value: "test1-value"
style: "test1-style"
}, {
type: TEXT
meta: "test2-meta"
value: "test2-value"
style: "test2-style"
}]
) {
id,
author {
displayName
}
}
}
can anyone see what issue could be causing this?
(as previously mentioned... the record is created successfully with all relationships as expected when checking directly into the database).
As described in the prisma docs the promise of the Prisma client functions to write data, e.g for the createMeme function, only returns the scalar fields of the object:
When creating new records in the database, the create-method takes one input object which wraps all the scalar fields of the record to be
created. It also provides a way to create relational data for the
model, this can be supplied using nested object writes.
Each method call returns a Promise for an object that contains all the
scalar fields of the model that was just created.
See: https://www.prisma.io/docs/prisma-client/basic-data-access/writing-data-JAVASCRIPT-rsc6/#creating-records
To also return the relations of the object you need to read the object again using an info fragment or the fluent api, see: https://www.prisma.io/docs/prisma-client/basic-data-access/reading-data-JAVASCRIPT-rsc2/#relations

Sequelize include with multiple where condition

I have a bit of problem for using Sequelize with include. The problem is that my model uses two primary keys in child table.
So it goes like this
Parent table
User : Id, ...
Post : Id, UserId(foreign key, binds to user id), ...
Post Hash Tag : HashTag, PostId(foreign key, binds to Post id), UserId(foreign key, binds to user id of Post table)
So the table hierarchy looks like this
user - post - post hash tag
Now when I try to do like this,
Post.findAll(
include: {
model: post hash tag
}
)
then it only searches the post hash tags for where post id of post hash tag table is equal to post id of post table
So I added like this
Post.findAll(
include: {
model: post hash tag
where: {
col1: models.sequelize.where(models.sequelize.col('POST.USER_ID'), '=', models.sequelize.col('POST_HASH_TAG.USER_ID'))
}
}
);
Then it will gives a problem at 'where' clause that Post.USER_ID cannot be found.
If I change col1 value to Post.userId then now it solves the above error but gives another error at 'on' clause
Do you have any idea how I can solve this?
The full model is given here
User
sequelize.define('User', {
id: { type: DataTypes.STRING(6), field: 'ID', primaryKey : true }
)
Post - I know multiple primary declaration is not working correctly, so don't bother to consider too much
sequelize.define('Post', {
id: { type: DataTypes.STRING(6), field: 'ID', primaryKey: true },
userId: { type: DataTypes.STRING(6), field: 'USER_ID', primaryKey: true }
)
Post hash tag
sequelize.define('PostHashTag', {
postId: { type: DataTypes.STRING(6), field: 'POST_ID', primaryKey: true },
hashTag: { type: DataTypes.STRING(20), field: 'HASH_TAG', primaryKey: true },
userId: { type: DataTypes.STRING(6), field: 'USER_ID', primaryKey: true }
}
)
and the query I used is
Post.findAll({
attributes: ['id', 'userId'],
where: {
userId: userId,
id: { $lt: postId }
},
include: [{
model: models.PostHashTag,
attributes: ['hashTag'],
where: {
col1: models.sequelize.where(models.sequelize.col('Post.USER_ID'), '=', models.sequelize.col('PostHashTag.userId'))
}]).then(...)
I found an answer by myself... col1:
models.sequelize.where(models.sequelize.col('Post.USER_ID'), '=', models.sequelize.col('PostHashTag.userId'))
this should be
userId: models.sequelize.where(models.sequelize.col('POST.userId'), '=', models.sequelize.col('POST_HASH_TAG.USER_ID'))
this will work. The physical names of table and column used in parenthesis

Joi object validation: How to validate values with unknown key names?

I have an object with key names I cannot possibly know - they are created by user. However I do know what values they (keys) are going to store, and they (values) are going to be ISO strings. How do I validate those values? And, optionally, how do I validate uknown object's keys, i.e.:
key: Joi.string().min(2).max(25)
What I have already tried was based on Joi API docs :
Another benefits of using Joi.object([schema]) instead of a plain JS object is >that you can set any options on the object like allowing unknown keys, e.g:
const schema = Joi.object({
arg: Joi.string().valid('firstname', 'lastname', 'title', 'company', 'jobtitle'),
value: Joi.string(),
}).pattern(/firstname|lastname/, Joi.string().min(2));
What I understood from the example is that arg key represents Joi.object()'s key, and value represents it's value.
My example:
campaign: Joi.object({
arg: Joi.string().valid( 'unknown' ),
value: Joi.date().iso(),
}).pattern( /unknown/, Joi.string().min(2).max(25) )
My input;
campaign: { g_ad_adwords: "2017-01-19T11:33:26.205Z" }
My error:
"campaign" fails because ["g_ad_adwords" is not allowed]
Try this. It'll basically accept any key within an object campaign and the value must validate against Joi.date().iso()
campaign: Joi.object().pattern(/^/, Joi.date().iso())
This however will match any key. You can restrict this by padding out the regex a little. e.g. only word characters between 2 and 25 chars
campaign: Joi.object().pattern(/\w{2,25}/, Joi.date().iso())
UPDATE
Regarding the example in the Joi docs, I haven't tested it but here's my interpretation. I can understand that it's not the most straightforward example they could have given...
const schema = Joi.object({
arg: Joi.string().valid('firstname', 'lastname', 'title', 'company', 'jobtitle'),
value: Joi.string(),
}).pattern(/firstname|lastname/, Joi.string().min(2));
The objects to validate must contain the two attributes arg and valuewhere arg's value can be one of 'firstname', 'lastname', 'title', 'company', 'jobtitle' and value is just a string.
{
arg: 'firstname',
value: 'john'
}
{
arg: 'lastname',
value: 'smith'
}
{
arg: 'jobtitle',
value: 'brewer'
}
However it will also allow the object to have the attributes firstname and lastname where both of their values is a string with more than two characters. So the above examples could be condensed into a single valid object.
{
firstname: 'john',
lastname: 'smith',
arg: 'jobtitle',
value: 'brewer'
}

Resources