Node.js: creare un motore di ricerca con MongoDB

In questo articolo vedremo come implementare un motore di ricerca con Node.js e MongoDB.

MySQL: stopwords, LIKE, AND e OR

WordPress implementa il suo motore di ricerca come segue:

  • valida la query string
  • crea un array di parole dalla query string
  • rimuove le stopwords dall'array
  • usa LIKE con AND/OR sul titolo, sul riassunto e sul contenuto dei post.

MongoDB: $text e $regex

Se usiamo $text dobbiamo creare degli indici:

db.posts.createIndex({title: "text"});
db.posts.createIndex({content: "text"});
db.posts.createIndex({excerpt: "text"});

Quindi:

db.posts.find({'$and': [{'title': {'$text': 'query'}}, {'content': {'$text': 'query'}}, {'excerpt': {'$text': 'query'}}]});

Dato che $text non usa le espressioni regolari, query e Query sono due termini diversi. La corrispondenza in questo caso è esatta, quindi per ottenere più risultati dobbiamo usare $regex con l'opzione i:

db.posts.find({'$and': [{'title': {'$regex': 'query', '$options': 'i'}}, {'content': {'$regex': 'query', '$options': 'i'}}, {'excerpt': {'$regex': 'query', '$options': 'i'}}]});

L'implementazione

In Node.js possiamo scrivere:

'use strict';

const stopwords = require('./src/stopwords'); // Array di stopwords
const Docs = require('./src/db/Docs');
const app = require('express')();

let queryVar = function(str) {
    let q = str.replace( /\r\n/g, '').replace(/^\s+|\s+$/, '').replace(/[^a-z\s]+/gi, '').replace(/\s+$/, '');

    let parts = q.split(/\s/);
    let terms = [];
    parts.forEach(part => {
        if(stopwords.indexOf(part) === -1) {
            terms.push(part);
        }
    });
    let query = {'$and': []};
    terms.forEach(term => {
       let queryFrag = {title: {'$regex': term, '$options': 'i'}};
       query['$and'].push(queryFrag);
    });
    return query;
};

app.get('/search', (req, res) => {
   let searchQuery = queryVar(req.query.term);
   Docs.find(searchQuery).then(results => {
       //...
   }).catch(err => {
       //...
   });
});

 app.listen(3000); 
Torna su