From dcb535c84e6c4a2a64e4f9e3a73e8b0998ee4312 Mon Sep 17 00:00:00 2001 From: Stefan Zermatten Date: Thu, 11 Feb 2021 22:09:43 +0200 Subject: [PATCH] Moved slot filler search to server side, limited docs in subscription --- app/.meteor/packages | 1 + app/.meteor/versions | 4 + app/imports/api/library/LibraryNodes.js | 8 ++ .../server/publications/slotFillers.js | 42 ++++++- .../ui/creature/slots/SlotFillDialog.vue | 117 +++++++++++------- 5 files changed, 123 insertions(+), 49 deletions(-) diff --git a/app/.meteor/packages b/app/.meteor/packages index 729ce9c2..6d49b5bc 100644 --- a/app/.meteor/packages +++ b/app/.meteor/packages @@ -53,3 +53,4 @@ peerlibrary:reactive-publish simple:rest simple:rest-method-mixin mikowals:batch-insert +peerlibrary:subscription-data diff --git a/app/.meteor/versions b/app/.meteor/versions index eef98fe2..82d6fa55 100644 --- a/app/.meteor/versions +++ b/app/.meteor/versions @@ -95,11 +95,15 @@ ongoworks:speakingurl@9.0.0 ordered-dict@1.1.0 patreon-oauth@0.1.0 peerlibrary:assert@0.3.0 +peerlibrary:check-extension@0.7.0 +peerlibrary:computed-field@0.10.0 +peerlibrary:data-lookup@0.3.0 peerlibrary:extend-publish@0.6.0 peerlibrary:fiber-utils@0.10.0 peerlibrary:reactive-mongo@0.4.0 peerlibrary:reactive-publish@0.10.0 peerlibrary:server-autorun@0.8.0 +peerlibrary:subscription-data@0.8.0 percolate:migrations@0.9.8 percolate:synced-cron@1.3.2 promise@0.11.2 diff --git a/app/imports/api/library/LibraryNodes.js b/app/imports/api/library/LibraryNodes.js index d7ae17eb..a62c3c0e 100644 --- a/app/imports/api/library/LibraryNodes.js +++ b/app/imports/api/library/LibraryNodes.js @@ -32,6 +32,14 @@ let LibraryNodeSchema = new SimpleSchema({ } }); +// Set up server side search index +if (Meteor.isServer) { + LibraryNodes._ensureIndex({ + 'name': 'text', + 'tags': 'text', + }); +} + for (let key in propertySchemasIndex){ let schema = new SimpleSchema({}); schema.extend(LibraryNodeSchema); diff --git a/app/imports/server/publications/slotFillers.js b/app/imports/server/publications/slotFillers.js index bb91d2f3..7457bb4d 100644 --- a/app/imports/server/publications/slotFillers.js +++ b/app/imports/server/publications/slotFillers.js @@ -1,8 +1,10 @@ +import { check } from 'meteor/check'; import Libraries from '/imports/api/library/Libraries.js'; import LibraryNodes from '/imports/api/library/LibraryNodes.js'; import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js'; Meteor.publish('slotFillers', function(slotId){ + let self = this; this.autorun(function (){ let userId = this.userId; if (!userId) { @@ -46,6 +48,44 @@ Meteor.publish('slotFillers', function(slotId){ slotFillerType: slot.slotType, }]; } - return LibraryNodes.find(filter); + this.autorun(function(){ + // Get the limit of the documents the user can fetch + var limit = self.data('limit') || 16; + check(limit, Number); + + // Get the search term + let searchTerm = self.data('searchTerm') || ''; + check(searchTerm, String); + + let options = undefined; + if (searchTerm){ + filter.$text = {$search: searchTerm}; + options = { + // relevant documents have a higher score. + fields: { + score: { $meta: 'textScore' } + }, + sort: { + // `score` property specified in the projection fields above. + score: { $meta: 'textScore' }, + name: 1, + order: 1, + } + } + } else { + options = {sort: { + name: 1, + order: 1, + }}; + } + options.limit = limit; + + self.autorun(function () { + self.setData('countAll', LibraryNodes.find(filter).count()); + }); + self.autorun(function () { + return LibraryNodes.find(filter, options); + }); + }); }); }); diff --git a/app/imports/ui/creature/slots/SlotFillDialog.vue b/app/imports/ui/creature/slots/SlotFillDialog.vue index b99e24c9..0bac9546 100644 --- a/app/imports/ui/creature/slots/SlotFillDialog.vue +++ b/app/imports/ui/creature/slots/SlotFillDialog.vue @@ -13,7 +13,7 @@ regular hide-details :value="searchValue" - :debounce="200" + :debounce="300" @change="searchChanged" @keyup.enter="insert" /> @@ -22,49 +22,60 @@ class="library-nodes" > - -
+ - - - -
-

- + + +
+

+ +

+ -

- -
-
-
+
+ + + +
+
+ + Load More +
- +

+
+
@@ -179,10 +192,16 @@ export default { return prop && prop.name; }, searchChanged(val, ack){ + this._subs['slotFillers'].setData('searchTerm', val); + this._subs['slotFillers'].setData('limit', undefined); this.selectedNode = undefined; this.searchValue = val; setTimeout(ack, 200); }, + loadMore(){ + if (this.currentLimit >= this.countAll) return; + this._subs['slotFillers'].setData('limit', this.currentLimit + 16); + }, insert(){ if (!this.selectedNode) return; this.$store.dispatch('popDialogStack', this.selectedNode); @@ -200,16 +219,16 @@ export default { creature(){ return Creatures.findOne(this.creatureId); }, + currentLimit(){ + return this._subs['slotFillers'].data('limit') || 16; + }, + countAll(){ + return this._subs['slotFillers'].data('countAll'); + }, libraryNodes(){ let filter = { removed: {$ne: true}, }; - if (this.searchValue){ - filter.name = { - $regex: this.searchValue.replace(/[-/\\^$*+?.()|[\]{}]/g, '\\$&'), - $options: 'i' - }; - } if (this.model.slotTags && this.model.slotTags.length){ filter.tags = {$all: this.model.slotTags}; } @@ -221,7 +240,9 @@ export default { slotFillerType: this.model.slotType, }]; } - let nodes = LibraryNodes.find(filter).fetch(); + let nodes = LibraryNodes.find(filter, { + sort: {name: 1, order: 1} + }).fetch(); // Filter out slotFillers whose condition isn't met or are too big to fit // the quantity to fill nodes = nodes.filter(node => {