Gathering detailed insights and metrics for odata-query
Gathering detailed insights and metrics for odata-query
Gathering detailed insights and metrics for odata-query
Gathering detailed insights and metrics for odata-query
npm install odata-query
Module System
Min. Node Version
Typescript Support
Node Version
NPM Version
233 Stars
263 Commits
59 Forks
11 Watching
4 Branches
18 Contributors
Updated on 21 Nov 2024
TypeScript (99.91%)
JavaScript (0.09%)
Cumulative downloads
Total Downloads
Last day
-0.8%
16,834
Compared to previous day
Last week
-20.9%
95,687
Compared to previous week
Last month
7.5%
431,115
Compared to previous month
Last year
66.4%
3,891,205
Compared to previous year
1
7
OData v4 query builder that uses a simple object-based syntax similar to MongoDB and js-data
npm install odata-query
and then use the library
1import buildQuery from 'odata-query' 2 3const query = buildQuery({...}) 4fetch(`http://localhost${query}`)
where the query object syntax for {...}
is defined below. There is also react-odata which utilizies this library for a declarative React component.
See tests for examples as well
1buildQuery({ filter: {...} }) 2=> '?$filter=...'
1const filter = { PropName: 1 }; 2buildQuery({ filter }) 3=> '?$filter=PropName eq 1'
1const filter = { PropName: { gt: 5 } }; 2buildQuery({ filter }) 3=> '?$filter=PropName gt 5'
Supported operators: eq
, ne
, gt
, ge
, lt
, le
, in
Using the in
operator is also similar to the previous example.
1const filter = { PropName: { in: [1, 2, 3] } }; 2buildQuery({ filter }) 3=> '?$filter=PropName in (1,2,3)'
and
with an array of objects1const filter = [{ SomeProp: 1 }, { AnotherProp: 2 }, 'startswith(Name, "foo")']; 2buildQuery({ filter }) 3=> '?$filter=SomeProp eq 1 and AnotherProp eq 2 and startswith(Name, "foo")'
and
with multiple comparison operators for a single propertyUseful to perform a between
query on a Date
property
1const startDate = new Date(Date.UTC(2017, 0, 1)) 2const endDate = new Date(Date.UTC(2017, 2, 1)) 3const filter = { DateProp: { ge: startDate, le: endDate } } 4buildQuery({ filter }) 5=> "?$filter=DateProp ge 2017-01-01T00:00:00Z and DateProp le 2017-03-01T00:00:00Z"
1const filter = { 2 and: [ 3 { SomeProp: 1 }, 4 { AnotherProp: 2 }, 5 'startswith(Name, "foo")' 6 ] 7}; 8 9buildQuery({ filter }) 10=> '?$filter=SomeProp eq 1 and AnotherProp eq 2 and startswith(Name, "foo")'
1const filter = { 2 not: { 3 and:[ 4 {SomeProp: 1}, 5 {AnotherProp: 2} 6 ] 7 } 8}; 9 10buildQuery({ filter }) 11=> '?$filter=(not (SomeProp eq 1) and (AnotherProp eq 2))'
Supported operators: and
, or
, and not
.
any
Using an empty object
1const filter = { 2 ItemsProp: { 3 any: {} 4 } 5}; 6 7buildQuery({ filter }) 8=> '?$filter=ItemsProp/any()'
or also as an empty array
1const filter = { 2 ItemsProp: { 3 any: [] 4 } 5}; 6 7buildQuery({ filter }) 8=> '?$filter=ItemsProp/any()'
and
Using an object
1const filter = { 2 ItemsProp: { 3 any: { 4 SomeProp: 1, 5 AnotherProp: 2 6 } 7 } 8}; 9 10buildQuery({ filter }) 11=> '?$filter=ItemsProp/any(i:i/SomeProp eq 1 and i/AnotherProp eq 2)'
or also as an array of object
1const filter = { 2 ItemsProp: { 3 any: [ 4 { SomeProp: 1 }, 5 { AnotherProp: 2}, 6 ] 7 } 8}; 9 10buildQuery({ filter }) 11=> '?$filter=ItemsProp/any(i:i/SomeProp eq 1 and i/AnotherProp eq 2)'
and
, or
, and not
)1const filter = { 2 ItemsProp: { 3 any: { 4 or: [ 5 { SomeProp: 1 }, 6 { AnotherProp: 2}, 7 ] 8 } 9 } 10}; 11 12buildQuery({ filter }) 13=> '?$filter=ItemsProp/any(i:(i/SomeProp eq 1 or i/AnotherProp eq 2)'
1const filter = { 2 not: { 3 ItemsProp: { 4 any: { 5 or: [ 6 { SomeProp: 1 }, 7 { AnotherProp: 2}, 8 ] 9 } 10 } 11 } 12}; 13 14buildQuery({ filter }) 15=> '?$filter=not ItemsProp/any(i:((i/SomeProp eq 1) or (i/AnotherProp eq 2)))'
ITEM_ROOT is special constant to mark collection with primitive type
'in' operator
1const filter = { 2 tags: { 3 any: { 4 [ITEM_ROOT]: { in: ['tag1', 'tag2']}, 5 }, 6 }, 7}; 8 9buildQuery({ filter }) 10=> "?$filter=tags/any(tags:tags in ('tag1','tag2'))"
'or' operator on collection item itself
1const filter = { 2 tags: { 3 any: { 4 or: [ 5 { [ITEM_ROOT]: 'tag1'}, 6 { [ITEM_ROOT]: 'tag2'}, 7 ] 8 } 9 } 10}; 11 12buildQuery({ filter }) 13=> "?$filter=tags/any(tags:((tags eq 'tag1') or (tags eq 'tag2')))";
'and' operator on collection item itself and nested item
1 const filter = { 2 tags: { 3 any: [ 4 { [ITEM_ROOT]: 'tag1'}, 5 { [ITEM_ROOT]: 'tag2'}, 6 { prop: 'tag3'}, 7 ] 8 } 9}; 10 11buildQuery({ filter }); 12=> "?$filter=tags/any(tags:tags eq 'tag1' and tags eq 'tag2' and tags/prop eq 'tag3')";
Function on collection item itself
1const filter = { 2 tags: { 3 any: { 4 [`tolower(${ITEM_ROOT})`]: 'tag1' 5 } 6 } 7}; 8 9buildQuery({ filter }); 10=> "?$filter=tags/any(tags:tolower(tags) eq 'tag1')";
Supported operators: any
, all
1const filter = { PropName: { contains: 'foo' } }; 2buildQuery({ filter }) 3=> "$filter=contains(PropName,'foo')"
Supported operators: startswith
, endswith
, contains
1const filter = { 'length(PropName)': { gt: 10 } }; 2buildQuery({ filter }) 3=> "$filter=length(PropName) gt 10"
Supported operators: length
, tolower
, toupper
, trim
,
day
, month
, year
, hour
, minute
, second
,
round
, floor
, ceiling
1const filter = { "indexof(PropName, 'foo')": { eq: 3 } }; 2buildQuery({ filter }) 3=> "$filter=indexof(PropName, 'foo') eq 3"
Supported operators: indexof
, substring
A string can also be passed as the value of the filter and it will be taken as is. This can be useful when using something like odata-filter-builder or if you want to just write the OData filter sytnax yourself but use the other benefits of the library, such as groupBy, expand, etc.
1import f from 'odata-filter-builder'; 2 3const filter = f().eq('TypeId', '1') 4 .contains(x => x.toLower('Name'), 'a') 5 .toString(); 6buildQuery({ filter })
GUID:
1const filter = { "someProp": { eq: { type: 'guid', value: 'cd5977c2-4a64-42de-b2fc-7fe4707c65cd' } } }; 2buildQuery({ filter }) 3=> "?$filter=someProp eq cd5977c2-4a64-42de-b2fc-7fe4707c65cd"
Duration:
1const filter = { "someProp": { eq: { type: 'duration', value: 'PT1H' } } }; 2buildQuery({ filter }) 3=> "?$filter=someProp eq duration'PT1H'"
Binary:
1const filter = { "someProp": { eq: { type: 'binary', value: 'YmluYXJ5RGF0YQ==' } } }; 2buildQuery({ filter }) 3=> "?$filter=someProp eq binary'YmluYXJ5RGF0YQ=='"
Decimal:
1const filter = { "someProp": { eq: { type: 'decimal', value: '12.3456789' } } }; 2buildQuery({ filter }) 3=> "?$filter=someProp eq 12.3456789M"
Raw:
1const filter = { "someProp": { eq: { type: 'raw', value: `datetime'${date.toISOString()}'` } } }; 2buildQuery({ filter }) 3=> "?$filter=someProp eq datetime'2021-07-08T12:27:08.122Z'"
Note that as per OData specification, binary data is transmitted as a base64 encoded string. Refer to Primitive Types in JSON Format, and binary representation.
1const search = 'blue OR green'; 2buildQuery({ search }); 3=> '?$search=blue OR green';
1const select = ['Foo', 'Bar']; 2buildQuery({ select }) 3=> '?$select=Foo,Bar'
1const orderBy = ['Foo desc', 'Bar']; 2buildQuery({ orderBy }) 3=> '?$orderby=Foo desc,Bar'
1const expand = 'Friends/Photos' 2buildQuery({ expand }) 3=> '?$expand=Friends($expand=Photos)';
1const expand = { Friends: { expand: 'Photos' } } 2buildQuery({ expand }) 3=> '?$expand=Friends($expand=Photos)';
Supports both string (with slash seperators) and objects
1const expand = ['Foo', 'Baz']; 2buildQuery({ expand }) 3=> '?$expand=Foo,Bar';
1const expand = { Trips: { filter: { Name: 'Trip in US' } } }; 2buildQuery({ expand }) 3=> "?$expand=Trips($filter=Name eq 'Trip in US')";
1const expand = { Friends: { select: ['Name', 'Age'] } }; 2buildQuery({ expand }) 3=> '?$expand=Friends($select=Name,Age)';
1const expand = { Friends: { top: 10 } }; 2buildQuery({ expand }) 3=> '?$expand=Friends($top=10)';
1const expand = { Products: { orderBy: 'ReleaseDate asc' } }; 2buildQuery({ expand }) 3=> "?$expand=Products($orderby=ReleaseDate asc)";
filter
, select
, top
, and orderBy
can be used togetherSelect only the first and last name of the top 10 friends who's first name starts with "R" and order by their last name
1const expand = { 2 Friends: { 3 select: ['FirstName', 'LastName'], 4 top: 10, 5 filter: { 6 FirstName: { startswith: 'R' } 7 }, 8 orderBy: 'LastName asc' 9 } 10}; 11buildQuery({ expand }) 12=> '?$expand=Friends($select=Name,Age;$top=10;$filter=startswith eq 'R'))';
1const page = 3; 2const perPage = 25; 3const top = perPage; 4const skip = perPage * (page - 1); 5buildQuery({ top, skip }) 6=> '?$top=25&$skip=50'
Simple value
1const key = 1; 2buildQuery({ key }) 3=> '(1)'
As object (explicit key property
1const key = { Id: 1 }; 2buildQuery({ key }) 3=> '(Id=1)'
Include count inline with result
1const count = true; 2const filter = { PropName: 1} 3buildQuery({ count, filter }) 4=> '?$count=true&$filter=PropName eq 1'
Or you can return only the count by passing a filter object to count
(or empty object to count all)
1const count = { PropName: 1 } 2const query = buildQuery({ count }) 3=> '/$count?$filter=PropName eq 1'
Action on an entity
1const key = 1; 2const action = 'Test'; 3buildQuery({ key, action }) 4=> '(1)/Test'
Action on a collection
1const action = 'Test'; 2buildQuery({ action }) 3=> '/Test'
Action parameters are passed in the body of the request.
Function on an entity
1const key = 1; 2const func = 'Test'; 3buildQuery({ key, func }) 4=> '(1)/Test'
Function on an entity with parameters
1const key = 1; 2const func = { Test: { One: 1, Two: 2 } }; 3buildQuery({ key, func }) 4=> '(1)/Test(One=1,Two=2)'
Function on a collection
1const func = 'Test'; 2buildQuery({ func }) 3=> '/Test'
Function on a collection with parameters
1const func = { Test: { One: 1, Two: 2 } }; 2buildQuery({ func }) 3=> '/Test(One=1,Two=2)'
Transforms can be passed as an object or an array (useful when applying the same transform more than once, such as filter
)
Aggregations
1const transform = { 2 aggregate: { 3 Amount: { 4 with: 'sum', 5 as: 'Total' 6 } 7 } 8}; 9buildQuery({ transform }); 10=> '?$apply=aggregate(Amount with sum as Total)';
Supported aggregations: sum
, min
, max
, average
, countdistinct
Group by (simple)
1const transform = [{ 2 groupBy: { 3 properties: ['SomeProp'], 4 } 5}] 6buildQuery({ transform }); 7=> '?$apply=groupby((SomeProp))';
Group by with aggregation
1const transform = { 2 groupBy: { 3 properties: ['SomeProp'], 4 transform: { 5 aggregate: { 6 Id: { 7 with: 'countdistinct', 8 as: 'Total' 9 } 10 } 11 } 12 } 13} 14buildQuery({ transform }); 15=> '?$apply=groupby((SomeProp),aggregate(Id with countdistinct as Total))';
Group by with filtering before and after
1const transform = [{ 2 filter: { 3 PropName: 1 4 } 5},{ 6 groupBy: { 7 properties: ['SomeProp'], 8 transform: [{ 9 aggregate: { 10 Id: { 11 with: 'countdistinct', 12 as: 'Total' 13 } 14 } 15 }] 16 } 17},{ 18 filter: { 19 Total: { ge: 5 } 20 } 21}] 22buildQuery({ transform }); 23=> '?$apply=filter(PropName eq 1)/groupby((SomeProp),aggregate(Id with countdistinct as Total))/filter(Total ge 5)';
Supported transforms: aggregate
, groupby
, filter
. Additional transforms may be added later
No vulnerabilities found.
Reason
no dangerous workflow patterns detected
Reason
no binaries found in the repo
Reason
license file detected
Details
Reason
Found 8/19 approved changesets -- score normalized to 4
Reason
dependency not pinned by hash detected -- score normalized to 3
Details
Reason
detected GitHub workflow tokens with excessive permissions
Details
Reason
0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0
Reason
no effort to earn an OpenSSF best practices badge detected
Reason
security policy file not detected
Details
Reason
project is not fuzzed
Details
Reason
branch protection not enabled on development/release branches
Details
Reason
SAST tool is not run on all commits -- score normalized to 0
Details
Reason
12 existing vulnerabilities detected
Details
Score
Last Scanned on 2024-11-18
The Open Source Security Foundation is a cross-industry collaboration to improve the security of open source software (OSS). The Scorecard provides security health metrics for open source projects.
Learn More