elastic / search-ui Goto Github PK
View Code? Open in Web Editor NEWSearch UI. Libraries for the fast development of modern, engaging search experiences.
Home Page: https://docs.elastic.co/search-ui
License: Apache License 2.0
Search UI. Libraries for the fast development of modern, engaging search experiences.
Home Page: https://docs.elastic.co/search-ui
License: Apache License 2.0
A benefit of this library is the fact that it manages state for you, and converts that state to API calls automatically.
If a user chooses not to do that, either because they have a different backend they'd like to support, or they're storing their state elsewhere, we should provide support for that.
An example:
https://github.com/react-tools/react-table#fully-controlled-component
In manage mode:
When parameter state is changed in the driver, instead of storing that state, it simply notifies of that state change. It would then also accept updated state after that state change is handled. It makes no API calls.
This CSS file need to be bundled in our CSS build, otherwise the consuming application needs to have a CSS bundler enabled.
Quote:
I try to use the import "@elastic/react-search-ui-views/lib/styles/styles.css";
, I get an error from webpack Error: Failed to find '~rc-pagination/dist/rc-pagination.min.css'
Error on CSS build:
To replicate in Rails project:
rails new test-app --webpack=react --database=sqlite3
cd test-app
bundle exec rake db:create
rails g controller home index
change routes.rb to "root 'home#index'"
add "<%= javascript_pack_tag 'application' %>" to application.html.erb
add example code from "https://github.com/elastic/search-ui/tree/master/packages/react-search-ui" to application.js in app/javascript/packs
npm install
npm install --save @elastic/react-search-ui
npm install --save @elastic/search-ui-app-search-connector
./bin/webpack-dev-server
Come up with domain specific state shape that works best for the app, and not necessarily derived from API syntax.
The current syntax is derived from the App Search API syntax. It is highly nested and difficult to work with, especially when try to add or remove filters. This is because the API syntax is geared towards providing a DSL for querying, it was not designing with the intention of state management.
For this reason, a new state shape for managing UI state is would be better.
This will also push the connectors in a slightly different direction. Rather than converting to/from the App Search API syntax, they'll be converting to / from Search UI state syntax.
Note: Not modeling value types
- Date, Text, Geo, Number
The App Search API does not distinguish between the Geo, Date, Text, and Number types. So likewise, the UI does not distinguish, because there is no way to tell what the schema is by looking at values in an API response. We just pass values through and let the API figure out what is what. This means that no code in Search UI can automatically detect a value as being a Date, for instance and format it or treat it as a date explicitly. Components will need to have optional flags that tell a component that they're dealing with Dates if they need to render values as dates, etc.
This is an overall positive approach I believe, because it's entirely possible that different backends will have different value types. For instance, we don't support a Boolean type, but other APIs may. This way, we just pass the value through and don't worry about the particular type, so it should "just work"
Filter
Facet
- field: String - Which field is this facet associated with
- type: String - "range" or "value"
- data:** [FacetValue]
FacetValue
- count: Number
- value: FilterValue
FilterType: String["all" | "none" | "or"]
** FieldValue: <String, Boolean, Number> **
FilterValue > FilterValueRange<T:FieldValue>
- to: <T>
- from: <T>
- name: String - We'll need this to correlate applied dynamic Date Range filters with options in corresponding range Facets on a page reload. This will not be an optional field.
FilterValue > FilterValueValue: FieldValue
Cases to consider:
It uses class instead of className
This action should take the search back to its initial state.
Currently, all facets are treated as AND filters.
I believe that there should be an option on certain Facet component types to make it a NONE filter.
Ex:
<Facet field="state" type="none" />
Facets currently have two problems:
BREAKING CHANGES:
Facet views have been renamed.
Make result_fields, search_fields, and highlighting hard configuration on the Driver, and not just part of searchOptions
searchOptions are for additional, API specific parameters that we don't support. For that reason, they should be part of the connector, NOT the driver.
Live Search is a search that is performed as a user types in a search box.
As part of this change, I believe that instead of tracking the SearchBox's "value" in local state, we should use the global searchTerm
value from the provider state.
This has a couple of of advantages:
The interface I had in mind requires a change to setSearchTerm
. It looks something like this:
setSearchTerm(searchTerm, {refresh = true, debounce = 200, suggest = false} = {})
To do a live search, you'd pass refresh: true
. To perform a regular search, you would pass false
, or omit the option.
the debounce
option would let you throttle requests a bit, as live search tends to generate many thrashing requests.
I won't be implementing suggest
yet, it is just there for reference.
Implemented by #79
This provides more flexibility for styling
When initializing a SearchProvider, there are two different scenarios for passing in state.
Default sate: The default state of the application. The default state is the state that is displayed if no request has been made, and the state that the application is returned to after a "reset" action is performed. An example would be setting a default sort on data.
Initial State: The initial state would be used for something like SSR, or pre-loading a previous search state. It would have priority over the Default state, but would be ephemeral, in that if a "reset" action was performed, it would return to the Default State, NOT the Initial State. Another way of thinking about this is the same way we read values from the query string, the query string would be another means of providing an Initial State.
It would be neat to add basic layout components to react-search-components
.
You'd get layout that basically allows you to implement a simple 2 column, facets on left, search bar on top.
This would make it easy for a quick an easy implementation.
Should this URI (https://github.com/elastic/search-ui/blob/master/packages/search-ui-site-search-connector/src/SiteSearchAPIConnector.js#L17) be configurable so that you could point it to a self-hosted or development version of the Site/App Search API?
Currently, range filters will not work with date fields.
This is currently blocked by: https://swiftype.atlassian.net/browse/ENG-1368
If an invalid page type is passed, the following error message is triggered:
index.js:1446 TypeError: Cannot read property 'map' of undefined
at getResults (responseAdapters.js:180)
at toResultList (responseAdapters.js:111)
at SiteSearchAPIConnector.js:258
Additionally, we need to make it clear in our App.js
example AND the documentation where you can find your documentType, and also, if it is a crawler based engine that your documentType is page
.
Currently, we pull in the Reference UI CSS and Markup. This is not ideal because the terms "reference-ui" are used all throughout it.
We should factor out common styles that are tied to individual components and have a separate stylesheet for react-search-components.
I also recommend setting up Storybook for styling these components individually.
To consider:
I have a dataset which includes a numeric field difficulty
.
I want to filter on this, so I add a filter with addFilter("difficulty", 2)
. This works great and filters my result set with the difficulty of 2. It also adds ?fv-difficulty=2
to my query parameters. If I then refresh my page I get the following error:
Error: [400] Filters contains invalid value for field: difficulty; must be a number, an array of numbers, or a range object with `to` and/or `from` keys
at handleErrorResponse (swiftype_app_search.umd.js:362)
We deal with a number of types in this Project. Making concrete types that could be shared across the various projects and externally to consumers would clean things up a lot.
Adding support for auto complete/suggestions into the search box component would be very handy and useful, especially with sitesearch/app search use cases
View components should provide a means to allow arbitrary properties like "className" and "data-" properties to be passed through.
Container components should pass through all additional properties to View components, so users can customize View properties through the container.
I.e.,
<SearchBox
onSelectAutoCompleteResult={console.log}
/>
onSelectAutoCompleteResult
is a property of the view, not the container, so it get passed through.
However, because of this, we don't want to pass ALL properties from the View to the underlying HTML, because you could end up with some weird attributes on your html. What we should DO is destructure props with all KNOWN, needed properties, and pass through everything else.
const {onSelectAutoCompleteResult, data, ...rest} = props;
return <input {...rest} />;
If I do
clearFilters
andsetSearchTerm("")
in the SearchBox’scomponentWillUnmount()
I get the following warning:Can't perform a React state update on an unmounted component.
Basically, we need to make sure we unsubscribe from the driver on unmount of SearchProvider.
See Debounce usage in elastic-co-search: https://github.com/elastic/elastic-co-search/commit/bb07ff13d756264d83f6a6776c14dcc106ee701d
Two actions should be able to be called one after the other, and both actions should be applied in batch, in just a single API call.
Document this as an Advanced Feature in the README
This was implemented in the Reference UI.
https://github.com/swiftype/app-search-reference-ui-react/releases/tag/v1.1.0
Files names, across the entire project use a mix of camel and snake casing. We should pick one and be consistent about it.
https://github.com/swiftype/marketing-website/pull/376#discussion_r252943923
For example, Mad's had to mock a synthetic event to get this working.
Currently, we make the following recommendation: https://github.com/elastic/search-ui/blob/master/packages/react-search-ui/README.md#customizebehavior.
I believe, for simplicity, we can actually remove this recommendation, along with the underlying mapContextToProps
function (mapViewProps
doesn't actually exist yet).
Mads was able to accomplish exactly what he needed by using wrapper components.
For ex, overriding props in containers:
<SearchProvider>
{({filters, ...rest}) => (
<SomeComponent filters={filters} {...rest} />
)}
</SearchProvider>
For ex, overriding props in views:
<SomeComponent view={
{({color, ...rest}) => (
<SomeComponentView color="red" {...rest}/>
)}
}/>
This would give us:
Currently, the options "20", "40", and "60" are hardcoded as options in the ResultsPerPage components: https://github.com/elastic/search-ui/blob/master/packages/react-search-ui/src/containers/ResultsPerPage.js#L38.
This should be configurable, as users may want options other than these defaults.
When configuring Search UI, we should use the App Search API as the format for configuration.
For example, right now, we have a facetConfig
configuration option. It has additional properties that do not match App Search. Those should be pulled out into different configuration, to alleviate any confusion.
This currently simply uses babel to compile our source from /src to /lib.
We should pull in Rollup, for two things:
EDIT:
It seems reasonable to just not use Rollup for this particular package. We'll punt on this until it's clear we have a use for it.
It best describes what it is, it renders a "facet" and lets you apply "filters" from those selections.
For example, if you select a "Last 30 Minutes" filter, and reload the page, the option that is generate for the last 30 days will not match what is stored in the URL. This causes the filter go be applied, but not show up as a selection in the Facet Filter.
Possible solutions:
Initially, we should just add CircleCI to run unit tests.
This does not need to include comprehensive integration and browser level tests.
Currently, all facets are treated as AND filters.
I believe that there should be an option on certain Facet component types to make it an OR filter.
I propose the following API:
// { field: "test", values: ["value", "value2"], type: "all" }
// { field: "test", values: ["value2"], type: "any" }
<Facet field="test" label="Test" filterType="any" />
Test
[ ] value
[ ] value 1
[x] value2 // "value2" is selected, pull from "any" list
<Facet field="test" label="Test" filterType="all" />
Test
[x] value // "value" selected, pull from "all" list
[] value 1
[x] value2 // "value2" selected, pull from "all" list
addFilter(name, value, type = "all")
ex.
// Defaults to 'all'
addFilter("test", "value")
// { field: "test", values: ["value"], type: "all" }
// If 'all' list for 'field' already exists, it will add to that list
addFilter("test", "value2", "all")
// { field: "test", values: ["value", "value2"], type: "all" }
// Will treat 'any' list for 'field' separately
addFilter("test", "value2", "any")
// { field: "test", values: ["value", "value2"], type: "all" }
// { field: "test", values: ["value2"], type: "any" }
removeFilter(name, value, type)
ex.
// Removes from all lists if none specified
// { field: "test", values: ["value", "value2"], type: "all" }
// { field: "test", values: ["value2"], type: "any" }
removeFilter("test", "value2")
// { field: "test", values: ["value2"], type: "all" }
// Removes just specified type of filter
// { field: "test", values: ["value", "value2"], type: "all" }
// { field: "test", values: ["value2"], type: "any" }
removeFilter("test", "value", "all")
// { field: "test", values: ["value2"], type: "all" }
// { field: "test", values: ["value2"], type: "any" }
setFilter(name, value, type)
ex.
// Defaults to 'all'
setFilter("test", "value2")
// { field: "test", values: ["value2"], type: "all" }
// Sets specified type of filter otherwise
// { field: "test", values: ["value", "value2"], type: "all" }
// { field: "test", values: ["value2"], type: "any" }
setFilter("test", "new", type: "all")
// { field: "test", values: ["new"], type: "all" }
// { field: "test", values: ["value2"], type: "any" }
This will likely be tied closely to Managed Mode
For example, a dropdown facet option requires a disjunctive facet, because it has no means of removing the facet once applied.
How do we represent search parameters from the App Search API in a query string?
Challenge: need to flatten highly nested structure and maintain typing.
Reference: https://swiftype.com/documentation/app-search/api/search/filters
{
“all”: [{
// Range with type Number
{"visitors": {
"from": 1,
"to": 100
}},
// Range with type Date
{"date_established": {
"from": "1900-01-01T12:00:00+00:00",
"to": "1950-01-01T00:00:00+00:00"
}},
// Geo with type Geolocation
{"location": {
"center": "37.386483, -122.083842",
"distance": 300,
"unit": "km"
}},
// Value with type Text
{"world_heritage_site": "true"},
// Value with type Date
{“created_date”: "1900-01-01T12:00:00+00:00"},
// Value with Type Number
{“visitors”: 100},
// Combining with "all"
{“category”: ”fun”},
{“category”: ”healthy”}
// Nested "or"s
{“category”: [”gradeA”, “gradeB”]}
}],
// Combining with "any"
“or”: { … },
// Combining with "none"
“none”: { … },
}
f_{filterType}{valueType}{andAnyNone}{sequence}{fieldName}{[]}={value}
Range with type Number
{"visitors": {
"from": 1,
"to": 100
}}
f_rna_visitors=1_100
Range with type Date
{"date_established": {
"from": "1900-01-01T12:00:00+00:00",
"to": "1950-01-01T00:00:00+00:00"
}}
f_rda_date_established=1900-01-01T12:00:00+00:00_1950-01-01T00:00:00+00:00
Geo with type Geolocation
{"location": {
"center": "37.386483, -122.083842",
"distance": 300,
"unit": "km"
}}
f_gga_location=37.386483, -122.083842_300_km
Value with type Text
{"world_heritage_site": "true"}
f_vta_world_heritage_site=true
Value with type Date
{“created_date”: "1900-01-01T12:00:00+00:00"}
f_vda_created_date=1900-01-01T12:00:00+00:00
Value with Type Number
{“visitors”: 100},
f_vna_visitors=100
Combining with "all"
{"all": [
{“category”: ”fun”},
{“category”: ”healthy”}
]}
f_vta_category=fun&f_vta_category_1=healthy
Nested "or"s
{"all": [
{“category”: ”fun”},
{“category”: ”healthy”},
{“category”: [”gradeA”, “gradeB”]}
]}
f_vta_category=fun&f_vta_category_2=healthy&f_vta_category_3[]=gradeA&f_vta_category_3[]=gradeB
Combining with "any"
{"any": [
{“category”: ”fun”},
{“category”: ”healthy”}
]}
f_vto_category=fun&f_vto_category_1=healthy
None filters
{"none": [
{“category”: ”fun”},
{“category”: ”healthy”}
]}
f_vtn_category=fun&f_vtn_category_1=healthy
Currently, if you try to use a Facet on a Geo field, it will not work.
@goodroot Reported this while testing. This should be investigated to make sure it is not an issue.
‘Results’ is not exported from ‘@elastic/react-search-ui’.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.