Spring data mongodb: Text search for 'phrase OR words in phrase'

11.7k views Asked by At

I need to search documents inside a collection named blog which has text indexes defined for title, tags, summary and body:

@Document(collection="blog")
public class Blog {
    @Id
    private String id;
    @TextIndexed(weight = 10)
    private String title;
    @TextIndexed(weight = 9)
    private String tags;
    @TextIndexed(weight = 8)
    private String summary;
    @TextIndexed(weight = 7)
    private String body;
    @TextScore
    private Float score;

    //getters and setters
}

Now, I have a requirement to perform text search on blog collection according to the following criteria:

  1. Check the user input whether it contains more than one word.
  2. If searchKey is single word perform text search and return sorted response according the weight.
  3. If searchKey contains more than one word the perform search for full PHRASE OR any word within the PHRASE.

For the 2nd case TextCriteria definition looks like:

TextCriteria criteria = TextCriteria.forDefaultLanguage().matching("SingleWord");

For the 3rd case, how to write criteria definition for the combination in single query:

query 1: db.articles.find( { $text: { $search: "\"coffee cake\"" } } ) //phrase search
query 2: db.articles.find( { $text: { $search: "coffee cake" } } ) //word search

Can I perform search with

query 1 OR query 2 with sorted result based on score.

Score should be higher for results matching for full phrase.

2

There are 2 answers

1
Vaibhav Raj On BEST ANSWER

Spring Data MongoDB supports following operations for text search:

  • TextCriteria.forDefaultLanguage().matchingAny("search term1", "search term2")
  • TextCriteria.forDefaultLanguage().matching("search term")
  • TextCriteria.forDefaultLanguage().matchingPhrase("search term")

First criteria can perform text search for: search, text1, and text2 Second criteria can perform text search for: search, term Third criteria is for phrase search: 'search term'

A text query can be formed using above criteria:

Query query = TextQuery.queryText(TextCriteria.forDefaultLanguage().matchingAny("search term").sortByScore().with(new PageRequest(pageNum, docCount, new Sort(new Order(Sort.Direction.DESC, "score"))));

To use score (text search score) for sorting we need to add a field named score in the respective POJO:

@TextScore
private Float score;

We can add other filters on the text query as following:

query.addCriteria(Criteria.where("city").is("Delhi").and("country").is("India").and("price").lte(200.50).gte(100.50);

Finally to execute this query:

List<Product> products = mongoOperations.find(query, Product.class)

Mongodb by default assigns higher score to the phrase matches. Therefore one doesn't need to find phrase matches first in cases where phrase matches are required with higher scores and then normal text matches.

0
fuat On

MongoRepository also support TextCriteria queries on full text document. It is described here;

@Document
class FullTextDocument {

  @Id String id;
  @TextIndexed String title;
  @TextIndexed String content;
  @TextScore Float score;
}

interface FullTextRepository extends Repository<FullTextDocument, String> {

  // Execute a full-text search and define sorting dynamically
  List<FullTextDocument> findAllBy(TextCriteria criteria, Sort sort);

  // Paginate over a full-text search result
  Page<FullTextDocument> findAllBy(TextCriteria criteria, Pageable pageable);

  // Combine a derived query with a full-text search
  List<FullTextDocument> findByTitleOrderByScoreDesc(String title, TextCriteria criteria);
}

Sort sort = Sort.by("score");
TextCriteria criteria = TextCriteria.forDefaultLanguage().matchingAny("spring", "data");
List<FullTextDocument> result = repository.findAllBy(criteria, sort);

criteria = TextCriteria.forDefaultLanguage().matching("film");
Page<FullTextDocument> page = repository.findAllBy(criteria, PageRequest.of(1, 1, sort));
List<FullTextDocument> result = repository.findByTitleOrderByScoreDesc("mongodb", criteria);