Enlaces internos del blog de Shopify
Este es unContent Creation, AI Summarizationflujo de automatización del dominio deautomatización que contiene 26 nodos.Utiliza principalmente nodos como If, Set, Code, DataTable, HttpRequest. Usar text-embedding-3-small de OpenAI para generar automáticamente artículos de blog relacionados para Shopify
- •Pueden requerirse credenciales de autenticación para la API de destino
Nodos utilizados (26)
Categoría
{
"id": "01ZGpwv1rdsk5Ng8",
"meta": {
"instanceId": "8e9162e70be518ca153a70a16d8785f5bfc6523821e135712fb7ef93fe97a5dd",
"templateCredsSetupCompleted": true
},
"name": "Internal linking shopify blog",
"tags": [],
"nodes": [
{
"id": "8848b159-986e-4b67-8bde-a38da0a25f36",
"name": "Disparador programado",
"type": "n8n-nodes-base.scheduleTrigger",
"position": [
-1808,
496
],
"parameters": {
"rule": {
"interval": [
{
"field": "weeks",
"triggerAtHour": 20
}
]
}
},
"typeVersion": 1.2
},
{
"id": "057f8499-694c-4391-80a1-e2f7ca093d25",
"name": "Configuración del flujo de trabajo",
"type": "n8n-nodes-base.set",
"position": [
-1584,
496
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "566ed7c6-84cf-4fcf-ae6e-5607fa4c3909",
"name": "shopifyBlogId",
"type": "string",
"value": ""
},
{
"id": "bdcbb25a-eba1-4a26-bc4e-e4dd8eb268db",
"name": "shopifyBlogDomain",
"type": "string",
"value": "<https://example.com/blogs/>"
},
{
"id": "6f1aaf1c-eb2f-42ac-b16e-6e4cd8cabd9f",
"name": "shopifyStoreName",
"type": "string",
"value": "<domain_before_myshopify.com>"
},
{
"id": "d704d6dd-d887-40d3-9f3b-1a61010095d2",
"name": "shopApiVersion",
"type": "string",
"value": "2025-07"
},
{
"id": "027f4123-2194-4f6d-8411-1305e7602692",
"name": "percent_minimum_similarity",
"type": "number",
"value": 70
}
]
}
},
"typeVersion": 3.4
},
{
"id": "ce7cbd23-b0ec-4420-a262-cc16ddd9f5bd",
"name": "Obtener artículos de shopify",
"type": "n8n-nodes-base.httpRequest",
"position": [
-1360,
496
],
"parameters": {
"url": "=https://{{ $('Workflow Configuration').first().json.shopifyStoreName }}.myshopify.com/admin/api/{{ $('Workflow Configuration').first().json.shopApiVersion }}/blogs/{{ $('Workflow Configuration').first().json.shopifyBlogId }}/articles.json",
"options": {},
"sendQuery": true,
"authentication": "predefinedCredentialType",
"queryParameters": {
"parameters": [
{
"name": "published_status",
"value": "published"
}
]
},
"nodeCredentialType": "shopifyAccessTokenApi"
},
"credentials": {
"shopifyAccessTokenApi": {
"id": "oDrAi4lTTxnawLSv",
"name": "Shopify Access Token account 2"
}
},
"typeVersion": 4.2
},
{
"id": "3623adca-6827-467c-81f7-6f138377aefd",
"name": "Limpiar y preparar texto",
"type": "n8n-nodes-base.code",
"position": [
-1136,
496
],
"parameters": {
"jsCode": "// Function to clean HTML and remove the related section\nfunction stripHtml(html) {\n if (!html) return '';\n return html\n .replace(/<style[^>]*>.*?<\\/style>/gi, '')\n .replace(/<script[^>]*>.*?<\\/script>/gi, '')\n .replace(/<div class=\"related-articles\"[^>]*>[\\s\\S]*?<\\/div>/gi, '') // remove related articles \n .replace(/<[^>]+>/g, ' ')\n .replace(/\\s+/g, ' ')\n .replace(/ /g, ' ')\n .replace(/&/g, '&')\n .replace(/</g, '<')\n .replace(/>/g, '>')\n .trim();\n}\n\n// Detect what we receive from previous node\nlet allItems = $input.all();\nlet articles = [];\n\n// 1: if multiple items\nif (allItems.length > 0) {\n allItems.forEach(item => {\n // Verificar si el item contiene un array \"articles\"\n if (item.json && Array.isArray(item.json.articles)) {\n articles = articles.concat(item.json.articles);\n } \n // if only one item\n else if (item.json && item.json.id && item.json.title) {\n articles.push(item.json);\n }\n });\n}\n\n// Fallback if no articles found\nif (articles.length === 0 && $input.item.json) {\n if (Array.isArray($input.item.json.articles)) {\n articles = $input.item.json.articles;\n } else if ($input.item.json.id && $input.item.json.title) {\n articles = [$input.item.json];\n }\n}\n\n\n// Process each article\nconst processedArticles = articles.map(article => {\n const cleanContent = stripHtml(article.body_html);\n const cleanSummary = stripHtml(article.summary_html);\n \n // Create text for embeddings \n const textForEmbedding = `Título: ${article.title}\n\nResumen: ${cleanSummary}\n\nContenido: ${cleanContent.substring(0, 15000)}`;\n\n return {\n shopify_id: article.id,\n blog_id: article.blog_id,\n shopify_handle: article.handle,\n title: article.title,\n content: cleanContent,\n summary: cleanSummary,\n url: $('Workflow Configuration').first().json.shopifyBlogDomain + article.handle,\n published_at: article.published_at,\n text_for_embedding: textForEmbedding,\n word_count: cleanContent.split(/\\s+/).filter(w => w.length > 0).length\n };\n});\n\nreturn processedArticles.map(article => ({ json: article }));"
},
"typeVersion": 2
},
{
"id": "a91f14ad-f42c-4ad9-9ddd-4a218e8b8514",
"name": "Iterar sobre elementos",
"type": "n8n-nodes-base.splitInBatches",
"position": [
-912,
496
],
"parameters": {
"options": {}
},
"typeVersion": 3
},
{
"id": "8a1ffb69-65c0-4cf5-a4cb-e9209a9dc0ea",
"name": "OpenAI Obtener incrustaciones",
"type": "n8n-nodes-base.httpRequest",
"position": [
-464,
512
],
"parameters": {
"url": "https://api.openai.com/v1/embeddings",
"method": "POST",
"options": {},
"sendBody": true,
"sendHeaders": true,
"authentication": "predefinedCredentialType",
"bodyParameters": {
"parameters": [
{
"name": "model",
"value": "text-embedding-3-small"
},
{
"name": "input",
"value": "={{ $json.embedding }}"
}
]
},
"headerParameters": {
"parameters": [
{
"name": "Content-Type",
"value": "application/json"
}
]
},
"nodeCredentialType": "openAiApi"
},
"credentials": {
"openAiApi": {
"id": "F6XMlOYUADkfTF7M",
"name": "OpenAi key"
}
},
"typeVersion": 4.2
},
{
"id": "7d83e353-0272-4857-ad4e-f3baab2e62b6",
"name": "Establecer campos del artículo",
"type": "n8n-nodes-base.set",
"position": [
-688,
512
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "e443624a-de8d-485f-a3a1-b567c3145c2c",
"name": "shopify_id",
"type": "number",
"value": "={{ $json.shopify_id }}"
},
{
"id": "139eedb1-6aa3-434a-9425-fac9234c5148",
"name": "blog_id",
"type": "number",
"value": "={{ $json.blog_id }}"
},
{
"id": "9950f411-833a-488f-9f6a-b17f338114bf",
"name": "shopify_handle",
"type": "string",
"value": "={{ $json.shopify_handle }}"
},
{
"id": "1d83dc8d-c93d-41bb-ba5e-0a01f055c0fe",
"name": "title",
"type": "string",
"value": "={{ $json.title }}"
},
{
"id": "4a2a7109-b8d1-43f7-9d69-6d1f0cbef95a",
"name": "content",
"type": "string",
"value": "={{ $json.content }}"
},
{
"id": "0db25418-7777-4fc2-b774-4abf85819790",
"name": "summary",
"type": "string",
"value": "={{ $json.summary }}"
},
{
"id": "960a20c6-77ae-434a-9646-7b562da78a2e",
"name": "url",
"type": "string",
"value": "={{ $json.url }}"
},
{
"id": "4080709d-46e3-4d5c-b4b8-4cc599fae16c",
"name": "published_at",
"type": "string",
"value": "={{ $json.published_at }}"
},
{
"id": "83f98c7d-6ce7-4b58-9034-72f7b78a59e8",
"name": "embedding",
"type": "string",
"value": "={{ $json.text_for_embedding }})"
},
{
"id": "9a2a581c-2c3b-4a27-bde8-a3a62590a9df",
"name": "analyzed_at",
"type": "string",
"value": "={{new Date().toISOString()}}"
},
{
"id": "3a53791e-f7c6-4cef-ad55-8a37c9239073",
"name": "word_count",
"type": "number",
"value": "={{ $json.word_count }}"
}
]
}
},
"typeVersion": 3.4
},
{
"id": "55ce71bc-afc1-4178-87a3-6fda9829bd68",
"name": "Calcular similitudes",
"type": "n8n-nodes-base.code",
"position": [
-688,
48
],
"parameters": {
"jsCode": "const doneItems = $(\"Loop Over Items\").all();\n\nconsole.log(`📦 Total items recibidos: ${doneItems.length}`);\n\n// Extract and prepare articles\nconst allArticles = doneItems.map(item => {\n const data = item.json;\n \n // Parsing embedding\n const embedding = typeof data.embedding === 'string' \n ? JSON.parse(data.embedding) \n : data.embedding;\n \n return {\n shopify_id: data.shopify_id,\n blog_id: data.blog_id,\n title: data.title,\n url: data.url,\n embedding: embedding\n };\n});\n\nconst uniqueIds = new Set(allArticles.map(a => a.shopify_id));\n\nif (allArticles.length < 2) {\n throw new Error(`❌ Se necesitan al menos 2 artículos. Solo hay ${allArticles.length}`);\n}\n\nif (uniqueIds.size < 2) {\n throw new Error(`❌ Todos los artículos tienen el mismo ID (${Array.from(uniqueIds)[0]})`);\n}\n\n// Function to calculate similarities\nfunction cosineSimilarity(vecA, vecB) {\n if (!vecA || !vecB || vecA.length !== vecB.length) {\n throw new Error('❌ Embeddings inválidos o de diferente longitud');\n }\n \n let dotProduct = 0;\n let normA = 0;\n let normB = 0;\n \n for (let i = 0; i < vecA.length; i++) {\n dotProduct += vecA[i] * vecB[i];\n normA += vecA[i] * vecA[i];\n normB += vecB[i] * vecB[i];\n }\n \n const similarity = dotProduct / (Math.sqrt(normA) * Math.sqrt(normB));\n return Math.round(similarity * 100 * 100) / 100;\n}\n\nconst relations = [];\nlet comparacionesRealizadas = 0;\n\nfor (let i = 0; i < allArticles.length; i++) {\n for (let j = 0; j < allArticles.length; j++) {\n \n // ⚠️ Rule to avoid comparing one article to itself\n if (allArticles[i].shopify_id === allArticles[j].shopify_id) {\n continue; // Saltar esta iteración\n }\n \n comparacionesRealizadas++;\n \n try {\n const similarity = cosineSimilarity(\n allArticles[i].embedding,\n allArticles[j].embedding\n );\n \n // Only keep similarity above the setting\n if (similarity >= $('Workflow Configuration').first().json.percent_minimum_similarity) {\n relations.push({\n source_shopify_id: allArticles[i].shopify_id,\n source_blog_id: allArticles[i].blog_id,\n source_title: allArticles[i].title,\n related_shopify_id: allArticles[j].shopify_id,\n related_title: allArticles[j].title,\n related_url: allArticles[j].url,\n relation_type: \"semantic_similarity\",\n similarity_score: similarity\n });\n }\n } catch (error) {\n console.error(`Error comparando ${allArticles[i].shopify_id} con ${allArticles[j].shopify_id}:`, error.message);\n }\n }\n}\n\n// In case, not enough similarity\nif (relations.length === 0) {\n return [{ \n json: { \n mensaje: \"No se encontraron relaciones con similitud >= 60%\",\n articulos_procesados: allArticles.length,\n articulos_unicos: uniqueIds.size,\n comparaciones_realizadas: comparacionesRealizadas,\n sugerencia: \"Intenta bajar el umbral de similitud o verifica que los embeddings sean correctos\",\n muestra_articulos: allArticles.slice(0, 3).map(a => ({\n shopify_id: a.shopify_id,\n title: a.title,\n longitud_embedding: a.embedding.length\n }))\n } \n }];\n}\n\nreturn relations.map(rel => ({ json: rel }));"
},
"typeVersion": 2,
"alwaysOutputData": false
},
{
"id": "762e212e-1907-4610-9bcd-cc7b79258172",
"name": "Actualizar/insertar artículos",
"type": "n8n-nodes-base.dataTable",
"position": [
-240,
512
],
"parameters": {
"columns": {
"value": {
"url": "={{ $('Set article fields').item.json.url }}",
"title": "={{ $('Set article fields').item.json.title }}",
"blog_id": "={{ $('Set article fields').item.json.blog_id }}",
"content": "={{ $('Set article fields').item.json.content }}",
"summary": "={{ $('Set article fields').item.json.summary }}",
"embedding": "={{ JSON.stringify($json.data[0].embedding) }}",
"shopify_id": "={{ $('Set article fields').item.json.shopify_id }}",
"word_count": "={{ $('Set article fields').item.json.word_count }}",
"published_at": "={{ $('Set article fields').item.json.published_at }}",
"shopify_handle": "={{ $('Set article fields').item.json.shopify_handle }}",
"last_analyzed_at": "={{ $('Set article fields').item.json.analyzed_at }}"
},
"schema": [
{
"id": "shopify_id",
"type": "number",
"display": true,
"removed": false,
"readOnly": false,
"required": false,
"displayName": "shopify_id",
"defaultMatch": false
},
{
"id": "shopify_handle",
"type": "string",
"display": true,
"removed": false,
"readOnly": false,
"required": false,
"displayName": "shopify_handle",
"defaultMatch": false
},
{
"id": "title",
"type": "string",
"display": true,
"removed": false,
"readOnly": false,
"required": false,
"displayName": "title",
"defaultMatch": false
},
{
"id": "content",
"type": "string",
"display": true,
"removed": false,
"readOnly": false,
"required": false,
"displayName": "content",
"defaultMatch": false
},
{
"id": "summary",
"type": "string",
"display": true,
"removed": false,
"readOnly": false,
"required": false,
"displayName": "summary",
"defaultMatch": false
},
{
"id": "url",
"type": "string",
"display": true,
"removed": false,
"readOnly": false,
"required": false,
"displayName": "url",
"defaultMatch": false
},
{
"id": "published_at",
"type": "string",
"display": true,
"removed": false,
"readOnly": false,
"required": false,
"displayName": "published_at",
"defaultMatch": false
},
{
"id": "embedding",
"type": "string",
"display": true,
"removed": false,
"readOnly": false,
"required": false,
"displayName": "embedding",
"defaultMatch": false
},
{
"id": "last_analyzed_at",
"type": "string",
"display": true,
"removed": false,
"readOnly": false,
"required": false,
"displayName": "last_analyzed_at",
"defaultMatch": false
},
{
"id": "word_count",
"type": "string",
"display": true,
"removed": false,
"readOnly": false,
"required": false,
"displayName": "word_count",
"defaultMatch": false
},
{
"id": "blog_id",
"type": "number",
"display": true,
"removed": false,
"readOnly": false,
"required": false,
"displayName": "blog_id",
"defaultMatch": false
}
],
"mappingMode": "defineBelow",
"matchingColumns": [],
"attemptToConvertTypes": false,
"convertFieldsToString": false
},
"filters": {
"conditions": [
{
"keyName": "shopify_id",
"keyValue": "={{ $('Set article fields').item.json.shopify_id }}"
}
]
},
"options": {},
"matchType": "allConditions",
"operation": "upsert",
"dataTableId": {
"__rl": true,
"mode": "list",
"value": "kjtDGPfteUD1V64N",
"cachedResultUrl": "/projects/XqoBcIeUrbWmIJt8/datatables/kjtDGPfteUD1V64N",
"cachedResultName": "articles"
}
},
"typeVersion": 1
},
{
"id": "6607766a-147e-49b6-b127-6655d3221c8c",
"name": "Actualizar/insertar relaciones de artículos",
"type": "n8n-nodes-base.dataTable",
"position": [
-464,
48
],
"parameters": {
"columns": {
"value": {
"related_url": "={{ $json.related_url }}",
"source_title": "={{ $json.source_title }}",
"related_title": "={{ $json.related_title }}",
"source_blog_id": "={{ $json.source_blog_id }}",
"similarity_score": "={{ $json.similarity_score }}",
"source_shopify_id": "={{ $json.source_shopify_id }}",
"related_shopify_id": "={{ $json.related_shopify_id }}"
},
"schema": [
{
"id": "source_shopify_id",
"type": "number",
"display": true,
"removed": false,
"readOnly": false,
"required": false,
"displayName": "source_shopify_id",
"defaultMatch": false
},
{
"id": "source_blog_id",
"type": "number",
"display": true,
"removed": false,
"readOnly": false,
"required": false,
"displayName": "source_blog_id",
"defaultMatch": false
},
{
"id": "source_title",
"type": "string",
"display": true,
"removed": false,
"readOnly": false,
"required": false,
"displayName": "source_title",
"defaultMatch": false
},
{
"id": "related_shopify_id",
"type": "number",
"display": true,
"removed": false,
"readOnly": false,
"required": false,
"displayName": "related_shopify_id",
"defaultMatch": false
},
{
"id": "related_title",
"type": "string",
"display": true,
"removed": false,
"readOnly": false,
"required": false,
"displayName": "related_title",
"defaultMatch": false
},
{
"id": "related_url",
"type": "string",
"display": true,
"removed": false,
"readOnly": false,
"required": false,
"displayName": "related_url",
"defaultMatch": false
},
{
"id": "relation_type",
"type": "string",
"display": true,
"removed": true,
"readOnly": false,
"required": false,
"displayName": "relation_type",
"defaultMatch": false
},
{
"id": "similarity_score",
"type": "number",
"display": true,
"removed": false,
"readOnly": false,
"required": false,
"displayName": "similarity_score",
"defaultMatch": false
}
],
"mappingMode": "defineBelow",
"matchingColumns": [],
"attemptToConvertTypes": false,
"convertFieldsToString": false
},
"filters": {
"conditions": [
{
"keyName": "source_shopify_id",
"keyValue": "={{ $json.source_shopify_id }}"
},
{
"keyName": "related_shopify_id",
"keyValue": "={{ $json.related_shopify_id }}"
}
]
},
"options": {},
"matchType": "allConditions",
"operation": "upsert",
"dataTableId": {
"__rl": true,
"mode": "list",
"value": "KXwn5vzq2gEz09tM",
"cachedResultUrl": "/projects/XqoBcIeUrbWmIJt8/datatables/KXwn5vzq2gEz09tM",
"cachedResultName": "article_relations"
}
},
"typeVersion": 1
},
{
"id": "ff514a42-ee01-448a-8951-4b6d82ecff01",
"name": "Top 3 artículos relacionados",
"type": "n8n-nodes-base.code",
"position": [
-240,
48
],
"parameters": {
"jsCode": "// ============================================\n// GROUP AND GET TOP 3 RELATED ARTICLES\n// ============================================\n\nconst relations = $(\"Calculate similarities\").all().map(item => item.json);\n\n// Group by source_shopify_id\nconst groupedBySource = relations.reduce((acc, relation) => {\n const sourceId = relation.source_shopify_id;\n \n if (!acc[sourceId]) {\n acc[sourceId] = {\n source_shopify_id: sourceId,\n source_blog_id: relation.source_blog_id,\n source_title: relation.source_title,\n source_url: relation.source_url,\n related_articles: []\n };\n }\n \n // Add related article with its similarity score\n acc[sourceId].related_articles.push({\n related_shopify_id: relation.related_shopify_id,\n related_title: relation.related_title,\n related_url: relation.related_url,\n similarity_score: relation.similarity_score\n });\n \n return acc;\n}, {});\n\n// Convert to array and get top 3 for each source\nconst result = Object.values(groupedBySource).map(group => {\n // Sort by similarity_score (highest first) and take top 3\n const top3Related = group.related_articles\n .sort((a, b) => b.similarity_score - a.similarity_score)\n .slice(0, 3)\n .map(article => article.related_shopify_id);\n \n return {\n source_shopify_id: group.source_shopify_id,\n related_shopify_ids: top3Related\n };\n});\n\n// Return as separate items for next node\nreturn result.map(item => ({ json: item }));"
},
"typeVersion": 2
},
{
"id": "088f6ac5-4c84-4c63-ba89-ab778c3ce547",
"name": "Bucle para artículos fuente",
"type": "n8n-nodes-base.splitInBatches",
"position": [
-16,
48
],
"parameters": {
"options": {}
},
"typeVersion": 3
},
{
"id": "28f1bf7c-db78-4f1f-8db3-b00474dd7284",
"name": "Obtener artículos relacionados previos",
"type": "n8n-nodes-base.dataTable",
"position": [
208,
64
],
"parameters": {
"filters": {
"conditions": [
{
"keyName": "source_shopify_id",
"keyValue": "={{ $json.source_shopify_id }}"
}
]
},
"operation": "get",
"dataTableId": {
"__rl": true,
"mode": "list",
"value": "7RrUjeEF2Zfcxpsn",
"cachedResultUrl": "/projects/XqoBcIeUrbWmIJt8/datatables/7RrUjeEF2Zfcxpsn",
"cachedResultName": "article_related_links_snapshot"
}
},
"typeVersion": 1,
"alwaysOutputData": true
},
{
"id": "268e6a49-f852-4056-b9cf-8b912f5b19e1",
"name": "Verificar si el artículo ya tiene relacionados",
"type": "n8n-nodes-base.if",
"position": [
432,
64
],
"parameters": {
"options": {},
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "6a466a42-552d-4be0-a443-e2907f660ba4",
"operator": {
"type": "number",
"operation": "exists",
"singleValue": true
},
"leftValue": "={{ $json.source_shopify_id }}",
"rightValue": "={{ $('Loop for source articles').item.json.related_shopify_ids }}"
}
]
}
},
"typeVersion": 2.2
},
{
"id": "b41317b7-4a8c-4043-94e8-89b6add1c772",
"name": "Actualizar/insertar relaciones",
"type": "n8n-nodes-base.dataTable",
"position": [
688,
80
],
"parameters": {
"columns": {
"value": {
"source_shopify_id": "={{ $('Loop for source articles').item.json.source_shopify_id }}",
"related_shopify_ids": "={{ $('Loop for source articles').item.json.related_shopify_ids.join() }}"
},
"schema": [
{
"id": "source_shopify_id",
"type": "number",
"display": true,
"removed": false,
"readOnly": false,
"required": false,
"displayName": "source_shopify_id",
"defaultMatch": false
},
{
"id": "related_shopify_ids",
"type": "string",
"display": true,
"removed": false,
"readOnly": false,
"required": false,
"displayName": "related_shopify_ids",
"defaultMatch": false
},
{
"id": "related_urls",
"type": "string",
"display": true,
"removed": true,
"readOnly": false,
"required": false,
"displayName": "related_urls",
"defaultMatch": false
}
],
"mappingMode": "defineBelow",
"matchingColumns": [],
"attemptToConvertTypes": false,
"convertFieldsToString": false
},
"filters": {
"conditions": [
{
"keyName": "source_shopify_id",
"keyValue": "={{ $('Loop for source articles').item.json.source_shopify_id }}"
}
]
},
"options": {},
"operation": "upsert",
"dataTableId": {
"__rl": true,
"mode": "list",
"value": "7RrUjeEF2Zfcxpsn",
"cachedResultUrl": "/projects/XqoBcIeUrbWmIJt8/datatables/7RrUjeEF2Zfcxpsn",
"cachedResultName": "article_related_links_snapshot"
}
},
"typeVersion": 1
},
{
"id": "2d051e8b-9f67-4ed3-8a2a-85d19281452f",
"name": "Preparar HTML para artículos relacionados",
"type": "n8n-nodes-base.code",
"position": [
1344,
80
],
"parameters": {
"jsCode": "// ============================================\n// PREPARE HTML FOR RELATED ARTICLES\n// ============================================\n\n// Get source article ID from \"Upsert relations\" node\nconst sourceArticle = $('Upsert relations').first().json;\nconst sourceShopifyId = sourceArticle.source_shopify_id;\n\n// Get related articles details from \"Get related article details\" node\nconst relatedArticles = $('Get related article details').all().map(i => i.json);\n\n// Build the HTML for related articles section\nlet relatedHTML = `\n<div class=\"related-articles\" style=\"margin: 40px 0;\">\n <h3>📚 Artículos relacionados que te pueden interesar</h3>\n <ul style=\"list-style: none; padding: 0;\">\n`;\n\n// Add each related article as a link\nrelatedArticles.forEach(article => {\n relatedHTML += `\n <li style=\"margin-bottom: 10px;\">\n <a href=\"${article.url}\">→ ${article.title}</a>\n </li>\n `;\n});\n\nrelatedHTML += `\n </ul>\n</div>\n`;\n\nreturn {\n json: {\n source_shopify_id: sourceShopifyId,\n related_articles_html: relatedHTML,\n related_count: relatedArticles.length\n }\n};"
},
"typeVersion": 2
},
{
"id": "0e6e4ad1-cf86-4e35-a862-bc8219c99838",
"name": "Verificar si los artículos relacionados son iguales",
"type": "n8n-nodes-base.if",
"position": [
608,
-288
],
"parameters": {
"options": {},
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "320c269c-572b-4888-b6c0-7ceec4db0e30",
"operator": {
"name": "filter.operator.equals",
"type": "string",
"operation": "equals"
},
"leftValue": "={{ $('Loop for source articles').item.json.related_shopify_ids.join() }}",
"rightValue": "={{ $('Get previous related_articles').item.json.related_shopify_ids }}"
}
]
}
},
"typeVersion": 2.2
},
{
"id": "73604394-344d-469e-b845-0f3660abccaf",
"name": "No es necesario cambiar la sección de relacionados",
"type": "n8n-nodes-base.noOp",
"position": [
816,
-304
],
"parameters": {},
"typeVersion": 1
},
{
"id": "2bc885ba-cbc4-4623-82ac-e85759170a6d",
"name": "Obtener detalles del artículo relacionado",
"type": "n8n-nodes-base.dataTable",
"position": [
1120,
80
],
"parameters": {
"filters": {
"conditions": [
{
"keyName": "shopify_id",
"keyValue": "={{ $json.related_shopify_id }}"
}
]
},
"operation": "get",
"dataTableId": {
"__rl": true,
"mode": "list",
"value": "kjtDGPfteUD1V64N",
"cachedResultUrl": "/projects/XqoBcIeUrbWmIJt8/datatables/kjtDGPfteUD1V64N",
"cachedResultName": "articles"
}
},
"typeVersion": 1
},
{
"id": "85357d92-5b8b-4e36-acba-a1f3473237ae",
"name": "Dividir IDs relacionados",
"type": "n8n-nodes-base.code",
"position": [
896,
80
],
"parameters": {
"jsCode": "return $input.all().flatMap(item => {\n const ids = item.json.related_shopify_ids.split(',').map(id => id.trim());\n return ids.map(relatedId => ({\n json: {\n source_shopify_id: item.json.source_shopify_id,\n related_shopify_id: relatedId\n }\n }));\n});"
},
"typeVersion": 2
},
{
"id": "36042f8c-f7b5-48bc-949f-44f8521d407e",
"name": "Insertar relacionados en el artículo",
"type": "n8n-nodes-base.code",
"position": [
1568,
80
],
"parameters": {
"jsCode": "// ============================================\n// UPDATE SHOPIFY ARTICLE WITH RELATED LINKS\n// ============================================\n\nconst item = $input.item.json;\n\n// Get current article content from Shopify\nconst currentArticle = $('Get articles from shopify').first().json.articles.find(a => a.id === $input.first().json.source_shopify_id);\nlet currentContent = currentArticle.body_html || '';\n\n// Remove old related articles section if exists\ncurrentContent = currentContent.replace(\n /<div class=\"related-articles\"[^>]*>[\\s\\S]*?<\\/div>/gi, \n ''\n);\n\n// Add new related articles at the end\nconst updatedContent = currentContent.trim() + '\\n\\n' + item.related_articles_html;\n\nconst article = {\n id: item.source_shopify_id,\n body_html: updatedContent\n}\n\nreturn [{ json: { article } }];"
},
"typeVersion": 2
},
{
"id": "1524f17c-e0b2-41dc-a948-4e085885b0a2",
"name": "Actualizar artículo con relacionados",
"type": "n8n-nodes-base.httpRequest",
"position": [
1792,
80
],
"parameters": {
"url": "=https://{{ $('Workflow Configuration').first().json.shopifyStoreName }}.myshopify.com/admin/api/{{ $('Workflow Configuration').first().json.shopApiVersion }}/blogs/{{ $('Workflow Configuration').first().json.shopifyBlogId }}/articles/{{ $json.article.id }}.json",
"method": "PUT",
"options": {},
"jsonBody": "={{ JSON.stringify($json) }}",
"sendBody": true,
"specifyBody": "json",
"authentication": "predefinedCredentialType",
"nodeCredentialType": "shopifyAccessTokenApi"
},
"credentials": {
"shopifyAccessTokenApi": {
"id": "oDrAi4lTTxnawLSv",
"name": "Shopify Access Token account 2"
}
},
"typeVersion": 4.2
},
{
"id": "e9d3c731-ff01-4bbc-9c8c-a6eed48abb20",
"name": "Nota adhesiva",
"type": "n8n-nodes-base.stickyNote",
"position": [
-976,
304
],
"parameters": {
"width": 896,
"height": 480,
"content": "## Analyze each article with \"text-embedding-3-small\" model and save to Data Table\n* Previously it cleaned the content for each article\n* Within this loop for each article it's generating the embedding that will then be used to analyze similarities\n* It saves each article in the \"articles\" Data Table"
},
"typeVersion": 1
},
{
"id": "d61c23f1-2de2-4b63-9e3b-a38cbb37688d",
"name": "Nota adhesiva1",
"type": "n8n-nodes-base.stickyNote",
"position": [
-768,
-448
],
"parameters": {
"color": 5,
"width": 2736,
"height": 736,
"content": "## Core process\n* It compares each article to each other to calculate the semantic similarity \n* In that particular example it looks for article that have more than 70% semantic similarity. This can be changed in the \"Workflow Configuration\" node, the variable is called \"percent_minimum_similarity\" \n* For each article, it gets the top 3 related articles ordered by highest semantic similarity. If you want to include more related articles you can change the node \"Top 3 related articles\"\n* It then checks in the \"article_related_links_snapshot\" Data table to check if this article already had related articles. Then it checks if the related are the same or need to be updated in the article\n* The rest of the process is to get the details of each related articles, create the html section, replace it within the article if there is already one and finally update the article through shopify API"
},
"typeVersion": 1
},
{
"id": "4d66e34f-f02c-4b30-8a7e-182b828330fd",
"name": "Nota adhesiva2",
"type": "n8n-nodes-base.stickyNote",
"position": [
-1856,
304
],
"parameters": {
"color": 4,
"width": 864,
"height": 480,
"content": "## Set up\n* **Workflow Configuration node need to be updated with your info**"
},
"typeVersion": 1
},
{
"id": "03a381af-5bf9-4fa1-a633-3e2a154b1ceb",
"name": "Nota adhesiva4",
"type": "n8n-nodes-base.stickyNote",
"position": [
-1856,
-448
],
"parameters": {
"color": 7,
"width": 1072,
"height": 736,
"content": "## IMPORTANT READ BEFORE LAUNCHING THE WORKFLOW\nFor this workflow to work you first need to create three Data Tables. Below is the details of each one. \n**articles**\n** shopify_id (number)\n** shopify_handle (string)\n** title (string)\n** content (string) \n** summary (string)\n** url (string)\n** published_at (datetime)\n** embedding (string)\n** last_analyzed_at (datetime)\n** word_count (number) \n** blog_id (number) \n\n**article_relations**\n** source_shopify_id (number) \n** source_blog_id (number) \n** source_title (string)\n** related_shopify_id (number) \n** related_title (string)\n** related_url (string) \n** similarity_score (number)\n\n**article_related_links_snapshot**\n** source_shopify_id (number)\n** related_shopify_ids (string) \n\n\nThen, the only thing you need to configure is the second node \"Workflow Configuration\". "
},
"typeVersion": 1
}
],
"active": false,
"pinData": {},
"settings": {
"callerPolicy": "workflowsFromSameOwner",
"errorWorkflow": "ib0LqomNsNIqEoi6",
"availableInMCP": false,
"executionOrder": "v1"
},
"versionId": "f2d0a7cc-1be7-4878-bd20-e82dae800398",
"connections": {
"a91f14ad-f42c-4ad9-9ddd-4a218e8b8514": {
"main": [
[
{
"node": "55ce71bc-afc1-4178-87a3-6fda9829bd68",
"type": "main",
"index": 0
}
],
[
{
"node": "7d83e353-0272-4857-ad4e-f3baab2e62b6",
"type": "main",
"index": 0
}
]
]
},
"762e212e-1907-4610-9bcd-cc7b79258172": {
"main": [
[
{
"node": "a91f14ad-f42c-4ad9-9ddd-4a218e8b8514",
"type": "main",
"index": 0
}
]
]
},
"8848b159-986e-4b67-8bde-a38da0a25f36": {
"main": [
[
{
"node": "057f8499-694c-4391-80a1-e2f7ca093d25",
"type": "main",
"index": 0
}
]
]
},
"b41317b7-4a8c-4043-94e8-89b6add1c772": {
"main": [
[
{
"node": "85357d92-5b8b-4e36-acba-a1f3473237ae",
"type": "main",
"index": 0
}
]
]
},
"85357d92-5b8b-4e36-acba-a1f3473237ae": {
"main": [
[
{
"node": "2bc885ba-cbc4-4623-82ac-e85759170a6d",
"type": "main",
"index": 0
}
]
]
},
"7d83e353-0272-4857-ad4e-f3baab2e62b6": {
"main": [
[
{
"node": "8a1ffb69-65c0-4cf5-a4cb-e9209a9dc0ea",
"type": "main",
"index": 0
}
]
]
},
"3623adca-6827-467c-81f7-6f138377aefd": {
"main": [
[
{
"node": "a91f14ad-f42c-4ad9-9ddd-4a218e8b8514",
"type": "main",
"index": 0
}
]
]
},
"8a1ffb69-65c0-4cf5-a4cb-e9209a9dc0ea": {
"main": [
[
{
"node": "762e212e-1907-4610-9bcd-cc7b79258172",
"type": "main",
"index": 0
}
]
]
},
"55ce71bc-afc1-4178-87a3-6fda9829bd68": {
"main": [
[
{
"node": "6607766a-147e-49b6-b127-6655d3221c8c",
"type": "main",
"index": 0
}
]
]
},
"ff514a42-ee01-448a-8951-4b6d82ecff01": {
"main": [
[
{
"node": "088f6ac5-4c84-4c63-ba89-ab778c3ce547",
"type": "main",
"index": 0
}
]
]
},
"057f8499-694c-4391-80a1-e2f7ca093d25": {
"main": [
[
{
"node": "ce7cbd23-b0ec-4420-a262-cc16ddd9f5bd",
"type": "main",
"index": 0
}
]
]
},
"088f6ac5-4c84-4c63-ba89-ab778c3ce547": {
"main": [
[],
[
{
"node": "28f1bf7c-db78-4f1f-8db3-b00474dd7284",
"type": "main",
"index": 0
}
]
]
},
"6607766a-147e-49b6-b127-6655d3221c8c": {
"main": [
[
{
"node": "ff514a42-ee01-448a-8951-4b6d82ecff01",
"type": "main",
"index": 0
}
]
]
},
"ce7cbd23-b0ec-4420-a262-cc16ddd9f5bd": {
"main": [
[
{
"node": "3623adca-6827-467c-81f7-6f138377aefd",
"type": "main",
"index": 0
}
]
]
},
"36042f8c-f7b5-48bc-949f-44f8521d407e": {
"main": [
[
{
"node": "1524f17c-e0b2-41dc-a948-4e085885b0a2",
"type": "main",
"index": 0
}
]
]
},
"2bc885ba-cbc4-4623-82ac-e85759170a6d": {
"main": [
[
{
"node": "2d051e8b-9f67-4ed3-8a2a-85d19281452f",
"type": "main",
"index": 0
}
]
]
},
"1524f17c-e0b2-41dc-a948-4e085885b0a2": {
"main": [
[
{
"node": "088f6ac5-4c84-4c63-ba89-ab778c3ce547",
"type": "main",
"index": 0
}
]
]
},
"28f1bf7c-db78-4f1f-8db3-b00474dd7284": {
"main": [
[
{
"node": "268e6a49-f852-4056-b9cf-8b912f5b19e1",
"type": "main",
"index": 0
}
]
]
},
"2d051e8b-9f67-4ed3-8a2a-85d19281452f": {
"main": [
[
{
"node": "36042f8c-f7b5-48bc-949f-44f8521d407e",
"type": "main",
"index": 0
}
]
]
},
"268e6a49-f852-4056-b9cf-8b912f5b19e1": {
"main": [
[
{
"node": "0e6e4ad1-cf86-4e35-a862-bc8219c99838",
"type": "main",
"index": 0
}
],
[
{
"node": "b41317b7-4a8c-4043-94e8-89b6add1c772",
"type": "main",
"index": 0
}
]
]
},
"73604394-344d-469e-b845-0f3660abccaf": {
"main": [
[
{
"node": "088f6ac5-4c84-4c63-ba89-ab778c3ce547",
"type": "main",
"index": 0
}
]
]
},
"0e6e4ad1-cf86-4e35-a862-bc8219c99838": {
"main": [
[
{
"node": "73604394-344d-469e-b845-0f3660abccaf",
"type": "main",
"index": 0
}
],
[
{
"node": "b41317b7-4a8c-4043-94e8-89b6add1c772",
"type": "main",
"index": 0
}
]
]
}
}
}¿Cómo usar este flujo de trabajo?
Copie el código de configuración JSON de arriba, cree un nuevo flujo de trabajo en su instancia de n8n y seleccione "Importar desde JSON", pegue la configuración y luego modifique la configuración de credenciales según sea necesario.
¿En qué escenarios es adecuado este flujo de trabajo?
Avanzado - Creación de contenido, Resumen de IA
¿Es de pago?
Este flujo de trabajo es completamente gratuito, puede importarlo y usarlo directamente. Sin embargo, tenga en cuenta que los servicios de terceros utilizados en el flujo de trabajo (como la API de OpenAI) pueden requerir un pago por su cuenta.
Flujos de trabajo relacionados recomendados
Compartir este flujo de trabajo