ניהול נתונים ב-Google Health API

העבודה עם נתונים ב-Google Health API היא בעיקרה מחזור של סנכרון נתונים בין מאגר הנתונים של Google Health API בענן לבין האפליקציה או מאגר הנתונים שלכם בקצה העורפי. עם זאת, המחזור הזה יכול להשתנות בהתאם למגוון גורמים:

  • האם אתם כותבים נתונים ב-Google Health API? לקריאה בלבד? או גם וגם?
  • האם מאגר הנתונים שלכם נמצא באופן מקומי באפליקציה או במכשיר? או בענן שלכם?
  • האם אתם צריכים לסנכרן את נתוני Google Health API בין האפליקציה של המשתמש לבין מכשיר לביש? באיזו תדירות אתם מסנכרנים מכשירים?
  • עם אילו סוגי נתונים אתם עובדים? ספירות בסיסיות? יחידות מידה? סדרות עם קצבי דגימה שונים?
  • האם אתם מתכננים לקרוא נתונים כשהאפליקציה פועלת ברקע?
  • האם אתם מתכננים לעבוד עם נתונים היסטוריים שתועדו לפני שהאפליקציה קיבלה הרשאות משתמש?

כדי להבין איך כל זה משתלב, כדאי לעיין במחזור החיים של הסנכרון של Google Health API. יש שתי גרסאות של מחזור החיים הזה: רגילה (קריאה וכתיבה) ולקריאה בלבד.

מחזור החיים הרגיל של הסנכרון

מחזור החיים של סנכרון רגיל ב-Google Health API
איור 1: מחזור חיים רגיל של סנכרון ב-Google Health API

שילוב עם Google Health API פירושו העתקת נתונים לאפליקציה או למאגר נתונים לקצה העורפי. כדי שיהיה קל יותר להשתמש במסמכי התיעוד האלה, נקרא למאגר הנתונים הזה מאגר הנתונים של המפתח.

המונח "העתקה" בהקשר הזה יכול להתייחס לכל פעילות נפרדת, כמו קריאה מ-Google Health API (העתקה למאגר הנתונים של המפתח) או כתיבה ב-Google Health API (העתקה ל-Google Health API). ביצוע הפעולות האלה שוב ושוב בסדר מסוים הוא מחזור החיים של הסנכרון.

איור 1 ממחיש את מחזור החיים הרגיל של סנכרון שכולל פעולות קריאה וכתיבה, בלי להתייחס לאף אחד מהגורמים שהוזכרו קודם.

כתיבה

  1. הכנת נתונים חדשים לכתיבה – העברת נתונים מאפליקציה או ממכשיר חיצוניים ועיצוב נקודות על הגרף לייצוגי JSON שתואמים לסוגי הנתונים של Google Health API. שימו לב: בשלב הזה, Health API לא תומך במזהים מותאמים אישית שהלקוח מקצה לפעולות כתיבה. אפשר לציין מזהים כאלה ב-POST, אבל המערכת מתעלמת מהם.
  2. עדכון או הוספה (upsert) של רשומות – שליחת נקודות על הגרף ל-Google Health API באמצעות נקודות קצה (endpoints) של REST. משתמשים ב-POST כדי ליצור רשומות, וב-PATCH כדי להוסיף ולעדכן רשומות קיימות. המזהים שנדרשים לפעולה PATCH יגיעו מפעולה קודמת של POST (השלב הבא במחזור קודם).
  3. עיבוד מזהי המשאבים שהוחזרו – כשמשתמשים במזהים שנוצרו בשרת, צריך לחלץ את המשאב name או המזהה שהוחזר מהשרת ולשמור אותם במאגר הנתונים של המפתחים כדי לאפשר עדכונים (PATCH) או מחיקות (DELETE) בעתיד. מידע נוסף על שני הסוגים זמין בקטע אסטרטגיות זיהוי.

קריאה

  1. קריאת רשומות – אחזור נתונים חדשים ושינויים בנתונים קיימים ב-Google Health API באמצעות נקודות קצה (endpoints) של REST (GET עם פרמטרים של שאילתה filter ועימוד pageToken, או נקודות קצה של צבירה כמו rollUp ו-dailyRollUp), או קבלת התראות באמצעות מינויים ל-Webhook (projects.subscribers). התראה רק מציינת שנתונים חדשים זמינים, ולא מהם הנתונים בפועל.
  2. התאמה של מאגר הנתונים של המפתח — התאמה של הנתונים החדשים והמעודכנים למאגר הנתונים של המפתח. מכשירים מחוברים יכולים ליצור מרווחי זמן חופפים במהלך הסנכרון. כדי להבין איך Google Health API פותר את הבעיות האלה, קראו את המאמר חותמות זמן של מרווחים וסנכרון של מכשירים מחוברים.

המחזור הזה חוזר על עצמו במרווחי זמן מתאימים, בהתאם לצרכים הספציפיים של האפליקציות או המכשירים החיצוניים. בדרך כלל זה הסדר שמומלץ לסנכרן בו נתונים בין מאגר הנתונים שלכם לבין Google Health API.

שיטות זיהוי

אם אתם מתכוונים לכתוב נתונים ב-Google Health API, לפני שאתם יוצרים את השילוב עם ממשקי ה-API של Google Health, אתם צריכים לבחור אסטרטגיה לזיהוי משאבים כשאתם יוצרים נקודות על הגרף (יחידת הנתונים הבסיסית).

בשלב הזה, Health API לא תומך במזהים שהלקוח מקצה לפעולות כתיבה. מזהים כאלה יכולים להופיע ב-POST, אבל המערכת מתעלמת מהם. הפרטים על האפשרות הזו מופיעים כאן למטרות מידע בלבד.

  1. מזהים שנוצרו על ידי השרת (אפשרות ברירת המחדל): הלקוח שולח נתונים בלי מזהה, וה-backend של Google Health API יוצר ומחזיר מזהה מערכת ייחודי.
  2. מזהים מותאמים אישית שהוקצו על ידי הלקוח (לפי AIP-133, עדיין לא נתמכים): אפליקציית הלקוח יוצרת מזהה ייחודי (לדוגמה, UUID או מפתח ראשי של מסד נתונים מקומי) ומספקת אותו בנתיב המשאב בזמן היצירה.

בטבלה הבאה מוצגת השוואה בין שתי שיטות הזיהוי, כדי לעזור לכם לבחור את הגישה המתאימה לשילוב שלכם:

תכונה מזהים שנוצרו על ידי השרת מזהים מותאמים אישית שהוקצו על ידי הלקוח
יצירת מזהה השרת יוצר מזהה מערכת אקראי במהלך ההפעלה של POST. הלקוח יוצר מזהה יציב באופן מקומי (UUID v4 או PK פנימי) לפני הכתיבה.
נתיב המשאב ‫.../dataPoints/{server_id} (מוחזר בתגובה) .../dataPoints/{custom_id}
שלב מקומי אחרי כתיבה חובה. צריך לאחסן את הערך המוחזר של server_id במסד נתונים מקומי כדי לאפשר עדכונים או מחיקות בעתיד. אין. המזהה כבר שייך לאפליקציה.
טבלת מיפוי מזהים חובה. הלקוח צריך לשמור על מיפוי דו-כיווני (local_id ↔ server_id). אין צורך. הלקוח משתמש ישירות במפתח הראשי שלו.
התנהגות של ניסיון חוזר (רשת חלשה) סיכון לכפילויות. אם מנסים שוב להפעיל POST שהזמן הקצוב לתפוגה שלו חלף, נוצרת רשומה כפולה עם מזהה שרת חדש. בטוחה ואידמפוטנטית. אם מנסים שוב את POST עם אותו custom_id, לא נוצר עותק כפול (מוחזר 409 ALREADY_EXISTS).
תמיכה בסנכרון אופליין מוגבלת. צריך להמתין לתגובה מהשרת כדי לקבל מזהי משאבים רשמיים לפני שמאזכרים אותם. מלא. אפשר ליצור ולשנות ישויות אופליין באמצעות מזהים יציבים, ואז לסנכרן אותן בצורה חלקה כשמתחברים מחדש.
אילוצי פורמט השרת מטפל בזה באופן מלא. חייב לעמוד בדרישות של ^[a-z0-9-]{4,63}$ (4-63 תווים אלפאנומריים קטנים ומקפים).
מתי כדאי לבחור באפשרות הזו

כדאי לבחור במזהים שנוצרו בשרת אם:

  • האפליקציה שלכם היא לכתיבה בלבד או להוספה בלבד (למשל, שליחת נתוני טלמטריה או ספירת צעדים שאף פעם לא מתעדכנים או נמחקים מאוחר יותר).
  • האפליקציה לא שומרת מסד נתונים מקומי קבוע של נקודות נתונים נפרדות.
  • אתם מעדיפים פשטות בלי לנהל אילוצי אימות של מחרוזות (כמו תווים מסוג 4-63).

כדאי לבחור מזהים מותאמים אישית אם:

  • אתם מפעילים אפליקציית סנכרון דו-כיוונית שקוראת, כותבת ומעדכנת סיכומי מצב בריאותי במכשירים שונים.
  • באפליקציה יש מסד נתונים מקומי (כמו Room או SQLite) שמאחסן רשומות עם מפתחות ראשיים מקומיים.
  • המשתמשים שלכם מתעדים נתונים אופליין או באמצעות חיבורים סלולריים לא יציבים, שבהם נדרשים ניסיונות חוזרים בטוחים.
  • אתם רוצים להימנע משימוש בטבלאות מיפוי מזהים בין מסד הנתונים של ה-backend לבין ה-API.

מחזור החיים של סנכרון לקריאה בלבד

מחזור החיים של סנכרון לקריאה בלבד ב-Google Health API
איור 2: מחזור החיים של סנכרון לקריאה בלבד ב-Google Health API

באפליקציה שנועדה רק לקרוא מ-Google Health API, צריך להעתיק את הנתונים למאגר הנתונים של המפתח ולטפל בחלק של ההתאמה במחזור החיים.

המשימות שמופיעות בקטע קריאה רלוונטיות גם כאן.

איור 2 ממחיש את מחזור החיים של מצב לקריאה בלבד.

חותמות זמן של מרווחים וסנכרון של מכשירים מחוברים

נתוני מרווחים מייצגים מדידות שנאספו לאורך זמן, כמו צעדים, דופק או אימוני כושר. לעומת זאת, מדידות נקודתיות כוללות רשומות ידניות כמו יומן מזון או קריאה של משקל. נתוני אינטרוול מגיעים בדרך כלל מסנכרון של מכשירים מחוברים, כמו שעונים חכמים וצמידי כושר.

חותמות זמן של מרווחים (startTime ו-endTime במטענים ייעודיים (payloads) של REST JSON, או start_time ו-end_time בביטויי סינון וב-gRPC) מציגות התנהגויות ייחודיות כשעובדים עם נתוני מרווחים. בקטע הזה מוסבר למה יש חפיפה בין מרווחי הזמן, ומוצגת השוואה בין נקודות הקצה list ו-reconcile.

מרווחי זמן חופפים ממכשירים מחוברים

מכשירים מחוברים כמו מכשירי מעקב של Fitbit ו-Google Pixel Watch אוספים באופן רציף נתונים ביומטריים בתדירות גבוהה בזמן שעונדים אותם. אחרי שהמכשיר מסנכרן את הנקודות על הגרף עם Google Health, הוא לא משנה רטרואקטיבית את הרשומות הקיימות. חותמות הזמן של המרווחים ששמורים בהם לא משתנות.

עם זאת, לפני מחזורי הסנכרון הבאים, אלגוריתמים במכשיר מפרשים מחדש לעיתים קרובות את נתוני הטלמטריה הגולמיים של החיישנים. המכשיר מקבץ מחדש את הקריאות שנאספו בשעות הקודמות. כשהמכשיר מסתנכרן שוב, הוא מעלה נקודות חדשות על הגרף. גבולות ההתחלה והסיום שלהם יכולים לחפוף למרווחים שכבר נשמרו.

לדוגמה, נניח שמשתמש עונד שעון חכם ונתוני הפעילות שלו מסונכרנים בשתי קבוצות עוקבות:

  1. במהלך הסנכרון הראשון, המכשיר מעלה נקודה על הגרף שכוללת את התאריכים 10:00:00Z עד 10:14:59Z.
  2. אחרי החישוב מחדש במכשיר, מתבצע סנכרון שני שמעלה עוד נקודה על הגרף שכוללת את הנתונים מ-10:14:00Z עד 10:28:59Z.

שני התיקים מאוחסנים בנפרד בקצה העורפי של Google Health. כתוצאה מכך, שתי נקודות על הגרף מכסות את המרווח בין 10:14:00Z ל-10:14:59Z. כתוצאה מכך, יש חפיפה של 59 שניות כששולחים שאילתות לגבי רשומות גולמיות.

השוואה בין הרשימה לבין נקודות הקצה והתאמה ביניהן

אפשר לטפל במרווחים החופפים האלה באמצעות נקודת הקצה list או reconcile. בוחרים את נקודת הקצה שמתאימה לדרישות של האפליקציה:

תכונה נקודת קצה (endpoint) אחת (list) נקודת קצה (endpoint) אחת (reconcile)
שיטת HTTP GET https://health.googleapis.com/v4/users/me/dataTypes/dataType/dataPoints GET https://health.googleapis.com/v4/users/me/dataTypes/dataType/dataPoints:reconcile
התנהגות החפיפה הפונקציה מחזירה את כל הרשומות שנשמרו כפי שהועלו, בלי לבטל כפילויות. כשיש חפיפה בין המרווחים, שתי הרשומות מוחזרות. פותרת התנגשויות ומסירה רשומות כפולות שחופפות בין מכשירים וסשנים של סנכרון, ויוצרת רצף אחד.
יתרונות מספק נתיב ביקורת מלא וללא שינויים של כל רשומה שהועלתה על ידי כל מכשיר וקבוצת סנכרון. מפשט את עיבוד ציר הזמן ואת חישובי משך הזמן על ידי טיפול אוטומטי במרווחים חופפים ובקונפליקטים בין כמה מכשירים.
חסרונות האפליקציה שלכם אחראית לזהות ולפתור מרווחים חופפים, התנגשויות בין כמה מכשירים ותקופות שבהן המכשיר לא על פרק כף היד. רשומות חופפות משניות מושמטות מהתגובה, כך שלא ניתן לבדוק בנפרד קבוצות סנכרון של מכשירים ספציפיים.

נקודת הקצה reconcile מיועדת ליצירת ממשקי משתמש, לעיבוד של צירי זמן של פעילות ולחישוב של סך משכי הזמן שלא חופפים. הוא פותר מרווחים סותרים מסשנים של סנכרון שחולקו מחדש לקטגוריות. הוא גם מסנכרן פעילות שמתועדת בו-זמנית בכמה מכשירים, כמו שעון וטלפון.

ההתאמה פותרת סשנים סותרים על ידי בחירת הרשומה המוסמכת במקום ליצור איחוד זמן מלאכותי. לדוגמה, הוא לא ממזג את 11:00:00Z עד 11:30:00Z ואת 11:20:00Z עד 11:50:00Z לטווח 11:00:00Z עד 11:50:00Z. התגובה המותאמת מחזירה את הנקודה על הגרף שזכתה עם המרווח המקורי שתועד. כך נשמרת התקינות של נתוני הטלמטריה והמדדים שנמדדו באותו סשן.

באיור 3 מוצג איך נקודת הקצה reconcile מטפלת בסשנים חופפים. הוא בוחר את הרשומה המוסמכת במקום ליצור איחוד זמן מלאכותי.

פתרון בעיות של מרווחים חופפים: התאמה בין ביטול כפילויות של נקודות קצה לבין מיזוג איחוד זמן מלאכותי
איור 3: התאמה של סשנים מתנגשים לעומת מיזוג מלאכותי של איחוד זמנים

במדריך לנקודות הקצה (endpoints) יש דוגמאות מלאות לבקשות ולתגובות. כדי להשוות בין רשומות גולמיות של list לבין פלט של reconcile, קראו את המאמר קבלת תצוגה מותאמת של נתוני מרווחים.

נקודת הקצה (endpoint) של list מיועדת למצב החומרה במכשיר ולביקורת נתונים. השתמשו בה אם תהליך העבודה שלכם דורש בדיקה של רשומות שלא השתנו, כפי שהועלו על ידי כל מכשיר. כששולחים שאילתה באמצעות list, הלוגיקה של הלקוח צריכה לטפל בכל חפיפה בין מרווחים בנתונים הגולמיים.

יכולת שינוי של חותמת הזמן ועדכוני בעלים

מכשירים מחוברים לא משנים באופן רטרואקטיבי חותמות זמן שמאוחסנות במהלך מחזורי סנכרון רגילים. עם זאת, חותמות זמן של מרווחים (startTime ו-endTime במטענים ייעודיים (payloads) של REST JSON, או start_time ו-end_time בביטויי סינון וב-gRPC) לא תמיד ניתנות לשינוי בכל מקורות הנתונים. רק היוצר או הבעלים המקוריים של רשומה יכולים לשנות את השדות שלה. אפליקציות אחרות לא יכולות לערוך נקודות על הגרף שהן לא יצרו.

אפליקציה שהיא בעלים יכולה להשתמש בנקודת הקצה patch כדי לעדכן את הרשומות הקיימות שלה. זה כולל שינוי של חותמות הזמן של ההתחלה או הסיום, שמעדכן גם את השדות של שעון UTC (startTime ו-endTime) וגם את השדות של השעון האזרחי (civilStartTime ו-civilEndTime) בשרת. דוגמה לעדכון חותמות זמן באמצעות PATCH מופיעה בקטע עדכון חותמות זמן של מרווחים לנתונים קיימים במדריך לנקודות קצה.

באופן דומה, נקודות על הגרף שמסונכרנות מפלטפורמות חיצוניות כמו Health Connect או אפליקציות של שותפים מקבלות עדכונים מהמקור המקורי. אם Health Connect מקבלת עדכון לרשומה מסונכרנת מאפליקציית הצד השלישי המקורית, השינויים האלה מועברים ומשכתבים את הרשומה הקיימת.