如何轻松实现选择器:搜索 Meteor,使用 React 而不是 Blaze [英] How to implement a selector in easy: search for Meteor, using React instead of Blaze

查看:45
本文介绍了如何轻松实现选择器:搜索 Meteor,使用 React 而不是 Blaze的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I'm trying to follow the documentation and examples to add a server-side selector to a search function in my Meteor app, implemented using the Easy Search plugin. The end goal is to ensure that only documents the user has permission to see are returned by searching.

我可以在 排行榜 示例中看到一个选择器,但我不能让它在我的代码中工作.

I can see a selector working in the Leaderboard example, but I can't get it to work in my code.

版本:

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

我修改了 Meteor 'todos' 示例应用 来演示问题,并且 我的演示代码在仓库中.

I modified the Meteor 'todos' example app to demonstrate the problem, and my demo code is in a repo.

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

NOTE! to demonstrate the problem, you need to create an account in the demo app, then create a list and make it private. This add the 'userId' field to the list.

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

Then you can search for the name of the list, by typing in the search box near the top of the main section; search results are written to the browser console.

第一个问题是,如果我从 示例中复制代码文档,我看到服务器错误searchObject 未定义:

The first problem is that if I copy the code from the example in the documentation, I see a server error 'searchObject is not defined:

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

copied from docs, causes an error: 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 引擎,一切都会完美运行.

Working instead from the leaderboard example, the code below runs but intermittently returns no results. For example if I have a list called "My list", and I type the search term 's', sometimes the list is returned from the search and sometimes it is not. If I use the MiniMongo engine it all works perfectly.

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

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

client and server: 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

client: 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

client: imports/ui/components/lists-show.html

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

我认为问题在于 Minimongo 引擎在客户端上运行,而 MongoDBEngine 在服务器上运行,结果存在计时问题.文档显示使用 Tracker.autorun,但这并不适合我的 React/Redux 应用程序.如果我设法弄明白了,我会发布一个答案 - 我不可能是唯一一个试图做这样的事情的人.

I think the problem is that while the Minimongo engine runs on the client, the MongoDBEngine runs on the server and there are timing issues with the results. The docs show using Tracker.autorun, but that's not a natural fit with my React / Redux app. I'll post an answer if I manage to figure something out - I can't be the only person trying to do something like this.

推荐答案

我让它在我的 React/Redux/Meteor 应用中工作.注意事项:

I got it working in my React / Redux / Meteor app. Things to note:

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

  1. The cursor MyIndex.search(searchTerm) is a reactive data source - you can't just use it as a return value. When searching on the client with MiniMongo this isn't an issue, but it's important when you use MongoDBEngine to search on the server, because it's asynchronous. In React you can wrap the cursor in withTracker to pass data to the component reactively. In Blaze you would use autorun.tracker. This is shown in the docs but not explained, and it took me a while to understand what was happening.

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

The docs have an error in the selector example, easily corrected but it's confusing if you have other problems in your code.

对于 MongoDBEngine,必须指定 'permission' - 它不默认为 'true'.没有它,您将看不到任何结果.

With MongoDBEngine, 'permission' must be specified - it does not default to 'true'. Without it, you will see no results.

将默认选择器对象写出到控制台让我看看它是如何构造的,然后创建一个新的选择器,返回公共或用户创建的 MyDocs.

Writing out the default selector object to the console let me see how it's constructed, and then create a new selector that returns MyDocs that are either public or created by the user.

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

My code is below. In case it helps anybody else, I've shown how to search on tags also, which are objects with a name property stored in a collection Tags. Each MyDoc has a 'tags' property which is an array of tag ids. The selector first searches the Tags collection to find tags whose name matches the search term, then selects docs in MyDocs with the ids of those tags in their doc.tags array.

可能有更好的方法来查找搜索词或构建标签搜索,但这是我可以做的.

There may be a better way to find the search term, or to structure the Tags search, but this is what I could get working.

在服务器和客户端:

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 组件:

React component in client only code:

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);

这篇关于如何轻松实现选择器:搜索 Meteor,使用 React 而不是 Blaze的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆