콘솔로 이동

전체 텍스트 검색

대부분의 앱에서는 사용자가 앱 콘텐츠를 검색할 수 있습니다. 예를 들어 특정 단어가 포함된 게시물 또는 특정 주제에 대해 작성한 메모를 검색할 수 있습니다.

Cloud Firestore는 기본 색인생성 또는 문서의 텍스트 필드 검색을 지원하지 않습니다. 또한 클라이언트에서 필드를 검색하고자 전체 컬렉션을 다운로드하는 방법은 실용적이지 않습니다.

솔루션: Algolia

Cloud Firestore 데이터의 전체 텍스트 검색을 지원하려면 Algolia 등의 타사 검색 서비스를 사용하세요. 각 메모가 하나의 문서인 메모 작성 앱이 있다고 가정해 보겠습니다.

// /notes/${ID}
{
  owner: "{UID}", // Firebase Authentication's User ID of note owner
  text: "This is my first note!"
}

Cloud 함수와 함께 Algolia를 사용하여 각 메모의 내용으로 색인을 생성하고 검색을 지원할 수 있습니다. 먼저 App ID 및 API 키를 사용해 Algolia 클라이언트를 구성합니다.

Node.js

// Initialize Algolia, requires installing Algolia dependencies:
// https://www.algolia.com/doc/api-client/javascript/getting-started/#install
//
// App ID and API Key are stored in functions config variables
const ALGOLIA_ID = functions.config().algolia.app_id;
const ALGOLIA_ADMIN_KEY = functions.config().algolia.api_key;
const ALGOLIA_SEARCH_KEY = functions.config().algolia.search_key;

const ALGOLIA_INDEX_NAME = 'notes';
const client = algoliasearch(ALGOLIA_ID, ALGOLIA_ADMIN_KEY);

다음으로, 메모가 작성될 때마다 색인을 업데이트하는 함수를 추가합니다.

Node.js

// Update the search index every time a blog post is written.
exports.onNoteCreated = functions.firestore.document('notes/{noteId}').onCreate((snap, context) => {
  // Get the note document
  const note = snap.data();

  // Add an 'objectID' field which Algolia requires
  note.objectID = context.params.noteId;

  // Write to the algolia index
  const index = client.initIndex(ALGOLIA_INDEX_NAME);
  return index.saveObject(note);
});

데이터가 색인에 추가되면 iOS, Android, 웹용 Algolia 통합을 사용하여 데이터를 검색할 수 있습니다.

var client = algoliasearch(ALGOLIA_APP_ID, ALGOLIA_SEARCH_KEY);
var index = client.initIndex('notes');

// Perform an Algolia search:
// https://www.algolia.com/doc/api-reference/api-methods/search/
index
  .seach({
    query
  })
  .then(function(responses) {
    // Response from Algolia:
    // https://www.algolia.com/doc/api-reference/api-methods/search/#response-format
    console.log(responses.hits);
  });

보안 추가

모든 사용자가 모든 메모를 검색해도 무방하다면 위 솔루션에 문제가 없습니다. 그러나 일반적인 사용 사례에서는 보안을 적용하여 결과를 제한해야 합니다. 이러한 경우 보안 API 키를 생성하는 HTTP Cloud 함수를 추가하여 사용자가 수행하는 모든 쿼리에 특정 필터를 추가할 수 있습니다.

Node.js

// This complex HTTP function will be created as an ExpressJS app:
// https://expressjs.com/en/4x/api.html
const app = require('express')();

// We'll enable CORS support to allow the function to be invoked
// from our app client-side.
app.use(require('cors')({origin: true}));

// Then we'll also use a special 'getFirebaseUser' middleware which
// verifies the Authorization header and adds a `user` field to the
// incoming request:
// https://gist.github.com/abehaskins/832d6f8665454d0cd99ef08c229afb42
app.use(getFirebaseUser);

// Add a route handler to the app to generate the secured key
app.get('/', (req, res) => {
  // Create the params object as described in the Algolia documentation:
  // https://www.algolia.com/doc/guides/security/api-keys/#generating-api-keys
  const params = {
    // This filter ensures that only documents where author == user_id will be readable
    filters: `author:${req.user.user_id}`,
    // We also proxy the user_id as a unique token for this key.
    userToken: req.user.user_id,
  };

  // Call the Algolia API to generate a unique key based on our search key
  const key = client.generateSecuredApiKey(ALGOLIA_SEARCH_KEY, params);

  // Then return this key as {key: '...key'}
  res.json({key});
});

// Finally, pass our ExpressJS app to Cloud Functions as a function
// called 'getSearchKey';
exports.getSearchKey = functions.https.onRequest(app);

이 함수를 사용하여 Algolia 검색 키를 제공하면 사용자는 author 필드가 자신의 사용자 ID와 정확히 일치하는 메모 검색할 수 있습니다.

// Use Firebase Authentication to request the underlying token
return firebase.auth().currentUser.getIdToken()
  .then(function(token) {
    // The token is then passed to our getSearchKey Cloud Function
    return fetch('https://us-central1-' + PROJECT_ID + '.cloudfunctions.net/getSearchKey/', {
        headers: { Authorization: 'Bearer ' + token }
    });
  })
  .then(function(response) {
    // The Fetch API returns a stream, which we convert into a JSON object.
    return response.json();
  })
  .then(function(data) {
    // Data will contain the restricted key in the `key` field.
    client = algoliasearch(ALGOLIA_APP_ID, data.key);
    index = client.initIndex('notes');

    // Perform the search as usual.
    return index.search({query});
  })
  .then(function(responses) {
    // Finally, use the search 'hits' returned from Algolia.
    return responses.hits;
  });

쿼리할 때마다 항상 검색 키를 가져올 필요는 없습니다. 검색 속도를 높이려면 가져온 키 또는 Algolia 클라이언트를 캐시해야 합니다.

제한사항

위 솔루션은 Cloud Firestore 데이터에 전체 텍스트 검색 기능을 추가하는 간단한 방법입니다. 그러나 타사 검색 제공업체는 Algolia뿐이 아닙니다. 솔루션을 제품으로 배포하기 전에 ElasticSearch와 같은 다른 제품도 고려해 보세요.