Создание простой поисковой системы (Gwoogl :p) для многоязычного сайта

Сайты с большим количеством пользовательского контента в последнее время начали принимать и поддерживать несколько языков! Я думаю, что это здорово, потому что в принципе, почему бы и нет? Ведь так должно было быть с самого начала существования Интернета, верно? Но как бы то ни было, некоторые из них полагаются на текстовый контент «листингов», такой как: «title», «tags», «description», чтобы проиндексировать весь контент, а затем предложить внутреннюю поисковую систему для последующих посетителей.

Среди них есть и такие сайты, для которых медиа-контент (изображения и видео) является сырой ценностью, предоставляемой пользователю (думаю, вы догадались, о чем я говорю 😉). Так что посетитель, ищущий «борьба стройных против толстых» 🙃, будет заинтересован во всех медиа, какими бы ни были их название, теги и описание, признавая, что они попадают в его/ее поиск. Так что да, этот случай, особенно на подъеме, и если у вас есть такие сайты и вы не начали поддерживать многоязычный поиск, то вот вам отправная точка.

Сначала мы будем использовать NodeJS в качестве веб-сервера и MongoDB в качестве базы данных. Это очень базовый вариант, просто чтобы проиллюстрировать, как вы можете воспользоваться нашим API Word to Word.

    // define your base query
    const baseQuery = { d: false, a: true }
    /**
     * Approximate multilingual search based on indexed text fields: title, desc, tags
     * @param {*} phrase sentence to search
     * @param {*} exact whether search the exact sentence or separate terms
     * @param {*} otherFields Of course you can add any other categorical fields to search
     * @param {*} lang which language the phrase is in
     * @return {Promise}
     */
    this.gwoogl = async function (phrase, exact, otherFields, lang) {
        // Limit search to newer content (ignoring very old content)
        const daysBefore = 100
        collection = mongoDB.collection('listing')
        const since = getObjectId(daysBefore)
        // quoted string value in Mongo to look for the exact match ! 
        phrase = exact ? `"${phrase.trim()}"` : phrase.trim()
        const query = JSON.parse(JSON.stringify(baseQuery)) // clone
        // Collation is very important, it helps Mongo with searching natural languages
        // something like "a car" can spot either "the car", "cars" etc
        // Note that Collation at the moment is an index on the whole collection, ie
        // You cannot apply different collation(s) on documents
        let collation = lang === 'und' ? baseCollation : { locale: lang }

        // THE FOLLOWING IS AN EXAMPLE 
        // HERE I'M FIRST SEARCHING FOR THE WHOLE PHRASE AS IS
        // THEN I CALL TRANSLATION API ON THE FIRST TERM IN THE PHRASE. YOU CAN ITERATE ON EACH WORD IN THE SAME MANNER !!
        // STACK AND FORMAT RESULTS AS YOU WISH !

        query.$text = { $search: phrase }
        query._id = { $gt: since }
        if (lang !== 'und') query.lang = lang
        // Dynamically adding other fields
        for (const [key, value] of Object.entries(otherFields)) {
            if (value !== 'und') query[key] = value
        }
        const docs = await collection
            .find(query, { score: { $meta: 'textScore' } })
            .collation(collation)
            .project(baseProjection)
            .sort({ score: { $meta: 'textScore' } })
            .limit(21)
            .toArray()
        const count = await collection.countDocuments(query)
        const result = { documents: docs, count: count, crossLangDocs: [] }

        if (count < 6 && phrase.indexOf(' ') < 0) {
            // Call translate API on each keyword, grab each query result (keywords), concatenate them as a single phrase and query again
            let translations = translate({ limit: 10, source: lang, score: '0.5', word: phrase, target: 'fr', target: 'es', target: 'fi' })
            for (const [lang, keywords] of Object.entries(translations)) {
                collation = { locale: lang }
                // Concatenating translations as a single phrase (you can do however you feel better) 
                phrase = keywords.join(' ')
                query.$text = { $search: phrase }
                const crossLangDocs = await collection
                    .find(query, { score: { $meta: 'textScore' } })
                    .collation(collation)
                    .project(baseProjection)
                    .sort({ score: { $meta: 'textScore' } })
                    .limit(3)
                    .toArray()
                // console.log(crossLangDocs)
                crossLangDocs.forEach((doc) => {
                    doc['crosslang'] = lang
                })
                result.crossLangDocs = result.crossLangDocs.concat(crossLangDocs)
            }
        }
        return result
    }
Вход в полноэкранный режим Выход из полноэкранного режима
// Helpers

/**
 * This function returns an ObjectId embedded with a given dateTime
 * Accepts number of days since document was created
 * Author: https://stackoverflow.com/a/8753670/1951298
 * @param {Number} days
 * @return {object}
 */
function getObjectId(days) {
    const yesterday = new Date()
    days = days || (process.env.NODE_ENV === 'localhost' ? 1000 : 14)
    yesterday.setDate(yesterday.getDate() - days)
    const hexSeconds = Math.floor(yesterday / 1000).toString(16)
    return new ObjectId(hexSeconds + '0000000000000000')
}

Войти в полноэкранный режим Выход из полноэкранного режима

Рассмотрим

    // Indexing title and description fields (ie fields to search for)
    // collection#text must be indexed as the following:
    const listingCollection = db.collection('listing')
    await listingCollection.dropIndexes()
    await listingCollection.createIndex({ title: 'text', cdesc: 'text' }, { weights: { title: 3, cdesc: 1 } })

Войти в полноэкранный режим Выйти из полноэкранного режима
// APIWrapper.js
// Requst Word to Word translator API

/*
* API wrapper
*
*/

import axios from "axios";

const translateParameters = (params) => {
    return {
        method: 'GET',
        url: 'https://word-to-word-translator1.p.rapidapi.com/api/translate',
        params,
        headers: { 'X-RapidAPI-Key': 'YOUR RAPID API GOES HERE' }
    }
};

const detectParameters = (params) => {
    return {
        method: 'GET',
        url: 'https://word-to-word-translator1.p.rapidapi.com/api/detect_language',
        params,
        headers: { 'X-RapidAPI-Key': 'YOUR RAPID API GOES HERE' }
    }
};


const translate = async (params) => {
    return await axios.request(translateParameters(params))
}

const detect = async (params) => {
    return await axios.request(detectParameters(params))
}

export { translate, detect }
Войти в полноэкранный режим Выйти из полноэкранного режима

Заключительные замечания

Вы видите, что создание простой поисковой системы не является простой задачей, поскольку в ней снова задействованы естественные языки. Но в то же время, производительное решение, созданное «своими силами», вполне возможно!!!
Сценарий использования нашей очень маленькой поисковой системы Gwoogl следующий:

1- Получение пользовательского ввода (который может быть в реальном времени (честно говоря, я думаю, что это возможно, но я лично не пробовал, чтобы проверить производительность)).
2- Возможно, сделать некоторую очистку данных и адаптацию (стемминг, лемматизация и т.д.). Обратите внимание, что, к счастью, наша система определения и перевода языка очень сильна в этом отношении, она использует контекстный поиск, поэтому, например, можно оставить форму множественного числа!
3- Определить язык ввода (Word2Word#detect)
4- Перевести введенные слова (Word2Word#translate)
5- Итерация над ключевыми словами и суммирование результатов!

Ура, теперь у вас есть многоязычная поисковая система!

Оцените статью
devanswers.ru
Добавить комментарий