【问题标题】:How to implement a selector in easy: search for Meteor, using React instead of Blaze如何轻松实现选择器:搜索 Meteor,使用 React 而不是 Blaze
【发布时间】:2020-01-02 08:41:06
【问题描述】:

我正在尝试按照 documentation 和示例将服务器端选择器添加到我的 Meteor 应用程序中的搜索功能,使用 Easy Search 插件实现。最终目标是确保仅通过搜索返回用户有权查看的文档。

我可以在 Leaderboard 示例中看到一个选择器,但我无法让它在我的代码中工作。

版本:

Meteor 1.7.0.1
easy:search@2.2.1
easysearch:components@2.2.2
easysearch:core@2.2.2

我修改了 Meteor 'todos' example app 来演示问题,并修改了my demo code is in a repo

注意!为了演示问题,您需要在演示应用程序中创建一个帐户,然后创建一个列表并将其设为私有。这会将“userId”字段添加到列表中。

然后您可以通过在主要部分顶部附近的搜索框中键入来搜索列表的名称;搜索结果被写入浏览器控制台。

第一个问题是,如果我从example in the documentation 复制代码,我会看到服务器错误'searchObject is not defined:

从文档复制,导致错误:imports/api/lists/lists.js

export const MyIndex = new Index({
    'collection': Lists,
    'fields': ['name'],
    engine: new MongoDBEngine({
    selector(searchDefinition, options, aggregation) {
      // retrieve the default selector
      const selector = this.defaultConfiguration()
        .selector(searchObject, options, aggregation)

      // options.search.userId contains the userId of the logged in user
      selector.userId = options.search.userId

      return selector
    },
  }),
});

文档中似乎有错误。

使用排行榜示例,下面的代码运行但间歇性地不返回任何结果。例如,如果我有一个名为“我的列表”的列表,并且我输入搜索词“s”,则有时该列表会从搜索中返回,有时则不是。如果我使用 MiniMongo 引擎,一切都会完美运行。

index selector {"$or":[{"name":{"$regex":".*my.*","$options":"i"}}],"userId":"Wtrr5FRHhkKuAcrLZ"}

客户端和服务器:imports/api/lists/lists.js

export const MyIndex = new Index({
  'collection': Lists,
  'fields': ['name'],
  'engine': new MongoDBEngine({
    selector: function (searchObject, options, aggregation) {
      let selector = this.defaultConfiguration().selector(searchObject, options, aggregation);

      selector.userId = options.search.userId;
      console.log('index selector', JSON.stringify(selector));
      return selector;
    }
  }),
  permission: () => {
    return true;
  },
});

客户端:imports/ui/components/lists-show.js

Template.Lists_show.events({
'keyup #search'(event) {
    console.log('search for ', event.target.value);

    const cursor = MyIndex.search(event.target.value);
    console.log('count',cursor.count());
    console.log('results', cursor.fetch());
  },
});

客户端:imports/ui/components/lists-show.html

<input id="search" type="text" placeholder="search..." />

编辑:我认为问题在于,虽然 Minimongo 引擎在客户端上运行,但 MongoDBEngine 在服务器上运行,结果存在时间问题。文档显示使用 Tracker.autorun,但这不适合我的 React / Redux 应用程序。如果我想办法解决问题,我会发布答案 - 我不能是唯一一个试图做这样的事情的人。

【问题讨论】:

    标签: search meteor permissions


    【解决方案1】:

    我让它在我的 React / Redux / Meteor 应用程序中运行。注意事项:

    1. 光标 MyIndex.search(searchTerm) 是一个响应式数据源 - 您不能只将它用作返回值。使用 MiniMongo 在客户端搜索时这不是问题,但是当您使用 MongoDBEngine 在服务器上搜索时这很重要,因为它是异步的。在 React 中,您可以将光标包装在 withTracker 中,以反应性地将数据传递给组件。在 Blaze 中,您将使用 autorun.tracker。这在文档中显示但没有解释,我花了一段时间才明白发生了什么。

    2. 文档在选择器示例中有一个错误,很容易纠正,但如果您的代码中存在其他问题,则会令人困惑。

    3. 对于 MongoDBEngine,必须指定“权限” - 它不默认为“真”。没有它,您将看不到任何结果。

    4. 将默认选择器对象写到控制台让我看看它是如何构造的,然后创建一个新的选择器来返回 MyDocs,这些 MyDocs 要么是公共的,要么是由用户创建的。

    我的代码如下。如果它对其他人有帮助,我还展示了如何搜索标签,这些标签是具有存储在标签集合中的名称属性的对象。每个 MyDoc 都有一个 'tags' 属性,它是一个标签 ID 数组。选择器首先搜索 Tags 集合以查找名称与搜索词匹配的标签,然后在 MyDocs 中选择带有 doc.tags 数组中这些标签的 id 的文档。

    可能有更好的方法来查找搜索词或构建标签搜索,但这是我可以着手的工作。

    在服务器和客户端上:

    import { Index, MongoDBEngine } from 'meteor/easy:search';
    
    export const MyDocs = new Mongo.Collection('mydocs');
    export const Tags = new Mongo.Collection('tags');
    
    export const MyIndex = new Index({
        'collection': MyDocs,
        'fields': ['name'],
        'engine': new MongoDBEngine({
            'selector': function (searchObject, options, aggregation) {
                const selector = this.defaultConfiguration().selector(searchObject, options, aggregation);
    
                console.log('default selector', selector); // this searches on name only
    
                // find docs by tag as well as by name
                const searchTerm = searchObject.name;
                const matchingTags = Tags.find({ 'name': { '$regex': searchTerm } }).fetch();
                const matchingTagIds = matchingTags.map((tag) => tag._id);
                selector.$or.push({ 'tags': { '$in': matchingTagIds } });
    
                const newSelector = {
                    '$and': [
                        {
                            '$or': [
                                { 'isPublic': { '$eq': true } },
                                { 'createdBy': options.search.userId },
                            ],
                        },
                        {
                            '$or': selector.$or,
                        },
                    ],
                };
    
                return newSelector;
            },
            'fields': (searchObject, options) => ({
                '_id': 1,
                'createdBy': 1,
                'name': 1,
            }),
            'sort': () => ({ 'name': 1 }),
        }),
        'permission': () => true,
    });
    

    仅客户端代码中的 React 组件:

    import React from 'react';
    import { connect } from 'react-redux';
    import { withTracker } from 'meteor/react-meteor-data';
    import PropTypes from 'prop-types';
    import store from '../modules/store';
    import {
        getSearchTerm,
        searchStart,
    } from '../modules/search'; // contains Redux actions and partial store for search
    import { MyIndex } from '../../modules/collection';
    
    function Search(props) {
    // functional React component that contains the search box
    ...
    const onChange = (value) => {
        clearTimeout(global.searchTimeout);
    
        if (value.length >= 2) {
            // user has entered a search term
            // of at least 2 characters
            // wait until they stop typing
            global.searchTimeout = setTimeout(() => {
                dispatch(searchStart(value)); // Redux action which sets the searchTerm in Redux state
            }, 500);
        }
    };
    ...
    // the component returns html which calls onChange when the user types in the search input
    // and a list which displays the search results, accessed in props.searchResults
    }
    
    const Tracker = withTracker(({ dispatch }) => {
        // searchTerm is saved in Redux state.
        const state = store.getState();
        const searchTerm = getSearchTerm(state); // Redux function to get searchTerm out of Redux state
    
        let results = [];
    
        if (searchTerm) {
            const cursor = MyIndex.search(searchTerm); // search is a reactive data source
    
            results = cursor.fetch();
            console.log('*** cursor count', cursor.count());
    
        return {
            'searchResults': results,
        };
    })(Search);
    
    export default connect()(Tracker);
    

    【讨论】:

      猜你喜欢
      • 2018-08-17
      • 1970-01-01
      • 1970-01-01
      • 2016-05-04
      • 1970-01-01
      • 2014-05-31
      • 1970-01-01
      • 2011-01-30
      • 1970-01-01
      相关资源
      最近更新 更多