From b5db531234ffac077f383b94fd33257d8671da1e Mon Sep 17 00:00:00 2001 From: Nick Date: Sat, 9 Mar 2019 00:51:02 -0500 Subject: [PATCH] feat: search + basic engine (wip) --- client/client-app.js | 1 + client/components/admin/admin-contribute.vue | 10 ++ client/components/admin/admin-search.vue | 158 ++++++++-------- client/components/common/nav-header.vue | 59 +++++- client/components/common/search-results.vue | 169 ++++++++++++++++++ .../common/common-pages-query-search.gql | 15 ++ client/scss/global.scss | 24 +++ client/static/svg/icon-no-results.svg | 8 + .../svg/icon-selective-highlighting.svg | 40 +++++ client/store/site.js | 6 +- client/themes/default/components/page.vue | 1 + dev/docker-postgres/docker-compose.yml | 17 ++ package.json | 4 +- server/app/data.yml | 2 + server/core/kernel.js | 1 + server/graph/resolvers/page.js | 13 +- server/graph/schemas/page.graphql | 20 +++ server/models/searchEngines.js | 13 ++ server/modules/search/algolia/definition.yml | 18 +- server/modules/search/db/definition.yml | 4 +- server/modules/search/db/engine.js | 121 +++++++++++-- .../search/elasticsearch/definition.yml | 39 +++- server/modules/search/postgres/definition.yml | 30 ++++ server/modules/search/postgres/engine.js | 72 ++++++++ server/modules/search/solr/definition.yml | 29 ++- 25 files changed, 766 insertions(+), 108 deletions(-) create mode 100644 client/components/common/search-results.vue create mode 100644 client/graph/common/common-pages-query-search.gql create mode 100644 client/static/svg/icon-no-results.svg create mode 100644 client/static/svg/icon-selective-highlighting.svg create mode 100644 server/modules/search/postgres/definition.yml create mode 100644 server/modules/search/postgres/engine.js diff --git a/client/client-app.js b/client/client-app.js index d2b59038..a0cd3a84 100644 --- a/client/client-app.js +++ b/client/client-app.js @@ -168,6 +168,7 @@ Vue.component('page-selector', () => import(/* webpackPrefetch: true, webpackChu Vue.component('profile', () => import(/* webpackChunkName: "profile" */ './components/profile.vue')) Vue.component('register', () => import(/* webpackChunkName: "register" */ './components/register.vue')) Vue.component('v-card-chin', () => import(/* webpackPrefetch: true, webpackChunkName: "ui-extra" */ './components/common/v-card-chin.vue')) +Vue.component('search-results', () => import(/* webpackPrefetch: true, webpackChunkName: "ui-extra" */ './components/common/search-results.vue')) Vue.component('nav-footer', () => import(/* webpackChunkName: "theme-page" */ './themes/' + process.env.CURRENT_THEME + '/components/nav-footer.vue')) Vue.component('nav-sidebar', () => import(/* webpackChunkName: "theme-page" */ './themes/' + process.env.CURRENT_THEME + '/components/nav-sidebar.vue')) diff --git a/client/components/admin/admin-contribute.vue b/client/components/admin/admin-contribute.vue index 4861b466..aeeec3d3 100644 --- a/client/components/admin/admin-contribute.vue +++ b/client/components/admin/admin-contribute.vue @@ -151,6 +151,16 @@ v-list-tile-action v-btn(icon, href='https://lokalise.co', target='_blank') v-icon(color='grey') public + v-divider + v-list-tile + v-list-tile-avatar(tile) + img(src='https://static.requarks.io/logo/netlify.svg', alt='Netlify') + v-list-tile-content + v-list-tile-title Netlify + v-list-tile-sub-title Deploy modern static websites with Netlify. Get CDN, Continuous deployment, 1-click HTTPS, and all the services you need. + v-list-tile-action + v-btn(icon, href='https://wwwnetlify.com', target='_blank') + v-icon(color='grey') public diff --git a/client/components/admin/admin-search.vue b/client/components/admin/admin-search.vue index d0abe78c..33e63dd9 100644 --- a/client/components/admin/admin-search.vue +++ b/client/components/admin/admin-search.vue @@ -6,7 +6,7 @@ img(src='/svg/icon-search.svg', alt='Search Engine', style='width: 80px;') .admin-header-title .headline.primary--text Search Engine - .subheading.grey--text Configure the search capabilities of your wiki #[v-chip(label, color='primary', small).white--text coming soon] + .subheading.grey--text Configure the search capabilities of your wiki v-spacer v-btn(outline, color='grey', @click='refresh', large) v-icon refresh @@ -17,74 +17,69 @@ v-icon(left) check span {{$t('common:actions.apply')}} - v-card.mt-3 - v-tabs(color='grey darken-2', fixed-tabs, slider-color='white', show-arrows, dark) - v-tab(key='settings'): v-icon settings - v-tab(v-for='engine in activeEngines', :key='engine.key') {{ engine.title }} + v-flex(lg3, xs12) + v-card + v-toolbar(flat, color='primary', dark, dense) + .subheading Search Engine + v-card-text + v-radio-group.my-0(v-model='selectedEngine') + v-radio.my-1( + v-for='(engine, n) in engines' + :key='engine.key' + :label='engine.title' + :value='engine.key' + color='primary' + hide-details + ) - v-tab-item(key='settings', :transition='false', :reverse-transition='false') - v-card.pa-3(flat, tile) - .body-2.grey--text.text--darken-1 Select which search engine to enable: - .caption.grey--text.pb-2 Some search engines require additional configuration in their dedicated tab (when selected). - v-form - v-radio-group(v-model='selectedEngine') - v-radio.my-1( - v-for='(engine, n) in engines' - :key='engine.key' - :label='engine.title' - :value='engine.key' - color='primary' - hide-details - disabled - ) - - v-tab-item(v-for='(engine, n) in activeEngines', :key='engine.key', :transition='false', :reverse-transition='false') - v-card.pa-3(flat, tile) - v-form - .enginelogo - img(:src='engine.logo', :alt='engine.title') - v-subheader.pl-0 {{engine.title}} - .caption {{engine.description}} - .caption: a(:href='engine.website') {{engine.website}} - v-divider.mt-3 - v-subheader.pl-0 Engine Configuration - .body-1.ml-3(v-if='!engine.config || engine.config.length < 1') This engine has no configuration options you can modify. - template(v-else, v-for='cfg in logger.config') - v-select( - v-if='cfg.value.type === "string" && cfg.value.enum' - outline - background-color='grey lighten-2' - :items='cfg.value.enum' - :key='cfg.key' - :label='cfg.value.title' - v-model='cfg.value.value' - prepend-icon='settings_applications' - :hint='cfg.value.hint ? cfg.value.hint : ""' - persistent-hint - :class='cfg.value.hint ? "mb-2" : ""' - ) - v-switch( - v-else-if='cfg.value.type === "boolean"' - :key='cfg.key' - :label='cfg.value.title' - v-model='cfg.value.value' - color='primary' - prepend-icon='settings_applications' - :hint='cfg.value.hint ? cfg.value.hint : ""' - persistent-hint - ) - v-text-field( - v-else - outline - background-color='grey lighten-2' - :key='cfg.key' - :label='cfg.value.title' - v-model='cfg.value.value' - prepend-icon='settings_applications' - :hint='cfg.value.hint ? cfg.value.hint : ""' - persistent-hint - :class='cfg.value.hint ? "mb-2" : ""' - ) + v-flex(lg9, xs12) + v-card.wiki-form + v-toolbar(color='primary', dense, flat, dark) + .subheading {{engine.title}} + v-card-text + .enginelogo + img(:src='engine.logo', :alt='engine.title') + .caption.pt-3 {{engine.description}} + .caption.pb-3: a(:href='engine.website') {{engine.website}} + v-divider.mt-3 + v-subheader.pl-0 Engine Configuration + .body-1.ml-3(v-if='!engine.config || engine.config.length < 1') This engine has no configuration options you can modify. + template(v-else, v-for='cfg in engine.config') + v-select( + v-if='cfg.value.type === "string" && cfg.value.enum' + outline + background-color='grey lighten-2' + :items='cfg.value.enum' + :key='cfg.key' + :label='cfg.value.title' + v-model='cfg.value.value' + prepend-icon='settings_applications' + :hint='cfg.value.hint ? cfg.value.hint : ""' + persistent-hint + :class='cfg.value.hint ? "mb-2" : ""' + ) + v-switch.mb-3( + v-else-if='cfg.value.type === "boolean"' + :key='cfg.key' + :label='cfg.value.title' + v-model='cfg.value.value' + color='primary' + prepend-icon='settings_applications' + :hint='cfg.value.hint ? cfg.value.hint : ""' + persistent-hint + ) + v-text-field( + v-else + outline + background-color='grey lighten-2' + :key='cfg.key' + :label='cfg.value.title' + v-model='cfg.value.value' + prepend-icon='settings_applications' + :hint='cfg.value.hint ? cfg.value.hint : ""' + persistent-hint + :class='cfg.value.hint ? "mb-2" : ""' + ) + + diff --git a/client/graph/common/common-pages-query-search.gql b/client/graph/common/common-pages-query-search.gql new file mode 100644 index 00000000..e36445b3 --- /dev/null +++ b/client/graph/common/common-pages-query-search.gql @@ -0,0 +1,15 @@ +query ($query: String!) { + pages { + search(query:$query) { + results { + id + title + description + path + locale + } + suggestions + totalHits + } + } +} diff --git a/client/scss/global.scss b/client/scss/global.scss index 5bb1cd7e..72a04902 100644 --- a/client/scss/global.scss +++ b/client/scss/global.scss @@ -6,3 +6,27 @@ $tablet: 769px !default; $desktop: 980px !default; $widescreen: 1180px !default; + +$grid-breakpoints: ( + 'xs': 0, + 'sm': 600px, + 'md': 960px, + 'lg': 1280px - 16px, + 'xl': 1920px - 16px +) !default; + +$display-breakpoints: ( + 'print-only': 'only print', + 'screen-only': 'only screen', + 'xs-only': 'only screen and (max-width: #{map-get($grid-breakpoints, 'sm') - 1})', + 'sm-only': 'only screen and (min-width: #{map-get($grid-breakpoints, 'sm')}) and (max-width: #{map-get($grid-breakpoints, 'md') - 1})', + 'sm-and-down': 'only screen and (max-width: #{map-get($grid-breakpoints, 'md') - 1})', + 'sm-and-up': 'only screen and (min-width: #{map-get($grid-breakpoints, 'sm')})', + 'md-only': 'only screen and (min-width: #{map-get($grid-breakpoints, 'md')}) and (max-width: #{map-get($grid-breakpoints, 'lg') - 1})', + 'md-and-down': 'only screen and (max-width: #{map-get($grid-breakpoints, 'lg') - 1})', + 'md-and-up': 'only screen and (min-width: #{map-get($grid-breakpoints, 'md')})', + 'lg-only': 'only screen and (min-width: #{map-get($grid-breakpoints, 'lg')}) and (max-width: #{map-get($grid-breakpoints, 'xl') - 1})', + 'lg-and-down': 'only screen and (max-width: #{map-get($grid-breakpoints, 'xl') - 1})', + 'lg-and-up': 'only screen and (min-width: #{map-get($grid-breakpoints, 'lg')})', + 'xl-only': 'only screen and (min-width: #{map-get($grid-breakpoints, 'xl')})' +) !default; diff --git a/client/static/svg/icon-no-results.svg b/client/static/svg/icon-no-results.svg new file mode 100644 index 00000000..2d1d0462 --- /dev/null +++ b/client/static/svg/icon-no-results.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/client/static/svg/icon-selective-highlighting.svg b/client/static/svg/icon-selective-highlighting.svg new file mode 100644 index 00000000..232d157d --- /dev/null +++ b/client/static/svg/icon-selective-highlighting.svg @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/client/store/site.js b/client/store/site.js index 72813eda..68035a93 100644 --- a/client/store/site.js +++ b/client/store/site.js @@ -6,7 +6,11 @@ const state = { company: '', dark: siteConfig.darkMode, mascot: true, - title: siteConfig.title + title: siteConfig.title, + search: '', + searchIsLoading: false, + searchRestrictLocale: false, + searchRestrictPath: false } export default { diff --git a/client/themes/default/components/page.vue b/client/themes/default/components/page.vue index ab78fc25..a8a29733 100644 --- a/client/themes/default/components/page.vue +++ b/client/themes/default/components/page.vue @@ -117,6 +117,7 @@ span Print Format v-spacer nav-footer + search-results v-fab-transition v-btn(v-if='upBtnShown', fab, fixed, bottom, right, small, @click='$vuetify.goTo(0, scrollOpts)', color='primary') v-icon arrow_upward diff --git a/dev/docker-postgres/docker-compose.yml b/dev/docker-postgres/docker-compose.yml index 06383e06..414b5d2c 100644 --- a/dev/docker-postgres/docker-compose.yml +++ b/dev/docker-postgres/docker-compose.yml @@ -27,12 +27,28 @@ services: ports: - "3001:8080" + solr: + image: solr:7-alpine + logging: + driver: "none" + networks: + - wikinet + ports: + - "8983:8983" + volumes: + - solr-data:/opt/solr/server/solr/mycores + entrypoint: + - docker-entrypoint.sh + - solr-precreate + - wiki + wiki: build: context: . dockerfile: dev/docker-postgres/Dockerfile depends_on: - db + - solr networks: - wikinet ports: @@ -47,3 +63,4 @@ networks: volumes: db-data: + solr-data: diff --git a/package.json b/package.json index 604b0a8b..28ff2b62 100644 --- a/package.json +++ b/package.json @@ -59,7 +59,8 @@ "dependency-graph": "0.8.0", "diff": "4.0.1", "diff2html": "2.7.0", - "dotize": "^0.3.0", + "dotize": "0.3.0", + "elasticsearch": "15.4.1", "express": "4.16.4", "express-brute": "1.0.1", "file-type": "10.7.1", @@ -146,6 +147,7 @@ "semver": "5.6.0", "serve-favicon": "2.5.0", "simple-git": "1.107.0", + "solr-node": "1.1.3", "sqlite3": "4.0.6", "subscriptions-transport-ws": "0.9.15", "twemoji": "11.3.0", diff --git a/server/app/data.yml b/server/app/data.yml index 59104295..c76d2663 100644 --- a/server/app/data.yml +++ b/server/app/data.yml @@ -45,6 +45,8 @@ defaults: maxAge: 600 methods: 'GET,POST' origin: true + search: + maxHits: 100 localeNamespaces: - admin - auth diff --git a/server/core/kernel.js b/server/core/kernel.js index 6926fa7b..8fb1b250 100644 --- a/server/core/kernel.js +++ b/server/core/kernel.js @@ -69,6 +69,7 @@ module.exports = { await WIKI.models.storage.refreshTargetsFromDisk() await WIKI.auth.activateStrategies() + await WIKI.models.searchEngines.initEngine() await WIKI.models.storage.initTargets() WIKI.scheduler.start() }, diff --git a/server/graph/resolvers/page.js b/server/graph/resolvers/page.js index 00413a90..df79cb3f 100644 --- a/server/graph/resolvers/page.js +++ b/server/graph/resolvers/page.js @@ -16,7 +16,18 @@ module.exports = { offsetPage: args.offsetPage || 0, offsetSize: args.offsetSize || 100 }) - } + }, + async search (obj, args, context) { + if (WIKI.data.searchEngine) { + return WIKI.data.searchEngine.query(args.query, args) + } else { + return { + results: [], + suggestions: [], + totalHits: 0 + } + } + }, }, PageMutation: { async create(obj, args, context) { diff --git a/server/graph/schemas/page.graphql b/server/graph/schemas/page.graphql index b9c9babe..1e16d4c3 100644 --- a/server/graph/schemas/page.graphql +++ b/server/graph/schemas/page.graphql @@ -20,6 +20,12 @@ type PageQuery { offsetPage: Int offsetSize: Int ): PageHistoryResult @auth(requires: ["manage:system", "read:pages"]) + + search( + query: String! + path: String + locale: String + ): PageSearchResponse! @auth(requires: ["manage:system", "read:pages"]) } # ----------------------------------------------- @@ -88,3 +94,17 @@ type PageHistoryResult { trail: [PageHistory] total: Int! } + +type PageSearchResponse { + results: [PageSearchResult]! + suggestions: [String]! + totalHits: Int! +} + +type PageSearchResult { + id: Int! + title: String! + description: String! + path: String! + locale: String! +} diff --git a/server/models/searchEngines.js b/server/models/searchEngines.js index e827c998..44d63aa9 100644 --- a/server/models/searchEngines.js +++ b/server/models/searchEngines.js @@ -95,6 +95,19 @@ module.exports = class SearchEngine extends Model { } } + static async initEngine() { + const searchEngine = await WIKI.models.searchEngines.query().findOne('isEnabled', true) + if (searchEngine) { + WIKI.data.searchEngine = require(`../modules/search/${searchEngine.key}/engine`) + WIKI.data.searchEngine.config = searchEngine.config + try { + await WIKI.data.searchEngine.init() + } catch (err) { + WIKI.logger.warn(err) + } + } + } + static async pageEvent({ event, page }) { const searchEngines = await WIKI.models.storage.query().where('isEnabled', true) if (searchEngines && searchEngines.length > 0) { diff --git a/server/modules/search/algolia/definition.yml b/server/modules/search/algolia/definition.yml index 0d92a779..50cbb501 100644 --- a/server/modules/search/algolia/definition.yml +++ b/server/modules/search/algolia/definition.yml @@ -4,4 +4,20 @@ description: Algolia is a powerful search-as-a-service solution, made easy to us author: requarks.io logo: https://static.requarks.io/logo/algolia.svg website: https://www.algolia.com/ -props: {} +props: + appId: + type: String + title: App ID + hint: Your Algolia Application ID, found under API Keys + order: 1 + apiKey: + type: String + title: Admin API Key + hint: Your Algolia Admin API Key, found under API Keys. + order: 2 + indexName: + type: String + title: Index Name + hint: The name of the index you created under Indices. + default: wiki + order: 3 diff --git a/server/modules/search/db/definition.yml b/server/modules/search/db/definition.yml index 05e5e75e..56a63196 100644 --- a/server/modules/search/db/definition.yml +++ b/server/modules/search/db/definition.yml @@ -1,6 +1,6 @@ key: db -title: Database (built-in) -description: Default database-based search engine. +title: Database - Basic +description: Default basic database-based search engine. author: requarks.io logo: https://static.requarks.io/logo/database.svg website: https://www.requarks.io/ diff --git a/server/modules/search/db/engine.js b/server/modules/search/db/engine.js index e7369ccd..1721a3bd 100644 --- a/server/modules/search/db/engine.js +++ b/server/modules/search/db/engine.js @@ -1,26 +1,121 @@ +const _ = require('lodash') + module.exports = { activate() { - + // not used }, deactivate() { - + // not used }, - query() { - + /** + * INIT + */ + init() { + // not used }, - created() { - + /** + * SUGGEST + * + * @param {String} q Query + * @param {Object} opts Additional options + */ + async suggest(q, opts) { + const results = await WIKI.models.pages.query() + .column('title') + .where(builder => { + builder.where('isPublished', true) + if (opts.locale) { + builder.andWhere('locale', opts.locale) + } + if (opts.path) { + builder.andWhere('path', 'like', `${opts.path}%`) + } + builder.andWhere('title', 'like', `%${q}%`) + }) + .limit(10) + return _.uniq(_.filter(_.flatten(results.map(r => r.title.split(' '))), w => w.indexOf(q) >= 0)) }, - updated() { + /** + * QUERY + * + * @param {String} q Query + * @param {Object} opts Additional options + */ + async query(q, opts) { + const results = await WIKI.models.pages.query() + .column('id', 'title', 'description', 'path', 'localeCode as locale') + .where(builder => { + builder.where('isPublished', true) + if (opts.locale) { + builder.andWhere('localeCode', opts.locale) + } + if (opts.path) { + builder.andWhere('path', 'like', `${opts.path}%`) + } + // TODO: Add user permissions filtering + builder.andWhere(builder => { + switch(WIKI.config.db.type) { + case 'postgres': + builder.where('title', 'ILIKE', `%${q}%`) + builder.orWhere('description', 'ILIKE', `%${q}%`) + break + case 'mysql': + case 'mariadb': + builder.whereRaw(`title LIKE '%?%' COLLATE utf8_general_ci`, [q]) + builder.orWhereRaw(`description LIKE '%?%' COLLATE utf8_general_ci`, [q]) + break + // TODO: MSSQL handling + default: + builder.where('title', 'LIKE', `%${q}%`) + builder.orWhere('description', 'LIKE', `%${q}%`) + break + } + }) + }) + .limit(WIKI.config.search.maxHits) + return { + results, + suggestions: [], + totalHits: results.length + } }, - deleted() { - + /** + * CREATE + * + * @param {Object} page Page to create + */ + async created(page) { + // not used }, - renamed() { - + /** + * UPDATE + * + * @param {Object} page Page to update + */ + async updated(page) { + // not used }, - rebuild() { - + /** + * DELETE + * + * @param {Object} page Page to delete + */ + async deleted(page) { + // not used + }, + /** + * RENAME + * + * @param {Object} page Page to rename + */ + async renamed(page) { + // not used + }, + /** + * REBUILD INDEX + */ + async rebuild() { + // not used } } diff --git a/server/modules/search/elasticsearch/definition.yml b/server/modules/search/elasticsearch/definition.yml index 3f559933..a10b066f 100644 --- a/server/modules/search/elasticsearch/definition.yml +++ b/server/modules/search/elasticsearch/definition.yml @@ -4,4 +4,41 @@ description: Elasticsearch is a distributed, RESTful search and analytics engine author: requarks.io logo: https://static.requarks.io/logo/elasticsearch.svg website: https://www.elastic.co/products/elasticsearch -props: {} +props: + apiVersion: + type: String + title: API Version + hint: Should match the version of the Elasticsearch nodes you are connecting to + order: 1 + enum: + - '6.6' + - '6.5' + - '6.4' + - '6.3' + default: '6.6' + host: + type: String + title: Host(s) + hint: Comma-separated list of Elasticsearch hosts to connect to + order: 2 + user: + type: String + title: Username + order: 3 + pass: + type: String + title: Password + order: 4 + sniff: + type: Boolean + title: Sniff on start + hint: 'Should Wiki.js attempt to detect the rest of the cluster on first connect? (Default: off)' + default: false + order: 5 + sniffInterval: + type: Number + title: Sniff Interval + hint: '0 = disabled, Interval in seconds to check for updated list of nodes in cluster. (Default: 0)' + order: 6 + default: 0 + diff --git a/server/modules/search/postgres/definition.yml b/server/modules/search/postgres/definition.yml new file mode 100644 index 00000000..085eabc9 --- /dev/null +++ b/server/modules/search/postgres/definition.yml @@ -0,0 +1,30 @@ +key: postgres +title: Database - PostgreSQL +description: Advanced PostgreSQL-based search engine. +author: requarks.io +logo: https://static.requarks.io/logo/postgresql.svg +website: https://www.requarks.io/ +props: + dictLanguage: + type: String + title: Dictionnary Language + hint: Language to use when creating and querying text search vectors. + default: english + enum: + - simple + - danish + - dutch + - english + - finnish + - french + - german + - hungarian + - italian + - norwegian + - portuguese + - romanian + - russian + - spanish + - swedish + - turkish + order: 1 diff --git a/server/modules/search/postgres/engine.js b/server/modules/search/postgres/engine.js new file mode 100644 index 00000000..d3178be5 --- /dev/null +++ b/server/modules/search/postgres/engine.js @@ -0,0 +1,72 @@ +const _ = require('lodash') + +module.exports = { + activate() { + // not used + }, + deactivate() { + // not used + }, + /** + * INIT + */ + init() { + // not used + }, + /** + * SUGGEST + * + * @param {String} q Query + * @param {Object} opts Additional options + */ + async suggest(q, opts) { + + }, + /** + * QUERY + * + * @param {String} q Query + * @param {Object} opts Additional options + */ + async query(q, opts) { + + }, + /** + * CREATE + * + * @param {Object} page Page to create + */ + async created(page) { + // not used + }, + /** + * UPDATE + * + * @param {Object} page Page to update + */ + async updated(page) { + // not used + }, + /** + * DELETE + * + * @param {Object} page Page to delete + */ + async deleted(page) { + // not used + }, + /** + * RENAME + * + * @param {Object} page Page to rename + */ + async renamed(page) { + // not used + }, + /** + * REBUILD INDEX + */ + async rebuild() { + // not used + } +} diff --git a/server/modules/search/solr/definition.yml b/server/modules/search/solr/definition.yml index 1a5216f7..299fdc85 100644 --- a/server/modules/search/solr/definition.yml +++ b/server/modules/search/solr/definition.yml @@ -4,4 +4,31 @@ description: Solr is the popular, blazing-fast, open source enterprise search pl author: requarks.io logo: https://static.requarks.io/logo/solr.svg website: http://lucene.apache.org/solr/ -props: {} +props: + host: + type: String + title: Host + hint: Host of the Solr server (e.g. 12.34.56.78 or solr.example.com) + default: solr + order: 1 + port: + type: Number + title: Port + hint: Port of the Solr server + default: 8983 + order: 2 + core: + type: String + title: Core + hint: Core name (e.g. wiki) + default: wiki + order: 3 + protocol: + type: String + title: Protocol + hint: Request protocol + default: http + enum: + - http + - https + order: 4