Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Search param builder #509

Merged
merged 12 commits into from
Apr 16, 2019
Merged

Search param builder #509

merged 12 commits into from
Apr 16, 2019

Conversation

patrickarlt
Copy link
Contributor

@patrickarlt patrickarlt commented Apr 11, 2019

After some initial research in #384 (comment) I decided to implement a builder for search queries. Here is some basics of the API:

new SearchQueryBuilder()
  .match("test")
  .in("tags"); // "tags: test"

new SearchQueryBuilder()
  .startGroup()
    .match("test")
    .in("tags")
  .endGroup()
  .not()
  .match("seed")
  .in("tags"); // "(tags: test) NOT tags: seed"

const NOW = Date.now();
const FIVE_DAYS_AGO = new Date(Date.now() - (24*60*60*1000) * 5);

new SearchQueryBuilder()
.from(FIVE_DAYS_AGO)
.to(NOW)
.in("created")
.and()
.match("test")
.in("tags"); // "created: [1554570502723 TO 1555002502723] AND tags: test"

new SearchQueryBuilder()
.match("patrickarlt7104")
.in("owner")
.and()
.startGroup()
  .match("Web Mapping Application")
  .in("type")
  .or()
  .match("Mobile Application")
  .in("type")
  .or()
  .match("Application")
  .in("type")
.endGroup()
.and()
.match("VTS")
.in("*"); //owner: patrickarlt7104 AND (type: Web Mapping Application OR type: Mobile Application OR type: Application) AND fire

const query = new SearchQueryBuilder()
.match("patrickarlt7104")
.in("owner")
.and()
.startGroup()
  .match("Web Mapping Application")
  .in("type")
  .or()
  .match("Mobile Application")
  .in("type")
  .or()
  .match("Application")
  .in("type")
.endGroup(); // "owner: patrickarlt7104 AND (type: Web Mapping Application OR type: Mobile Application OR type: Application)"

query.clone()
  .and()
  .match("fire")
  .in("*"); // "owner: patrickarlt7104 AND (type: Web Mapping Application OR type: Mobile Application OR type: Application) AND fire"

Instead of trying to implement all sorts of crazy helpers like andHasTags and orHasTags ect... I sent for a simpler approach that matches what the REST API search query thing is. This simply tries to build the string as best as possible and should allow for almost ANY permutation of what can be passed to the search API.

In the end there is no way to type option 2 from #384 (comment) without a TON of extra work (it might not even be possible at all) so I went with a class based approach.

I think that eventually we will want both IParamBuilder for building a single param and IParamsBuilder for returning an object with multiple params. This PR limits itself to IParamBuilder for now and leaves IParamsBuilder open for things like routing or feature layer/webmap creation later. I think that for building complex JSON objects we could use something like https://medium.com/@bensammons/building-a-fluent-interface-with-typescript-using-generics-in-typescript-3-4d206f00dba5 would be 💯 but I'll play around with it later.

This PR is still in draft while:

  1. We discuss the general API around IParamBuilder
  2. We discuss the specific API of SearchQueryBuilder
  3. I harden the API of SearchQueryBuilder to be more forgiving of stupid things like new SearchQueryBuilder().endGroup().and().or().from("foo").and().not("bar")
  4. I write tests for SearchQueryBuilder
  5. I write tests for IParamBuilder
  6. Write more documentation around
    1. Adding more builders for other params
    2. A guide topic to using the SearchQueryBuilder
  7. Replace the hellscape of https://github.com/Esri/arcgis-rest-js/blob/master/demos/node-cli-item-management/index.js#L102-L132 with the new builder

@jgravois
Copy link
Contributor

cc/ @pranavkulkarni

Copy link
Member

@tomwayson tomwayson left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Impressive @patrickarlt! I didn't look too deeply into the code, but at the macro level I like the direction you are headed.

Definitely wise to scope down to SearchQueryBuilder and IParamBuilder. The API looks good to me.

I'm cool w/ classes, and am thankful for all the thought you've put into the alternatives. The more "own special hells" we can avoid the better.

I'm especially cool w/ classes if we can move the builders into their own package as I suggest here: #137 (comment)

Finally, I assume the reason you brought withOptions() and withUrl() into this is just to that they play nicely w/ the builders, right? In general, I'd like to see those decoupled from the builder, and maybe even defaults (full disclosure, I haven't looked at #508 yet). I mean, it seems like they could be PR'd against master today, right?

packages/arcgis-rest-request/src/utils/process-params.ts Outdated Show resolved Hide resolved
@pranavkulkarni
Copy link
Contributor

This is awesome. I am currently porting some of the existing query building logic opendata-ui to hub.js/search. I could use this SearchQueryBuilder which is very intuitive and will help develop hub.js better. Any idea when this will roll out tentatively?

@jgravois
Copy link
Contributor

jgravois commented Apr 12, 2019

Any idea when this will roll out tentatively?

our goal is to include this in a v2.0.0 release fairly quickly. my hope would be to ship by late next week.

if you could symlink pat's branch in the meantime and provide more detailed feedback, that would be really cool.

@patrickarlt
Copy link
Contributor Author

@pranavkulkarni I would really want to land this in 2.0.0 next week.

@jgravois I'm probably going to be in Redlands sometime next week but I might only have 1-2 meetings do you and maybe @tomwayson want to spend a day on releasing 2.0.0?

@patrickarlt patrickarlt force-pushed the search-param-builder branch from 7b6204d to c3dd95a Compare April 12, 2019 21:30
@jgravois
Copy link
Contributor

do you and maybe @tomwayson want to spend a day on releasing 2.0.0?

for. sure.

Copy link
Contributor

@jgravois jgravois left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i'd say rename it to toParam() and this is g2g!

it seems like they could be PR'd against master today, right?

it could, but i think we should release with v2.0.0. its a great 🥕.

@patrickarlt
Copy link
Contributor Author

Since most people seem ok with the API I went ahead a wrote a pretty full test suite and added a bunch of warnings when people do stupid things like:

const query = new SearchQueryBuilder()
  .match("test")
  .not()
  .and()
  .toParam();

I would still like to write at least API doc for this. I'll do written doc some time after this merges. I also might replace IParamBuilder with something more like IOptionsBuilder and have it build out IRequestOptions. Then make a custom SearchOptions builder class just to demonstrate the other levels of builders. I'm just not 100% on board with making people leverage params.

@patrickarlt
Copy link
Contributor Author

Since this will go into 2.0.0 I'm also going to handle #137 (comment).

@patrickarlt patrickarlt changed the base branch from composition-and-defaults to v2.0.0 April 15, 2019 23:09
@patrickarlt patrickarlt marked this pull request as ready for review April 15, 2019 23:11
@patrickarlt patrickarlt changed the base branch from v2.0.0 to master April 15, 2019 23:13
@patrickarlt
Copy link
Contributor Author

@jgravois @tomwayson OK I have done some major refactoring of searchItems. This is technically for 2.0.0 but I'm making the PR against master and @jgravois and I can figure it out from there.

Does the following:

  1. Add support for a IParamBuilder which will allow for custom builder classes to provide the value of a single param in the API.
  2. Concrete implementation of IParamBuilder in SearchQueryBuilder for the q param in search items.
  3. Rewrite appendCustomParams and move it to common. appendCustomParams now accepts a custom IWhateverOptions object and a whitelist of keys to copy to params and returns the finished IRequestOptions object to use. You can optionally pass in a default set of options which will be merge before the keys are copied over.

This has 100% test coverage and should be ready to merge.

@jgravois feel free to refactor this and change the base to the 2.0.0 branch.

Copy link
Member

@tomwayson tomwayson left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm happy to see searchForm go bye bye!

packages/arcgis-rest-request/src/utils/IParamsBuilder.ts Outdated Show resolved Hide resolved
@@ -13,7 +13,8 @@
"dist/**"
],
"dependencies": {
"tslib": "^1.9.3"
"tslib": "^1.9.3",
"@esri/arcgis-rest-request": "^1.19.2"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should this be a devDependency?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i'd say yes. fixed in 99300a8

@@ -29,6 +27,7 @@ export interface ISearchResult {
num: number;
nextStart: number;
results: IItem[];
nextPage?: () => Promise<ISearchResult>;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is clever. Should we also include a previousPage()?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

gotta save something for 2.1!

@codecov
Copy link

codecov bot commented Apr 16, 2019

Codecov Report

❗ No coverage uploaded for pull request base (v2.0.0@0f750ea). Click here to learn what that means.
The diff coverage is 100%.

Impacted file tree graph

@@           Coverage Diff           @@
##             v2.0.0   #509   +/-   ##
=======================================
  Coverage          ?   100%           
=======================================
  Files             ?     93           
  Lines             ?   1317           
  Branches          ?    231           
=======================================
  Hits              ?   1317           
  Misses            ?      0           
  Partials          ?      0
Impacted Files Coverage Δ
packages/arcgis-rest-request/src/request.ts 100% <ø> (ø)
packages/arcgis-rest-request/src/index.ts 100% <ø> (ø)
packages/arcgis-rest-feature-service/src/delete.ts 100% <100%> (ø)
...es/arcgis-rest-request/src/utils/process-params.ts 100% <100%> (ø)
...es/arcgis-rest-request/test/mocks/param-builder.ts 100% <100%> (ø)
packages/arcgis-rest-common/src/index.ts 100% <100%> (ø)
packages/arcgis-rest-geocoder/src/geocode.ts 100% <100%> (ø)
...arcgis-rest-portal/src/items/SearchQueryBuilder.ts 100% <100%> (ø)
packages/arcgis-rest-portal/src/items/add.ts 100% <100%> (ø)
...es/arcgis-rest-feature-service/src/queryRelated.ts 100% <100%> (ø)
... and 8 more

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 0f750ea...3a56a23. Read the comment docs.

@jgravois
Copy link
Contributor

i just pushed up two more commits with a handful of tweaks to get tests passing on CI and use @patrickarlt's new appendCustomParams method in feature-service and geocoder.

doing this helped me catch (and fix) a couple more edge cases in the refactored utility method.

if @tomwayson is happy, i can rebase this branch on top of v2.0.0 and resolve the conflicts.

@patrickarlt
Copy link
Contributor Author

@jgravois your commits LGTM. I'll let you refactor this into 2.0.0.

@jgravois jgravois changed the base branch from master to v2.0.0 April 16, 2019 16:22
@jgravois jgravois merged commit 3f2d786 into v2.0.0 Apr 16, 2019
@jgravois jgravois deleted the search-param-builder branch April 16, 2019 16:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants