4

在 iTunes 搜索 API文档中,有一个搜索名为maroon的艺术家的示例,其 url 如下所示:

https://itunes.apple.com/search?term=maroon&entity=allArtist&attribute=allArtistTerm

这将返回 50 多个结果,这些结果如下所示:

{
    "resultCount": 50,
    "results": [
        {
            "wrapperType": "artist",
            "artistType": "Artist",
            "artistName": "Maroon 5",
            "artistLinkUrl": "https://itunes.apple.com/us/artist/maroon-5/id1798556?uo=4",
            "artistId": 1798556,
            "amgArtistId": 529962,
            "primaryGenreName": "Pop",
            "primaryGenreId": 14,
            "radioStationUrl": "https://itunes.apple.com/station/idra.1798556"
        },
        {
            "wrapperType": "artist",
            "artistType": "Software Artist",
            "artistName": "MaroonEntertainment",
            "artistLinkUrl": "https://itunes.apple.com/us/artist/maroonentertainment/id537029262?uo=4",
            "artistId": 537029262,
            "radioStationUrl": "https://itunes.apple.com/station/idra.537029262"
        },

这很好。但是,这是我的问题:我想通过结合对艺术家和歌曲名称以及专辑名称的搜索来创建一个尽可能具体的搜索查询。

例如,我得到了这首歌:

  • 歌曲:跨越大鸿沟
  • 专辑:大分裂
  • 艺术家:半音速

我只能搜索艺术家姓名:

https://itunes.apple.com/search?term=Semisonic&entity=allArtist&attribute=allArtistTerm

我只能搜索歌曲词:

https://itunes.apple.com/search?term=Across the Great Divide&entity=song&attribute=songTerm

我只能搜索专辑名称:

https://itunes.apple.com/search?term=Great Divide&entity=album&attribute=albumTerm

然而,这些人都没有给我我想要的结果(我可以在其他 50 个人中找到我正在寻找的结果......但我只是希望搜索查询足够具体,以避免任何客户端过滤之类的事情)。

如何组合这些搜索?如果我只是将两个搜索添加在一起(在本例中,我正在搜索歌曲艺术家):

https://itunes.apple.com/search?term=Across the Great Divide&entity=song&attribute=songTerm&term=Semisonic&entity=allArtist&attribute=allArtistTerm

然后苹果将简单地忽略第一个搜索类型(即歌曲)并仅返回艺术家的结果)。

想法?

4

2 回答 2

2

Well this is more of a "workaround" answer.. but it's the solution I'm using.. so might as well spread the love eh?

This is a 100% client side solution (ie the entire database of itunes music can be downloaded into my own server.. then I can create all sots of search wrappers around it.. but that's a project in itself).

this is what i got:

// this is just a wrapper around the apple search api.. it makes your 
// average joe http get request
[[AppleServer shared] searchForSongWithTitle:track.title andAlbumName:track.albumName completion:^(NSArray *results, NSError *error){
    if ([results count] >0) {
        NSLog(@"[%d] unfiltered songs retrieved from apple search api", [results count]);
        NSDictionary *filteredResult = [[self class] filterResults:results ToMatchTrack:track];
        if (!filteredResult) {
            NSLog(@"Filtering may be too strict, we got [%d] results from apple search api but none past our filter", [results count]);
            return;
        }

        .. process results


+ (NSDictionary *)filterResults:(NSArray *)results ToMatchTrack:(VBSong *)track
{

    NSPredicate *predicate = [NSPredicate predicateWithBlock:^BOOL(NSDictionary *evaluatedTrack, NSDictionary *bindings){    
        BOOL result =
         ([track.title isLooselyEqualToString:evaluatedTrack[@"trackName"]] &&
          [track.artistName isLooselyEqualToString:evaluatedTrack[@"artistName"]] &&
          [track.albumName isLooselyEqualToString:evaluatedTrack[@"collectionName"]]);

        NSLog(@"match?[%d]", result);

        return result;
    }];

    return [[results filteredArrayUsingPredicate:predicate] firstObject];
}

the key method here is isLooselyEqualToString.. it's defined in an NSString category like so:

/**
 * Tests if one string equals another substring, relaxing the following contraints
 *   - one string can be a substring of another
 *   - it's a case insensitive comparison
 *   - all special characters are removed from both strings
 *
 *     ie this should return true for this comparison:
 *     - comparing self:"Circus One (Presented By Doctor P and Flux Pavilion)" 
                and str:"Circus One presented by Doctor P"
 *
 * @param str string to compare self against
 * @return if self is the same as str, relaxing the contraints described above
 */
- (BOOL)isLooselyEqualToString:(NSString *)str
{
    return [[self removeSpecialCharacters] containSubstringBothDirections:[str removeSpecialCharacters]];
}

/**
 * Tests if one string is a substring of another
 *     ie this should return true for both these comparisons:
 *     - comparing self:"Doctor P & Flux Pavilion" and substring:"Flux Pavilion"
 *     - comparing self:"Flux Pavilion" and substring:"Doctor P & Flux Pavilion"
 *
 * @param substring to compare self against
 * @return if self is a substring of substring
 */
-(BOOL)containSubstringBothDirections:(NSString*)substring
{
    if (substring == nil) return self.length == 0;

    if ([self rangeOfString:substring options:NSCaseInsensitiveSearch].location == NSNotFound) {
        if ([substring rangeOfString:self options:NSCaseInsensitiveSearch].location == NSNotFound) {
            return NO;
        } else {
            return YES;
        }
    } else {
        return YES;
    }
}

- (NSString *)removeSpecialCharacters
{
    NSMutableCharacterSet *specialCharsSet = [[NSCharacterSet letterCharacterSet] mutableCopy];
    [specialCharsSet formUnionWithCharacterSet:[NSCharacterSet whitespaceCharacterSet]];
    return [[self componentsSeparatedByCharactersInSet:[specialCharsSet invertedSet]] componentsJoinedByString:@""];
}

Bonus This is the solution we are currently using.. I'm fully aware that some terms may come up that break this algorithm.. so we have a unit test for this that we incrementally add terms to ensure we keep on improving our algorithm while not causing regression bugs.. i'll post it if i get enough votes on this answer heh.

于 2015-04-09T04:53:58.620 回答
1

住处,

对不起,你不能从这里到那里!(除非其他人发现了新东西。)

我目前正在开发一个应用程序,它将结合多个查询的结果。

对于更喜欢冒险的人,Apple 向附属合作伙伴提供“来自 iTunes 和 App Store 的完整元数据集的数据馈送”。为了使用它,我将在云中的某处放置一个数据库服务,并使用它来进行更详细的查询并显示搜索 API 未返回的详细信息。

如果我完成了我的应用程序并且它实际上被超过 5 人使用,我可能会考虑做整个数据库版本。

大卫

于 2015-04-09T04:27:21.117 回答