From bde9be5f8968ced6bb11fd0da4c36a965ec2271a Mon Sep 17 00:00:00 2001 From: Ngo Hung Date: Tue, 7 Feb 2017 16:57:15 +0800 Subject: [PATCH 1/2] add facets filtering --- Example/Example/Info.plist | 4 +- ViSearchSDK.podspec | 2 +- .../ViSearchSDK.xcodeproj/project.pbxproj | 8 ++++ .../Classes/Request/ViBaseSearchParams.swift | 15 +++++++ .../Classes/Response/ViFacet.swift | 35 +++++++++++++++ .../Classes/Response/ViFacetItem.swift | 24 +++++++++++ .../Classes/Response/ViResponseData.swift | 43 +++++++++++++++++++ .../ViSearchSDK/Classes/ViSearchClient.swift | 2 +- ViSearchSDK/ViSearchSDK/Info.plist | 2 +- 9 files changed, 130 insertions(+), 5 deletions(-) create mode 100644 ViSearchSDK/ViSearchSDK/Classes/Response/ViFacet.swift create mode 100644 ViSearchSDK/ViSearchSDK/Classes/Response/ViFacetItem.swift diff --git a/Example/Example/Info.plist b/Example/Example/Info.plist index 974a6f6..e5d0298 100644 --- a/Example/Example/Info.plist +++ b/Example/Example/Info.plist @@ -17,9 +17,9 @@ CFBundlePackageType APPL CFBundleShortVersionString - 1.1 + 1.2.0 CFBundleVersion - 2 + 3 LSRequiresIPhoneOS NSAppTransportSecurity diff --git a/ViSearchSDK.podspec b/ViSearchSDK.podspec index 8931a93..8e8ec57 100644 --- a/ViSearchSDK.podspec +++ b/ViSearchSDK.podspec @@ -1,5 +1,5 @@ # -# Be sure to run `pod spec lint ViSearchSwiftDev.podspec' to ensure this is a +# Be sure to run `pod spec lint ViSearchSwift.podspec' to ensure this is a # valid spec and to remove all comments including this before submitting the spec. # # To learn more about Podspec attributes see http://docs.cocoapods.org/specification.html diff --git a/ViSearchSDK/ViSearchSDK.xcodeproj/project.pbxproj b/ViSearchSDK/ViSearchSDK.xcodeproj/project.pbxproj index e858d25..9fa510a 100644 --- a/ViSearchSDK/ViSearchSDK.xcodeproj/project.pbxproj +++ b/ViSearchSDK/ViSearchSDK.xcodeproj/project.pbxproj @@ -12,6 +12,8 @@ BD50FD621DA8B4220035FD78 /* UIColorExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = BD50FD611DA8B4220035FD78 /* UIColorExtension.swift */; }; BD6540571DA6302D00E10162 /* ViUploadSearchParamsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = BD6540561DA6302D00E10162 /* ViUploadSearchParamsTests.swift */; }; BD65405A1DA63F5200E10162 /* ViSearch.swift in Sources */ = {isa = PBXBuildFile; fileRef = BD6540591DA63F5200E10162 /* ViSearch.swift */; }; + BD7020ED1E49BB1D006BAE40 /* ViFacet.swift in Sources */ = {isa = PBXBuildFile; fileRef = BD7020EC1E49BB1D006BAE40 /* ViFacet.swift */; }; + BD7020EF1E49BB99006BAE40 /* ViFacetItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = BD7020EE1E49BB99006BAE40 /* ViFacetItem.swift */; }; BD7DED8C1DA2AFCF00CDF6DE /* ViSearchSDK.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BD7DED821DA2AFCF00CDF6DE /* ViSearchSDK.framework */; }; BD7DED911DA2AFCF00CDF6DE /* ViSearchSDKTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = BD7DED901DA2AFCF00CDF6DE /* ViSearchSDKTests.swift */; }; BD7DED931DA2AFCF00CDF6DE /* ViSearchSDK.h in Headers */ = {isa = PBXBuildFile; fileRef = BD7DED851DA2AFCF00CDF6DE /* ViSearchSDK.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -52,6 +54,8 @@ BD50FD611DA8B4220035FD78 /* UIColorExtension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = UIColorExtension.swift; path = Helper/UIColorExtension.swift; sourceTree = ""; }; BD6540561DA6302D00E10162 /* ViUploadSearchParamsTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ViUploadSearchParamsTests.swift; sourceTree = ""; }; BD6540591DA63F5200E10162 /* ViSearch.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ViSearch.swift; sourceTree = ""; }; + BD7020EC1E49BB1D006BAE40 /* ViFacet.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ViFacet.swift; sourceTree = ""; }; + BD7020EE1E49BB99006BAE40 /* ViFacetItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ViFacetItem.swift; sourceTree = ""; }; BD7DED821DA2AFCF00CDF6DE /* ViSearchSDK.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = ViSearchSDK.framework; sourceTree = BUILT_PRODUCTS_DIR; }; BD7DED851DA2AFCF00CDF6DE /* ViSearchSDK.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViSearchSDK.h; sourceTree = ""; }; BD7DED861DA2AFCF00CDF6DE /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; @@ -176,6 +180,8 @@ BDB1F7CB1DA4E01D000C5EAA /* ViImageResult.swift */, BDB1F7CD1DA4EA9C000C5EAA /* ViProductType.swift */, BDB1F7CF1DA4EBBC000C5EAA /* ViProductTypeList.swift */, + BD7020EC1E49BB1D006BAE40 /* ViFacet.swift */, + BD7020EE1E49BB99006BAE40 /* ViFacetItem.swift */, ); path = Response; sourceTree = ""; @@ -311,8 +317,10 @@ BD7DEDB11DA2C2C900CDF6DE /* ViSearchParams.swift in Sources */, BDB1F7D01DA4EBBC000C5EAA /* ViProductTypeList.swift in Sources */, BD7DEDAF1DA2C10100CDF6DE /* ViImageSettings.swift in Sources */, + BD7020EF1E49BB99006BAE40 /* ViFacetItem.swift in Sources */, BD65405A1DA63F5200E10162 /* ViSearch.swift in Sources */, BD7DEDAB1DA2B6C400CDF6DE /* ViColorSearchParams.swift in Sources */, + BD7020ED1E49BB1D006BAE40 /* ViFacet.swift in Sources */, BDB1F7CC1DA4E01D000C5EAA /* ViImageResult.swift in Sources */, BD7DEDA61DA2B14100CDF6DE /* ViBox.swift in Sources */, BDC096421DA357C8002166B4 /* SettingHelper.swift in Sources */, diff --git a/ViSearchSDK/ViSearchSDK/Classes/Request/ViBaseSearchParams.swift b/ViSearchSDK/ViSearchSDK/Classes/Request/ViBaseSearchParams.swift index 50f2651..ec0c011 100644 --- a/ViSearchSDK/ViSearchSDK/Classes/Request/ViBaseSearchParams.swift +++ b/ViSearchSDK/ViSearchSDK/Classes/Request/ViBaseSearchParams.swift @@ -57,6 +57,15 @@ open class ViBaseSearchParams : ViSearchParamsProtocol { /// Used for automatic object recognition public var detection : String? = nil + /// List of fields to enable faceting + public var facets : [String] = [] + + /// Limit of the number of facet values to be returned. Only for non-numerical fields + public var facetsLimit : Int = 10 + + /// whether to show the facets count in the response. + public var facetShowCount : Bool = false + // MARK: search protocol public func toDict() -> [String: Any] { var dict : [String:Any] = [:] @@ -98,6 +107,12 @@ open class ViBaseSearchParams : ViSearchParamsProtocol { dict["fl"] = fl } + if facets.count > 0 { + dict["facets"] = self.facets + dict["facets_limit"] = self.facetsLimit + dict["facets_show_count"] = self.facetShowCount ? "true" : "false" + } + return dict ; } diff --git a/ViSearchSDK/ViSearchSDK/Classes/Response/ViFacet.swift b/ViSearchSDK/ViSearchSDK/Classes/Response/ViFacet.swift new file mode 100644 index 0000000..8eadf51 --- /dev/null +++ b/ViSearchSDK/ViSearchSDK/Classes/Response/ViFacet.swift @@ -0,0 +1,35 @@ +// +// ViFacet.swift +// ViSearchSDK +// +// Created by Hung on 7/2/17. +// Copyright © 2017 Hung. All rights reserved. +// + +import Foundation + +open class ViFacet: NSObject { + + /// facet field name + public var key : String + + /// facet field items for string facets fields + public var items : [ViFacetItem] = [] + + // for numeric facet, a range will be return instead + public var min: Int? = nil + + // for numeric facet, a range with min, max will be returned insted + public var max: Int? = nil + + public init(key: String) { + self.key = key + } + + public init(key: String, items: [ViFacetItem] ) { + self.key = key + self.items = items + } + + +} diff --git a/ViSearchSDK/ViSearchSDK/Classes/Response/ViFacetItem.swift b/ViSearchSDK/ViSearchSDK/Classes/Response/ViFacetItem.swift new file mode 100644 index 0000000..ceafcee --- /dev/null +++ b/ViSearchSDK/ViSearchSDK/Classes/Response/ViFacetItem.swift @@ -0,0 +1,24 @@ +// +// ViFacetItem.swift +// ViSearchSDK +// +// Created by Hung on 7/2/17. +// Copyright © 2017 Hung. All rights reserved. +// + +import Foundation + +open class ViFacetItem: NSObject { + public var value : String + public var count : Int? = nil + + public init(value: String) { + self.value = value + } + + public init(value: String, count: Int?) { + self.value = value + self.count = count + } + +} diff --git a/ViSearchSDK/ViSearchSDK/Classes/Response/ViResponseData.swift b/ViSearchSDK/ViSearchSDK/Classes/Response/ViResponseData.swift index 7dc8bf4..aac19f7 100644 --- a/ViSearchSDK/ViSearchSDK/Classes/Response/ViResponseData.swift +++ b/ViSearchSDK/ViSearchSDK/Classes/Response/ViResponseData.swift @@ -40,6 +40,9 @@ open class ViResponseData: NSObject { /// for automatic object detection. See http://developers.visenze.com/api/?shell#automatic-object-recognition-beta for details public var productTypeList : [ViProductTypeList] = [] + /// facet results. Refer to http://developers.visenze.com/api/index.php#facet-and-filtering for details + public var facets : [ViFacet] = [] + /// store list of error messages if request is not successful public var error: [String] = [] @@ -98,6 +101,10 @@ open class ViResponseData: NSObject { if let pTypeListJson = json["product_types_list"] as? [Any] { self.productTypeList = ViResponseData.parseProductTypeList(pTypeListJson) } + + if let facetListJson = json["facets"] as? [Any] { + self.facets = ViResponseData.parseFacets(facetListJson) + } } catch { @@ -139,6 +146,42 @@ open class ViResponseData: NSObject { return results } + public static func parseFacets(_ arr: [Any]) -> [ViFacet]{ + var results = [ViFacet]() + for jsonItem in arr { + if let dict = jsonItem as? [String:Any] { + let key = dict["key"] as! String + let item = ViFacet(key: key) + + // string facet + if let itemArr = dict["items"] as? [Any] { + var facetItems : [ViFacetItem] = [] + for itemDict in itemArr { + + if let itemDict = itemDict as? [String:Any] { + let val = itemDict["value"] as! String + let count = itemDict["count"] as? Int + let facetItem = ViFacetItem(value: val, count: count) + facetItems.append(facetItem) + } + } + item.items = facetItems + } + + // numeric facet + if let range = dict["range"] as? [String: String] { + // extract min and max + item.min = Int(range["min"]!) + item.max = Int(range["max"]!) + } + + results.append(item) + } + } + + return results + } + // generate image results from the json array public static func parseResults(_ arr: [Any]) -> [ViImageResult]{ var results = [ViImageResult]() diff --git a/ViSearchSDK/ViSearchSDK/Classes/ViSearchClient.swift b/ViSearchSDK/ViSearchSDK/Classes/ViSearchClient.swift index c621f95..bfd2d2f 100644 --- a/ViSearchSDK/ViSearchSDK/Classes/ViSearchClient.swift +++ b/ViSearchSDK/ViSearchSDK/Classes/ViSearchClient.swift @@ -41,7 +41,7 @@ open class ViSearchClient: NSObject, URLSessionDelegate { public var timeoutInterval : TimeInterval = 10 // how long to timeout request public var requestSerialization: ViRequestSerialization - public var userAgent : String = "visearch-swift-sdk/1.1.0" + public var userAgent : String = "visearch-swift-sdk/1.2.0" private static let userAgentHeader : String = "X-Requested-With" // whether to authenticate by appkey or by access/secret key point diff --git a/ViSearchSDK/ViSearchSDK/Info.plist b/ViSearchSDK/ViSearchSDK/Info.plist index 9c590b8..d7fb954 100644 --- a/ViSearchSDK/ViSearchSDK/Info.plist +++ b/ViSearchSDK/ViSearchSDK/Info.plist @@ -15,7 +15,7 @@ CFBundlePackageType FMWK CFBundleShortVersionString - 1.1.0 + 1.2.0 CFBundleVersion $(CURRENT_PROJECT_VERSION) NSPrincipalClass From 8d773550c192202eb0ba09d6013c98321a8cef04 Mon Sep 17 00:00:00 2001 From: Ngo Hung Date: Tue, 7 Feb 2017 17:12:55 +0800 Subject: [PATCH 2/2] update docs and podspec --- Example/Example/SearchViewController.swift | 6 +++- README.md | 36 ++++++++++++++++++++++ ViSearchSDK.podspec | 2 +- 3 files changed, 42 insertions(+), 2 deletions(-) diff --git a/Example/Example/SearchViewController.swift b/Example/Example/SearchViewController.swift index 00a56b3..2e195f6 100644 --- a/Example/Example/SearchViewController.swift +++ b/Example/Example/SearchViewController.swift @@ -146,6 +146,10 @@ class SearchViewController: UIViewController, UITextFieldDelegate, SwiftHUEColor params!.fl = ["im_url"] // retrieve image url. By default the API only return im_name if does not specify fl parameter params!.limit = 15 // display 15 results per page +// params?.facets = ["price", "brand"] +// params?.facetsLimit = 10 +// params?.facetShowCount = true + ViSearch.sharedInstance.colorSearch( params: params!, successHandler: { (data : ViResponseData?) -> Void in @@ -162,7 +166,7 @@ class SearchViewController: UIViewController, UITextFieldDelegate, SwiftHUEColor } else { // perform segue here - //dump(data) +// dump(data) self.recentResponseData = data DispatchQueue.main.async { diff --git a/README.md b/README.md index f4977db..f13b950 100644 --- a/README.md +++ b/README.md @@ -25,6 +25,7 @@ - 6.2 [Filtering Results](#62-filtering-results) - 6.3 [Result Score](#63-result-score) - 6.4 [Automatic Object Recognition Beta](#64-automatic-object-recognition-beta) + - 6.5 [Facets Filtering](#65-facets-filtering) 7. [Event Tracking](#7-event-tracking) --- @@ -564,6 +565,41 @@ params.detection = "bag"; The detected product types are listed in `product_types` together with the match score and box area of the detected object. Multiple objects can be detected from the query image and they are ranked from the highest score to lowest. The full list of supported product types by our API will also be returned in `product_types_list`. +### 6.5 Facets Filtering + +You can get the facet results by sending a list of fields to enable faceting on. Here are some limitations on the request: + +- Facet fields need to be marked as `searchable` on ViSenze dashboard. +Text field is not supported as facet field even it is `searchable`. +System will return value range, the min, max value for numerical fields which are in ‘int’, ‘float’ type. + +- Only facet values that exist in current search results will be returned. For example, if your search results contain 10 unique brands, then the facet filters will return the value for these 10 brands. + +- Facet value list is ordered by the item count descendingly. +When the value is set to all (facets = *), all the searchable fields will be used as facet fields. + +Name | Type | Description +--- | --- | --- | +facets | array | List of fields to enable faceting. +facets_limit | Int | Limit of the number of facet values to be returned. Only for non-numerical fields. +facets_show_count | Boolean | Option to show the facets count in the response. + +```swift +params?.facets = ["price", "brand"] +params?.facetsLimit = 10 +params?.facetShowCount = true + +// view facet results +ViResponseData.facets + +// numerical facet would have a min and max +// ViFacet.min , ViFacet.max + +// string fields would have a count (if facetShowCount is set ) +// ViFacet.items + +``` + ## 7. Event Tracking ### Send Action For Tracking diff --git a/ViSearchSDK.podspec b/ViSearchSDK.podspec index 8e8ec57..79cb626 100644 --- a/ViSearchSDK.podspec +++ b/ViSearchSDK.podspec @@ -10,7 +10,7 @@ Pod::Spec.new do |s| s.name = "ViSearchSDK" - s.version = "1.1.0" + s.version = "1.2.0" s.summary = "A Visual Search API solution (Swift SDK)" s.description = <<-DESC