تخطّ إلى المحتوى الرئيسي

سمات المستخدم المخصصة

سمات المستخدم هي خصائص تصف مستخدميك. استخدمها لتخصيص الرسائل وإنشاء الشرائح وتحليل قاعدة المستخدمين.

ما هي سمات المستخدم؟​

السمات أزواج مفتاح وقيمة مرتبطة بملفات المستخدمين:

{
"userId": "user_123",
"email": "[email protected]",
"attributes": {
"firstName": "John",
"lastName": "Doe",
"plan": "premium",
"signupDate": "2024-01-15",
"totalPurchases": 5,
"lastLoginDate": "2024-01-20",
"preferences": {
"newsletter": true,
"notifications": "email"
}
}
}

ضبط السمات​

عبر SDK، من جانب العميل​

import JoryioSDK from '@joryio/web-sdk';

const joryio = new JoryioSDK({ sdkKey: 'jry_sdk_web_...' });

// Identify a user
joryio.identify('user_123');

// Set attributes separately
joryio.setAttributes({
firstName: 'John',
lastName: 'Doe',
plan: 'premium',
signupDate: '2024-01-15'
});

// Update attributes later
joryio.setAttributes({
plan: 'enterprise',
lastUpgrade: new Date().toISOString()
});

عبر API، من جانب الخادم​

// Create or update user with attributes
fetch('https://api-eu1.joryio.com/api/users/track', {
method: 'POST',
headers: {
'Authorization': 'Bearer jry_live_your_api_key',
'Content-Type': 'application/json'
},
body: JSON.stringify({
attributes: {
externalId: 'user_123',
attributes: {
firstName: 'John',
plan: 'premium',
totalOrders: 10,
lifetimeValue: 1250.00
}
}
})
});

السمات القياسية مقابل المخصصة​

السمات القياسية​

أرسل أرقام الهاتف مع + ورمز الدولة

أرسل أرقام الهاتف دائمًا بالصيغة الدولية الكاملة (E.164): + ثم رمز الدولة ثم الرقم - مثل +972501234567. الرقم الذي لا يبدأ بـ + يُقرأ وفق الدولة الافتراضية لأرقام الهاتف في مساحة العمل؛ وإذا لم تُعيَّن، أو أمكن قراءة الأرقام بطريقتين، يُحفظ غير موثّق ولا يُستخدم للرسائل. كيف تُعالَج أرقام الهاتف

يحجزها Joryio وتُخزن في المستوى الجذر:

  • userId - معرّف المستخدم الفريد.
  • email - عنوان Email.
  • phone - رقم الهاتف.
  • externalId - معرّف النظام الخارجي.
  • createdAt - طابع إنشاء المستخدم.
  • updatedAt - طابع آخر تحديث.

السمات المخصصة​

أي خصائص أخرى تعرّفها، وتُخزن في كائن attributes:

  • firstName وlastName وname.
  • plan وsubscriptionTier.
  • company وindustry وjobTitle.
  • totalPurchases وlifetimeValue.
  • preferences وsettings.
  • وأي شيء آخر تحتاجه.

أنواع البيانات​

تدعم السمات أنواع بيانات متعددة.

String​

{
firstName: "John",
plan: "premium",
country: "US"
}

Number​

{
age: 30,
totalPurchases: 15,
lifetimeValue: 1250.50
}

Boolean​

{
emailVerified: true,
newsletter: false,
isPremium: true
}

Date/Timestamp​

{
signupDate: "2024-01-15",
lastLoginDate: "2024-01-20T10:30:00Z",
trialEndsAt: "2024-02-15T23:59:59Z"
}
استخدم تنسيق ISO 8601

للتواريخ، استخدم صيغة ISO 8601: YYYY-MM-DDTHH:mm:ssZ.

Array​

{
tags: ["vip", "early-adopter"],
interests: ["technology", "sports", "music"],
purchasedProducts: ["product_1", "product_2"]
}

Object / متداخل​

{
preferences: {
theme: "dark",
language: "en",
notifications: {
email: true,
sms: false,
push: true
}
},
address: {
street: "123 Main St",
city: "San Francisco",
state: "CA",
zip: "94102"
}
}

كيف يُحدَّد النوع​

لا يخمّن Joryio أبدًا من اسم الحقل. يُحدَّد النوع بهذا الترتيب:

  1. مُحدَّد - قام شخص بتحديد النوع في صفحة السمات (أو في صفحة الأحداث لخصائص الأحداث). هذا يتقدم دائمًا.
  2. حقل تجارة قياسي - تُعامَل price وtotal وrevenue وquantity وtax وshipping وdiscount وsubtotal وvalue كأرقام افتراضيًا، سواء أرسلت 12.34 أو "12.34". أما المعرّفات مثل order_id وsku فهي مستثناة عمدًا: كثيرًا ما تبدو رقمية، وتحديدها كأرقام هو سبب توقف طلب مثل 007 عن المطابقة.
  3. مُستنتَج من بياناتك - النوع الأكثر شيوعًا في عيّنة، لكل حدث على حدة. تُحدَّد Product Added.step وOnboarding Step Tapped.step بشكل منفصل.

عندما تختلف القيم​

إذا كان أكثر من ‎20%‎ تقريبًا من القيم من نوع مختلف، تظهر السمة على أنها مختلطة مع الأعداد (مثل string 994 - number 448). لا يختار Joryio جانبًا: يقارن كنص - وهو ما لا يعطي إجابة خاطئة أبدًا، بل تقريبية فقط - ويطلب منك تحديد النوع.

يُصلح تحديد النوع المقارنة للقيم المخزَّنة بالفعل: حقل محدَّد كرقم يقارن "12.34" و12.34 كالرقم نفسه، فيتوقف price > 100 عن مقارنة النص (حيث "90" > "100" صحيحة).

ملاحظة

تحديد النوع يغيّر طريقة مقارنة القيم. ولا يعيد كتابة ما تم تخزينه - تحتفظ بياناتك التاريخية بالقيم التي أرسلتها تمامًا.

ما الذي يكلّفه تحديد النوع​

تحديد النوع قرار بشأن كيفية التعامل مع اختلاف، وليس وسيلة لإنهائه. القيم التي لا يمكن قراءتها بالنوع الذي اخترته تتوقف عن مطابقة المرشحات - وتخرج من طرفي النطاق معًا.

لنفترض أن step يحمل الأرقام 1 و2 و3 في معظم الأحداث والكلمتين "invite" و"photo" في القليل منها، ثم حددته كـرقم:

المرشحجهة اتصال قيمة step لديها "invite"
step > 1لا تطابق
step <= 1لا تطابق
step = 1لا تطابق
step لا يساوي 1تطابق
step موجودتطابق

وهكذا فإن شريحتين قُصد بهما تقسيم جمهورك إلى نصفين - step > 1 وstep <= 1 - تتركان تلك الجهات خارج كليهما. واختبار تقسيم مبني بهذه الطريقة يفقدها بصمت.

الأرقام المكتوبة كنص لا مشكلة فيها: "12.34" تُقرأ كـ12.34 وتطابق كالمعتاد. تتأثر فقط القيم التي ليس لها قراءة بالنوع المختار.

تعرض قائمة السمات عدد القيم المتأثرة بجانب النوع، فيبقى الاختلاف مرئيًا بعد قرارك. الإصلاح يكون عند المصدر - أرسل القيمة بصيغة واحدة متسقة - لأن لا شيء هنا يعيد كتابة بيانات أرسلتها بالفعل.

الحقول القياسية تُحدَّد أنواعها نيابة عنك​

بعض الحقول لها معنى نحدده نحن لا تختاره أنت، ونوعها ثابت:

  • سمات المنصة التي يكتبها النظام - lastEventTime (تاريخ، رغم تخزينه كنص)، total_purchases، ml_churn_score، is_purchase_overdue.
  • خصائص التجارة القياسية - price، total، revenue، quantity، tax، shipping، discount، subtotal، value.

لا تحتاج إلى تحديدها، ولا يمكنك تغييرها. وهذا مقصود: لو أمكن للحقل القياسي نفسه أن يعني رقمًا في مساحة عمل ونصًا في أخرى، لما استطاع أي شيء مبني عليه - التقارير والتوصيات والإيرادات - الاعتماد عليه.

إذا كان أحد هذه الأسماء يعني شيئًا مختلفًا في بياناتك، أرسله باسم مختلف. فـprice الذي هو في الحقيقة تسمية ينبغي أن يكون price_label. وكل ما هو خارج هذه القوائم متروك لك لتحديده.

أمثلة شائعة للسمات​

التجارة الإلكترونية​

joryio.setAttributes({
// Account info
accountType: "premium",
memberSince: "2024-01-15",

// Purchase history
totalOrders: 15,
lastOrderDate: "2024-01-20",
lifetimeValue: 2500.00,
avgOrderValue: 166.67,

// Preferences
favoriteCategory: "electronics",
preferredShipping: "express",

// Engagement
cartAbandoned: false,
wishlistItems: 5,
reviewsWritten: 3
});

SaaS​

joryio.setAttributes({
// Subscription
plan: "pro",
billingCycle: "monthly",
subscriptionStatus: "active",
trialEndsAt: "2024-02-15T23:59:59Z",
mrr: 99,

// Usage
loginCount: 45,
lastLoginDate: "2024-01-20",
featuresUsed: ["export", "api", "integrations"],
apiCallsThisMonth: 10500,
storageUsedGB: 15.5,

// Team
teamSize: 8,
role: "admin",
companyName: "Acme Corp"
});

الوسائط والمحتوى​

joryio.setAttributes({
// Subscription
subscriptionTier: "premium",
contentAccessLevel: "unlimited",

// Engagement
articlesRead: 125,
videosWatched: 45,
podcastsListened: 30,
favoriteTopics: ["technology", "business"],

// Behavior
avgSessionDuration: 25.5,
lastVisit: "2024-01-20T14:30:00Z",
deviceType: "mobile"
});

تحديث السمات​

الدمج مقابل الاستبدال​

الدمج، الافتراضي يحدّث الحقول المحددة:

// Initial attributes
{
firstName: "John",
plan: "free",
country: "US"
}

// Update attributes (merges with existing)
joryio.setAttributes({
plan: "premium"
});

// Result (plan updated, others preserved)
{
firstName: "John",
plan: "premium", // Updated
country: "US" // Preserved
}
تُدمج السمات

يؤدي استدعاء setAttributes() إلى دمج السمات الجديدة مع الحالية؛ تحفظ السمات القائمة غير المذكورة في الاستدعاء.

الزيادة أو الإنقاص​

للعدادات، زد القيمة بدلاً من جلب القيمة الحالية:

// Bad: Race condition possible
const current = await getUserAttribute('loginCount');
joryio.setAttributes({ loginCount: current + 1 });

// Good: Atomic increment
joryio.incrementAttribute('loginCount', 1);

// Decrement
joryio.incrementAttribute('creditsRemaining', -10);

سمات المصفوفة​

يمكنك تخزين المصفوفات والتعامل معها كقيم سمات:

// Set array attribute
joryio.setAttributes({
tags: ['vip', 'early-adopter'],
interests: ['technology', 'sports']
});

// Add to array (only adds if value doesn't already exist)
joryio.addToArray('tags', 'premium');
// Result: ['vip', 'early-adopter', 'premium']

// Add duplicate (no-op, prevents duplicates)
joryio.addToArray('tags', 'vip');
// Result: ['vip', 'early-adopter', 'premium'] (unchanged)

// Remove from array
joryio.removeFromArray('tags', 'early-adopter');
// Result: ['vip', 'premium']
الطريقةالوصف
addToArray(key, value)تضيف قيمة إلى المصفوفة إذا لم تكن موجودة، فتمنع التكرارات. وتنشئ مصفوفة إن لم تكن السمة موجودة.
removeFromArray(key, value)تزيل كل مثيلات القيمة من المصفوفة، وتنبه إن لم تكن السمة مصفوفة.
حالات الاستخدام

تصلح سمات المصفوفة لـ:

  • وسوم المستخدم: ['vip', 'trial', 'beta-tester'].
  • الاهتمامات: ['sports', 'technology', 'fashion'].
  • المنتجات المشتراة: ['prod_123', 'prod_456'].
  • أعلام الميزات: ['feature-a', 'feature-b'].
  • الأدوار: ['admin', 'editor'].

استخدام السمات للتقسيم​

أنشئ شرائح بناءً على السمات.

فلتر سمة بسيط​

plan equals "premium"

مقارنة رقمية​

lifetimeValue >= 1000
totalPurchases > 5
age between 25 and 45

فلاتر تعتمد على التاريخ​

signupDate is within last 30 days
trialEndsAt is within next 7 days
lastLoginDate is more than 14 days ago

مطابقة النص​

email contains "@company.com"
country equals "US"
firstName exists
plan is not "free"

فلاتر المصفوفات​

tags contains "vip"
interests contains any of ["technology", "business"]

تخصيص الرسائل​

استخدم السمات في محتوى الحملات.

تخصيص Email​

Hi {{firstName}},

Your {{plan}} plan includes these benefits:
...

{{#if trialEndsAt}}
Your trial ends on {{trialEndsAt}}. Upgrade now!
{{/if}}

محتوى ديناميكي​

{{#if plan == "free"}}
<p>Upgrade to Premium for more features!</p>
{{else}}
<p>Thanks for being a {{plan}} member!</p>
{{/if}}

كتل شرطية​

{{#if totalPurchases > 10}}
<div class="vip-offer">
As a valued customer, here's an exclusive offer...
</div>
{{/if}}

أفضل ممارسات السمات​

1. استخدم أسماء وصفية​

جيد:

{
subscriptionTier: "premium",
lifetimeValueUSD: 1250.00,
emailVerified: true
}

غير جيد:

{
sub: "p",
ltv: 1250,
verified: 1
}

2. كن متسقاً​

استخدم اصطلاح تسمية واحداً.

جيد، camelCase:

{
firstName: "John",
lastName: "Doe",
signupDate: "2024-01-15"
}

غير جيد، مختلط:

{
first_name: "John",
LastName: "Doe",
"signup-date": "2024-01-15"
}

3. استخدم الأنواع المناسبة​

جيد:

{
age: 30, // Number
isPremium: true, // Boolean
signupDate: "2024-01-15", // ISO Date String
tags: ["vip", "beta"] // Array
}

غير جيد:

{
age: "30", // String instead of number
isPremium: "true", // String instead of boolean
signupDate: 1705276800, // Timestamp instead of ISO
tags: "vip,beta" // String instead of array
}

4. لا تخزن بيانات حساسة​

لا تخزن أبداً:

  • كلمات المرور أو تجزئاتها.
  • أرقام بطاقات الائتمان كاملة.
  • أرقام الضمان الاجتماعي.
  • التفاصيل المصرفية.
  • المعلومات الصحية.

يمكن تخزينه بأمان:

  • آخر 4 أرقام من البطاقة.
  • نوع وسيلة الدفع، مثل visa أو mastercard.
  • الرموز المشفرة أو المجزأة.
  • معلومات الملف العام.

5. اجعل أسماء السمات قصيرة​

جيد: plan وltv وmrr.

غير جيد: currentSubscriptionPlanTierLevel.

أسماء السمات المحجوزة​

تجنب الأسماء المحجوزة التالية:

  • userId وuser_id وid.
  • email.
  • phone.
  • createdAt وcreated_at.
  • updatedAt وupdated_at.
  • $app_id و$platform، البادئة بـ $.

تحديثات جماعية​

حدّث السمات لمستخدمين متعددين:

// API: Bulk update - each field of POST /users/track accepts an array (attributes: max 1000)
POST /users/track
{
"attributes": [
{
"externalId": "user_1",
"attributes": { "plan": "premium" }
},
{
"externalId": "user_2",
"attributes": { "plan": "enterprise" }
}
]
}

حدود السمات​

الحدالقيمة
أقصى سمات للمستخدم200
أقصى طول لاسم السمة100 حرف
أقصى طول لقيمة نصية10,000 حرف
أقصى طول للمصفوفة100 عنصر
أقصى عمق للتداخل5 مستويات

استكشاف الأخطاء وإصلاحها​

السمات لا تظهر​

  1. تحقق من هجاء اسم السمة.
  2. تحقق من تعريف المستخدم.
  3. فعّل وضع التصحيح لرؤية استدعاءات API.
  4. تحقق من أخطاء API في الاستجابة.

السمة لا تتحدث​

  1. تأكد من استخدام الدمج لا الاستبدال.
  2. تحقق من تطابق نوع البيانات.
  3. تحقق من صحة اسم السمة.
  4. تحقق من حدود المعدل.

التقسيم لا يعمل​

  1. تحقق من وجود السمة للمستخدمين.
  2. تحقق من نوع بيانات السمة.
  3. اختبر منطق الفلتر مع مستخدمين معروفين.
  4. تأكد من تطابق قيم السمة والفلتر تماماً.

الخطوات التالية​