How to combine a Spring Data MongoDB Query with full text search?
Asked Answered
P

5

5

I've recently upgraded to Spring Data MongoDB 1.6.0.RC1, which is very good and implements the full text search feature of MongoDB 2.6 (huuurah!). My question is: how can I combine the Criteria and TextCriteria classes to generate complex queries ?

Example object model:

{
  textProperty: "a text that is indexed for full text search",
  language:"en",
  aBooleanProperty: true,
  anIntegerProperty: 1
}

Query:

db.collection({ anIntegerProperty: 1,  $text: { $search: "indexed", $language: "en" } })

So, how can I write the above query with Spring Data MongoDB classes?

Peria answered 28/8, 2014 at 14:5 Comment(0)
P
11

Note: this post had non working code previously but is now fixed!

A TextCriteria is a CriteriaDefinition and thus can be used with a Query like this:

Quer query = Query.query(
  Criteria.where("aBooleanProperty").is(true).
  and(anIntegerProperty).is(1)).
  addCriteria(TextCriteria.
    forLanguage("en"). // effectively the same as forDefaultLanguage() here
    matching("a text that is indexed for full text search")));

List<YourDocumentType> result = mongoTemplate.findAll(query. YourDocumentType.class);

Polish this up by using static imports for Query.query and Criteria.where and it reads pretty fluent.

Patronizing answered 29/8, 2014 at 14:17 Comment(4)
How did you manage to add TextCriteria to the query with 'and' operand? Because I can not do it nowShy
This one does not work. TextCriteria is not the same object as CriteriaHardening
Hey everyone! Sorry for the confusion. I was writing the previous example down from the top of my head. I've fixed this now. The trick is to use addCriteria(…) on the query instead of and(…) on the Criteria.Patronizing
Is TextSearch not applicable for number search ? If yes, what is the way to get the search for numbers? Please see the issue #36164106Akron
R
4

Spring data supports MongoDB full text search queries. Consider in the below example, we are trying to search the text inside textProperty field. You need to add @TextIndexed annotation for the field you want to search.

Model object :

public class TextExample {

@Id
private String id;

private String textProperty;

@TextIndexed
private String language;

private String aBooleanProperty;

private int anIntegerProperty;  

}

While constructing the TextCriteria you can provide the language option(I have considered a default language which is english)

Query :

TextCriteria criteria = TextCriteria.forDefaultLanguage().matchingAny(
            "en");

    Query query = TextQuery.queryText(criteria).sortByScore();
    query.addCriteria(Criteria.where("anIntegerProperty").is(1));   

    List<TextExample> textExamples = mongoTemplate.find(query, TextExample.class);

This should return the matching records with the language as "en" and anIntegerProperty as 1.

Please refer to link for the TextCriteria documentation.

Radiology answered 7/8, 2015 at 13:4 Comment(0)
H
1

Building Query entity, you can combine criteria by using the method Query.addCriteria(), that accepts CriteriaDefinition parameter. Both TextCriteria and Criteria implement CriteriaDefinition. For example, if you want to combine TextCriteria and Criteria, you can do it as follows:

// Create TextCriteria
TextCriteria criteria = TextCriteria.forDefaultLanguage().matchingAny("some key word"); 
// Build Query
Query query = TextQuery.queryText(criteria);
// Add the additional criteria to query
query.addCriteria(Criteria.where("field").is("fieldValue"));
Homograft answered 27/4, 2015 at 10:52 Comment(1)
Is there any way we can use $near with it ?Manchukuo
F
1

According to my research TextCriteria and Criteria there is no way to combine them using Spring Data MongoDB 1.6.2.RELEASE for aggregation (group by - aggregate feature). I made this ugly hack for it:

private static class QueryHack extends Query implements CriteriaDefinition {
    @Override
    public DBObject getCriteriaObject() {
        return getQueryObject();
    }

    @Override
    public String getKey() {
        return null;
    }
}

And then:

QueryHack hack = new QueryHack();
hack.addCriteria(textCriteria).addCriteria(criteria);
MatchOperation match1 = new MatchOperation(hack); //Also it's not possible to use static method match(textCriteria) - it takes Criteria only. 

I will add JIRA ticket to Spring MongoDB project.

Fino answered 28/4, 2015 at 17:17 Comment(3)
Did you end up filing a JIRA ticket for this? I ended up using a variation of your trick to compose multiple Criteria and TextCriteria in an Aggregation.match(). Thanks for the insight.Fredrika
I think I forgot to do that. Please do it yourself. I don't use MongoDB anymore and you will probably able to describe details better.Fino
Thanks, Vity. This still works in 2018! Let us know if better approach exists using MatchOperation.Krouse
S
1

You can create a compound index with Spring Data MongoDB like this:

@Document
@CompoundIndex(def = "{'anIntegerProperty': 1, 'textProperty': 'text'}")

Then create a Repository:

@Repository
public interface TheDocuemntRepository extends MongoRepository<TheDocuemnt, String> {

    Stream<TheDocuemnt> findByAnIntegerPropertyOrderByScoreDesc(Integer anIntegerProperty, TextCriteria criteria);

}

Consider MongoDB documentation:

Compound Index

A compound index can include a text index key in combination with ascending/descending index keys. However, these compound indexes have the following restrictions:

  • A compound text index cannot include any other special index types, such as multi-key or geospatial index fields.

  • If the compound text index includes keys preceding the text index key, to perform a $text search, the query predicate must include equality match conditions on the preceding keys.

Filter search results by language

If you also want to filter search results by language, you should try this approach: Multi-language documents example

The entity

@CompoundIndex(def = "{'anIntegerProperty': 1, 'language': 1, 'textProperty': 'text'}")

The repository

Sort TEXT_SCORE_SORT = new Sort(Direction.DESC, "score");

@Query("{'anIntegerProperty': ?0, 'language': ?1, $text: {$search: ?2, $language: ?1}}")
Stream<TheDocuemnt> findTheDocument(Integer property, String language, String criteria, Sort sort);
Synergetic answered 16/4, 2017 at 4:42 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.