diff --git a/api/resolvers/item.js b/api/resolvers/item.js index 115f7c4a..5d788735 100644 --- a/api/resolvers/item.js +++ b/api/resolvers/item.js @@ -274,6 +274,64 @@ export default { }, comments: async (parent, { id, sort }, { models }) => { return comments(models, id, sort) + }, + search: async (parent, { query, cursor }, { models, search }) => { + const decodedCursor = decodeCursor(cursor) + + const sitems = await search.search({ + index: 'item', + size: LIMIT, + from: decodedCursor.offset, + body: { + query: { + bool: { + must: { + bool: { + should: [ + { + // all terms are matched in fields + multi_match: { + query, + type: 'most_fields', + fields: ['title^20', 'text'], + fuzziness: 'AUTO', + prefix_length: 3, + minimum_should_match: '100%', + boost: 2 + } + }, + { + // only some terms must match + multi_match: { + query, + type: 'most_fields', + fields: ['title^20', 'text'], + fuzziness: 'AUTO', + minimum_should_match: '60%' + } + } + // TODO: add wildcard matches for + // user.name and url + ] + } + }, + filter: { + range: { + createdAt: { + lte: decodedCursor.time + } + } + } + } + } + } + }) + + const items = sitems.body.hits.hits.map(e => e._source) + return { + cursor: items.length === LIMIT ? nextCursorEncoded(decodedCursor) : null, + items + } } }, diff --git a/api/search/index.js b/api/search/index.js index 5f4474ae..4ef38d77 100644 --- a/api/search/index.js +++ b/api/search/index.js @@ -1,5 +1,5 @@ -import es from '@elastic/elasticsearch' +import es from '@opensearch-project/opensearch' -global.es ||= new es.Client() +global.es ||= new es.Client({ node: 'http://localhost:9200' }) export default global.es diff --git a/api/ssrApollo.js b/api/ssrApollo.js index 6817bd7a..d66ca4c9 100644 --- a/api/ssrApollo.js +++ b/api/ssrApollo.js @@ -7,6 +7,7 @@ import typeDefs from './typeDefs' import models from './models' import { print } from 'graphql' import lnd from './lnd' +import search from './search' import { ME } from '../fragments/users' import { getPrice } from '../components/price' @@ -22,7 +23,8 @@ export default async function getSSRApolloClient (req, me = null) { context: { models, me: session ? session.user : me, - lnd + lnd, + search } }), cache: new InMemoryCache() diff --git a/api/typeDefs/item.js b/api/typeDefs/item.js index dbbd2787..945fe50c 100644 --- a/api/typeDefs/item.js +++ b/api/typeDefs/item.js @@ -9,6 +9,7 @@ export default gql` pageTitle(url: String!): String dupes(url: String!): [Item!] allItems(cursor: String): Items + search(query: String, cursor: String): Items } type ItemActResult { diff --git a/pages/api/graphql.js b/pages/api/graphql.js index 285889e8..2bc14047 100644 --- a/pages/api/graphql.js +++ b/pages/api/graphql.js @@ -4,6 +4,7 @@ import models from '../../api/models' import lnd from '../../api/lnd' import typeDefs from '../../api/typeDefs' import { getSession } from 'next-auth/client' +import search from '../../api/search' global.apolloServer ||= new ApolloServer({ typeDefs, @@ -14,7 +15,8 @@ global.apolloServer ||= new ApolloServer({ return { models, lnd, - me: session ? session.user : null + me: session ? session.user : null, + search } } })