שאילתות — סינון, מיון ועימוד
כל השליפות עוברות דרך אותה נקודת קצה. הכול ב‑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 על שדה בלי אינדקס טקסט |
400 עם {"code":107,"error":"bad constraint: $or"} |
$or מחוץ לרמה העליונה של אובייקט ה‑where |
400 עם bad $text: $search, should be object |
$search חייב להיות {"$term":"…"} ולא מחרוזת |
| מערך ריק על טבלה שאמורה להיות מלאה | שם טבלה שגוי: טבלה שאינה קיימת מחזירה 200 ומערך ריק, לא שגיאה |
| בדיוק 100 תוצאות, תמיד | לא ציינתם limit |
| בדיוק 5 תוצאות, תמיד | אתם בשכבת ה‑MCP, לא ב‑REST |
| רשומות חסרות בייצוא מלא | skip עמוק על טבלה שמשתנה, או createdAt בלי objectId כשובר‑שוויון |