|
@@ -318,6 +318,11 @@ export async function initializeStoreCollections(
|
|
|
{ field: 'status', type: 'keyword' },
|
|
{ field: 'status', type: 'keyword' },
|
|
|
{ field: 'total_price', type: 'float' },
|
|
{ field: 'total_price', type: 'float' },
|
|
|
{ field: 'customer_email', type: 'keyword' },
|
|
{ field: 'customer_email', type: 'keyword' },
|
|
|
|
|
+ { field: 'customer_phone', type: 'keyword' },
|
|
|
|
|
+ { field: 'billing_city', type: 'keyword' },
|
|
|
|
|
+ { field: 'billing_country', type: 'keyword' },
|
|
|
|
|
+ { field: 'shipping_city', type: 'keyword' },
|
|
|
|
|
+ { field: 'shipping_country', type: 'keyword' },
|
|
|
]);
|
|
]);
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
@@ -331,6 +336,9 @@ export async function initializeStoreCollections(
|
|
|
{ field: 'customer_id', type: 'keyword' },
|
|
{ field: 'customer_id', type: 'keyword' },
|
|
|
{ field: 'platform', type: 'keyword' },
|
|
{ field: 'platform', type: 'keyword' },
|
|
|
{ field: 'email', type: 'keyword' },
|
|
{ field: 'email', type: 'keyword' },
|
|
|
|
|
+ { field: 'phone', type: 'keyword' },
|
|
|
|
|
+ { field: 'city', type: 'keyword' },
|
|
|
|
|
+ { field: 'country', type: 'keyword' },
|
|
|
]);
|
|
]);
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
@@ -352,35 +360,116 @@ export function generateSimpleEmbedding(text: string): number[] {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
|
- * Create text representation of product for embedding
|
|
|
|
|
|
|
+ * Create enhanced text representation of product for embedding
|
|
|
|
|
+ * Includes all available metadata and extra information
|
|
|
*/
|
|
*/
|
|
|
export function createProductText(product: any): string {
|
|
export function createProductText(product: any): string {
|
|
|
const parts: string[] = [];
|
|
const parts: string[] = [];
|
|
|
|
|
|
|
|
|
|
+ // Product title/name
|
|
|
if (product.title || product.name) {
|
|
if (product.title || product.name) {
|
|
|
parts.push(product.title || product.name);
|
|
parts.push(product.title || product.name);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ // Primary description (cleaned up HTML)
|
|
|
if (product.description) {
|
|
if (product.description) {
|
|
|
- // Truncate long descriptions
|
|
|
|
|
- const desc = product.description.replace(/<[^>]*>/g, '').substring(0, 500);
|
|
|
|
|
- parts.push(desc);
|
|
|
|
|
|
|
+ const desc = product.description.replace(/<[^>]*>/g, '').trim();
|
|
|
|
|
+ if (desc) {
|
|
|
|
|
+ parts.push(desc);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // Short description (WooCommerce)
|
|
|
|
|
+ if (product.short_description && product.short_description !== product.description) {
|
|
|
|
|
+ const shortDesc = product.short_description.replace(/<[^>]*>/g, '').trim();
|
|
|
|
|
+ if (shortDesc) {
|
|
|
|
|
+ parts.push(shortDesc);
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ // SKU and product identifiers
|
|
|
if (product.sku) {
|
|
if (product.sku) {
|
|
|
parts.push(`SKU: ${product.sku}`);
|
|
parts.push(`SKU: ${product.sku}`);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ // Vendor/Brand information
|
|
|
if (product.vendor) {
|
|
if (product.vendor) {
|
|
|
parts.push(`Vendor: ${product.vendor}`);
|
|
parts.push(`Vendor: ${product.vendor}`);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ // Product type/category
|
|
|
if (product.product_type) {
|
|
if (product.product_type) {
|
|
|
parts.push(`Type: ${product.product_type}`);
|
|
parts.push(`Type: ${product.product_type}`);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ // Categories (WooCommerce)
|
|
|
|
|
+ if (product.categories && Array.isArray(product.categories)) {
|
|
|
|
|
+ const categoryNames = product.categories
|
|
|
|
|
+ .map((c: any) => c.name || c)
|
|
|
|
|
+ .filter(Boolean);
|
|
|
|
|
+ if (categoryNames.length > 0) {
|
|
|
|
|
+ parts.push(`Categories: ${categoryNames.join(', ')}`);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // Tags
|
|
|
if (product.tags && Array.isArray(product.tags)) {
|
|
if (product.tags && Array.isArray(product.tags)) {
|
|
|
- parts.push(`Tags: ${product.tags.join(', ')}`);
|
|
|
|
|
|
|
+ const tagList = product.tags.filter(Boolean);
|
|
|
|
|
+ if (tagList.length > 0) {
|
|
|
|
|
+ parts.push(`Tags: ${tagList.join(', ')}`);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // Variant information
|
|
|
|
|
+ if (product.variants && Array.isArray(product.variants) && product.variants.length > 0) {
|
|
|
|
|
+ const variantInfo: string[] = [];
|
|
|
|
|
+ product.variants.forEach((variant: any) => {
|
|
|
|
|
+ if (variant.title && variant.title !== 'Default Title') {
|
|
|
|
|
+ variantInfo.push(variant.title);
|
|
|
|
|
+ }
|
|
|
|
|
+ if (variant.option1) variantInfo.push(variant.option1);
|
|
|
|
|
+ if (variant.option2) variantInfo.push(variant.option2);
|
|
|
|
|
+ if (variant.option3) variantInfo.push(variant.option3);
|
|
|
|
|
+ });
|
|
|
|
|
+ if (variantInfo.length > 0) {
|
|
|
|
|
+ parts.push(`Variants: ${[...new Set(variantInfo)].join(', ')}`);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // Product attributes (WooCommerce)
|
|
|
|
|
+ if (product.attributes && Array.isArray(product.attributes)) {
|
|
|
|
|
+ const attributes = product.attributes
|
|
|
|
|
+ .map((attr: any) => {
|
|
|
|
|
+ if (attr.name && attr.options) {
|
|
|
|
|
+ const options = Array.isArray(attr.options) ? attr.options.join(', ') : attr.options;
|
|
|
|
|
+ return `${attr.name}: ${options}`;
|
|
|
|
|
+ }
|
|
|
|
|
+ return null;
|
|
|
|
|
+ })
|
|
|
|
|
+ .filter(Boolean);
|
|
|
|
|
+ if (attributes.length > 0) {
|
|
|
|
|
+ parts.push(attributes.join(' | '));
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // Meta description (ShopRenter/SEO)
|
|
|
|
|
+ if (product.meta_description && product.meta_description !== product.description) {
|
|
|
|
|
+ const metaDesc = product.meta_description.replace(/<[^>]*>/g, '').trim();
|
|
|
|
|
+ if (metaDesc) {
|
|
|
|
|
+ parts.push(metaDesc);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // Price information for context
|
|
|
|
|
+ if (product.price) {
|
|
|
|
|
+ parts.push(`Price: ${product.price}`);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // Stock status (helpful context)
|
|
|
|
|
+ if (product.stock_status) {
|
|
|
|
|
+ parts.push(`Stock: ${product.stock_status}`);
|
|
|
|
|
+ } else if (product.status) {
|
|
|
|
|
+ parts.push(`Status: ${product.status}`);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
return parts.join(' | ');
|
|
return parts.join(' | ');
|
|
@@ -388,6 +477,7 @@ export function createProductText(product: any): string {
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
|
* Create text representation of order for embedding
|
|
* Create text representation of order for embedding
|
|
|
|
|
+ * Includes customer contact information and addresses
|
|
|
*/
|
|
*/
|
|
|
export function createOrderText(order: any): string {
|
|
export function createOrderText(order: any): string {
|
|
|
const parts: string[] = [];
|
|
const parts: string[] = [];
|
|
@@ -398,12 +488,60 @@ export function createOrderText(order: any): string {
|
|
|
parts.push(`Customer: ${order.customer_name}`);
|
|
parts.push(`Customer: ${order.customer_name}`);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- if (order.total_price) {
|
|
|
|
|
- parts.push(`Total: ${order.total_price} ${order.currency || ''}`);
|
|
|
|
|
|
|
+ // Customer email
|
|
|
|
|
+ if (order.customer_email || order.email) {
|
|
|
|
|
+ parts.push(`Email: ${order.customer_email || order.email}`);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // Customer phone
|
|
|
|
|
+ if (order.customer_phone || order.phone) {
|
|
|
|
|
+ parts.push(`Phone: ${order.customer_phone || order.phone}`);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // Billing address
|
|
|
|
|
+ if (order.billing_address) {
|
|
|
|
|
+ const addr = order.billing_address;
|
|
|
|
|
+ const addressParts = [
|
|
|
|
|
+ addr.address1,
|
|
|
|
|
+ addr.address2,
|
|
|
|
|
+ addr.city,
|
|
|
|
|
+ addr.province || addr.state,
|
|
|
|
|
+ addr.zip || addr.postcode,
|
|
|
|
|
+ addr.country
|
|
|
|
|
+ ].filter(Boolean);
|
|
|
|
|
+ if (addressParts.length > 0) {
|
|
|
|
|
+ parts.push(`Billing: ${addressParts.join(', ')}`);
|
|
|
|
|
+ }
|
|
|
|
|
+ if (addr.phone && addr.phone !== (order.customer_phone || order.phone)) {
|
|
|
|
|
+ parts.push(`Billing Phone: ${addr.phone}`);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // Shipping address
|
|
|
|
|
+ if (order.shipping_address) {
|
|
|
|
|
+ const addr = order.shipping_address;
|
|
|
|
|
+ const addressParts = [
|
|
|
|
|
+ addr.address1,
|
|
|
|
|
+ addr.address2,
|
|
|
|
|
+ addr.city,
|
|
|
|
|
+ addr.province || addr.state,
|
|
|
|
|
+ addr.zip || addr.postcode,
|
|
|
|
|
+ addr.country
|
|
|
|
|
+ ].filter(Boolean);
|
|
|
|
|
+ if (addressParts.length > 0) {
|
|
|
|
|
+ parts.push(`Shipping: ${addressParts.join(', ')}`);
|
|
|
|
|
+ }
|
|
|
|
|
+ if (addr.phone && addr.phone !== (order.customer_phone || order.phone)) {
|
|
|
|
|
+ parts.push(`Shipping Phone: ${addr.phone}`);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (order.total_price || order.total) {
|
|
|
|
|
+ parts.push(`Total: ${order.total_price || order.total} ${order.currency || ''}`);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- if (order.financial_status) {
|
|
|
|
|
- parts.push(`Payment: ${order.financial_status}`);
|
|
|
|
|
|
|
+ if (order.financial_status || order.status) {
|
|
|
|
|
+ parts.push(`Payment: ${order.financial_status || order.status}`);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
if (order.fulfillment_status) {
|
|
if (order.fulfillment_status) {
|
|
@@ -417,11 +555,18 @@ export function createOrderText(order: any): string {
|
|
|
parts.push(`Items: ${items}`);
|
|
parts.push(`Items: ${items}`);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ // Order note
|
|
|
|
|
+ if (order.note) {
|
|
|
|
|
+ const note = order.note.substring(0, 200); // Limit note length
|
|
|
|
|
+ parts.push(`Note: ${note}`);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
return parts.join(' | ');
|
|
return parts.join(' | ');
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
|
* Create text representation of customer for embedding
|
|
* Create text representation of customer for embedding
|
|
|
|
|
+ * Includes contact information and addresses
|
|
|
*/
|
|
*/
|
|
|
export function createCustomerText(customer: any): string {
|
|
export function createCustomerText(customer: any): string {
|
|
|
const parts: string[] = [];
|
|
const parts: string[] = [];
|
|
@@ -430,6 +575,10 @@ export function createCustomerText(customer: any): string {
|
|
|
parts.push(`${customer.first_name || ''} ${customer.last_name || ''}`.trim());
|
|
parts.push(`${customer.first_name || ''} ${customer.last_name || ''}`.trim());
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ if (customer.username) {
|
|
|
|
|
+ parts.push(`Username: ${customer.username}`);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
if (customer.email) {
|
|
if (customer.email) {
|
|
|
parts.push(`Email: ${customer.email}`);
|
|
parts.push(`Email: ${customer.email}`);
|
|
|
}
|
|
}
|
|
@@ -438,6 +587,65 @@ export function createCustomerText(customer: any): string {
|
|
|
parts.push(`Phone: ${customer.phone}`);
|
|
parts.push(`Phone: ${customer.phone}`);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ // Default address or billing address
|
|
|
|
|
+ if (customer.default_address) {
|
|
|
|
|
+ const addr = customer.default_address;
|
|
|
|
|
+ const addressParts = [
|
|
|
|
|
+ addr.address1,
|
|
|
|
|
+ addr.address2,
|
|
|
|
|
+ addr.city,
|
|
|
|
|
+ addr.province || addr.state,
|
|
|
|
|
+ addr.zip || addr.postcode,
|
|
|
|
|
+ addr.country
|
|
|
|
|
+ ].filter(Boolean);
|
|
|
|
|
+ if (addressParts.length > 0) {
|
|
|
|
|
+ parts.push(`Address: ${addressParts.join(', ')}`);
|
|
|
|
|
+ }
|
|
|
|
|
+ if (addr.phone && addr.phone !== customer.phone) {
|
|
|
|
|
+ parts.push(`Address Phone: ${addr.phone}`);
|
|
|
|
|
+ }
|
|
|
|
|
+ } else if (customer.billing_address || customer.billing) {
|
|
|
|
|
+ const addr = customer.billing_address || customer.billing;
|
|
|
|
|
+ const addressParts = [
|
|
|
|
|
+ addr.address1,
|
|
|
|
|
+ addr.address2,
|
|
|
|
|
+ addr.city,
|
|
|
|
|
+ addr.province || addr.state,
|
|
|
|
|
+ addr.zip || addr.postcode,
|
|
|
|
|
+ addr.country
|
|
|
|
|
+ ].filter(Boolean);
|
|
|
|
|
+ if (addressParts.length > 0) {
|
|
|
|
|
+ parts.push(`Billing: ${addressParts.join(', ')}`);
|
|
|
|
|
+ }
|
|
|
|
|
+ if (addr.phone && addr.phone !== customer.phone) {
|
|
|
|
|
+ parts.push(`Billing Phone: ${addr.phone}`);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // Shipping address (if different from billing)
|
|
|
|
|
+ if (customer.shipping_address || customer.shipping) {
|
|
|
|
|
+ const addr = customer.shipping_address || customer.shipping;
|
|
|
|
|
+ const addressParts = [
|
|
|
|
|
+ addr.address1,
|
|
|
|
|
+ addr.address2,
|
|
|
|
|
+ addr.city,
|
|
|
|
|
+ addr.province || addr.state,
|
|
|
|
|
+ addr.zip || addr.postcode,
|
|
|
|
|
+ addr.country
|
|
|
|
|
+ ].filter(Boolean);
|
|
|
|
|
+ if (addressParts.length > 0) {
|
|
|
|
|
+ parts.push(`Shipping: ${addressParts.join(', ')}`);
|
|
|
|
|
+ }
|
|
|
|
|
+ if (addr.phone && addr.phone !== customer.phone) {
|
|
|
|
|
+ parts.push(`Shipping Phone: ${addr.phone}`);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // Additional addresses (Shopify)
|
|
|
|
|
+ if (customer.addresses && Array.isArray(customer.addresses) && customer.addresses.length > 1) {
|
|
|
|
|
+ parts.push(`Additional addresses: ${customer.addresses.length - 1}`);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
if (customer.orders_count) {
|
|
if (customer.orders_count) {
|
|
|
parts.push(`Orders: ${customer.orders_count}`);
|
|
parts.push(`Orders: ${customer.orders_count}`);
|
|
|
}
|
|
}
|
|
@@ -446,8 +654,25 @@ export function createCustomerText(customer: any): string {
|
|
|
parts.push(`Total spent: ${customer.total_spent} ${customer.currency || ''}`);
|
|
parts.push(`Total spent: ${customer.total_spent} ${customer.currency || ''}`);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ if (customer.company) {
|
|
|
|
|
+ parts.push(`Company: ${customer.company}`);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
if (customer.tags && Array.isArray(customer.tags)) {
|
|
if (customer.tags && Array.isArray(customer.tags)) {
|
|
|
- parts.push(`Tags: ${customer.tags.join(', ')}`);
|
|
|
|
|
|
|
+ const tagList = customer.tags.filter(Boolean);
|
|
|
|
|
+ if (tagList.length > 0) {
|
|
|
|
|
+ parts.push(`Tags: ${tagList.join(', ')}`);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // Customer state/status
|
|
|
|
|
+ if (customer.state) {
|
|
|
|
|
+ parts.push(`Status: ${customer.state}`);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // Marketing preferences
|
|
|
|
|
+ if (customer.accepts_marketing !== undefined) {
|
|
|
|
|
+ parts.push(`Marketing: ${customer.accepts_marketing ? 'Yes' : 'No'}`);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
return parts.join(' | ');
|
|
return parts.join(' | ');
|