سمات المستخدم المخصصة
سمات المستخدم هي خصائص تصف مستخدميك. استخدمها لتخصيص الرسائل وإنشاء الشرائح وتحليل قاعدة المستخدمين.
ما هي سمات المستخدم؟
السمات أزواج مفتاح وقيمة مرتبطة بملفات المستخدمين:
{
"userId": "user_123",
"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: 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 أبدًا من اسم الحقل. يُحدَّد النوع بهذا الترتيب:
- مُحدَّد - قام شخص بتحديد النوع في صفحة السمات (أو في صفحة الأحداث لخصائص الأحداث). هذا يتقدم دائمًا.
- حقل تجارة قياسي - تُعامَل
priceوtotalوrevenueوquantityوtaxوshippingوdiscountوsubtotalوvalueكأرقام افتراضيًا، سواء أرسلت12.34أو"12.34". أما المعرّفات مثلorder_idوskuفهي مستثناة عمدًا: كثيرًا ما تبدو رقمية، وتحديدها كأرقام هو سبب توقف طلب مثل007عن المطابقة. - مُستنتَج من بياناتك - النوع الأكثر شيوعًا في عيّنة، لكل حدث على حدة. تُحدَّد
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 مستويات |
استكشاف الأخطاء وإصلاحها
السمات لا تظهر
- تحقق من هجاء اسم السمة.
- تحقق من تعريف المستخدم.
- فعّل وضع التصحيح لرؤية استدعاءات API.
- تحقق من أخطاء API في الاستجابة.
السمة لا تتحدث
- تأكد من استخدام الدمج لا الاستبدال.
- تحقق من تطابق نوع البيانات.
- تحقق من صحة اسم السمة.
- تحقق من حدود المعدل.
التقسيم لا يعمل
- تحقق من وجود السمة للمستخدمين.
- تحقق من نوع بيانات السمة.
- اختبر منطق الفلتر مع مستخدمين معروفين.
- تأكد من تطابق قيم السمة والفلتر تماماً.