Add lower() to a field in addIndex() in sequelize migration - node.js

To create a unique index in sequelize migrations we can do something like below,
await queryInterface.addIndex(SCHOOL_TABLE, {
fields: ['name', 'school_id'],
unique: true,
name: SCHOOL_NAME_ID_UNIQUE_INDEX,
where: {
is_deleted: false
},
transaction,
})
The problem is It allows duplicates due to case senstivity.
In the doc here, It is mentioned that fields should be an array of attributes.
How can I apply lower() to name field so that It can become case insensitive?

I am using a workaround for now, using raw query. I don't think addIndex() supports using functions on fields.
await queryInterface.sequelize.query(`CREATE UNIQUE INDEX IF NOT EXISTS ${SCHOOL_NAME_ID_UNIQUE_INDEX}
ON ${SCHEMA}.schools USING btree (lower(name), school_id) where is_deleted = false;
`, { transaction});

Related

Mongoose Query with Dynamic Key that also contains the "and" operator

So I'm trying to query my MongoDB database using mongoose to fetch documents that have a specific family AND a specific analysis ID at the same time. Here is an example of the document structure:
_id: ObjectId("62b2fb397fda9ba6fe24aa5c")
day: 1
family: "AUTOMOTIVE"
prediction: -233.99999999999892
analysis: ObjectId("629c86fc67cfee013c5bf147")
The problem I face in this case is that the name of the key of the family field is set dynamically and could therefore have any other name such as "product_family", "category", etc. This is why, in order to fetch documents with a dynamic key name, I have to use the where() and equals() operators like so:
// Get the key of the field that is set dyncamically.
let dynamicKey = req.body.dynamicKey;
// Perform a query using a dynamic key.
documents = await Model.find().where(dynamicKey).equals(req.body.value);
HOWEVER, my goal here is NOT to just fetch all the documents with the dynamic key, but rather to fetch the documents that have BOTH the dynamic key name AND ALSO a specific analysis Id.
Had the family field NOT been dynamic, I could have simply used a query like so:
documents = await Model.find({
$and: [{analysis: req.body.analysis_id}, {family: req.body.value}]
});
but this does not seem possible in this case since the keys inside the find() operator are mere text strings and not variables. I also tried using the following queries with no luck:
documents = await Model.find().where(dynamicKey).equals(req.body.value).where('analysis').equals(req.body.analysis_id);
documents = await Model.find().where(dynamicKey).equals(req.body.value).where('analysis').equals(req.body.analysis_id);
Can somebody please help?
As #rickhg12hs mentioned in the comments, part of the answer is to use the [] brackets to specify your dynamic key like so:
await Model.find({[dynamicKey]: req.body.value, analysis: req.body.analysis_id});
I also found out that another query that can work is this:
await Model.find({analysis:req.body.analysis_id}).where(dynamicKey).equals(req.body.value);
However, it seems that for either of these solutions to work you also need to set your schema's strict mode to "false", since we are working with a dynamic key value.
Example:
var predictionSchema = new mongoose.Schema({
day: {
type: Number,
required: true
},
prediction: {
type: Number,
required: true
},
analysis: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Analysis', // Reference the Analysis Schema
required: true
}
}, { strict: false });

One to many relation in Dynamodb Node js (Dynamoose)

I am using Dynamodb with nodejs for my reservation system. And Dynamoose as ORM. I have two tables i.e Table and Reservation. To create relation between them, I have added tableId attribute in Reservation which is of type Model type (of type Table type), as mentioned in the dyanmoose docs. Using the document.populate I am able to get the Table data through the tableId attribute from Reservation table. But how can I retrieve all Reservation for a Table? (Reservation and Table has one to many relation)?
These are my Model:
Table Model:
const tableSchema = new Schema ({
tableId: {
type: String,
required: true,
unique: true,
hashKey: true
},
name: {
type: String,
default: null
},
});
*Reservation Model:*
const reservationSchema = new Schema ({
id: {
type: Number,
required: true,
unique: true,
hashKey: true
},
tableId: table, \\as per doc attribute of Table (Model) type
date: {
type: String
}
});
This is how I retrieve table data from reservation model
reservationModel.scan().exec()
.then(posts => {
return posts.populate({
path: 'tableId',
model: 'Space'
});
})
.then(populatedPosts => {
console.log('pp',populatedPosts);
return {
allData: {
message: "Executedddd succesfully",
data: populatedPosts
}
}
})
Anyone please help to retrieve all Reservation data from Table??
As of v2.8.2, Dynamoose does not support this. Dynamoose is focused on one directional simple relationships. This is partly due to the fact that we discourage use of model.populate. It is important to note that model.populate does another completely separate request to DynamoDB. This increases the latency and decreases the performance of your application.
DynamoDB truly requires a shift in how you think about modeling your data compared to SQL. I recommend watching AWS re:Invent 2019: Data modeling with Amazon DynamoDB (CMY304) for a great explanation of how you can model your data in DynamoDB in a highly efficient manner.
At some point Dynamoose might add support for this, but it's really hard to say if we will.
If you truly want to do this, I'd recommend adding a global index to your tableId property in your reservation schema. Then you can run something like the following:
async function code(id) {
const reservation = await reservationModel.get(id);
const tables = await tableModel.query("tableId").eq(id).exec(); // This will be an array of `table` entries where `"tableId"=id`. Remember, it is required you add an index for this to work.
}
Remember, this will cause multiple calls to DynamoDB and isn't as efficient. I'd highly recommend watching the video I linked above to get more information about how to model your data in an more efficient manner.
Finally, I'd like to point out that your unique: true code does nothing. As seen in the Dynamoose Attribute Settings Documentation, unique is not a valid setting. In your case since you don't have a rangeKey, it's not possible for two items to have the same hashKey, so technically it's already a unique property based on that. However it is important to note that you can overwrite existing items when creating an item. You can set overwrite to false for document.save or Model.create to prevent that behavior and throw an error instead of overwriting your document.

Sequelize & Apollo server: map findAll "sequelize.literal" field to an Apollo schema field

I've got an apollo schema like this one:
type Batch {
Batch: String,
ItemCode: String,
PV: String,
ExpirationDate: DateTime,
Freezed: Boolean,
Item:Item,
Quantity: Float,
Fefo: Boolean
}
and a query in sequelize like that:
await models.Batch.findAll({
attributes: ['ItemCode','Lotto','ExpirationDate','Freezed', [Sequelize.literal(`CASE WHEN "Batch" ='${Batch}' THEN true ELSE false END`), "Fefo"] ],
where: whereClause,
include: [models.Item],
order: [
// Will escape title and validate DESC against a list of valid direction parameters
['ExpirationDate']
]
// attributes: models.mapAttribute(models.Batches, info)
})
returning an array of Batch objects.
All runs without problems, graphql query works well:
{
getFefoBatches (ItemCode:"200200201",Batch:"20201118B")
{
Batch, ExpirationDate, Fefo, Item { ItemCode ItemName }
}
}
but the "Fefo" field is not valorized with the "sequelize.literal" value. It's always null.
Maybe I don't known the proper syntax, but I tried everything possible, and also I searched for a solution but without success.
How is it possible to make a correct mapping between sequelize field and Apollo server schema?
Thanks in advance.
Okay, I discovered that the problem is not on sequelize query or on Apollo gql schema but is related to the model.
I added a virtual field on model with the same name of my query/schema field and everything works fine.

How to create a UNIQUE constraint on a JSONB field with Sequelize

I'm using the Sequelize ORM in NodeJS to manage a postgreSQL database.
I'm using the JSONB datatype in my table, I need an index on the JSONB field and an unique constraint on a property of this JSON.
If I have to do in a classic SQL here my script :
CREATE TABLE tableJson (id SERIAL PRIMARY KEY,
content JSONB NOT NULL);
CREATE INDEX j_idx ON tableJson USING gin(content jsonb_path_ops);
CREATE UNIQUE INDEX content_name_idx ON tableJson(((content->>'name')::varchar));
I've found how to create the table with the INDEX but not how to deal with the UNIQUE constraint. Here is a sample of my script :
var tableJson = sequelize.define('tableJson', {
content: Sequelize.JSONB
}, {
indexes: [{
fields: ['content'],
using: 'gin',
operator: 'jsonb_path_ops'
}
});
Is there a solution for my problem? If not I'll probably use the sequelize.query method to execute raw query but this is not very evolutive.
Any help would be appreciated!
This is the workaround I use to add indexes on JSONB fields with Postgres and Sequelize.
First, set the quoteIdentifiers option to false in your Sequelize client.
const seq = new Sequelize(db, user, pass, {
host: host,
dialect: 'postgres',
quoteIdentifiers: false,
});
(Watch out though, this will make all your table names case insensitive when created by Sequelize.sync())
You can now add indexes on JSONB fields this way in your model definition :
indexes: [
{
fields: ['content'],
using: 'gin',
operator: 'jsonb_path_ops'
},
{
fields: ['((content->>\'name\')::varchar)'],
unique: true,
name: 'content_name_idx',
}
],

sequelize for Node.js : ER_NO_SUCH_TABLE

I'm new to sequelize and Node.js.
I coded for test sequelize, but error occured "ER_NO_SUCH_TABLE : Table 'db.node_tests' doesn't exist"
Error is very simple.
However, I want to get data from "node_test" table.
I think sequelize appends 's' character.
There is my source code.
var Sequelize = require('sequelize');
var sequelize = new Sequelize('db', 'user', 'pass');
var nodeTest = sequelize.define('node_test',
{ uid: Sequelize.INTEGER
, val: Sequelize.STRING} );
nodeTest.find({where:{uid:'1'}})
.success(function(tbl){
console.log(tbl);
});
I already create table "node_test", and inserted data using mysql client.
Does I misunderstood usage?
I found the answer my own question.
I appended Sequelize method option following. {define:{freezeTableName:true}}
Then sequelize not appends 's' character after table name.
Though the answer works nicely, I nowadays recommend the use of the tableName option when declaring the model:
sequelize.define('node_test', {
uid: Sequelize.INTEGER,
val: Sequelize.STRING
}, {
tableName: 'node_test'
});
http://docs.sequelizejs.com/manual/tutorial/models-definition.html
Sequelize is using by default the plural of the passed model name. So it will look for the table "node_tests" or "NodeTests". Also it can create the table for you if you want that.
nodeTest.sync().success(function() {
// here comes your find command.
})
Sync will try to create the table if it does not already exist. You can also drop the existing table and create a new one from scratch by using sync({ force: true }). Check the SQL commands on your command line for more details about what is going on.
When you define a model to an existing table, you need to set two options for sequelize to:
find your table name as-is and
not fret about sequelize's default columns updatedAt and createdAt that it expects.
Simply add both options like so:
var nodeTest = sequelize.define('node_test',
{ uid: Sequelize.INTEGER , val: Sequelize.STRING},
{ freezeTableName: true , timestamps: false} //add both options here
);
Note the options parameter:
sequelize.define('name_of_your_table',
{attributes_of_your_table_columns},
{options}
);
Missing either options triggers respective errors when using sequelize methods such as nodeTest.findAll().
> ER_NO_SUCH_TABLE //freezeTableName
> ER_BAD_FIELD_ERROR //timestamps
Alternatively, you can:
create a fresh table through sequelize. It will append "s" to the table name and create two timestamp columns as defaults or
use sequelize-auto, an awesome npm package to generate sequelize models from your existing database programmatically.
Here's the sequelize documentation for option configurations.
In my case, it was due to case. I was having:
sequelize.define('User', {
The correct way is to use lowercase:
sequelize.define('user', {

Resources