maxi182
maxi182

Reputation: 33

Realm Query not giving correct results

I have the following data structure which consist in a list of providers, having products and each product has different types

Provider Class

public class Provider extends RealmObject {

    @SerializedName("id")
    @PrimaryKey
    public int providerId;
    public String name;
    public RealmList<Product> products;

}

Product Class

public class Product extends RealmObject {

    @SerializedName("id")
    @PrimaryKey
    public int productId;
    public String name;
    public RealmList<ProductType> types;

}

Product type class

public class ProductType extends RealmObject {

    @SerializedName("id")
    @PrimaryKey
    public int productTypeId;
    public String name;
    public Packaging packaging; }

Here is a JSON example of what is loaded into realm:

{
  "providers": [{
      "id": 1,
      "name": "PROVIDER_1",
      "products": [{
        "id": 10,
        "name": "Banana",
        "types": [{
            "id": 101,
            "name": "Costa rica",
            "packaging": {
              "isFree": true,
              "value": 0
            }
          },
          {
            "id": 102,
            "name": "Brasil",
            "packaging": {
              "isFree": true,
              "value": 0
            }
          }
        ]
      }]
    },{
      "id": 4,
      "name": "PROVIDER_2",
      "products": [{
        "id": 10,
        "name": "Banana",
        "types": [{
            "id": 103,
            "name": "Ecuador Prem",
            "packaging": {
              "isFree": true,
              "value": 0
            }
          }
        ]
      },
      {
        "id": 21,
        "name": "Apple",
        "types": [{
            "id": 212,
            "name": "Red del",
            "packaging": {
              "isFree": true,
              "value": 0
            }
          }
        ]
      }]
    }

    ]
}

So, the problem I am having is appear when I try to fetch types of Bananas using the following query

RealmResults<Provider> results = mRealm.where(Provider.class).equalTo("providerId", providerId).findAll();
Product product = results.where().equalTo("providerId", providerId).findFirst().products.where().equalTo("productId", productId).findFirst();
RealmList<ProductType> types = product.types;

the list of Types returned is giving me always the types of the second provider. In current example, I am getting "Ecuador Prem" even if I am requesting provider id 1 which is PROVIDER_1, and that provider must return as types "Costa rica" and "Brasil".

Upvotes: 0

Views: 141

Answers (1)

EpicPandaForce
EpicPandaForce

Reputation: 81539

You have either one of two or three problems:

1.) your API returns that the product type is of id for Banana, but you are getting id=10;Banana both for Provider1 and Provider2, therefore the Banana that has Provider1 is saved, after which Banana that has Provider2 with the same ID is saved (and therefore overwriting the original Banana with Provider1 instead of "merging them" for example).

So instead of directly mapping API responses to RealmObjects, you're supposed to introduce a manually computed composite key, which is most likely a String field that contains both the ProviderID concatenated with the ProductTypeID.

public class Product extends RealmObject {

    @PrimaryKey
    public String providerWithProductId; // must be set, like "$PROVIDERID_$PRODUCTID"

    @Index
    public int productId;
    public String name;
    public RealmList<ProductType> types;

}

I hope this is not a Sync Realm!

2.) You are directly mapping the JSON objects to Realm, possibly out of "convenience", but this deprives you of setting up an optimal schema for your queries.

In reality, "Provider" doesn't seem particularly important beyond that a particular banana belongs to a certain provider; this can be modelled as a field, instead of an object link.

// public class Provider extends RealmObject {
// 
//     @SerializedName("id")
//     @PrimaryKey
//     public int providerId;
//     public String name;
//    public RealmList<Product> products;
// 
// }

public class Product extends RealmObject {

    @PrimaryKey
    public String providerWithProductId; // must be set

    @Index
    public int productId;
    public String productName;

    @Index
    public int providerId;
    public String providerName;

    public RealmList<ProductType> productTypes;

}

On top of that, packaging is just two fields that could easily be merged into ProductType. And also, if Ecuador Prem type can belong to multiple products and multiple providers, then it should also have a composite key constructed from all 3 of these IDs!

public class ProductType extends RealmObject {
    @PrimaryKey
    public String providerWithProductWithProductTypeId; // you must fill this yourself

    @Index
    public int providerId;

    @Index
    public int productTypeId;

    @Index
    public int productId;

    public String productTypeName;

    //public Packaging packaging; 
    public boolean packagingIsFree;

    public long packagingValue;   
}

3.) Once you have that, you could easily rely on either of checking directly for the providerId also saved into the ProductType, or otherwise rely on @LinkingObjects to invert the direction of your query.

public class ProductType extends RealmObject {
    @PrimaryKey
    public String providerWithProductWithProductTypeId; // you must fill this yourself

    @Index
    public int providerId;

    @Index
    public int productTypeId;

    @Index
    public int productId;

    public String productTypeName;

    //public Packaging packaging; 
    public boolean packagingIsFree;

    public long packagingValue;   

    @LinkingObjects("productTypes")
    public final RealmResults<Product> isTypeOfProducts = null;
}

After which you could do either of

// RealmResults<Provider> resultsmRealm.where(Provider.class).equalTo("providerId", providerId).findAll();
// Product product = results.where().equalTo("providerId", providerId).findFirst().products.where().equalTo("productId", productId).findFirst();
// RealmList<ProductType> types = product.types;

RealmResults<ProductType> types = mRealm.where(ProductType.class).equalTo("providerId", providerId).findAll();

// or
RealmResults<ProductType> types = mRealm.where(ProductType.class).equalTo("isTypeOfProducts.providerId", providerId).findAll();

So if this app is not in production yet, then completely reconsider your Realm schema.

Upvotes: 1

Related Questions