不要怂,就是干,撸起袖子干!

Commit c381625d by Sushant Committed by GitHub

Fixed: jsonb/json update or softdelete (#7499)

1 parent 18f63e3f
......@@ -67,6 +67,7 @@
- [ADDED] Added support for associations aliases in orders and groups. [#7425](https://github.com/sequelize/sequelize/issues/7425)
- [REMOVED] Removes support for `{raw: 'injection goes here'}` for order and group. [#7188](https://github.com/sequelize/sequelize/issues/7188)
- [FIXED] `showIndex` breaks with newline characters [#7492](https://github.com/sequelize/sequelize/pull/7492)
- [FIXED] Update or soft delete breaks when querying on `JSON/JSONB` [#7376](https://github.com/sequelize/sequelize/issues/7376) [#7400](https://github.com/sequelize/sequelize/issues/7400) [#7444](https://github.com/sequelize/sequelize/issues/7444)
## BC breaks:
- Model.validate instance method now runs validation hooks by default. Previously you needed to pass { hooks: true }. You can override this behavior by passing { hooks: false }
......
......@@ -362,7 +362,7 @@ const QueryGenerator = {
table: this.quoteTable(tableName),
values: values.join(','),
output: outputFragment,
where: this.whereQuery(where),
where: this.whereQuery(where, options),
tmpTable
};
......@@ -1930,7 +1930,7 @@ const QueryGenerator = {
let outerBinding;
let comparator = '=';
let field = options.field || options.model && options.model.rawAttributes && options.model.rawAttributes[key] || options.model && options.model.fieldRawAttributesMap && options.model.fieldRawAttributesMap[key];
let fieldType = options.type || field && field.type;
let fieldType = field && field.type || options.type;
if (key && typeof key === 'string' && key.indexOf('.') !== -1 && options.model) {
if (options.model.rawAttributes[key.split('.')[0]] && options.model.rawAttributes[key.split('.')[0]].type instanceof DataTypes.JSON) {
......
......@@ -241,7 +241,7 @@ const QueryGenerator = {
values.push(this.quoteIdentifier(key) + '=' + this.escape(value, modelAttributeMap && modelAttributeMap[key] || undefined, { context: 'UPDATE' }));
}
return `UPDATE ${this.quoteTable(tableName)} SET ${values.join(',')} ${this.whereQuery(where)}`;
return `UPDATE ${this.quoteTable(tableName)} SET ${values.join(',')} ${this.whereQuery(where, options)}`;
},
deleteQuery(tableName, where, options, model) {
......
......@@ -56,67 +56,36 @@ describe(Support.getTestDialectTeaser('Model'), () => {
});
}
it('should create an instance with JSON data', function() {
return this.Event.create({
data: {
name: {
first: 'Homer',
last: 'Simpson'
},
employment: 'Nuclear Safety Inspector'
}
}).then(() => {
return this.Event.findAll().then(events => {
const event = events[0];
expect(event.get('data')).to.eql({
name: {
first: 'Homer',
last: 'Simpson'
},
employment: 'Nuclear Safety Inspector'
});
});
});
});
it('should update an instance with JSON data', function() {
return this.Event.create({
data: {
name: {
first: 'Homer',
last: 'Simpson'
},
employment: 'Nuclear Safety Inspector'
}
}).then(event => {
return event.update({
describe('create', () => {
it('should create an instance with JSON data', function() {
return this.Event.create({
data: {
name: {
first: 'Homer',
last: 'Simpson'
},
employment: null
employment: 'Nuclear Safety Inspector'
}
});
}).then(() => {
return this.Event.findAll().then(events => {
const event = events[0];
}).then(() => {
return this.Event.findAll().then(events => {
const event = events[0];
expect(event.get('data')).to.eql({
name: {
first: 'Homer',
last: 'Simpson'
},
employment: null
expect(event.get('data')).to.eql({
name: {
first: 'Homer',
last: 'Simpson'
},
employment: 'Nuclear Safety Inspector'
});
});
});
});
});
it('should be possible to query a nested value', function() {
return Promise.join(
this.Event.create({
describe('update', () => {
it('should update with JSON column (dot notation)', function() {
return this.Event.bulkCreate([{
id: 1,
data: {
name: {
first: 'Homer',
......@@ -124,85 +93,86 @@ describe(Support.getTestDialectTeaser('Model'), () => {
},
employment: 'Nuclear Safety Inspector'
}
}),
this.Event.create({
}, {
id: 2,
data: {
name: {
first: 'Marge',
last: 'Simpson'
first: 'Rick',
last: 'Sanchez'
},
employment: 'Multiverse Scientist'
}
}]).then(() => this.Event.update({
'data': {
name: {
first: 'Rick',
last: 'Sanchez'
},
employment: 'Housewife'
employment: 'Galactic Fed Prisioner'
}
})
).then(() => {
return this.Event.findAll({
}, {
where: {
data: {
employment: 'Housewife'
}
'data.name.first': 'Rick'
}
}).then(events => {
const event = events[0];
expect(events.length).to.equal(1);
})).then(() => this.Event.findById(2)).then(event => {
expect(event.get('data')).to.eql({
name: {
first: 'Marge',
last: 'Simpson'
first: 'Rick',
last: 'Sanchez'
},
employment: 'Housewife'
employment: 'Galactic Fed Prisioner'
});
});
});
});
it('should be possible to query a nested integer value', function() {
return Promise.join(
this.Event.create({
it('should update with JSON column (JSON notation)', function() {
return this.Event.bulkCreate([{
id: 1,
data: {
name: {
first: 'Homer',
last: 'Simpson'
},
age: 40
employment: 'Nuclear Safety Inspector'
}
}),
this.Event.create({
}, {
id: 2,
data: {
name: {
first: 'Marge',
last: 'Simpson'
first: 'Rick',
last: 'Sanchez'
},
age: 37
employment: 'Multiverse Scientist'
}
})
).then(() => {
return this.Event.findAll({
}]).then(() => this.Event.update({
'data': {
name: {
first: 'Rick',
last: 'Sanchez'
},
employment: 'Galactic Fed Prisioner'
}
}, {
where: {
data: {
age: {
$gt: 38
name: {
first: 'Rick'
}
}
}
}).then(events => {
const event = events[0];
expect(events.length).to.equal(1);
})).then(() => this.Event.findById(2)).then(event => {
expect(event.get('data')).to.eql({
name: {
first: 'Homer',
last: 'Simpson'
first: 'Rick',
last: 'Sanchez'
},
age: 40
employment: 'Galactic Fed Prisioner'
});
});
});
});
it('should be possible to query a nested null value', function() {
return Promise.join(
this.Event.create({
it('should update an instance with JSON data', function() {
return this.Event.create({
data: {
name: {
first: 'Homer',
......@@ -210,146 +180,272 @@ describe(Support.getTestDialectTeaser('Model'), () => {
},
employment: 'Nuclear Safety Inspector'
}
}),
this.Event.create({
data: {
name: {
first: 'Marge',
last: 'Simpson'
},
employment: null
}
})
).then(() => {
return this.Event.findAll({
where: {
}).then(event => {
return event.update({
data: {
name: {
first: 'Homer',
last: 'Simpson'
},
employment: null
}
}
}).then(events => {
expect(events.length).to.equal(1);
expect(events[0].get('data')).to.eql({
name: {
first: 'Marge',
last: 'Simpson'
},
employment: null
});
}).then(() => {
return this.Event.findAll().then(events => {
const event = events[0];
expect(event.get('data')).to.eql({
name: {
first: 'Homer',
last: 'Simpson'
},
employment: null
});
});
});
});
});
it('should be possible to query multiple nested values', function() {
return this.Event.create({
data: {
name: {
first: 'Homer',
last: 'Simpson'
},
employment: 'Nuclear Safety Inspector'
}
}).then(() => {
describe('find', () => {
it('should be possible to query a nested value', function() {
return Promise.join(
this.Event.create({
data: {
name: {
first: 'Homer',
last: 'Simpson'
},
employment: 'Nuclear Safety Inspector'
}
}),
this.Event.create({
data: {
name: {
first: 'Marge',
last: 'Simpson'
},
employment: 'Housewife'
}
})
).then(() => {
return this.Event.findAll({
where: {
data: {
employment: 'Housewife'
}
}
}).then(events => {
const event = events[0];
expect(events.length).to.equal(1);
expect(event.get('data')).to.eql({
name: {
first: 'Marge',
last: 'Simpson'
},
employment: 'Housewife'
});
});
});
});
it('should be possible to query a nested integer value', function() {
return Promise.join(
this.Event.create({
data: {
name: {
first: 'Homer',
last: 'Simpson'
},
age: 40
}
}),
this.Event.create({
data: {
name: {
first: 'Bart',
first: 'Marge',
last: 'Simpson'
},
employment: 'None'
age: 37
}
})
);
}).then(() => {
return this.Event.findAll({
where: {
).then(() => {
return this.Event.findAll({
where: {
data: {
age: {
$gt: 38
}
}
}
}).then(events => {
const event = events[0];
expect(events.length).to.equal(1);
expect(event.get('data')).to.eql({
name: {
first: 'Homer',
last: 'Simpson'
},
age: 40
});
});
});
});
it('should be possible to query a nested null value', function() {
return Promise.join(
this.Event.create({
data: {
name: {
first: 'Homer',
last: 'Simpson'
},
employment: {
$ne: 'None'
employment: 'Nuclear Safety Inspector'
}
}),
this.Event.create({
data: {
name: {
first: 'Marge',
last: 'Simpson'
},
employment: null
}
})
).then(() => {
return this.Event.findAll({
where: {
data: {
employment: null
}
}
},
order: [
['id', 'ASC']
]
}).then(events => {
expect(events.length).to.equal(2);
}).then(events => {
expect(events.length).to.equal(1);
expect(events[0].get('data')).to.eql({
name: {
first: 'Marge',
last: 'Simpson'
},
employment: null
});
});
});
});
expect(events[0].get('data')).to.eql({
it('should be possible to query multiple nested values', function() {
return this.Event.create({
data: {
name: {
first: 'Homer',
last: 'Simpson'
},
employment: 'Nuclear Safety Inspector'
});
expect(events[1].get('data')).to.eql({
name: {
first: 'Marge',
last: 'Simpson'
}
}).then(() => {
return Promise.join(
this.Event.create({
data: {
name: {
first: 'Marge',
last: 'Simpson'
},
employment: 'Housewife'
}
}),
this.Event.create({
data: {
name: {
first: 'Bart',
last: 'Simpson'
},
employment: 'None'
}
})
);
}).then(() => {
return this.Event.findAll({
where: {
data: {
name: {
last: 'Simpson'
},
employment: {
$ne: 'None'
}
}
},
employment: 'Housewife'
order: [
['id', 'ASC']
]
}).then(events => {
expect(events.length).to.equal(2);
expect(events[0].get('data')).to.eql({
name: {
first: 'Homer',
last: 'Simpson'
},
employment: 'Nuclear Safety Inspector'
});
expect(events[1].get('data')).to.eql({
name: {
first: 'Marge',
last: 'Simpson'
},
employment: 'Housewife'
});
});
});
});
});
it('should be possible to destroy with where', function() {
const conditionSearch = {
where: {
data: {
employment: 'Hacker'
describe('destroy', () => {
it('should be possible to destroy with where', function() {
const conditionSearch = {
where: {
data: {
employment: 'Hacker'
}
}
}
};
};
return Promise.join(
this.Event.create({
data: {
name: {
first: 'Elliot',
last: 'Alderson'
},
employment: 'Hacker'
}
}),
this.Event.create({
data: {
name: {
first: 'Christian',
last: 'Slater'
},
employment: 'Hacker'
}
}),
this.Event.create({
data: {
name: {
first: ' Tyrell',
last: 'Wellick'
},
employment: 'CTO'
}
})
).then(() => {
return expect(this.Event.findAll(conditionSearch)).to.eventually.have.length(2);
}).then(() => {
return this.Event.destroy(conditionSearch);
}).then(() => {
return expect(this.Event.findAll(conditionSearch)).to.eventually.have.length(0);
return Promise.join(
this.Event.create({
data: {
name: {
first: 'Elliot',
last: 'Alderson'
},
employment: 'Hacker'
}
}),
this.Event.create({
data: {
name: {
first: 'Christian',
last: 'Slater'
},
employment: 'Hacker'
}
}),
this.Event.create({
data: {
name: {
first: ' Tyrell',
last: 'Wellick'
},
employment: 'CTO'
}
})
).then(() => {
return expect(this.Event.findAll(conditionSearch)).to.eventually.have.length(2);
}).then(() => {
return this.Event.destroy(conditionSearch);
}).then(() => {
return expect(this.Event.findAll(conditionSearch)).to.eventually.have.length(0);
});
});
});
......
......@@ -5,6 +5,7 @@ const DataTypes = require(__dirname + '/../../../lib/data-types');
const chai = require('chai');
const expect = chai.expect;
const sinon = require('sinon');
const current = Support.sequelize;
describe(Support.getTestDialectTeaser('Model'), () => {
describe('paranoid', () => {
......@@ -100,5 +101,62 @@ describe(Support.getTestDialectTeaser('Model'), () => {
expect(count).to.be.equal(1);
});
});
if (current.dialect.supports.JSON) {
describe('JSONB', () => {
before(function() {
this.Model = this.sequelize.define('Model', {
name: {
type: DataTypes.STRING
},
data: {
type: DataTypes.JSONB
},
deletedAt: {
type: DataTypes.DATE,
allowNull: true,
field: 'deleted_at'
}
}, {
paranoid: true,
timestamps: true,
deletedAt: 'deletedAt'
});
});
beforeEach(function() {
return this.Model.sync({ force: true });
});
it('should soft delete with JSONB condition', function() {
return this.Model.bulkCreate([{
name: 'One',
data: {
field: {
deep: true
}
}
}, {
name: 'Two',
data: {
field: {
deep: false
}
}
}]).then(() => this.Model.destroy({
where: {
data: {
field: {
deep: true
}
}
}
})).then(() => this.Model.findAll()).then(records => {
expect(records.length).to.equal(1);
expect(records[0].get('name')).to.equal('Two');
});
});
});
}
});
});
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!