У меня есть проект React с сервером GraphQL с использованием клиента Apollo. Я пытаюсь понять, как изменить результат запроса на основе текста поиска и выбора фильтра. Если бы кто-нибудь мог взглянуть на мой код и помочь мне, я был бы очень признателен. Извините за весь код, я подумал, может быть, все это может быть актуально.
сервер / index.js
const resolvers = {
Query: {
getClasses: () => classes,
searchClasses: () => classes
},
};
const server = new ApolloServer({ typeDefs, resolvers });
const app = express();
app.use(cors());
app.use(bodyParser.text({ type: 'application/graphql' }));
server.applyMiddleware({ app });
app.listen({ port: 4000 }, () =>
console.info(`? Server ready at http://localhost:4000${server.graphqlPath}`)
);
сервер / schema.js
const typeDefs = `
type Class {
entry_id: ID!
title: String
url: String
thumb: String
duration: [String]
style: [String]
intensity: [String]
anatomical_focus: [String]
level: [String]
teacher: [String]
authorId: Int
live_start: Int
live_end: Int
type: String
logged_in_member_id: Int
favorite: Boolean
favorite_order: Int
favorited_on: Int
body_snippet: String
}
input Filters {
teacher: String
duration: String
level: String
style: String
anatomy: String
}
type Query {
getClasses: [Class]!
searchClasses(search: String, filters: Filters): [Class]!
}
`;
module.exports = typeDefs;
клиент / SRC / index.js
const client = new ApolloClient({
uri: 'http://localhost:4000/graphql',
cache: new InMemoryCache()
});
ReactDOM.render(
<ApolloProvider client = {client}>
<Header />
<Router>
<Route exact path='/' component = {App}/>
</Router>
</ApolloProvider>,
document.getElementById('root')
);
serviceWorker.unregister();
запросы / поиск
const SEARCH_CLASSES = gql`
query SearchClasses($search: String, $filters: Filters) {
searchClasses(search: $search, filters: $filters) {
entry_id
title
url
thumb
duration
style
intensity
level
teacher
body_snippet
}
}
`;
клиент / SRC / страницы / Classes.js
class Classes extends Component {
constructor(props) {
super(props);
this.state = { keyword: '', filters: { teacher: '', duration: '', level: '', style: '', anatomy: '' } };
this.setSearch = this.setSearch.bind(this);
this.setFilters = this.setFilters.bind(this);
}
setSearch(e) {
this.setState({
keyword: e.target.value
});
}
setFilters(e) {
this.setState({
filters: e.target.value
})
}
componentDidMount() {
var ddToggle = document.querySelectorAll('.dropdown-toggle');
ddToggle.forEach(function (dd) {
dd.addEventListener("click", function(ev) {
dd.nextSibling.classList.toggle('show');
});
});
}
render() {
let text = this.state.keyword;
let teacher = this.state.filters.teacher;
let duration = this.state.filters.duration;
let level = this.state.filters.level;
let style = this.state.filters.style;
let anatomy = this.state.filters.anatomy;
return (
<Query
query = {Queries.Search}
variables = {{
search: text,
filters: {
teacher: '',
duration: '',
level: '',
style: '',
anatomy: ''
}
}}>
{({ loading, error, data }) => {
if (loading) return <p>Loading...</p>;
if (error) return <p>Error: ${error}</p>;
return (
<div>
<section className = "ygi-search">
<div className = "container px-3">
<div className = "ygi-search__wrapper">
<SearchBar handleSearch = {this.setSearch} search = { text } />
<button className = "ygi-search__filter-btn mx-auto mb-2 " aria-label = "Shows Filters">Show Filters</button>
<div className = "yi-dropdowns__wrapper-visibility col-lg-10 ">
<div className = "row">
<TeachersDD handleSelect = {this.setFilters} selection = { teacher } />
<DurationDD handleSelect = {this.setFilters} selection = { duration } />
<LevelDD handleSelect = {this.setFilters} selection = { level } />
<StyleDD handleSelect = {this.setFilters} selection = { style } />
<AnatomyDD handleSelect = {this.setFilters} selection = { anatomy } />
</div>
</div>
</div>
</div>
</section>
<div className = "flex">
{data.searchClasses.map(single => (
<Class class = {single} key = {single.entry_id} />
))}
</div>
</div>
)
}}
</Query>
);
}
}
export default Classes;
клиент / SRC / компоненты / Search.js
class SearchBar extends Component {
render() {
return (
<div className = "ygi-search-bar col col-12 col-lg-2">
<div className = "ygi-search-bar__wrapper mt-2">
<input onChange = {this.props.handleSearch} placeholder = "Search" value = {this.props.search} type = "text" className = "ygi-search-bar__input" />
<a href = "true" className = "icon-wrapper ygi-search-bar__icon-wrapper" style = {{ bottom: "calc(50%)" }}>
<svg role = "presentation" className = "icon-search-grey">
<use xlinkHref = "#icon-search-grey"></use>
</svg>
</a>
</div>
</div>
);
}
}
SearchBar.propTypes = {
handleSearch: PropTypes.func,
text: PropTypes.string
};
export default SearchBar;





Похоже, проблема связана с вашим распознавателем запроса searchClasses. Судя по коду в вашем вопросе, ваш преобразователь всегда будет возвращать одно и то же (classes):
const resolvers = {
Query: {
getClasses: () => classes,
searchClasses: () => classes // always returning all classes!
}
};
Вам необходимо обновить свой преобразователь, чтобы отфильтровать классы на основе переданной в search строки. Попробуйте что-то вроде этого (я не знаю, как выглядит структура данных вашего класса, поэтому я просто предполагаю здесь):
const resolvers = {
Query: {
getClasses: () => classes,
searchClasses: (obj, args, context, info) => classes.filter(
class => class.title === args.search
)
}
};
Ты спасатель, мужик! Большое спасибо! Я знал, что это как-то связано с резолвером, просто не знал, как это сделать.
Ха-ха нп! Рад помочь
Извините, но еще кое-что. Итак, я сделал преобразователь, как вы сказали, и он отлично работает для поля поиска для одного поля (т.е. заголовка). Как я могу сравнить несколько полей с args.search, чтобы поиск смотрел на несколько полей? Также будет ли это работать, если я хочу включить раскрывающиеся списки фильтров вместе с панелью поиска? Извините, на самом деле это две вещи.
@Leebs, вам нужно будет добавить эту логику в преобразователь. Например, используя ваш существующий запрос, ваш преобразователь args будет иметь два ключа: args.search и args.filters. Итак, в зависимости от того, как вы хотите, чтобы ваш поиск выполнялся, вам нужно будет записать эту логику в свой преобразователь (например, следует ли объединять поиск и фильтры с помощью И / ИЛИ?). В качестве примера, если вы хотите выполнить грубый поиск по поиску и И с точными совпадениями в фильтрах, вам нужно будет сделать что-то вроде этого внутри карты: class.title.includes (args.search) && (args.filters.teacher === class.teacher) && ..
Я также могу обновить ответ, чтобы привести более конкретный пример, если это будет полезно!
Огромное спасибо. Для всех, кому нужна помощь, решением для просмотра нескольких полей для поиска, а также раскрывающегося списка фильтров было classes.filter (class => (class.title.toLowerCase (). Includes (args.search) || class.description. toLowerCase (). включает (args.search)) && class.teach.toLowerCase () === args.teacher.toLowerCase () && ...). Обратите внимание на круглые скобки вокруг OR в поле поиска.
Пожалуйста, разместите свой код в вашем вопросе, а не во внешней ссылке