1 | var once = require('once'),
|
2 | db = require('../db')
|
3 |
|
4 | module.exports = function scan(store, data, cb) {
|
5 | cb = once(cb)
|
6 |
|
7 | store.getTable(data.TableName, function(err, table) {
|
8 | if (err) return cb(err)
|
9 |
|
10 | var keySchema = table.KeySchema, startKeyNames = keySchema.map(function(key) { return key.AttributeName }),
|
11 | fetchFromItemDb = false, isLocal
|
12 |
|
13 | if (data.IndexName) {
|
14 | var index = db.traverseIndexes(table, function(attr, type, index, isGlobal) {
|
15 | if (index.IndexName == data.IndexName) {
|
16 | isLocal = !isGlobal
|
17 | return index
|
18 | }
|
19 | })
|
20 | if (index == null) {
|
21 | return cb(db.validationError('The table does not have the specified index: ' + data.IndexName))
|
22 | }
|
23 | if (!isLocal && data.ConsistentRead) {
|
24 | return cb(db.validationError('Consistent reads are not supported on global secondary indexes'))
|
25 | }
|
26 | keySchema = index.KeySchema
|
27 | fetchFromItemDb = data.Select == 'ALL_ATTRIBUTES' && index.Projection.ProjectionType != 'ALL'
|
28 | keySchema.forEach(function(key) { if (!~startKeyNames.indexOf(key.AttributeName)) startKeyNames.push(key.AttributeName) })
|
29 | }
|
30 |
|
31 | if (data.ExclusiveStartKey && Object.keys(data.ExclusiveStartKey).length != startKeyNames.length) {
|
32 | return data.IndexName ? cb(db.validationError('The provided starting key is invalid')) :
|
33 | cb(db.validationError('The provided starting key is invalid: The provided key element does not match the schema'))
|
34 | }
|
35 |
|
36 | if (data.IndexName && data.ExclusiveStartKey) {
|
37 | err = db.traverseKey(table, keySchema, function(attr, type, isHash) {
|
38 | if (!data.ExclusiveStartKey[attr]) {
|
39 | return db.validationError('The provided starting key is invalid')
|
40 | }
|
41 | return db.validateKeyPiece(data.ExclusiveStartKey, attr, type, isHash)
|
42 | })
|
43 | if (err) return cb(err)
|
44 | }
|
45 |
|
46 | if (fetchFromItemDb && !isLocal) {
|
47 | return cb(db.validationError('One or more parameter values were invalid: ' +
|
48 | 'Select type ALL_ATTRIBUTES is not supported for global secondary index ' +
|
49 | data.IndexName + ' because its projection type is not ALL'))
|
50 | }
|
51 |
|
52 | if (data.ExclusiveStartKey) {
|
53 | var tableStartKey = table.KeySchema.reduce(function(obj, attr) {
|
54 | obj[attr.AttributeName] = data.ExclusiveStartKey[attr.AttributeName]
|
55 | return obj
|
56 | }, {})
|
57 | if ((err = db.validateKey(tableStartKey, table)) != null) {
|
58 | return cb(db.validationError('The provided starting key is invalid: ' + err.message))
|
59 | }
|
60 | }
|
61 |
|
62 | if (data.TotalSegments > 1) {
|
63 | if (data.Segment > 0)
|
64 | var hashStart = ('00' + Math.ceil(4096 * data.Segment / data.TotalSegments).toString(16)).slice(-3)
|
65 | var hashEnd = ('00' + (Math.ceil(4096 * (data.Segment + 1) / data.TotalSegments) - 1).toString(16)).slice(-3) + '~'
|
66 | }
|
67 |
|
68 | if (data.ExclusiveStartKey) {
|
69 | var startKey = db.createIndexKey(data.ExclusiveStartKey, table, keySchema)
|
70 |
|
71 | if (data.TotalSegments > 1 && (startKey < hashStart || startKey > hashEnd)) {
|
72 | return cb(db.validationError('The provided starting key is invalid: Invalid ExclusiveStartKey. ' +
|
73 | 'Please use ExclusiveStartKey with correct Segment. ' +
|
74 | 'TotalSegments: ' + data.TotalSegments + ' Segment: ' + data.Segment))
|
75 | }
|
76 |
|
77 | hashStart = startKey
|
78 | }
|
79 |
|
80 | if ((err = db.validateKeyPaths((data._projection || {}).nestedPaths, table)) != null) return cb(err)
|
81 |
|
82 | if ((err = db.validateKeyPaths((data._filter || {}).nestedPaths, table)) != null) return cb(err)
|
83 |
|
84 | var opts = {limit: data.Limit ? data.Limit + 1 : -1}
|
85 |
|
86 | if (hashStart != null) {
|
87 | opts.gt = hashStart
|
88 | }
|
89 | if (hashEnd != null) {
|
90 | opts.lt = hashEnd
|
91 | }
|
92 |
|
93 | db.queryTable(store, table, data, opts, isLocal, fetchFromItemDb, startKeyNames, cb)
|
94 | })
|
95 | }
|