Moved slot filler search to server side, limited docs in subscription

This commit is contained in:
Stefan Zermatten
2021-02-11 22:09:43 +02:00
parent 8c477ad4b1
commit dcb535c84e
5 changed files with 123 additions and 49 deletions

View File

@@ -53,3 +53,4 @@ peerlibrary:reactive-publish
simple:rest simple:rest
simple:rest-method-mixin simple:rest-method-mixin
mikowals:batch-insert mikowals:batch-insert
peerlibrary:subscription-data

View File

@@ -95,11 +95,15 @@ ongoworks:speakingurl@9.0.0
ordered-dict@1.1.0 ordered-dict@1.1.0
patreon-oauth@0.1.0 patreon-oauth@0.1.0
peerlibrary:assert@0.3.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:extend-publish@0.6.0
peerlibrary:fiber-utils@0.10.0 peerlibrary:fiber-utils@0.10.0
peerlibrary:reactive-mongo@0.4.0 peerlibrary:reactive-mongo@0.4.0
peerlibrary:reactive-publish@0.10.0 peerlibrary:reactive-publish@0.10.0
peerlibrary:server-autorun@0.8.0 peerlibrary:server-autorun@0.8.0
peerlibrary:subscription-data@0.8.0
percolate:migrations@0.9.8 percolate:migrations@0.9.8
percolate:synced-cron@1.3.2 percolate:synced-cron@1.3.2
promise@0.11.2 promise@0.11.2

View File

@@ -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){ for (let key in propertySchemasIndex){
let schema = new SimpleSchema({}); let schema = new SimpleSchema({});
schema.extend(LibraryNodeSchema); schema.extend(LibraryNodeSchema);

View File

@@ -1,8 +1,10 @@
import { check } from 'meteor/check';
import Libraries from '/imports/api/library/Libraries.js'; import Libraries from '/imports/api/library/Libraries.js';
import LibraryNodes from '/imports/api/library/LibraryNodes.js'; import LibraryNodes from '/imports/api/library/LibraryNodes.js';
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js'; import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js';
Meteor.publish('slotFillers', function(slotId){ Meteor.publish('slotFillers', function(slotId){
let self = this;
this.autorun(function (){ this.autorun(function (){
let userId = this.userId; let userId = this.userId;
if (!userId) { if (!userId) {
@@ -46,6 +48,44 @@ Meteor.publish('slotFillers', function(slotId){
slotFillerType: slot.slotType, 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);
});
});
}); });
}); });

View File

@@ -13,7 +13,7 @@
regular regular
hide-details hide-details
:value="searchValue" :value="searchValue"
:debounce="200" :debounce="300"
@change="searchChanged" @change="searchChanged"
@keyup.enter="insert" @keyup.enter="insert"
/> />
@@ -22,49 +22,60 @@
class="library-nodes" class="library-nodes"
> >
<v-fade-transition mode="out-in"> <v-fade-transition mode="out-in">
<column-layout <div v-if="libraryNodes && libraryNodes.length">
v-if="$subReady.slotFillers && libraryNodes && libraryNodes.length" <column-layout
wide-columns wide-columns
>
<div
v-for="node in libraryNodes"
:key="node._id"
> >
<v-card <div
hover v-for="node in libraryNodes"
ripple :key="node._id"
class="slot-card"
:class="{'primary': node._id === (selectedNode && selectedNode._id)}"
:dark="node._id === (selectedNode && selectedNode._id)"
@click="selectedNode = node"
> >
<v-img <v-card
v-if="node.picture" hover
:src="node.picture" ripple
:height="200" class="slot-card"
contain :class="{'primary': node._id === (selectedNode && selectedNode._id)}"
/> :dark="node._id === (selectedNode && selectedNode._id)"
<v-card-title primary-title> @click="selectedNode = node"
<div> >
<h3 class="title mb-0"> <v-img
<tree-node-view v-if="node.picture"
class="mr-2" :src="node.picture"
:class="{'theme--dark': node._id === (selectedNode && selectedNode._id)}" :height="200"
:hide-icon="node.picture" contain
:model="node" />
:color="node.color" <v-card-title primary-title>
<div>
<h3 class="title mb-0">
<tree-node-view
class="mr-2"
:class="{'theme--dark': node._id === (selectedNode && selectedNode._id)}"
:hide-icon="node.picture"
:model="node"
:color="node.color"
/>
</h3>
<property-description
:string="model.description"
:calculations="model.descriptionCalculations"
:inactive="model.inactive"
/> />
</h3> </div>
<property-description </v-card-title>
:string="model.description" </v-card>
:calculations="model.descriptionCalculations" </div>
:inactive="model.inactive" </column-layout>
/> <div class="layout row justify-center">
</div> <v-btn
</v-card-title> v-if="currentLimit < countAll"
</v-card> :loading="!$subReady.slotFillers"
class="primary"
@click="loadMore"
>
Load More
</v-btn>
</div> </div>
</column-layout> </div>
<div <div
v-else-if="$subReady.slotFillers" v-else-if="$subReady.slotFillers"
class="ma-4" class="ma-4"
@@ -94,8 +105,10 @@
</span> </span>
</p> </p>
</div> </div>
</v-fade-transition>
<v-fade-transition mode="out-in">
<div <div
v-else v-if="!$subReady.slotFillers"
key="character-loading" key="character-loading"
class="fill-height layout justify-center align-center" class="fill-height layout justify-center align-center"
> >
@@ -179,10 +192,16 @@ export default {
return prop && prop.name; return prop && prop.name;
}, },
searchChanged(val, ack){ searchChanged(val, ack){
this._subs['slotFillers'].setData('searchTerm', val);
this._subs['slotFillers'].setData('limit', undefined);
this.selectedNode = undefined; this.selectedNode = undefined;
this.searchValue = val; this.searchValue = val;
setTimeout(ack, 200); setTimeout(ack, 200);
}, },
loadMore(){
if (this.currentLimit >= this.countAll) return;
this._subs['slotFillers'].setData('limit', this.currentLimit + 16);
},
insert(){ insert(){
if (!this.selectedNode) return; if (!this.selectedNode) return;
this.$store.dispatch('popDialogStack', this.selectedNode); this.$store.dispatch('popDialogStack', this.selectedNode);
@@ -200,16 +219,16 @@ export default {
creature(){ creature(){
return Creatures.findOne(this.creatureId); return Creatures.findOne(this.creatureId);
}, },
currentLimit(){
return this._subs['slotFillers'].data('limit') || 16;
},
countAll(){
return this._subs['slotFillers'].data('countAll');
},
libraryNodes(){ libraryNodes(){
let filter = { let filter = {
removed: {$ne: true}, removed: {$ne: true},
}; };
if (this.searchValue){
filter.name = {
$regex: this.searchValue.replace(/[-/\\^$*+?.()|[\]{}]/g, '\\$&'),
$options: 'i'
};
}
if (this.model.slotTags && this.model.slotTags.length){ if (this.model.slotTags && this.model.slotTags.length){
filter.tags = {$all: this.model.slotTags}; filter.tags = {$all: this.model.slotTags};
} }
@@ -221,7 +240,9 @@ export default {
slotFillerType: this.model.slotType, 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 // Filter out slotFillers whose condition isn't met or are too big to fit
// the quantity to fill // the quantity to fill
nodes = nodes.filter(node => { nodes = nodes.filter(node => {