cosmos-db-repositories
Version:
cosmos-db repositories
181 lines (180 loc) • 8.78 kB
JavaScript
;
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const cosmos_1 = require("@azure/cosmos");
const entity_utils_1 = __importDefault(require("./entity.utils"));
const uuid_1 = require("uuid");
const time_utils_1 = require("./time.utils");
let container;
const containerInstance = () => __awaiter(void 0, void 0, void 0, function* () {
if (!container) {
const endpoint = process.env.AZURE_DATASOURCE_URL;
const key = process.env.AZURE_DATASOURCE_KEY;
const databaseId = process.env.AZURE_DATASOURCE_DATABASE_ID;
const containerId = process.env.AZURE_DATASOURCE_CONTAINER_ID;
container = new cosmos_1.CosmosClient({ endpoint, key }).database(databaseId).container(containerId);
}
return container;
});
const save = (item) => __awaiter(void 0, void 0, void 0, function* () {
const container = yield containerInstance();
const { resource } = yield container.items.create(item);
return entity_utils_1.default.cleanUp(resource);
});
function chunk(array, size) {
return Array.from({ length: Math.ceil(array.length / size) }, (_, i) => array.slice(i * size, i * size + size));
}
const bulk = (operations, chunkSize = 100) => __awaiter(void 0, void 0, void 0, function* () {
const partitionKey = process.env.AZURE_DATASOURCE_PARTITION_KEY;
const failed = [];
const token = (0, uuid_1.v4)();
const startDate = new Date();
let importDataCount = 0;
let importDataFailedCount = 0;
console.log('Start migrate (' + operations.length + ') users at ', startDate.toISOString());
// Group by partition key
const groups = new Map();
for (const item of operations) {
const pk = item.resourceBody[partitionKey];
item.partitionKey = pk;
if (!groups.has(pk)) {
groups.set(pk, []);
}
groups.get(pk).push(item);
}
const container = yield containerInstance();
for (const [partitionKey, groupItems] of groups) {
const chunks = chunk(groupItems, chunkSize);
let groupImportDataCount = 0;
let groupImportDataFailedCount = 0;
for (const chunkItems of chunks) {
const response = yield container.items.bulk(chunkItems);
let chunkImportDataCount = 0;
let chunkImportDataFailedCount = 0;
response.forEach((res, i) => {
var _a, _b;
if (res.statusCode >= 200 && res.statusCode < 300) {
chunkImportDataCount++;
groupImportDataCount++;
importDataCount++;
}
else {
chunkImportDataFailedCount++;
groupImportDataFailedCount++;
importDataFailedCount++;
failed.push(chunkItems[i].resourceBody);
console.error(`Failed upsert for item [${(_b = (_a = chunkItems[i]) === null || _a === void 0 ? void 0 : _a.resourceBody) === null || _b === void 0 ? void 0 : _b.id}] with statusCode: ${res.statusCode}`);
}
});
console.log(`Partition [${partitionKey}] Summary: ${groupImportDataCount} succeeded, ${groupImportDataFailedCount} failed of ${groupItems.length} total items`);
}
}
const now = new Date();
const duration = (0, time_utils_1.timeDuration)(startDate.getTime(), now.getTime());
return {
total: operations.length,
imported: importDataCount,
failed,
duration,
token,
message: 'Data import [' + importDataCount + '] successfully completed and [' + importDataFailedCount + '] failed'
};
});
const get = (id, partitionKey) => __awaiter(void 0, void 0, void 0, function* () {
const container = yield containerInstance();
const { resource } = yield container.item(id, partitionKey).read();
return entity_utils_1.default.cleanUp(resource);
});
const update = (updatedData) => __awaiter(void 0, void 0, void 0, function* () {
const container = yield containerInstance();
const id = updatedData.id;
const partitionKey = updatedData[process.env.AZURE_DATASOURCE_PARTITION_KEY];
const { resource: existingItem } = yield container.item(id, partitionKey).read();
// Merge the updates into the existing item
const updatedItem = Object.assign(Object.assign({}, existingItem), updatedData);
const { resource } = yield container.item(id, partitionKey).replace(updatedItem);
return entity_utils_1.default.cleanUp(resource);
});
const remove = (id, partitionKey) => __awaiter(void 0, void 0, void 0, function* () {
const container = yield containerInstance();
const { resource } = yield container.item(id, partitionKey).delete();
return entity_utils_1.default.cleanUp(resource);
});
const query = (querySpec, pageRequest) => __awaiter(void 0, void 0, void 0, function* () {
const pageable = Object.assign({ pageIndex: 1, rowsPerPage: 25 }, (pageRequest || {}));
const { pageIndex, rowsPerPage } = pageable;
const container = yield containerInstance();
const totalItems = yield fetchTotalCount(querySpec, container); // Total item count
// Iterate through pages until the desired pageIndex is reached
const { results, continuationToken } = yield fetchResults(querySpec, pageable, container);
const data = {
pageIndex,
rowsPerPage,
totalItems,
continuationToken,
totalPages: Math.ceil(totalItems / rowsPerPage),
content: results.map(entity_utils_1.default.cleanUp),
map: (converter) => data.content = data.content.map(converter)
};
return data;
});
const nativeQuery = (query, options) => __awaiter(void 0, void 0, void 0, function* () {
const container = yield containerInstance();
const queryIterator = container.items.query(query, options);
// Fetch results for the current page
const { resources: results } = yield queryIterator.fetchAll();
return results || [];
});
const fetchTotalCount = (querySpec, container) => __awaiter(void 0, void 0, void 0, function* () {
const { query, parameters } = querySpec;
const tokens = query.split("WHERE");
let totalQuery = 'SELECT VALUE COUNT(1) FROM c ';
if (tokens.length > 1) {
totalQuery += 'WHERE' + tokens[1];
}
const totalQuerySpec = { query: totalQuery.trim(), parameters };
const { resources } = yield container.items.query(totalQuerySpec).fetchAll();
return resources ? resources[0] : 0; // Single value for the total count
});
const fetchPaginatedResults = (querySpec, rowsPerPage, continuationToken = null, container) => __awaiter(void 0, void 0, void 0, function* () {
// Calculate the offset based on pageIndex and rowsPerPage
const options = {
maxItemCount: rowsPerPage,
continuationToken, // Token for the next page
};
const queryIterator = container.items.query(querySpec, options);
// Fetch results for the current page
const { resources: results, continuationToken: nextToken } = yield queryIterator.fetchNext();
return { results: results || [], nextToken };
});
const fetchResults = (querySpec, pageable, container) => __awaiter(void 0, void 0, void 0, function* () {
const { pageIndex, rowsPerPage } = pageable;
let continuationToken = null;
let currentPageIndex = 1; // 1 base page index
let pageResults = [];
// Iterate through pages until the desired pageIndex is reached
do {
const { results, nextToken } = yield fetchPaginatedResults(querySpec, rowsPerPage, continuationToken, container);
continuationToken = nextToken;
if (currentPageIndex === pageIndex) {
pageResults = results;
break;
}
currentPageIndex++;
} while (continuationToken);
return { results: pageResults, continuationToken };
});
exports.default = {
get, query, save, update, remove, bulk, nativeQuery, containerInstance
};