@relewise/create-relewise-learning-example
Version:
CLI tool to scaffold new Relewise learning projects with TypeScript, examples, and AI instructions
219 lines (196 loc) β’ 8.71 kB
text/typescript
/**
* Example: Historical Order Data Import to Relewise
*
* This file demonstrates how to import historical order data into Relewise for immediate personalization benefits.
*
* Key Features:
* - Loads historical order data from a local JSON file
* - Handles multi-market orders (DK, SE, NO, GB) and B2B/B2C channels
* - Proper user classification and company association
* - Rate limiting to avoid API throttling
* - Comprehensive error handling and progress tracking
* - Uses actual product IDs from the sample product data
*
* Usage:
* 1. Ensure your `.env` file contains RELEWISE_DATASET_ID, RELEWISE_API_KEY, and RELEWISE_SERVER_URL.
* 2. Historical order data is loaded from `product_data/historical_orders_example.json`.
* 3. Run with `npm run dev historicalDataImportExample` or import/run from `index.ts`.
* 4. Use this as a template for importing your own historical order data.
*
* Important Notes:
* - Historical data import should be done ONCE during initial setup
* - After going live, use real-time tracking only
* - Ensure product IDs in historical data match your current catalog
* - Uses HistoricOrderDate data field to preserve original order timeline for personalization
* - Orders track to current import time for analytics, but use data field for recommendation weighting
* - For optimal results, import historical data in chronological order
*
* For more, see:
* - https://github.com/Relewise/relewise-sdk-javascript
* - Project's .github/copilot-instructions.md
*/
import 'dotenv/config';
import { UserFactory, Order, Trackable, DataValueFactory } from '@relewise/client';
import { integrator } from '../../config/relewiseConfig.js';
import fs from 'fs/promises';
import path from 'path';
import { fileURLToPath } from 'url';
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
interface HistoricalOrder {
orderNumber: string;
customerId: string;
companyId?: string;
orderDate: string;
currency: string;
userClassifications: Record<string, string>;
lineItems: Array<{
productId: string;
quantity: number;
lineTotal: number;
}>;
}
/**
* Imports historical order data from a local JSON file into Relewise.
*
* This provides immediate personalization benefits by establishing baseline
* behavioral data before implementing real-time tracking.
*
* Note: SDK limitation - all orders will be timestamped with current import time,
* not the original historical order dates. The benefit is in customer purchase
* patterns and product relationships, not temporal data.
*/
async function importHistoricalOrders() {
console.log('π Starting historical order data import...');
// Load historical order data from local JSON file
const dataPath = path.resolve(
__dirname,
'../../../product_data/historical_orders_example.json',
);
let historicalOrders: HistoricalOrder[];
try {
const fileContents = await fs.readFile(dataPath, 'utf-8');
historicalOrders = JSON.parse(fileContents) as HistoricalOrder[];
console.log(`π Loaded ${historicalOrders.length} historical orders from ${dataPath}`);
} catch (err) {
console.error(`β Failed to load historical order data from ${dataPath}:`, err);
return;
}
// Process orders in batches to avoid API rate limits
const batchSize = 10; // Conservative batch size for historical data
const orders: Trackable[] = [];
let processedCount = 0;
let errorCount = 0;
for (const orderData of historicalOrders) {
try {
// Create user with proper classifications
const user = UserFactory.byAuthenticatedId(orderData.customerId);
user.classifications = orderData.userClassifications;
// Add company association for B2B orders
if (orderData.companyId) {
user.company = { id: orderData.companyId };
}
// Calculate order total
const orderTotal = orderData.lineItems.reduce(
(total, item) => total + item.quantity * item.lineTotal,
0,
);
// Create order tracking object with historical date
const order: Order = {
$type: 'Relewise.Client.DataTypes.Order, Relewise.Client',
orderNumber: orderData.orderNumber,
lineItems: orderData.lineItems.map((item) => ({
product: { id: item.productId },
quantity: item.quantity,
lineTotal: item.lineTotal,
})),
cartName: 'default',
user: user,
subtotal: {
amount: orderTotal,
currency: { value: orderData.currency },
},
// CRITICAL: Include historical order date for proper recommendation weighting
// This data field preserves the original order timeline for personalization algorithms
data: {
'HistoricOrderDate': DataValueFactory.string(new Date(orderData.orderDate).toISOString()),
},
};
orders.push(order);
processedCount++;
// Process in batches
if (orders.length >= batchSize) {
await processBatch(orders, processedCount, historicalOrders.length);
orders.length = 0; // Clear the array
// Rate limiting - pause between batches
console.log('βΈοΈ Pausing 2 seconds between batches to respect API limits...');
await new Promise((resolve) => setTimeout(resolve, 2000));
}
} catch (error) {
console.error(`β Error processing order ${orderData.orderNumber}:`, error);
errorCount++;
}
}
// Process remaining orders
if (orders.length > 0) {
await processBatch(orders, processedCount, historicalOrders.length);
}
// Final summary
console.log('\nπ Historical Data Import Summary:');
console.log(`β
Successfully processed: ${processedCount} orders`);
console.log(`β Errors encountered: ${errorCount} orders`);
console.log(
`π Import completion: ${((processedCount / historicalOrders.length) * 100).toFixed(1)}%`,
);
if (errorCount === 0) {
console.log('\nπ Historical data import completed successfully!');
console.log('π‘ Personalization benefits should be available immediately.');
console.log(
'π You can now run search and recommendation examples to see improved results.',
);
} else {
console.log(
`\nβ οΈ Import completed with ${errorCount} errors. Check logs above for details.`,
);
}
}
/**
* Processes a batch of orders and sends them to Relewise
*/
async function processBatch(orders: Trackable[], processedCount: number, totalCount: number) {
try {
console.log(
`π€ Processing batch: ${processedCount - orders.length + 1}-${processedCount} of ${totalCount}`,
);
const batchResponse = await integrator.batch(orders);
if (batchResponse) {
console.log(`β
Batch processed successfully (${orders.length} orders)`);
} else {
console.log(`β οΈ Batch response was empty, but no error thrown`);
}
} catch (error) {
console.error(`β Failed to process batch:`, error);
throw error; // Re-throw to be handled by caller
}
}
/**
* Demonstrates different order patterns from the historical data
*/
function demonstrateOrderPatterns() {
console.log('\nπ Historical Order Patterns Included:');
console.log('π©π° Danish B2C customers - Premium electronics bundles');
console.log('πΈπͺ Swedish B2C customers - Audio equipment focus');
console.log('π³π΄ Norwegian B2C customers - Gaming monitor purchases');
console.log('π¬π§ British B2C customers - Apple ecosystem products');
console.log('π’ B2B Company 1 - Kitchen appliances and consumables');
console.log('π’ B2B Company 2 - Dishwasher tablets and appliances');
console.log('\nπ‘ This data will enable:');
console.log(' β’ Cross-market personalization patterns');
console.log(' β’ B2B vs B2C behavioral differences');
console.log(' β’ Product affinity and bundle recommendations');
}
/** Default export: runnable from index.ts */
export default async function runHistoricalDataImportExample() {
demonstrateOrderPatterns();
await importHistoricalOrders();
}