Restkit request dynamic mapping - ios

I'm using RestKit for posting objects to the server. In my object I have two properties: name and socialId.
I want to send to server only properties that have data.
If name != nil send:
{
"name" : "name",
}
If socialId != 0 send:
{
"socialId" : socialId,
}
But RestKit sends all data. For example:
{
"name" : "",
"socialId" : 0,
}
How can I change this behavior?

The answer is to using [RKDynamicMapping setObjectMappingForRepresentationBlock:]
You should check representation properties in block and add attributes mapping for desired properties.

Related

Can't count the occurences of the entity with a field of particular value inside a nested property using Spring Data ElasticSearch Repository

I have the Article entity and inside it there is a nested property, let's say Metadata.
I need to count all articles, which have a particular field inside this nested property, let's say indexed, assigned to e.g. 1.
Java Document Snippet:
#Document(indexName = "article", type = "article", useServerConfiguration = true, createIndex = false)
#Setting(settingPath = "/mappings/settings.json")
#Mapping(mappingPath = "/mappings/articles.json")
public class Article {
// getters and setters, empty constructor are omitted for brevity
#Id
private String id;
private Metadata metadata;
// remainder of the body is omitted
}
Metadata.class snippet
public class Metadata {
// getters and setters, empty constructor are omitted for brevity
private Integer indexed;
// remainder of the body is omitted
}
The query I use to retrieve articles, which satisfy the given criteria and which I put as a value of #org.springframework.data.elasticsearch.annotations.Query on top of the custom method:
{
"query": {
"bool": {
"must": [
{
"nested": {
"path": "metadata",
"query": {
"bool": {
"must": [
{
"match": {
"metadata.indexed": 1
}
}
]
}
}
}
}
]
}
}
}
My custom Spring Data ElasticSearch repository snippet with a custom method:
public CustomSpringDataElasticsearchRepository extends ElasticsearchRepository<Article, String> {
#Query("The query from above")
Long countByMetadata_Indexed(int value);
}
When I use the repository method shown above , I get java.lang.IllegalArgumentException: Expected 1 but found n results.
Custom Spring Data Elasticsearch Repository method(without #Query) returns 0(version without underscore returns 0 as well) though it should return everything correctly.
How do I get the correct results using Spring Data ElasticSearch Repository? Why does the custom method without #Query doesn't work as well?
UPD: The version of spring-data-elasticsearch used is 3.1.1.RELEASE.
Repository query methods currently(3.2.4.RELEASE) don't support the count by the fields inside nested fields.
As was mentioned previously, #Query annotation doesn't support custom count queries as of the latest version(3.2.4.RELEASE).
In other words, currently, the only way to do this query through Spring Data ElasticSearch is to use ElasticsearchTemplate bean or ElasticsearchOperations bean.
Credit: P.J.Meisch

Perform firebase query in a nested style. Best practice?

I am doing a practice app on an instagram like app on firebase and swift. The question I have is that I am finding myself nesting my firebase "observeSingleEventOfType" to obtain all the data I need such that I can put it in my "Post Class". I was wondering if there is a better way of doing it as it seems like it could take up alot of time just to load one post? (I am grabing one info, wait, then grab the next, wait, then grab the next,wait,grab the next... and put everything as one Post Item at the end of the chain)
Upon initial load, I need the location, top comments, whether or not the user liked the post, whether the user followed the post or not. Currently what I am doing is
->Create a geo query, grab the geo information. Inside the completion block, I have a observeSingleEventOfType to look for information from my posts node and store as dictionary. Inside the completion block of this, I create another observeSingleEventOfType call to get the top comments, then inside this completionblock, I create another observeSingleEventOfType to look for whether or not the user is following the post. Then inside this, I have all the data to store inside my Post class that looks like
init(postKey: String, distance: Double, topComments: [Comment], liked: Bool, followingPost: Bool, dictionary: Dictionary<String, AnyObject>) {}
Here is my Json structure.
{
"comments" : {
"postKEY123" : {
"CommentKEY123" : {
"comment" : "aa"
}
}
},
"follow" : {
"userId123" : {
"postKEY123" : true
}
},
"posts" : {
"postKEY123" : {
"commentCount" : 1,
"postDescription" : "Aa",
"likeCount" : 0,
"userId" : "userId123"
}
},
"users" : {
"userId123" : {
"email" : "user1#mail.com"
"liked" : {
"postKey123": true
}
},
}
"location" : {
"postKey123" : {
".priority" : "9q9hrjj7cd",
"g" : "9q9hrjj7cd",
"l" : [ 37.33769226, -122.02885785 ]
},
},
}
The reason that I have done it this way is so that the Json data is not nested. As an possible alternative method, would it be possible to fetch all the data for the post async in background and return it when all the info for that post is ready? I didnt think I could do that as you wouldnt be able to guarantee what and when the item comes back. This is why I grabbed the data one by one inside completion block of one another?
Thanks,

RestKit dynamic mapping with nested items

I am having trouble configuring a dynamic mapping for an array of results. The result objects I'd like to map are embedded within a "container" that describes the type of the enclosed object.
I have JSON similar to the following:
"list": {
"name": ""
"item_infos": [
{
"type": "TaskItem",
"item": {
"id": 0
"title": "A Task Item",
"task": "..."
}
"url": "..."
},
{
"type": "ReminderItem",
"item": {
"id": 0
"title": "A Reminder Item",
"reminder": "..."
}
"url": "..."
}]
}
I'd like to map this as a List object with an array of Items, where each item might be of a different type. The different types are finite and known.
class List: NSManagedObject {
#NSManaged var name: String
#NSManaged var items: NSOrderedSet
}
class Item: NSManagedObject {
#NSManaged var identifier: Int32
#NSManaged var title: String
// Each mappable `Item` is responsible for providing its own
// mapping object. It's overridden in the subclasses below.
class func responseMappingForManagedObjectStore(dos: RKManagedObjectStore) -> RKEntityMapping { ... }
}
class TaskItem: Item {
#NSManaged var task: String
}
class ReminderItem: Item {
#NSManaged var reminder: String
}
How can I map the embedded item directly under the list using RKDynamicMapping while still making use of the type field? I'm trying something along these lines for the response mapping:
let listMapping = RKEntityMapping(forEntityForName: "List", inManagedObjectStore: store)
responseMapping.addAttributeMappingsFromDictionary(["name": "title"])
let dynamicItemMapping = RKDynamicMapping()
dynamicItemMapping.setObjectMappingForRepresentationBlock { representation in
let itemMapping: RKEntityMapping
switch representation.valueForKeyPath("type") as? String {
case .Some("TaskItem"): itemMapping = TaskItem.responseMappingForManagedObjectStore(objectStore)
case .Some("ReminderItem"): itemMapping = ReminderItem.responseMappingForManagedObjectStore(objectStore)
default: return nil
// This is the bit I'm failing to solve. How can I return a mapping
// that essentially "skips" a level of the JSON, and just maps the
// embedded `item`, not the `item_info`.
let itemInfoMapping = RKObjectMapping(forClass: NSMutableDictionary.self)
itemInfoMapping.addRelationshipMappingWithSourceKeyPath("item", mapping: itemMapping)
return itemInfoMapping
}
listMapping.addPropertyMapping(RKRelationshipMapping(
fromKeyPath: "item_infos",
toKeyPath: "items",
withMapping: dynamicItemMapping))
With this mapping, an exception is raised:
-[__NSDictionaryM _isKindOfEntity:]: unrecognized selector sent to instance 0x7fd2603cb400
Which doesn't surprise me, as the way the dynamic mapping is set up just doesn't feel right anyway – I'm looking for a way to "skip" a level of the JSON and only map the embedded item.
An alternative attempt was to fully specify the fromKeyPath as "item_infos.item" for the relationship to the listMapping, but then I cannot use the type field in the dynamic mapping block to determine the type of Item mapping to use:
// ...
dynamicItemMapping.setObjectMappingForRepresentationBlock { representation in
// `type` inaccessible from the nested item representation
switch representation.valueForKeyPath("type") as? String {
case .Some("TaskItem"): return TaskItem.responseMappingForManagedObjectStore(objectStore)
case .Some("ReminderItem"): return ReminderItem.responseMappingForManagedObjectStore(objectStore)
default: return nil
}
listMapping.addPropertyMapping(RKRelationshipMapping(
fromKeyPath: "item_infos.item",
toKeyPath: "items",
withMapping: dynamicItemMapping))
You can't do exactly what you're trying to do, because you're connecting a core data relationship to what will be an array of dictionaries which contains managed objects.
The simplest solution is to take your skip logic out from where it is and create all of the mappings for the managed objects (task and reminder) by adding item. at the start of the source key (path). So
"item.title" : "title"

Handling ignored properties in Mantle

I need to have one property which is not present in JSON but it need to be set when I try to use this Model Object. I think it'd be easier to show you the example...
Here is my sample JSON:
[{
"name" : "Safari",
"key" : "safari",
"app_url_scheme" : "http://www.twitter.com",
"actions" :
[{
"key" : "show_profile",
"url_format" : "http://www.twitter.com/{{profile_screenname}}"
}]
}
]
And JSONKeyPathsByPropertyKey method looks like this:
+ (NSDictionary *)JSONKeyPathsByPropertyKey
{
return #{
#"appName" : #"name",
#"appKey" : #"key",
#"appURLScheme" : #"app_url_scheme",
#"appActions" : #"actions",
#"isInstalled" : NSNull.null
};
}
So, as you can see there is this isInstalled method which should be ignored during mapping (as there isn't field like this in JSON) but I need to set this property as soon as this Object is fully mapped. What is more I need to set it based on other property provided in JSON. How to achieve this?
I've found solution like this:
#"videoType" : #"#Selector(videoTypeFromString:, type)",
//! implemented on instance you are parsing
- (NSUInteger)videoTypeFromString:(NSString *)type
{
if ([type isEqualToString:#"shortVideo"]) {
return VideoTypeShort;
}
return VideoTypeLong;
}
But this #selector is never being called...

Nested Entities in JsonResultsAdapter

I am trying to connect to a third party service using Breeze with a custom JsonResultsAdapter.
The third party service has the "metadata" related to an entity in the root node of the array, then the variables are in a "data" property on the "metadata" object.
The format has two ways of defining relationships. One is via a "#ref" field which references the id of another entity. The other is by having the related object defined inline (instead of the "#ref") which does not have an explicit id, but which is only ever referenced by the "parent" object.
The data looks like:
[{
"id" : "abc",
"type" : "foo",
"data": { "relationshipRef" : { "#ref" : "someid" } }
},
{
"id": "someid",
"type" : "bar",
"data" : { "relationshipInline" : { "type" : "baz",
"data" : { "something" : "whatever",
"innerRelation" : { "#ref" : "abc"}
}
}
}]
I'm currently (in JsonResultsAdapter's visitNode function) moving the properties in the "data" object up into the "root" node, and then replacing any object with an "#ref" property with the value of the "#ref" key and appending an ID to the end (so that relationships can use the original name in the EntityType). IE, the first object would become:
{
"id" : "abc",
"type" : "foo",
"relationshipRefID" : "someid"
}
This works for top level entities and relationships, but I'm having problems with the nested ones.
How would you approach solving this problem?
I was going to use ComplexTypes but the documentation mentioned that they cannot have "navigationProperties" (relationships), which as you can see above is required (the "innerRelation" property).
In some cases, the entities can be nested down to 3 levels or so.
Here is my current visitNode function:
visitNode: function(node, parseContext, nodeContext) {
if(node instanceof Object && node.type != null) {
if(node.deleted) {
//TODO: make sure the object is removed from the manager
return {ignore:true};
}
//We need to tweak the data structure to fit what breeze expects.
//It expects properties to be in the same level as the "metadata" for an object ("type" etc),
//So we need to move the properties from the data object into the node, and fix up relationships.
if(parseContext.entityManager.metadataStore.getEntityType(node.type, true) != null) {
var data = node.data;
for(var key in data) {
var prop = data[key];
//Move any foreign key fields to be "relationID":id instead of "relation":{"#ref":id}
if(prop instanceof Object) {
var ref = prop["#ref"];
if(ref != null) {
node[key+"ID"] = ref
data[key] = null;
continue;
}
}
//TODO: Handle inline references <- This is where I need help!
node[key] = data[key];
}
return {
entityType: node.type,
nodeId: node.id
}
}
else {
return {ignore:true};
}
}
}
Well, apparently I should have tested more before asking here.
It turns out that this works automatically based on the navigationProperties defined in the model! Awesome. I did have to generate ids for the inner nodes that did not have them, but that was simple.

Resources