Skip to content
Toggle navigation
Projects
Groups
Snippets
Help
public
/
sequelize
This project
Loading...
Sign in
Toggle navigation
Go to a project
Project
Repository
Issues
0
Merge Requests
0
Pipelines
Wiki
Snippets
Settings
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
不要怂,就是干,撸起袖子干!
Commit 315b4cf7
authored
Apr 21, 2015
by
Mick Hansen
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
feat: JSONB GIN indexing
1 parent
41a9e502
Hide whitespace changes
Inline
Side-by-side
Showing
7 changed files
with
136 additions
and
44 deletions
lib/dialects/abstract/query-generator.js
lib/dialects/postgres/index.js
lib/model.js
lib/query-interface.js
test/integration/dialects/postgres/query-generator.test.js
test/unit/model/indexes.test.js
test/unit/sql/index.test.js
lib/dialects/abstract/query-generator.js
View file @
315b4cf
...
@@ -429,8 +429,8 @@ module.exports = (function() {
...
@@ -429,8 +429,8 @@ module.exports = (function() {
nameIndexes
:
function
(
indexes
,
rawTablename
)
{
nameIndexes
:
function
(
indexes
,
rawTablename
)
{
return
Utils
.
_
.
map
(
indexes
,
function
(
index
)
{
return
Utils
.
_
.
map
(
indexes
,
function
(
index
)
{
if
(
!
index
.
hasOwnProperty
(
'name'
))
{
if
(
!
index
.
hasOwnProperty
(
'name'
))
{
var
onlyAttributeNames
=
index
.
fields
.
map
(
function
(
attribute
)
{
var
onlyAttributeNames
=
index
.
fields
.
map
(
function
(
field
)
{
return
(
typeof
attribute
===
'string'
)
?
attribute
:
attribute
.
attribute
;
return
(
typeof
field
===
'string'
)
?
field
:
(
field
.
name
||
field
.
attribute
)
;
}.
bind
(
this
));
}.
bind
(
this
));
index
.
name
=
Utils
.
inflection
.
underscore
(
rawTablename
+
'_'
+
onlyAttributeNames
.
join
(
'_'
));
index
.
name
=
Utils
.
inflection
.
underscore
(
rawTablename
+
'_'
+
onlyAttributeNames
.
join
(
'_'
));
...
@@ -444,45 +444,68 @@ module.exports = (function() {
...
@@ -444,45 +444,68 @@ module.exports = (function() {
Returns an add index query.
Returns an add index query.
Parameters:
Parameters:
- tableName -> Name of an existing table, possibly with schema.
- tableName -> Name of an existing table, possibly with schema.
- attributes:
An array of attributes as string or as hash.
If the attribute is a hash, it must have the following content:
- attribute: The name of the attribute/column
- length: An integer. Optional
- order: 'ASC' or 'DESC'. Optional
- options:
- options:
- indicesType: UNIQUE|FULLTEXT|SPATIAL
- type: UNIQUE|FULLTEXT|SPATIAL
- indexName: The name of the index. Default is <tableName>_<attrName1>_<attrName2>
- name: The name of the index. Default is <table>_<attr1>_<attr2>
- fields: An array of attributes as string or as hash.
If the attribute is a hash, it must have the following content:
- name: The name of the attribute/column
- length: An integer. Optional
- order: 'ASC' or 'DESC'. Optional
- parser
- parser
- rawTablename, the name of the table, without schema. Used to create the name of the index
- rawTablename, the name of the table, without schema. Used to create the name of the index
*/
*/
addIndexQuery
:
function
(
tableName
,
attributes
,
options
,
rawTablename
)
{
addIndexQuery
:
function
(
tableName
,
attributes
,
options
,
rawTablename
)
{
var
fieldsSql
;
options
=
options
||
{};
options
=
options
||
{};
var
transformedAttributes
=
attributes
.
map
(
function
(
attribute
)
{
if
(
!
Array
.
isArray
(
attributes
))
{
if
(
typeof
attribute
===
'string'
)
{
options
=
attributes
;
return
this
.
quoteIdentifier
(
attribute
);
attributes
=
undefined
;
}
else
if
(
attribute
.
_isSequelizeMethod
)
{
}
else
{
return
this
.
handleSequelizeMethod
(
attribute
);
options
.
fields
=
attributes
;
}
// Backwards compatability
if
(
options
.
indexName
)
{
options
.
name
=
options
.
indexName
;
}
options
.
prefix
=
options
.
prefix
||
rawTablename
||
tableName
;
if
(
options
.
prefix
)
{
options
.
prefix
=
options
.
prefix
.
replace
(
/
\.
/g
,
'_'
);
options
.
prefix
=
options
.
prefix
.
replace
(
/
\"\'
/g
,
''
);
}
fieldsSql
=
options
.
fields
.
map
(
function
(
field
)
{
if
(
typeof
field
===
'string'
)
{
return
this
.
quoteIdentifier
(
field
);
}
else
if
(
field
.
_isSequelizeMethod
)
{
return
this
.
handleSequelizeMethod
(
field
);
}
else
{
}
else
{
var
result
=
''
;
var
result
=
''
;
if
(
!
attribute
.
attribute
)
{
if
(
field
.
attribute
)
{
throw
new
Error
(
'The following index attribute has no attribute: '
+
util
.
inspect
(
attribute
));
field
.
name
=
field
.
attribute
;
}
if
(
!
field
.
name
)
{
throw
new
Error
(
'The following index field has no name: '
+
util
.
inspect
(
field
));
}
}
result
+=
this
.
quoteIdentifier
(
attribute
.
attribut
e
);
result
+=
this
.
quoteIdentifier
(
field
.
nam
e
);
if
(
this
.
_dialect
.
supports
.
index
.
collate
&&
attribute
.
collate
)
{
if
(
this
.
_dialect
.
supports
.
index
.
collate
&&
field
.
collate
)
{
result
+=
' COLLATE '
+
this
.
quoteIdentifier
(
attribute
.
collate
);
result
+=
' COLLATE '
+
this
.
quoteIdentifier
(
field
.
collate
);
}
}
if
(
this
.
_dialect
.
supports
.
index
.
length
&&
attribute
.
length
)
{
if
(
this
.
_dialect
.
supports
.
index
.
length
&&
field
.
length
)
{
result
+=
'('
+
attribute
.
length
+
')'
;
result
+=
'('
+
field
.
length
+
')'
;
}
}
if
(
attribute
.
order
)
{
if
(
field
.
order
)
{
result
+=
' '
+
attribute
.
order
;
result
+=
' '
+
field
.
order
;
}
}
return
result
;
return
result
;
...
@@ -492,37 +515,37 @@ module.exports = (function() {
...
@@ -492,37 +515,37 @@ module.exports = (function() {
if
(
!
options
.
name
)
{
if
(
!
options
.
name
)
{
// Mostly for cases where addIndex is called directly by the user without an options object (for example in migrations)
// Mostly for cases where addIndex is called directly by the user without an options object (for example in migrations)
// All calls that go through sequelize should already have a name
// All calls that go through sequelize should already have a name
options
.
fields
=
options
.
fields
||
attributes
;
options
=
this
.
nameIndexes
([
options
],
options
.
prefix
)[
0
];
options
=
this
.
nameIndexes
([
options
],
rawTablename
)[
0
];
}
}
options
=
Utils
.
_
.
defaults
(
options
,
{
options
=
Utils
.
_
.
defaults
(
options
,
{
type
:
''
,
type
:
''
,
indicesType
:
options
.
type
||
''
,
indexType
:
options
.
method
||
undefined
,
indexName
:
options
.
name
,
parser
:
null
parser
:
null
});
});
if
(
options
.
indicesT
ype
.
toLowerCase
()
===
'unique'
)
{
if
(
options
.
t
ype
.
toLowerCase
()
===
'unique'
)
{
options
.
unique
=
true
;
options
.
unique
=
true
;
delete
options
.
indicesT
ype
;
delete
options
.
t
ype
;
}
}
if
(
!
this
.
_dialect
.
supports
.
index
.
type
)
{
if
(
!
this
.
_dialect
.
supports
.
index
.
type
)
{
delete
options
.
indicesType
;
delete
options
.
type
;
}
if
(
options
.
indexType
||
options
.
method
)
{
options
.
using
=
options
.
indexType
||
options
.
method
;
}
}
return
Utils
.
_
.
compact
([
return
Utils
.
_
.
compact
([
'CREATE'
,
'CREATE'
,
options
.
unique
?
'UNIQUE'
:
''
,
options
.
unique
?
'UNIQUE'
:
''
,
options
.
indicesT
ype
,
'INDEX'
,
options
.
t
ype
,
'INDEX'
,
this
.
_dialect
.
supports
.
index
.
concurrently
&&
options
.
concurrently
?
'CONCURRENTLY'
:
undefined
,
this
.
_dialect
.
supports
.
index
.
concurrently
&&
options
.
concurrently
?
'CONCURRENTLY'
:
undefined
,
this
.
quoteIdentifiers
(
options
.
indexN
ame
),
this
.
quoteIdentifiers
(
options
.
n
ame
),
this
.
_dialect
.
supports
.
index
.
using
===
1
&&
options
.
indexType
?
'USING '
+
options
.
indexType
:
''
,
this
.
_dialect
.
supports
.
index
.
using
===
1
&&
options
.
using
?
'USING '
+
options
.
using
:
''
,
'ON'
,
this
.
quoteIdentifiers
(
tableName
),
'ON'
,
this
.
quoteIdentifiers
(
tableName
),
this
.
_dialect
.
supports
.
index
.
using
===
2
&&
options
.
indexType
?
'USING '
+
options
.
indexType
:
''
,
this
.
_dialect
.
supports
.
index
.
using
===
2
&&
options
.
using
?
'USING '
+
options
.
using
:
''
,
'('
+
transformedAttributes
.
join
(
',
'
)
+
')'
,
'('
+
fieldsSql
.
join
(
', '
)
+
(
options
.
operator
?
' '
+
options
.
operator
:
'
'
)
+
')'
,
(
this
.
_dialect
.
supports
.
index
.
parser
&&
options
.
parser
?
'WITH PARSER '
+
options
.
parser
:
undefined
)
(
this
.
_dialect
.
supports
.
index
.
parser
&&
options
.
parser
?
'WITH PARSER '
+
options
.
parser
:
undefined
)
]).
join
(
' '
);
]).
join
(
' '
);
},
},
...
...
lib/dialects/postgres/index.js
View file @
315b4cf
...
@@ -38,7 +38,7 @@ PostgresDialect.prototype.supports = _.merge(_.cloneDeep(Abstract.prototype.supp
...
@@ -38,7 +38,7 @@ PostgresDialect.prototype.supports = _.merge(_.cloneDeep(Abstract.prototype.supp
concurrently
:
true
,
concurrently
:
true
,
using
:
2
,
using
:
2
,
},
},
JSON
:
true
,
JSON
:
true
});
});
PostgresDialect
.
prototype
.
Query
=
Query
;
PostgresDialect
.
prototype
.
Query
=
Query
;
...
...
lib/model.js
View file @
315b4cf
...
@@ -36,7 +36,8 @@ module.exports = (function() {
...
@@ -36,7 +36,8 @@ module.exports = (function() {
schemaDelimiter
:
''
,
schemaDelimiter
:
''
,
defaultScope
:
null
,
defaultScope
:
null
,
scopes
:
null
,
scopes
:
null
,
hooks
:
{}
hooks
:
{},
indexes
:
[]
},
options
||
{});
},
options
||
{});
this
.
associations
=
{};
this
.
associations
=
{};
...
@@ -169,7 +170,6 @@ module.exports = (function() {
...
@@ -169,7 +170,6 @@ module.exports = (function() {
self
.
options
.
uniqueKeys
[
idxName
].
msg
=
self
.
options
.
uniqueKeys
[
idxName
].
msg
||
options
.
unique
.
msg
||
null
;
self
.
options
.
uniqueKeys
[
idxName
].
msg
=
self
.
options
.
uniqueKeys
[
idxName
].
msg
||
options
.
unique
.
msg
||
null
;
self
.
options
.
uniqueKeys
[
idxName
].
name
=
idxName
||
false
;
self
.
options
.
uniqueKeys
[
idxName
].
name
=
idxName
||
false
;
}
}
}
}
if
(
options
.
primaryKey
===
true
)
{
if
(
options
.
primaryKey
===
true
)
{
...
@@ -327,6 +327,15 @@ module.exports = (function() {
...
@@ -327,6 +327,15 @@ module.exports = (function() {
if
(
definition
.
hasOwnProperty
(
'validate'
))
{
if
(
definition
.
hasOwnProperty
(
'validate'
))
{
self
.
Instance
.
prototype
.
validators
[
name
]
=
definition
.
validate
;
self
.
Instance
.
prototype
.
validators
[
name
]
=
definition
.
validate
;
}
}
if
(
definition
.
index
===
true
&&
definition
.
type
instanceof
DataTypes
.
JSONB
)
{
self
.
options
.
indexes
.
push
({
fields
:
[
definition
.
field
||
name
],
using
:
'gin'
});
delete
definition
.
index
;
}
});
});
this
.
_hasBooleanAttributes
=
!!
this
.
_booleanAttributes
.
length
;
this
.
_hasBooleanAttributes
=
!!
this
.
_booleanAttributes
.
length
;
...
@@ -415,7 +424,7 @@ module.exports = (function() {
...
@@ -415,7 +424,7 @@ module.exports = (function() {
});
});
return
Promise
.
map
(
indexes
,
function
(
index
)
{
return
Promise
.
map
(
indexes
,
function
(
index
)
{
return
self
.
QueryInterface
.
addIndex
(
self
.
getTableName
(
options
),
index
.
fields
,
index
,
self
.
tableName
);
return
self
.
QueryInterface
.
addIndex
(
self
.
getTableName
(
options
),
index
,
self
.
tableName
);
});
});
}).
return
(
this
);
}).
return
(
this
);
};
};
...
...
lib/query-interface.js
View file @
315b4cf
...
@@ -441,7 +441,8 @@ module.exports = (function() {
...
@@ -441,7 +441,8 @@ module.exports = (function() {
}
}
options
=
options
||
{};
options
=
options
||
{};
var
sql
=
this
.
QueryGenerator
.
addIndexQuery
(
tableName
,
attributes
,
options
,
rawTablename
);
options
.
fields
=
attributes
;
var
sql
=
this
.
QueryGenerator
.
addIndexQuery
(
tableName
,
options
,
rawTablename
);
return
this
.
sequelize
.
query
(
sql
,
null
,
{
logging
:
options
.
hasOwnProperty
(
'logging'
)
?
options
.
logging
:
this
.
sequelize
.
options
.
logging
});
return
this
.
sequelize
.
query
(
sql
,
null
,
{
logging
:
options
.
hasOwnProperty
(
'logging'
)
?
options
.
logging
:
this
.
sequelize
.
options
.
logging
});
};
};
...
...
test/integration/dialects/postgres/query-generator.test.js
View file @
315b4cf
...
@@ -868,7 +868,7 @@ if (dialect.match(/^postgres/)) {
...
@@ -868,7 +868,7 @@ if (dialect.match(/^postgres/)) {
],
],
expectation
:
'CREATE INDEX \"user_username_is_admin\" ON \"User\" (\"username\" ASC, \"isAdmin\")'
expectation
:
'CREATE INDEX \"user_username_is_admin\" ON \"User\" (\"username\" ASC, \"isAdmin\")'
},
{
},
{
arguments
:
[
'User'
,
[
'username'
,
'isAdmin'
],
{
indexName
:
'bar'
},
{},
'User'
],
arguments
:
[
'User'
,
[
'username'
,
'isAdmin'
],
{
indexName
:
'bar'
},
'User'
],
expectation
:
'CREATE INDEX \"bar\" ON \"User\" (\"username\", \"isAdmin\")'
expectation
:
'CREATE INDEX \"bar\" ON \"User\" (\"username\", \"isAdmin\")'
},
{
},
{
arguments
:
[
'mySchema.User'
,
[
'username'
,
'isAdmin'
],
{},
'User'
],
arguments
:
[
'mySchema.User'
,
[
'username'
,
'isAdmin'
],
{},
'User'
],
...
@@ -905,7 +905,7 @@ if (dialect.match(/^postgres/)) {
...
@@ -905,7 +905,7 @@ if (dialect.match(/^postgres/)) {
expectation
:
'CREATE INDEX user_username_is_admin ON User (username ASC, isAdmin)'
,
expectation
:
'CREATE INDEX user_username_is_admin ON User (username ASC, isAdmin)'
,
context
:
{
options
:
{
quoteIdentifiers
:
false
}}
context
:
{
options
:
{
quoteIdentifiers
:
false
}}
},
{
},
{
arguments
:
[
'User'
,
[
'username'
,
'isAdmin'
],
{
indexName
:
'bar'
},
{},
'User'
],
arguments
:
[
'User'
,
[
'username'
,
'isAdmin'
],
{
indexName
:
'bar'
},
'User'
],
expectation
:
'CREATE INDEX bar ON User (username, isAdmin)'
,
expectation
:
'CREATE INDEX bar ON User (username, isAdmin)'
,
context
:
{
options
:
{
quoteIdentifiers
:
false
}}
context
:
{
options
:
{
quoteIdentifiers
:
false
}}
},
{
},
{
...
...
test/unit/model/indexes.test.js
0 → 100644
View file @
315b4cf
'use strict'
;
/* jshint -W030 */
var
chai
=
require
(
'chai'
)
,
expect
=
chai
.
expect
,
Support
=
require
(
__dirname
+
'/../support'
)
,
current
=
Support
.
sequelize
,
DataTypes
=
require
(
__dirname
+
'/../../../lib/data-types'
);
describe
(
Support
.
getTestDialectTeaser
(
'Model'
),
function
()
{
describe
(
'indexes'
,
function
()
{
it
(
'should automatically set a gin index for JSONB indexes'
,
function
()
{
var
Model
=
current
.
define
(
'event'
,
{
eventData
:
{
type
:
DataTypes
.
JSONB
,
index
:
true
,
field
:
'data'
}
});
expect
(
Model
.
rawAttributes
.
eventData
.
index
).
not
.
to
.
equal
(
true
);
expect
(
Model
.
options
.
indexes
.
length
).
to
.
equal
(
1
);
expect
(
Model
.
options
.
indexes
[
0
].
fields
).
to
.
eql
([
'data'
]);
expect
(
Model
.
options
.
indexes
[
0
].
using
).
to
.
equal
(
'gin'
);
});
});
});
\ No newline at end of file
test/unit/sql/index.test.js
View file @
315b4cf
...
@@ -14,6 +14,14 @@ suite(Support.getTestDialectTeaser('SQL'), function() {
...
@@ -14,6 +14,14 @@ suite(Support.getTestDialectTeaser('SQL'), function() {
expectsql
(
sql
.
addIndexQuery
(
'table'
,
[
'column1'
,
'column2'
],
{},
'table'
),
{
expectsql
(
sql
.
addIndexQuery
(
'table'
,
[
'column1'
,
'column2'
],
{},
'table'
),
{
default
:
'CREATE INDEX [table_column1_column2] ON [table] ([column1], [column2])'
default
:
'CREATE INDEX [table_column1_column2] ON [table] ([column1], [column2])'
});
});
expectsql
(
sql
.
addIndexQuery
(
'schema.table'
,
[
'column1'
,
'column2'
],
{}),
{
default
:
'CREATE INDEX [schema_table_column1_column2] ON [schema].[table] ([column1], [column2])'
});
expectsql
(
sql
.
addIndexQuery
(
'"schema"."table"'
,
[
'column1'
,
'column2'
],
{}),
{
default
:
'CREATE INDEX [schema_table_column1_column2] ON [schema].[table] ([column1], [column2])'
});
});
});
test
(
'POJO field'
,
function
()
{
test
(
'POJO field'
,
function
()
{
...
@@ -30,5 +38,28 @@ suite(Support.getTestDialectTeaser('SQL'), function() {
...
@@ -30,5 +38,28 @@ suite(Support.getTestDialectTeaser('SQL'), function() {
default
:
'CREATE INDEX [myindex] ON [table] (UPPER([test]))'
default
:
'CREATE INDEX [myindex] ON [table] (UPPER([test]))'
});
});
});
});
if
(
current
.
dialect
.
supports
.
index
.
using
===
2
)
{
test
(
'USING'
,
function
()
{
expectsql
(
sql
.
addIndexQuery
(
'table'
,
{
fields
:
[
'event'
],
using
:
'gin'
}),
{
postgres
:
'CREATE INDEX "table_event" ON "table" USING gin ("event")'
});
});
}
if
(
current
.
dialect
.
supports
.
JSON
)
{
test
(
'operator'
,
function
()
{
expectsql
(
sql
.
addIndexQuery
(
'table'
,
{
fields
:
[
'event'
],
using
:
'gin'
,
operator
:
'jsonb_path_ops'
}),
{
postgres
:
'CREATE INDEX "table_event" ON "table" USING gin ("event" jsonb_path_ops)'
});
});
}
});
});
});
});
Write
Preview
Markdown
is supported
Attach a file
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to post a comment