2014-07-19 47 views
1

我试图一次在多个Parse类上执行复杂的全文搜索功能(class1上的query1或者class2上的query2等)。按照Parse's的建议,最好对字符串进行标记,并在过滤之后将其存储在一个可以利用whereKey:containsAllObjectsInArray:的阵列中。Parse.com - 标记化数组字段中的子字符串搜索

我的问题是:

  • 它搜索的所有条款,而不是任何
  • 它不搜索字符串(如解析阵列:["David", "Peter", "Vivien"],搜索vi返回0对象)

截至目前看来,唯一“体面”的方法是将班内的所有字符串连接成一个大字符串并使用whereKey:containsString:。我假设这将比标记化方法效率低。

你们有什么建议或建议如何开发这两种方式 - 高效和可扩展的搜索多个解析类?

谢谢!

回答

0

全文搜索是一个复杂的问题,许多数据库都有特殊的支持,您可以在考虑成本后打开...解析不支持全文搜索。

关于做你想要解析的内容,whereKey:equalTo:将在一个数组上找到其中一个项目与提供的字符串匹配的地方。

如果你想匹配任何你可以用一个OR query

PFQuery *term1Query = [[PFQuery queryWithClassName:@"Class"] whereKey:@"terms" equalTo:@"David"]; 
PFQuery *term2Query = [[PFQuery queryWithClassName:@"Class"] whereKey:@"terms" equalTo:@"Peter"]; 

PFQuery *query = [PFQuery orQueryWithSubqueries:@[term1Query, term2Query]]; 

如果您希望能够以匹配部分字符串,你需要改变你的架构了一下。创建一个名为“SearchToken”的新类,或者一个类型为“token”的字段。

确保在创建/更新时始终强制将其设为小写。然后,您可以在相关类之间共享令牌(例如,不要创建两个“大卫”令牌,只需链接到新类上的现有令牌)。强制你的输入也是小写,因为所有的字符串匹配区分大小写。

您的“条款”列现在需要包含指针而不是单词。

您现在可以做对你的“SearchToken”类的查询和你的父类使用whereKey:matchesQuery:标准,如:

PFQuery *tokenQuery = [PFQuery queryWithClassName:@"SearchToken"]; 
[tokenQuery whereKey:@"token" containsString:@"vi"]; 

PFQuery *mainQuery = [PFQuery queryWithClassName:@"MyClass"]; 
[mainQuery whereKey:@"terms" matchesQuery:tokenQuery]; 

你甚至可以使用一个或查询像上面具有完全匹配的混合物和部分匹配。

请注意,令牌查询可能会匹配很多行,并受100行默认值的限制,如果您认为需要设置较高的限制,则最大值为1000。

在关于创建SearchToken行,假设你已经从一个字符串收集令牌,迫使他们为小写,现在有令牌链接/创建:

// these would be generated, manually creating for demonstration 
NSArray *tokens = @[@"david", @"and", @"the", @"lion"]; 

PFQuery *existingTokens [PFQuery queryWithClassName:@"SearchToken"]; 
[existingTokens whereKey:@"token" containedIn:tokens]; 

// results of the above already exist, create new rows for the remainder 

有可能的种族问题 - 在查询运行后,其他设备可能会创建“david”令牌的条件。你可以有一份工作,寻找重复和删除重复行,固定所有指针,因为它去...虽然即使在那里你想要处理竞争条件,所以逻辑可能是标记重复,并让所有其他查询搜索[query whereKeyDoesNotExist: @"duplicateOf"];然后找到指向指向重复的行的所有记录,并将它们指向duplicateOf。所有这一切取决于你对避免重复的重要性,也许这对你来说不是问题。

+0

嗯,这是一个耻辱。至少我学到了一些新东西。我会用替代解决方案更新我的答案。 –

+0

hm这很有趣...所以SearchToken类需要5个额外的关系列(因为我想能够搜索5个类的内容)来保持对结果类的引用? –

+0

在这种情况下,是的,你可以做到这一点,并创建反向关系,而不是/也。然后,您的查询将查找令牌,然后查询每个关系字段以获取匹配的类。或者,您可以使用上面的示例对每个类执行'whereKey:matchesQuery:'并重新使用'tokenQuery',而无需添加额外的关系列。 –

0

我已经成功实现了搜索功能,只需稍作修改。令人信服的解析执行全文搜索相当昂贵(保存:2个API调用:获取现有令牌+保存新令牌,搜索:1 +(1 * N):获取匹配搜索项的令牌+ N ==否可搜索对象)。 我想知道是否有更好的方法来达到同样的效果,有人提出如何提高成本的建议吗?

虽然储蓄是为蒂莫西描述的不太一样,检索如下:

// 1) check for sanitised terms in SearchToken class 
//  _marrInputTokens - mutable array of sanitised search terms 
NSMutableArray *_marrInnerQueries = [[NSMutableArray alloc] init]; 
for(NSString *_token in _marrInputTokens) 
{ 
    PFQuery *_innerQuery = [PFQuery queryWithClassName:@"SearchToken"]; 
    [_innerQuery whereKey:@"token" containsString:_token]; 
    [_marrInnerQueries addObject:_innerQuery]; 
} 

PFQuery *_pSearchTokenQuery = [PFQuery orQueryWithSubqueries:_marrInnerQueries]; 
[_pSearchTokenQuery setLimit:1000]; 
[_pSearchTokenQuery findObjectsInBackgroundWithBlock:^(NSArray *tokenObjects, NSError *error) { 
    if(error) 
    { 
     QLog([error localizedDescription]); 
    } 
    else 
    { 
     // 2) if not all search terms (even as substrings) were found in SearchToken, no need to search further 
     //  as no object can meet the search criteria and we can save some calls 
     if([tokenObjects count] >= [_marrInputTokens count]) 
     { 

      // 3) all search terms were found in SearchToken 
      // fetch all SearchableObject_1 corresponding objects 
      PFQuery *_pResultQuery = [PFQuery queryWithClassName:@"SearchableObject_1"]; 

      // 4) need to make sure all tokens are conjugated with AND operator 
      for(PFObject *_tokenObject in _marrSearchTokenResults) 
      { 
       [_pResultQuery whereKey:@"searchIndex" equalTo:_tokenObject]; 
      } 

      // 5) pagination (adding setSkip: for real pagination) 
      [_pResultQuery setLimit:PARSE_RESULT_LIMIT]; 
      [_pResultQuery findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) { 

       if(error) 
       { 
        QLog([error localizedDescription]); 
       } 
       else 
       { 
        // 6) objects contains all matching entries for SearchableObject_1 

        // 7) nested additional SearchableObject_x searches 
        // .... 
       } 
      }]; 
     } 
    } 
}];