10

I have an entity called lastName with value "Benjamin". Is there a way in objectify that if the user put "Ben" or "jam" or "Benja". I still be able to find this entity using query.filter(). I must use the query as there are other search criteria iam checking.

I saw something in "Obgaektify" called "starts with" operator. but it isnot working. Any suggestions would be appreciated. Thanks

Shady Hussein
  • 513
  • 8
  • 24
  • 1
    Why would you want to do a substring search within names? I've never seen a good use-case for this. – Nick Johnson Aug 11 '11 at 01:51
  • It isnot within names actully. It is within text search in general. but for simplicity i just said name :) – Shady Hussein Aug 11 '11 at 09:20
  • 3
    Well, the question remained. Fulltext search does stemming and normalization, then searches on whole words, because there's very little point in having a query for 'cat' return 'defecate'. – Nick Johnson Aug 11 '11 at 23:48
  • Well right now the requirements changed, I only need to check if it starts with the given text. like "benjamin" starts with "ben" not "jam" or anything else. So i will try the solution posted below and see the results. Thanks for your help – Shady Hussein Aug 12 '11 at 12:22

4 Answers4

12

There's no "LIKE" type queries for sub-string, however a case sensitive "starts with" could be simulated by taking advantage of the > and < operators on indexes.

// The start string
String searchStr = "Ben";

// emulate a "starts with" query
Query q = new Query("MyEntity")
q.addFilter("name", Query.FilterOperator.GREATER_THAN_OR_EQUAL, searchStr);
q.addFilter("name", Query.FilterOperator.LESS_THAN, searchStr + "\ufffd");

The query will 'search' the name property for items that begining with "Ben", and are less than "Ben\ufffd", where \ufffd is the highest possible unicode character.

Oliver Hausler
  • 4,900
  • 4
  • 35
  • 70
Chris Farmiloe
  • 13,935
  • 5
  • 48
  • 57
2

There is no standard existing index for contains-like queries. Btw, you can always introduce your own. At this case you can do:

  1. add and synthetic field like String[] lastNameIndex
  2. add method marked as @PrePersist that will fill lastNameIndex field with all available combinations
  3. When you want to find entities using this index do query.filter('lastNameIndex =', val)
Igor Artamonov
  • 35,450
  • 10
  • 82
  • 113
0

Putting the answer from Chris and the comment from Nick together, here is the code to build a query filter for objectify V4:

public <T> Query<T> fieldStartsWith(Query<T> query, String field, String search){
    query = query.filter(field + " >=", search);
    return query.filter(field + " <", searchStr+"\ufffd");
}
Zensursula
  • 173
  • 4
  • 9
  • I believe it should be `< "\ufffd"` and not `<=` because FFFD is not a printable ascii character so you would want to exclude it. In practice it probably won't matter, though. Correct me if I'm wrong. – Oliver Hausler Feb 05 '15 at 17:10
  • Thanks Oliver, you are right. I modified my answer! – Zensursula Mar 19 '15 at 07:51
0

I have used the tokenization method. Here is the code in Java:

private String tokenize(String phrase) {
StringBuilder tokens = new StringBuilder();
try {
  for (String word : phrase.split(" ")) {
    if (word.length() < 1) {
      continue;
    }
    int j = 1;
    while (true) {
      for (int i = 0; i < word.length() - j + 1; i++) {
        tokens.append(word.substring(i, i + j)).append(" ");
      }
      if (j == word.length()) {
        break;
      }
      j++;
    }
  }
} catch (Throwable t) {
  t.printStackTrace();
}
return tokens.toString();}

This allows to define an indexable field, then process standard Ofy queries and SearchService.

Aram Paronikyan
  • 1,598
  • 17
  • 22