PsoftQL Operators & Filtering Examples
PsoftQL’s criteriaFields clause lets you filter rows with any of the operators SQL supports for scalar comparisons. The examples below demonstrate each operator, the two wildcards supported by LIKE, how multiple criteria combine with implicit AND, and how criteriaFields can coexist with a free-form sqlWhereClause.
All examples here are drawn verbatim from the integration test suite in doc-internal/test/test-pql-criteria-operators.http. Each test is numbered to match the source file.
These examples assume the target records are whitelisted for the calling service β see the PsoftQL Web Service documentation for configuration.
Supported Operators
| Operator | Meaning | Notes |
|---|---|---|
= | Equal | Default operator if omitted |
< | Less than | String or numeric comparison, depends on field type |
> | Greater than | |
<= | Less than or equal | |
>= | Greater than or equal | |
<> | Not equal | Equivalent to != |
!= | Not equal (alternate syntax) | Equivalent to <> |
LIKE | Pattern match | % matches any substring, _ matches exactly one character |
IN | Membership | Listed in schema, not currently honored β see note below |
Multiple entries in the criteriaFields array are joined with AND. For OR logic, move the condition into sqlWhereClause.
The IN operator appears in the JSON schema’s example list but the server currently does not produce a matching WHERE β¦ IN (β¦) clause β every attempted form (comma-separated string in fieldValue, parenthesized list, or a sibling fieldValues array) silently returns zero rows with a 200/responseCode: 200. Until this gap is closed, express membership filters with sqlWhereClause, for example:
{
"records": [
{
"recordName": "PSROLEDEFN",
"sqlWhereClause": "ROLENAME IN ('SA Administrator','SAIP Administrator','ACM Administrator')"
}
]
}
This route uses the raw SQL IN operator and works today. The PsoftQL-native IN operator is tracked on the road map.
Test 1: Greater Than (>) β PSROLEDEFN by last-update timestamp
Use > when you want rows strictly after a threshold. A common use case is fetching roles that have been edited since your last sync.
Request
POST /PSIGW/RESTListeningConnector/PSFT_CS/CHG_SWS_PSOFTQL HTTP/1.1
Authorization: Basic ...redacted...
Content-Type: application/json
{
"isDebugMode": false,
"includeFieldTypes": false,
"includeAllDescriptions": false,
"includeKeyFieldIndicators": false,
"rowLimit": 5,
"pageNumber": 1,
"records": [
{
"recordName": "PSROLEDEFN",
"criteriaFields": [
{
"fieldName": "LASTUPDDTTM",
"fieldValue": "2020-01-01",
"operator": ">"
}
]
}
]
}
Response
Trimmed to two rows for readability. Both rows satisfy LASTUPDDTTM > '2020-01-01'; the presence of nextPageNumber: 2 confirms there is more data to page through.
HTTP/1.1 200 OK
content-type: application/json; encoding=UTF-8
{
"data": {
"PSROLEDEFN": {
"objectType": "record",
"objectName": "PSROLEDEFN",
"fields": [
{
"rowNumber": 1,
"ROLENAME": "ACM Administrator",
"VERSION": 1,
"ROLETYPE": "U",
"DESCR": "ACM Administrator",
"ROLESTATUS": "A",
"DESCRLONG": "",
"ALLOWNOTIFY": "Y",
"ALLOWLOOKUP": "Y",
"LASTUPDDTTM": "2021-05-19-07.38.09.805472",
"LASTUPDOPRID": "PPLSOFT"
},
{
"rowNumber": 2,
"ROLENAME": "ADS Designer",
"VERSION": 1,
"ROLETYPE": "U",
"DESCR": "ADS Designer",
"ROLESTATUS": "A",
"DESCRLONG": "Role for Application Dataset Designer",
"ALLOWNOTIFY": "Y",
"ALLOWLOOKUP": "Y",
"LASTUPDDTTM": "2021-05-19-07.38.09.805512",
"LASTUPDOPRID": "PPLSOFT"
}
]
}
},
"responseCode": 200,
"errorMessages": "",
"meta": {
"toolsVer": "8.61.03",
"dbname": "CS92DEV",
"dbType": "ORACLE",
"serverTimeZone": "PST",
"debugMessages": ""
},
"pageNumber": 1,
"nextPageNumber": 2,
"productVersion": "2026-04-20"
}
Test 2: Less Than (<) β PSXLATITEM by field name
Use < for rows strictly before a threshold. This example also shows how noEffectiveDateLogic and noEffectiveStatusLogic flags disable the default SWS filters β useful when you want raw rows from tables that would otherwise be filtered to the current effective row or active status.
Request
POST /PSIGW/RESTListeningConnector/PSFT_CS/CHG_SWS_PSOFTQL HTTP/1.1
Authorization: Basic ...redacted...
Content-Type: application/json
{
"isDebugMode": false,
"includeFieldTypes": false,
"includeAllDescriptions": false,
"includeKeyFieldIndicators": false,
"noEffectiveDateLogic": true,
"noEffectiveStatusLogic": true,
"rowLimit": 10,
"pageNumber": 1,
"records": [
{
"recordName": "PSXLATITEM",
"criteriaFields": [
{
"fieldName": "FIELDNAME",
"fieldValue": "D",
"operator": "<"
}
]
}
]
}
Response
Both FIELDNAME values sort alphabetically before "D". Because noEffectiveDateLogic and noEffectiveStatusLogic are both true, you’ll see EFFDT and EFF_STATUS fields in the raw output rather than them being filtered behind the scenes.
HTTP/1.1 200 OK
content-type: application/json; encoding=UTF-8
{
"data": {
"PSXLATITEM": {
"objectType": "record",
"objectName": "PSXLATITEM",
"fields": [
{
"rowNumber": 1,
"FIELDNAME": "AAP_PLAN_TYPE",
"FIELDVALUE": "E",
"EFFDT": "1900-01-01",
"EFF_STATUS": "A",
"XLATLONGNAME": "Establishment Level Plan",
"XLATSHORTNAME": "Estab",
"LASTUPDDTTM": "2001-09-14-10.23.47.000000",
"LASTUPDOPRID": "PPLSOFT",
"SYNCID": 2
},
{
"rowNumber": 2,
"FIELDNAME": "AAP_PLAN_TYPE",
"FIELDVALUE": "J",
"EFFDT": "1900-01-01",
"EFF_STATUS": "A",
"XLATLONGNAME": "Job Group Level Plan",
"XLATSHORTNAME": "Job Group",
"LASTUPDDTTM": "2001-09-14-10.23.29.000000",
"LASTUPDOPRID": "PPLSOFT",
"SYNCID": 3
}
]
}
},
"responseCode": 200,
"errorMessages": "",
"meta": {
"toolsVer": "8.61.03",
"dbname": "CS92DEV",
"dbType": "ORACLE",
"serverTimeZone": "PST",
"debugMessages": ""
},
"pageNumber": 1,
"nextPageNumber": 2,
"productVersion": "2026-04-20"
}
Tests 3 & 4: Greater-or-Equal (>=) and Less-or-Equal (<=) β CRSE_CATALOG range
Range filters use the inclusive comparison operators. The pair below walks the Course Catalog: first everything from CRSE_ID = '000100' onward, then everything up to and including CRSE_ID = '000010'.
Request β Test 3: >=
POST /PSIGW/RESTListeningConnector/PSFT_CS/CHG_SWS_PSOFTQL HTTP/1.1
Authorization: Basic ...redacted...
Content-Type: application/json
{
"isDebugMode": false,
"includeFieldTypes": false,
"includeAllDescriptions": false,
"includeKeyFieldIndicators": false,
"rowLimit": 5,
"pageNumber": 1,
"records": [
{
"recordName": "CRSE_CATALOG",
"criteriaFields": [
{
"fieldName": "CRSE_ID",
"fieldValue": "000100",
"operator": ">="
}
]
}
]
}
Request β Test 4: <=
POST /PSIGW/RESTListeningConnector/PSFT_CS/CHG_SWS_PSOFTQL HTTP/1.1
Authorization: Basic ...redacted...
Content-Type: application/json
{
"isDebugMode": false,
"includeFieldTypes": false,
"includeAllDescriptions": false,
"includeKeyFieldIndicators": false,
"rowLimit": 10,
"pageNumber": 1,
"records": [
{
"recordName": "CRSE_CATALOG",
"criteriaFields": [
{
"fieldName": "CRSE_ID",
"fieldValue": "000010",
"operator": "<="
}
]
}
]
}
Response β Test 3 (>=)
The first two rows returned. Both have CRSE_ID >= '000100'. Each row in the real response carries the full record field set; non-essential fields are elided here with ... for readability.
HTTP/1.1 200 OK
content-type: application/json; encoding=UTF-8
{
"data": {
"CRSE_CATALOG": {
"objectType": "record",
"objectName": "CRSE_CATALOG",
"fields": [
{
"rowNumber": 1,
"CRSE_ID": "000100",
"EFFDT": "1900-01-01",
"EFF_STATUS": "A",
"DESCR": "Social Work Practice 2B",
"SSR_COMPONENT": "LEC",
"GRADING_BASIS": "GRD",
"UNITS_MINIMUM": 15,
"UNITS_MAXIMUM": 15
},
{
"rowNumber": 2,
"CRSE_ID": "000101",
"EFFDT": "2009-01-01",
"EFF_STATUS": "A",
"DESCR": "Chemistry 1",
"SSR_COMPONENT": "LEC",
"GRADING_BASIS": "GRD",
"UNITS_MINIMUM": 15,
"UNITS_MAXIMUM": 15
}
]
}
},
"responseCode": 200,
"errorMessages": "",
"pageNumber": 1,
"nextPageNumber": 2,
"productVersion": "2026-04-20"
}
Response β Test 4 (<=)
Same record, opposite end. Both rows have CRSE_ID <= '000010':
HTTP/1.1 200 OK
content-type: application/json; encoding=UTF-8
{
"data": {
"CRSE_CATALOG": {
"objectType": "record",
"objectName": "CRSE_CATALOG",
"fields": [
{
"rowNumber": 1,
"CRSE_ID": "000001",
"EFFDT": "1900-01-01",
"EFF_STATUS": "A",
"DESCR": "Basic Musical Techniques"
},
{
"rowNumber": 2,
"CRSE_ID": "000002",
"EFFDT": "1900-01-01",
"EFF_STATUS": "A",
"DESCR": "Musicianship and Materials"
}
]
}
},
"responseCode": 200,
"errorMessages": "",
"pageNumber": 1,
"nextPageNumber": 2,
"productVersion": "2026-04-20"
}
Tests 5 & 6: Not-Equal (<> and !=) β PSROLEDEFN by role status
PsoftQL accepts both <> and != for inequality. They generate identical SQL β pick whichever reads more naturally for your team.
Request β Test 5: <>
POST /PSIGW/RESTListeningConnector/PSFT_CS/CHG_SWS_PSOFTQL HTTP/1.1
Authorization: Basic ...redacted...
Content-Type: application/json
{
"isDebugMode": false,
"rowLimit": 10,
"pageNumber": 1,
"records": [
{
"recordName": "PSROLEDEFN",
"criteriaFields": [
{
"fieldName": "ROLESTATUS",
"fieldValue": "A",
"operator": "<>"
}
]
}
]
}
Request β Test 6: !=
POST /PSIGW/RESTListeningConnector/PSFT_CS/CHG_SWS_PSOFTQL HTTP/1.1
Authorization: Basic ...redacted...
Content-Type: application/json
{
"isDebugMode": false,
"rowLimit": 10,
"pageNumber": 1,
"records": [
{
"recordName": "PSROLEDEFN",
"criteriaFields": [
{
"fieldName": "ROLESTATUS",
"fieldValue": "I",
"operator": "!="
}
]
}
]
}
Response β Test 5 (<>)
On the demo database every role is active, so filtering for ROLESTATUS <> 'A' returns zero rows. The response is still a successful 200 β an empty fields array is the normal “no matches” shape, not an error:
HTTP/1.1 200 OK
content-type: application/json; encoding=UTF-8
{
"data": {
"PSROLEDEFN": {
"objectType": "record",
"objectName": "PSROLEDEFN",
"fields": []
}
},
"responseCode": 200,
"errorMessages": "",
"pageNumber": 1,
"productVersion": "2026-04-20"
}
Two things worth noting in zero-row responses:
nextPageNumberis absent β the same indicator you use to detect “last page” also signals “no data at all.”errorMessagesis still empty. An empty result and an error are distinct outcomes.
Response β Test 6 (!=)
ROLESTATUS != 'I' returns every role whose status is not Inactive β i.e. all active roles. First two rows shown:
HTTP/1.1 200 OK
content-type: application/json; encoding=UTF-8
{
"data": {
"PSROLEDEFN": {
"objectType": "record",
"objectName": "PSROLEDEFN",
"fields": [
{
"rowNumber": 1,
"ROLENAME": "ACM Administrator",
"ROLETYPE": "U",
"DESCR": "ACM Administrator",
"ROLESTATUS": "A",
"LASTUPDDTTM": "2021-05-19-07.38.09.805472",
"LASTUPDOPRID": "PPLSOFT"
},
{
"rowNumber": 2,
"ROLENAME": "ADS Designer",
"ROLETYPE": "U",
"DESCR": "ADS Designer",
"ROLESTATUS": "A",
"LASTUPDDTTM": "2021-05-19-07.38.09.805512",
"LASTUPDOPRID": "PPLSOFT"
}
]
}
},
"responseCode": 200,
"errorMessages": "",
"pageNumber": 1,
"nextPageNumber": 2,
"productVersion": "2026-04-20"
}
Test 8: Multiple Criteria (AND logic) β PSOPRDEFN
When you supply more than one entry in criteriaFields, PsoftQL joins them with AND. The example below finds users whose OPRID begins with A and whose account is locked, while using excludeFields to strip sensitive password columns from the response.
Always use excludeFields to drop OPERPSWD, PTOPERPSWDV2, OPERPSWDSALT, and ENCRYPTED when querying PSOPRDEFN. These fields contain hashed password material and should never leave the database.
Request
POST /PSIGW/RESTListeningConnector/PSFT_CS/CHG_SWS_PSOFTQL HTTP/1.1
Authorization: Basic ...redacted...
Content-Type: application/json
{
"isDebugMode": true,
"rowLimit": 10,
"pageNumber": 1,
"records": [
{
"recordName": "PSOPRDEFN",
"excludeFields": ["OPERPSWD", "PTOPERPSWDV2", "OPERPSWDSALT", "ENCRYPTED"],
"criteriaFields": [
{
"fieldName": "OPRID",
"fieldValue": "A%",
"operator": "LIKE"
},
{
"fieldName": "ACCTLOCK",
"fieldValue": "1",
"operator": "="
}
]
}
]
}
Response
Every row satisfies both conditions: OPRID starts with A and ACCTLOCK = 1. Note that OPERPSWD, PTOPERPSWDV2, OPERPSWDSALT, and ENCRYPTED are absent from each row β excludeFields filtered them out before serialization, so they were never on the wire:
HTTP/1.1 200 OK
content-type: application/json; encoding=UTF-8
{
"data": {
"PSOPRDEFN": {
"objectType": "record",
"objectName": "PSOPRDEFN",
"fields": [
{
"rowNumber": 1,
"OPRID": "AADAMS",
"OPRDEFNDESC": "Andrew Adams",
"EMPLID": "PU310",
"EMAILID": "",
"OPRCLASS": "HCPPALL",
"ROWSECCLASS": "HCDPALL",
"ACCTLOCK": 1,
"FAILEDLOGINS": 0,
"LASTPSWDCHANGE": "2024-05-23",
"LASTUPDDTTM": "2021-09-08-05.01.17.440969",
"LASTUPDOPRID": "PPLSOFT"
},
{
"rowNumber": 2,
"OPRID": "ACARR",
"OPRDEFNDESC": "Alan Carr",
"EMPLID": "KU0150",
"EMAILID": "",
"OPRCLASS": "HCCPCSSA1010",
"ROWSECCLASS": "HCCPCSSA1010",
"ACCTLOCK": 1,
"FAILEDLOGINS": 0,
"LASTPSWDCHANGE": "2024-05-23",
"LASTUPDDTTM": "2021-09-08-05.01.17.440969",
"LASTUPDOPRID": "PPLSOFT"
}
]
}
},
"responseCode": 200,
"errorMessages": "",
"pageNumber": 1,
"nextPageNumber": 2,
"productVersion": "2026-04-20"
}
Tests 9, 10, 11: LIKE Wildcard Patterns
PsoftQL passes LIKE patterns through to SQL, so the standard wildcards apply:
%matches any sequence of zero or more characters_matches exactly one character
The three examples below demonstrate each placement pattern: trailing, surrounding, and single-character.
Test 9: Leading wildcard (%ADMIN) β roles ending in ADMIN
POST /PSIGW/RESTListeningConnector/PSFT_CS/CHG_SWS_PSOFTQL HTTP/1.1
Authorization: Basic ...redacted...
Content-Type: application/json
{
"isDebugMode": false,
"rowLimit": 10,
"pageNumber": 1,
"records": [
{
"recordName": "PSROLEDEFN",
"criteriaFields": [
{
"fieldName": "ROLENAME",
"fieldValue": "%ADMIN",
"operator": "LIKE"
}
]
}
]
}
Test 10: Wildcards on both sides (%USER%) β roles containing USER
POST /PSIGW/RESTListeningConnector/PSFT_CS/CHG_SWS_PSOFTQL HTTP/1.1
Authorization: Basic ...redacted...
Content-Type: application/json
{
"isDebugMode": false,
"rowLimit": 10,
"pageNumber": 1,
"records": [
{
"recordName": "PSROLEDEFN",
"criteriaFields": [
{
"fieldName": "ROLENAME",
"fieldValue": "%USER%",
"operator": "LIKE"
}
]
}
]
}
Test 11: Single-character wildcard (_lient) β 6-letter XLATs ending in “lient”
POST /PSIGW/RESTListeningConnector/PSFT_CS/CHG_SWS_PSOFTQL HTTP/1.1
Authorization: Basic ...redacted...
Content-Type: application/json
{
"isDebugMode": false,
"noEffectiveDateLogic": true,
"noEffectiveStatusLogic": true,
"rowLimit": 20,
"pageNumber": 1,
"records": [
{
"recordName": "PSXLATITEM",
"criteriaFields": [
{
"fieldName": "XLATLONGNAME",
"fieldValue": "_lient",
"operator": "LIKE"
}
]
}
]
}
Response β Test 9 (%ADMIN)
Every returned ROLENAME ends in ADMIN:
HTTP/1.1 200 OK
content-type: application/json; encoding=UTF-8
{
"data": {
"PSROLEDEFN": {
"objectType": "record",
"objectName": "PSROLEDEFN",
"fields": [
{
"rowNumber": 1,
"ROLENAME": "CHG_SWS_ADMIN",
"ROLETYPE": "U",
"DESCR": "SWS Admin User",
"ROLESTATUS": "A",
"DESCRLONG": "Access to setup SWS Configurations\r\n\r\nSee Cedar Hills Group, INC. SWS Documentation.",
"LASTUPDDTTM": "2024-08-01-01.22.35.757884",
"LASTUPDOPRID": "PS"
},
{
"rowNumber": 2,
"ROLENAME": "EOFD_ADMIN",
"ROLETYPE": "U",
"DESCR": "Fluid Discussions Admin",
"ROLESTATUS": "A",
"LASTUPDDTTM": "2021-05-19-07.38.10.000000",
"LASTUPDOPRID": "PPLSOFT"
}
]
}
},
"responseCode": 200,
"errorMessages": "",
"pageNumber": 1,
"nextPageNumber": 2,
"productVersion": "2026-04-20"
}
Response β Test 10 (%USER%)
Every returned ROLENAME contains USER somewhere β leading text, embedded, or trailing:
HTTP/1.1 200 OK
content-type: application/json; encoding=UTF-8
{
"data": {
"PSROLEDEFN": {
"objectType": "record",
"objectName": "PSROLEDEFN",
"fields": [
{
"rowNumber": 1,
"ROLENAME": "CHG_PSOFT_LENS_API_USER",
"ROLETYPE": "U",
"DESCR": "API User",
"ROLESTATUS": "A",
"LASTUPDDTTM": "2026-01-18-16.56.00.000000",
"LASTUPDOPRID": "PS"
},
{
"rowNumber": 2,
"ROLENAME": "CHG_SWS_USER",
"ROLETYPE": "U",
"DESCR": "SWS User",
"ROLESTATUS": "A",
"LASTUPDDTTM": "2024-08-01-01.22.35.757884",
"LASTUPDOPRID": "PS"
}
]
}
},
"responseCode": 200,
"errorMessages": "",
"pageNumber": 1,
"nextPageNumber": 2,
"productVersion": "2026-04-20"
}
Response β Test 11 (_lient)
The _ matches exactly one character. Here both matches are the literal XLAT value "Client" (_ matched the C):
HTTP/1.1 200 OK
content-type: application/json; encoding=UTF-8
{
"data": {
"PSXLATITEM": {
"objectType": "record",
"objectName": "PSXLATITEM",
"fields": [
{
"rowNumber": 1,
"FIELDNAME": "PTSF_NODE_TYPE",
"FIELDVALUE": "2",
"EFFDT": "1900-01-01",
"EFF_STATUS": "A",
"XLATLONGNAME": "Client",
"XLATSHORTNAME": "Client"
},
{
"rowNumber": 2,
"FIELDNAME": "RUNLOCATION",
"FIELDVALUE": "1",
"EFFDT": "1900-01-01",
"EFF_STATUS": "I",
"XLATLONGNAME": "Client",
"XLATSHORTNAME": "Client"
}
]
}
},
"responseCode": 200,
"errorMessages": "",
"pageNumber": 1,
"productVersion": "2026-04-20"
}
If your database has no 6-letter strings ending in lient, the same request returns "fields": [] and no nextPageNumber β a successful empty result, identical in shape to the zero-row example shown for Test 5 above.
Test 12: Combining criteriaFields with sqlWhereClause β TERM_TBL
You can mix structured criteria with a free-form SQL clause. They are joined with AND. Use sqlWhereClause for expressions that don’t fit the scalar comparison model β subqueries, functions, OR logic β and keep criteriaFields for straightforward column-value checks so the intent is obvious.
Request
POST /PSIGW/RESTListeningConnector/PSFT_CS/CHG_SWS_PSOFTQL HTTP/1.1
Authorization: Basic ...redacted...
Content-Type: application/json
{
"isDebugMode": false,
"rowLimit": 10,
"pageNumber": 1,
"records": [
{
"recordName": "TERM_TBL",
"sqlWhereClause": "ACAD_CAREER = 'CNED'",
"criteriaFields": [
{
"fieldName": "INSTITUTION",
"fieldValue": "PSUNV",
"operator": "="
}
]
}
]
}
Response
Every row satisfies both INSTITUTION = 'PSUNV' (from criteriaFields) and ACAD_CAREER = 'CNED' (from sqlWhereClause):
HTTP/1.1 200 OK
content-type: application/json; encoding=UTF-8
{
"data": {
"TERM_TBL": {
"objectType": "record",
"objectName": "TERM_TBL",
"fields": [
{
"rowNumber": 1,
"INSTITUTION": "PSUNV",
"ACAD_CAREER": "CNED",
"STRM": "0290",
"DESCR": "1997 Fall",
"TERM_BEGIN_DT": "1997-08-27",
"TERM_END_DT": "1997-12-15",
"SESSION_CODE": "1",
"WEEKS_OF_INSTRUCT": 15,
"TERM_CATEGORY": "R",
"ACAD_YEAR": "1997"
},
{
"rowNumber": 2,
"INSTITUTION": "PSUNV",
"ACAD_CAREER": "CNED",
"STRM": "0292",
"DESCR": "1997 Fall Qtr",
"TERM_BEGIN_DT": "1997-09-01",
"TERM_END_DT": "1997-11-30",
"SESSION_CODE": "1",
"WEEKS_OF_INSTRUCT": 10,
"TERM_CATEGORY": "R",
"ACAD_YEAR": "1997"
}
]
}
},
"responseCode": 200,
"errorMessages": "",
"pageNumber": 1,
"nextPageNumber": 2,
"productVersion": "2026-04-20"
}
The PsoftQL engine builds a single SQL WHERE that ANDs the two conditions together. There is no precedence ambiguity: criteriaFields entries and sqlWhereClause always join with AND.
Where to go next
- PsoftQL Syntax β the full reference for every request field
- Aggregate SQL Examples β filtering combined with
isAggregate: true - Campus Solutions Examples β filtering applied to real Campus records