-
Notifications
You must be signed in to change notification settings - Fork 108
/
Copy pathquery.js
181 lines (156 loc) · 6.95 KB
/
query.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
var once = require('once'),
db = require('../db')
module.exports = function query (store, data, cb) {
cb = once(cb)
store.getTable(data.TableName, function (err, table) {
if (err) return cb(err)
var keySchema = table.KeySchema, startKeyNames = keySchema.map(function (key) { return key.AttributeName }),
hashKey = startKeyNames[0], rangeKey = startKeyNames[1], fetchFromItemDb = false, isLocal
if (data.IndexName) {
var index = db.traverseIndexes(table, function (attr, type, index, isGlobal) {
if (index.IndexName == data.IndexName) {
isLocal = !isGlobal
return index
}
})
if (index == null) {
return cb(db.validationError('The table does not have the specified index: ' + data.IndexName))
}
if (!isLocal && data.ConsistentRead) {
return cb(db.validationError('Consistent reads are not supported on global secondary indexes'))
}
keySchema = index.KeySchema
fetchFromItemDb = data.Select == 'ALL_ATTRIBUTES' && index.Projection.ProjectionType != 'ALL'
keySchema.forEach(function (key) { if (!~startKeyNames.indexOf(key.AttributeName)) startKeyNames.push(key.AttributeName) })
hashKey = keySchema[0].AttributeName
rangeKey = keySchema[1] && keySchema[1].AttributeName
}
if (data.ExclusiveStartKey && Object.keys(data.ExclusiveStartKey).length != startKeyNames.length) {
return cb(db.validationError('The provided starting key is invalid'))
}
err = db.traverseKey(table, keySchema, function (attr, type, isHash) {
if (data.ExclusiveStartKey) {
if (!data.ExclusiveStartKey[attr]) {
return db.validationError('The provided starting key is invalid')
}
err = db.validateKeyPiece(data.ExclusiveStartKey, attr, type, isHash)
if (err) return err
}
if (isHash && keySchema.length == 1 && Object.keys(data.KeyConditions).length > 1) {
return db.validationError('Query key condition not supported')
}
if (!data.KeyConditions[attr]) {
if (isHash || Object.keys(data.KeyConditions).length > 1) {
return db.validationError('Query condition missed key schema element: ' + attr)
}
return
}
var comparisonOperator = data.KeyConditions[attr].ComparisonOperator
if (~[ 'NULL', 'NOT_NULL', 'NE', 'CONTAINS', 'NOT_CONTAINS', 'IN' ].indexOf(comparisonOperator)) {
return db.validationError('Attempted conditional constraint is not an indexable operation')
}
if (data.KeyConditions[attr].AttributeValueList.some(function (attrVal) { return attrVal[type] == null })) {
return db.validationError('One or more parameter values were invalid: Condition parameter type does not match schema type')
}
if (isHash && comparisonOperator != 'EQ') {
return db.validationError('Query key condition not supported')
}
})
if (err) return cb(err)
var hashType = Object.keys(data.KeyConditions[hashKey].AttributeValueList[0])[0]
var hashVal = data.KeyConditions[hashKey].AttributeValueList[0][hashType]
if (data.ExclusiveStartKey) {
var tableStartKey = table.KeySchema.reduce(function (obj, attr) {
obj[attr.AttributeName] = data.ExclusiveStartKey[attr.AttributeName]
return obj
}, {})
let invalid = db.validateKey(tableStartKey, table)
if (invalid != null) return cb(db.validationError('The provided starting key is invalid: ' + invalid.message))
if (!rangeKey || !data.KeyConditions[rangeKey]) {
if (data.ExclusiveStartKey[hashKey][hashType] != hashVal) {
return cb(db.validationError('The provided starting key is outside query boundaries based on provided conditions'))
}
}
else {
var matchesRange = db.compare(data.KeyConditions[rangeKey].ComparisonOperator,
data.ExclusiveStartKey[rangeKey], data.KeyConditions[rangeKey].AttributeValueList)
if (!matchesRange) {
return cb(db.validationError('The provided starting key does not match the range key predicate'))
}
if (data.ExclusiveStartKey[hashKey][hashType] != hashVal) {
return cb(db.validationError('The query can return at most one row and cannot be restarted'))
}
}
}
let invalid
invalid = db.validateKeyPaths((data._projection || {}).nestedPaths, table)
if (invalid != null) return cb(invalid)
if (data.QueryFilter || data._filter) {
var pathHeads = data.QueryFilter ? data.QueryFilter : data._filter.pathHeads
var propertyName = data.QueryFilter ? 'QueryFilter' : 'Filter Expression'
err = db.traverseKey(table, keySchema, function (attr) {
if (pathHeads[attr]) {
return db.validationError(propertyName + ' can only contain non-primary key attributes: ' +
'Primary key attribute: ' + attr)
}
})
if (err) return cb(err)
}
if (fetchFromItemDb && !isLocal) {
return cb(db.validationError('One or more parameter values were invalid: ' +
'Select type ALL_ATTRIBUTES is not supported for global secondary index ' +
data.IndexName + ' because its projection type is not ALL'))
}
invalid = db.validateKeyPaths((data._filter || {}).nestedPaths, table)
if (invalid != null) return cb(invalid)
var opts = { reverse: data.ScanIndexForward === false, limit: data.Limit ? data.Limit + 1 : -1 }
opts.gte = db.hashPrefix(hashVal, hashType) + '/' + db.toRangeStr(hashVal, hashType) + '/'
opts.lt = opts.gte + '~'
if (data.KeyConditions[rangeKey]) {
var rangeStrPrefix = db.toRangeStr(data.KeyConditions[rangeKey].AttributeValueList[0])
var rangeStr = rangeStrPrefix + '/'
var comp = data.KeyConditions[rangeKey].ComparisonOperator
if (comp == 'EQ') {
opts.gte += rangeStr
opts.lte = opts.gte + '~'
delete opts.lt
}
else if (comp == 'LT') {
opts.lt = opts.gte + rangeStr
}
else if (comp == 'LE') {
opts.lte = opts.gte + rangeStr + '~'
delete opts.lt
}
else if (comp == 'GT') {
opts.gt = opts.gte + rangeStr + '~'
delete opts.gte
}
else if (comp == 'GE') {
opts.gte += rangeStr
}
else if (comp == 'BEGINS_WITH') {
opts.lt = opts.gte + rangeStrPrefix + '~'
opts.gte += rangeStr
}
else if (comp == 'BETWEEN') {
opts.lte = opts.gte + db.toRangeStr(data.KeyConditions[rangeKey].AttributeValueList[1]) + '/~'
opts.gte += rangeStr
delete opts.lt
}
}
if (data.ExclusiveStartKey) {
var createKey = data.IndexName ? db.createIndexKey : db.createKey
var startKey = createKey(data.ExclusiveStartKey, table, keySchema)
if (data.ScanIndexForward === false) {
opts.lt = startKey
delete opts.lte
}
else {
opts.gt = startKey
delete opts.gte
}
}
db.queryTable(store, table, data, opts, isLocal, fetchFromItemDb, startKeyNames, cb)
})
}