שאילתות — סינון, מיון ועימוד
כל השליפות עוברות דרך אותה נקודת קצה, ומשתנות רק בפרמטרים. הכול ב‑query string, הכול אופציונלי מלבד הטבלה עצמה.
פרמטרי השאילתה
Section titled “פרמטרי השאילתה”| פרמטר | טיפוס | משמעות | דוגמה |
|---|---|---|---|
where |
JSON מקודד ל‑URL | תנאי הסינון | where={"PhoneNumber":"0501234567"} |
keys |
רשימה מופרדת בפסיקים | אילו שדות להחזיר (projection) | keys=Name,PhoneNumber,Email |
include |
רשימה מופרדת בפסיקים | הרחבת שדות Pointer לאובייקט מלא בתשובה | include=AccountId,OwnerId |
order |
מחרוזת | מיון. - בהתחלה = יורד; פסיקים לריבוי שדות |
order=-createdAt,Name |
limit |
מספר | כמות. ברירת מחדל 100. אין תקרה נאכפת — ראו למטה | limit=500 |
skip |
מספר | דילוג — לעימוד | skip=500 |
count |
0 / 1 |
count=1 מוסיף ספירה כוללת. עם limit=0 — ספירה בלבד |
limit=0&count=1 |
תבנית התשובה: { "results": [ … ] }, ועם count=1 גם "count": N.
אופרטורי where
Section titled “אופרטורי where”| אופרטור | משמעות | דוגמה |
|---|---|---|
| ערך חשוף | שווה ל | {"Status":"Active"} |
$lt $lte $gt $gte |
קטן / קטן‑שווה / גדול / גדול‑שווה | {"Total":{"$gte":1000,"$lte":3000}} |
$ne |
שונה מ | {"IsAccount":{"$ne":true}} |
$in |
ברשימה | {"StatusId":{"$in":["E9cYlAlooc","piKPNxodMJ"]}} |
$nin |
לא ברשימה | {"City":{"$nin":["תל אביב","חיפה"]}} |
$exists |
לשדה יש ערך (או אין). null נחשב “יש”; "" לא נתפס באף צד — ראו עבודה עם רשומות |
{"Email":{"$exists":true}} |
$regex |
ביטוי רגולרי | {"Name":{"$regex":"^א"}} |
$all |
מערך מכיל את כל הערכים | {"Tags":{"$all":["דחוף","VIP"]}} |
$text |
חיפוש טקסט חופשי | רק על שדות מאונדקסים — ראו האזהרה למטה |
$inQuery |
Pointer שמצביע על רשומה מתוך תת‑שאילתה | ראו למטה |
$notInQuery |
ההפך מ‑$inQuery |
|
$select |
שדה סקלרי שווה לערך של מפתח מתוצאת תת‑שאילתה | ראו למטה |
$dontSelect |
ההפך מ‑$select |
|
$or |
OR — ברמה העליונה של האובייקט בלבד | {"$or":[{"PhoneNumber":"050…"},{"Email":"x@y"}]} |
מפתחות מרובים באותה רמה מתחברים ב‑AND. {"IsAccount":true,"City":"תל אביב"} = לקוחות וגם מתל אביב.
סינון לפי Pointer — אובייקט מלא, לא מחרוזת
Section titled “סינון לפי Pointer — אובייקט מלא, לא מחרוזת”{ "AccountId": { "__type": "Pointer", "className": "Accounts", "objectId": "xK9mP2qRsT" } }מחרוזת חשופה ({"AccountId":"xK9mP2qRsT"}) לא תתאים לשום דבר ותחזיר מערך ריק — בלי שגיאה.
סינון לפי תאריך — גם כאן אובייקט עטוף
Section titled “סינון לפי תאריך — גם כאן אובייקט עטוף”{ "createdAt": { "$gt": { "__type": "Date", "iso": "2026-01-01T00:00:00.000Z" } } }זה נכון גם ל‑createdAt ו‑updatedAt, למרות שהם חוזרים בתשובה כמחרוזת ISO שטוחה. ראו סוגי נתונים.
$inQuery — תת‑שאילתה דרך Pointer
Section titled “$inQuery — תת‑שאילתה דרך Pointer”“כל המשימות של לקוחות מתל אביב”, בלי שתי קריאות:
{ "AccountId": { "$inQuery": { "className": "Accounts", "where": { "City": "תל אביב" } } }}דוגמאות מלאות
Section titled “דוגמאות מלאות”ספירה בלבד — הכי זול שיש
Section titled “ספירה בלבד — הכי זול שיש”curl -sS -G "https://api.mbapps.co.il/parse/classes/Accounts" \ -H "X-Parse-Application-Id: $APP_ID" \ -H "X-Parse-API-Key: $API_KEY" \ --data-urlencode 'where={"IsAccount":true}' \ --data-urlencode 'limit=0' \ --data-urlencode 'count=1'const params = new URLSearchParams({ where: JSON.stringify({ IsAccount: true }), limit: '0', count: '1',});const res = await fetch(`https://api.mbapps.co.il/parse/classes/Accounts?${params}`, { headers: { 'X-Parse-Application-Id': APP_ID, 'X-Parse-API-Key': API_KEY, },});import json, requests
res = requests.get( "https://api.mbapps.co.il/parse/classes/Accounts", headers={ "X-Parse-Application-Id": APP_ID, "X-Parse-API-Key": API_KEY, }, params={ "where": json.dumps({"IsAccount": True}), "limit": 0, "count": 1, }, timeout=30,)<?php$params = http_build_query([ 'where' => json_encode(['IsAccount' => true]), 'limit' => 0, 'count' => 1,]);$ch = curl_init("https://api.mbapps.co.il/parse/classes/Accounts?$params");curl_setopt_array($ch, [ CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ 'X-Parse-Application-Id: ' . $appId, 'X-Parse-API-Key: ' . $apiKey, ],]);$response = curl_exec($ch);curl_close($ch);{ "results": [], "count": 412 }שליפה ממוקדת עם הרחבת Pointer
Section titled “שליפה ממוקדת עם הרחבת Pointer”curl -sS -G "https://api.mbapps.co.il/parse/classes/Sales" \ -H "X-Parse-Application-Id: $APP_ID" \ -H "X-Parse-API-Key: $API_KEY" \ --data-urlencode 'where={"Total":{"$gt":10000},"SaleStatusId":{"__type":"Pointer","className":"SaleStatuses","objectId":"zrP1MSVBoq"}}' \ --data-urlencode 'keys=Name,Total,ClosingDate,AccountId' \ --data-urlencode 'include=AccountId' \ --data-urlencode 'order=-Total' \ --data-urlencode 'limit=20'const params = new URLSearchParams({ where: JSON.stringify({ Total: { $gt: 10000 }, SaleStatusId: { __type: 'Pointer', className: 'SaleStatuses', objectId: 'zrP1MSVBoq' }, }), keys: 'Name,Total,ClosingDate,AccountId', include: 'AccountId', order: '-Total', limit: '20',});const res = await fetch(`https://api.mbapps.co.il/parse/classes/Sales?${params}`, { headers: { 'X-Parse-Application-Id': APP_ID, 'X-Parse-API-Key': API_KEY, },});import json, requests
res = requests.get( "https://api.mbapps.co.il/parse/classes/Sales", headers={ "X-Parse-Application-Id": APP_ID, "X-Parse-API-Key": API_KEY, }, params={ "where": json.dumps({ "Total": {"$gt": 10000}, "SaleStatusId": {"__type": "Pointer", "className": "SaleStatuses", "objectId": "zrP1MSVBoq"}, }), "keys": "Name,Total,ClosingDate,AccountId", "include": "AccountId", "order": "-Total", "limit": 20, }, timeout=30,)<?php$params = http_build_query([ 'where' => json_encode([ 'Total' => ['$gt' => 10000], 'SaleStatusId' => ['__type' => 'Pointer', 'className' => 'SaleStatuses', 'objectId' => 'zrP1MSVBoq'], ]), 'keys' => 'Name,Total,ClosingDate,AccountId', 'include' => 'AccountId', 'order' => '-Total', 'limit' => 20,]);$ch = curl_init("https://api.mbapps.co.il/parse/classes/Sales?$params");curl_setopt_array($ch, [ CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ 'X-Parse-Application-Id: ' . $appId, 'X-Parse-API-Key: ' . $apiKey, ],]);$response = curl_exec($ch);curl_close($ch);עם include, שדה ה‑Pointer חוזר כאובייקט מלא ולא כמצביע:
{ "results": [ { "objectId": "aB3dEf9HiJ", "Name": "חידוש רישיון 2026", "Total": 24000, "ClosingDate": { "__type": "Date", "iso": "2026-07-31T00:00:00.000Z" }, "AccountId": { "__type": "Object", "className": "Accounts", "objectId": "xK9mP2qRsT", "Name": "חברת דוגמה בע\"מ", "Email": "office@example.co.il" } } ]}OR בין טלפון לאימייל — דפוס חיפוש לפני יצירה
Section titled “OR בין טלפון לאימייל — דפוס חיפוש לפני יצירה”curl -sS -G "https://api.mbapps.co.il/parse/classes/Accounts" \ -H "X-Parse-Application-Id: $APP_ID" \ -H "X-Parse-API-Key: $API_KEY" \ --data-urlencode 'where={"$or":[{"PhoneNumber":"0501234567"},{"Email":"israel@example.co.il"}]}' \ --data-urlencode 'limit=5'const params = new URLSearchParams({ where: JSON.stringify({ $or: [ { PhoneNumber: '0501234567' }, { Email: 'israel@example.co.il' }, ], }), limit: '5',});const res = await fetch(`https://api.mbapps.co.il/parse/classes/Accounts?${params}`, { headers: { 'X-Parse-Application-Id': APP_ID, 'X-Parse-API-Key': API_KEY, },});import json, requests
res = requests.get( "https://api.mbapps.co.il/parse/classes/Accounts", headers={ "X-Parse-Application-Id": APP_ID, "X-Parse-API-Key": API_KEY, }, params={ "where": json.dumps({ "$or": [ {"PhoneNumber": "0501234567"}, {"Email": "israel@example.co.il"}, ], }), "limit": 5, }, timeout=30,)<?php$params = http_build_query([ 'where' => json_encode([ '$or' => [ ['PhoneNumber' => '0501234567'], ['Email' => 'israel@example.co.il'], ], ]), 'limit' => 5,]);$ch = curl_init("https://api.mbapps.co.il/parse/classes/Accounts?$params");curl_setopt_array($ch, [ CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ 'X-Parse-Application-Id: ' . $appId, 'X-Parse-API-Key: ' . $apiKey, ],]);$response = curl_exec($ch);curl_close($ch);שתי מגבלות limit שאסור לבלבל ביניהן
Section titled “שתי מגבלות limit שאסור לבלבל ביניהן”| השכבה | ברירת מחדל | מקסימום |
|---|---|---|
REST — /parse/classes/… |
100 | אין תקרה נאכפת (עד 2000 ומעלה) |
MCP — הכלי Get-Data |
5 | 2000 — נאכף בסכימת הכלי |
מי שמעביר לוגיקה מסוכן AI לשרת ביניים ומניח “ברירת המחדל היא 5” יקבל פי 20 רשומות. מי שהולך בכיוון ההפוך יחשוב שהטבלה כמעט ריקה. תמיד ציינו limit במפורש.
בשכבת ה‑MCP המגבלה כן נאכפת: limit=2001 נדחה עוד לפני שהגיע לשרת הנתונים —
Input validation error … limit: Too big: expected number to be <=2000.
הדפוס הבסיסי — skip + limit
Section titled “הדפוס הבסיסי — skip + limit”async function* allRows(table, pageSize = 1000) { let skip = 0; for (;;) { const qs = new URLSearchParams({ limit: String(pageSize), skip: String(skip), order: 'createdAt,objectId', // מיון יציב — חובה שובר‑שוויון, ראו האזהרה למטה }); const res = await fetch(`https://api.mbapps.co.il/parse/classes/${table}?${qs}`, { headers: H }); if (!res.ok) throw new Error(`${res.status}: ${await res.text()}`); const { results } = await res.json(); if (!results.length) return; yield* results; if (results.length < pageSize) return; // העמוד האחרון skip += pageSize; }}הדפוס העמיד — עימוד לפי createdAt + objectId
Section titled “הדפוס העמיד — עימוד לפי createdAt + objectId”במקום לספור שורות, זכרו איפה עצרתם. createdAt אינו משתנה אחרי היצירה, ולכן הוא סמן יציב — אבל כיוון שהוא אינו ייחודי, הסמן חייב להיות הזוג (createdAt, objectId):
import json, requests
API = "https://api.mbapps.co.il/parse"H = {"X-Parse-Application-Id": APP_ID, "X-Parse-API-Key": API_KEY}
def iter_all(table, page_size=1000, since=None): """מייצר את כל הרשומות בטבלה בסדר (createdAt, objectId) עולה, בלי skip.""" cursor = since # (iso, objectId), או None להתחלה while True: if cursor: iso, oid = cursor where = {"$or": [ {"createdAt": {"$gt": {"__type": "Date", "iso": iso}}}, {"createdAt": {"__type": "Date", "iso": iso}, "objectId": {"$gt": oid}}, ]} else: where = {} params = { "limit": page_size, "order": "createdAt,objectId", "where": json.dumps(where), } r = requests.get(f"{API}/classes/{table}", headers=H, params=params, timeout=30) r.raise_for_status() rows = r.json()["results"] if not rows: return for row in rows: yield row cursor = (rows[-1]["createdAt"], rows[-1]["objectId"]) # createdAt חוזר כמחרוזת ISO שטוחה if len(rows) < page_size: returnבונוס: אותה פונקציה היא גם סנכרון דלתא. שמרו את ה‑cursor האחרון, והריצה הבאה תתחיל בדיוק ממנו.
כמה זה בכלל? תשאלו לפני שתמשכו
Section titled “כמה זה בכלל? תשאלו לפני שתמשכו”curl -sS -G "https://api.mbapps.co.il/parse/classes/Accounts" \ -H "X-Parse-Application-Id: $APP_ID" -H "X-Parse-API-Key: $API_KEY" \ --data-urlencode 'limit=0' --data-urlencode 'count=1'const params = new URLSearchParams({ limit: '0', count: '1' });const res = await fetch(`https://api.mbapps.co.il/parse/classes/Accounts?${params}`, { headers: { 'X-Parse-Application-Id': APP_ID, 'X-Parse-API-Key': API_KEY, },});import requests
res = requests.get( "https://api.mbapps.co.il/parse/classes/Accounts", headers={ "X-Parse-Application-Id": APP_ID, "X-Parse-API-Key": API_KEY, }, params={"limit": 0, "count": 1}, timeout=30,)<?php$params = http_build_query(['limit' => 0, 'count' => 1]);$ch = curl_init("https://api.mbapps.co.il/parse/classes/Accounts?$params");curl_setopt_array($ch, [ CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ 'X-Parse-Application-Id: ' . $appId, 'X-Parse-API-Key: ' . $apiKey, ],]);$response = curl_exec($ch);curl_close($ch);1,800 רשומות = שתי קריאות. 180,000 = 180 קריאות, וכדאי לתכנן השהיה ביניהן: אין מגבלות קצב מתועדות לשרת, ובאלפי קריאות רצופות לא חוזר אף 429 — אבל “לא נצפה” אינו “לא קיים”, ולכן הכלל המעשי הוא לחתוך ולהאט מרצון. ראו מגבלות ומכסות.
מלכודות מהירות
Section titled “מלכודות מהירות”| התסמין | הסיבה |
|---|---|
400 עם {"code":102,"error":"Invalid parameter for query: X"} |
פרמטר שאינו נתמך — למשל distinct או readPreference |
500 עם {"code":1,"message":"Internal server error."} |
where שאינו JSON תקין |
500 עם IndexNotFound |
$text על שדה בלי אינדקס טקסט |
| מערך ריק, בלי שגיאה | סינון Pointer במחרוזת חשופה במקום באובייקט |
| מערך ריק על שאילתת תאריך | תאריך שנשלח כמחרוזת שטוחה ב‑where |
| מערך ריק על טבלה שאמורה להיות מלאה | שם טבלה שגוי — טבלה שאינה קיימת מחזירה 200 ומערך ריק, לא שגיאה |
| בדיוק 100 תוצאות, תמיד | לא ציינתם limit — זו ברירת המחדל של REST |
| בדיוק 5 תוצאות, תמיד | אתם בשכבת ה‑MCP, לא ב‑REST |
$or “לא עובד” |
$or תקף רק ברמה העליונה של אובייקט ה‑where (אחרת: 400 עם {"code":107,"error":"bad constraint: $or"}) |
$select על Pointer מחזיר ריק |
$select משווה ערך סקלרי; ל‑Pointer — $inQuery |
400 עם bad $text: $search, should be object |
$search חייב להיות {"$term":"…"} ולא מחרוזת |
| רשומות חסרות בייצוא מלא | skip עמוק על טבלה שממשיכה להשתנות — או מיון לפי createdAt בלי objectId כשובר‑שוויון |
רשומה עם "" לא מופיעה לא ב‑$exists:true ולא ב‑$exists:false |
מחרוזת ריקה נופלת בין הכיסאות; השתמשו ב‑{"$in":[null,""]} |
המשך מכאן
Section titled “המשך מכאן”- עבודה עם רשומות — יצירה, עדכון ומחיקה
- אגרגציות — כשצריך סכום ולא רשימה
- batch — כמה פעולות בבקשה אחת
- מגבלות ומכסות — כל המספרים במקום אחד
- שגיאות ופתרון תקלות