realm-java icon indicating copy to clipboard operation
realm-java copied to clipboard

Merge two RealmResult

Open DastanIqbal opened this issue 7 years ago • 21 comments

We LOVE to help with any issues or bug you have!

Questions: If you have questions about how to use Realm, please ask on SO - we monitor the Realm tag.

Feature Request: Just fill in the first two sections below.

Bugs: To help you as fast as possible with an issue or bug please describe your issue and the steps you have taken to reproduce it in as many details as possible.

Thanks for helping us help you :-)

Remove this and above before submitting.

Goal

What do you want to achieve? I want to merge two result of Table User in RealmResult. RealmResults<User> r1 = realm.where(User.class) .equalTo(some condition) .findAllSorted("timestamp", Sort.ASCENDING); RealmResults<User> r2 = realm.where(User.class) .equalTo(some condition) .findAllSorted("timestamp", Sort.DESCENDING);

Expected Results

r=r1+r2

?

Actual Results

E.g. full stack trace with exception

Steps & Code to Reproduce

Describe your current debugging efforts.

Code Sample


> Your code here. Bigger samples should ideally be as separate Android Studio project, 
> in gists/repositories or privately at [email protected])

Version of Realm and tooling

Realm version(s): ? 2.2.1

Realm sync feature enabled: yes/no don't know Android Studio version: ? 2.3

Which Android version and device: ? 6 Moto X Play

DastanIqbal avatar Mar 08 '17 16:03 DastanIqbal

Hi @DastanIqbal Interesting, sort of like a CompositRealmResults, but can you explain more about your use case?

  • Should the merged result be auto-updated?
  • How should the elements be merged? Just [result1, result2] or possibly mixed somehow?

Note, it can already be accomplished today using:

List<User> combined = new ArrayList();
combined.addAll(result1);
combined.addAll(result2);

You would miss out on some of the RealmResults special features, though.

cmelchior avatar Mar 09 '17 07:03 cmelchior

@DastanIqbal You are looking for http://stackoverflow.com/a/34976540/2413303 (although he is missing the RealmChangeListener)

Zhuinden avatar Mar 09 '17 08:03 Zhuinden

Realm Core does not provide this functionality currently.

kneth avatar Mar 09 '17 09:03 kneth

Thanks for the response guys.

@cmelchior Should the merged result be auto-updated? ya it will but the end result will be result1+result2.

How should the elements be merged? Just [result1, result2] or possibly mixed somehow?
No, it will be just result1+result2.

Actually my first result1, Users based on status like new,ongoing in ascending order(by timestamp) and then result2, Users based on status like canceled,resolved in descending order(by timestamp).

its means new,ongoing will be on top, and then others.

and my RecylcerViews Adapter are total depend on RelamResult. so I can't use ArrayList for now.

@Zhuinden Ya I am using RealmChangeListener when anything changes in list.

@kneth Thanks kneth, but Can you consider this issue in future release build?

DastanIqbal avatar Mar 09 '17 14:03 DastanIqbal

You could conceivably implement this yourself, by creating a CompositeRealmResults class that extended RealmResults and accepts two or more RealmResults as input. You would then need to override all methods to account for the change in semantics, e.g addChangeListener should add the listener to more than one RealmResults, first() should only pick the result from the first RealmResults and so on.

I won't rule out we can provide an implementation of this at one point, but it will unfortunately not have a high priority unless there is a big demand for it.

cmelchior avatar Mar 09 '17 14:03 cmelchior

k thanks @cmelchior let me try what you explained

And realm is awesome. :)

DastanIqbal avatar Mar 09 '17 14:03 DastanIqbal

@cmelchior RealmResults is a final class, can't extend.

DastanIqbal avatar Mar 09 '17 14:03 DastanIqbal

It was made non-final in 2.2.2

cmelchior avatar Mar 09 '17 14:03 cmelchior

@DastanIqbal no it's not since I think something like Realm ~~2.2.0~~ 2.2.2

But even then, you conveniently skipped this answer http://stackoverflow.com/a/34976540/2413303

Zhuinden avatar Mar 09 '17 14:03 Zhuinden

@Zhuinden I checked that answer, but not helping, my adapter using RealmResult, cannot migrate to List. :(

DastanIqbal avatar Mar 09 '17 14:03 DastanIqbal

That statement is false.

Zhuinden avatar Mar 09 '17 15:03 Zhuinden

@cmelchior How do I access BaseRealm its local class, require in constructor and constructor is also local. v3.0.0

DastanIqbal avatar Mar 09 '17 15:03 DastanIqbal

Just created local package io.realm, everything is accessible now.

DastanIqbal avatar Mar 09 '17 15:03 DastanIqbal

@DastanIqbal Any progress? I'm interested in what you were able to implement.

rscottcarson avatar May 02 '17 21:05 rscottcarson

I have a use case for this also; 'pinning' items to the top of an adapter.

staber avatar May 18 '17 17:05 staber

Hi @rscottcarson, Sorry for late reply,

I used Arraylist to combined the result as @cmelchior mentioned in above comment

public ArrayList<RealmResults<Bean>> getXObject() {
        RealmResults<Bean> withName = realm.where(Bean.class).isNotNull("name").findAllSorted("name");
        RealmResults<Bean> withOutName = realm.where(Bean.class).isNull("name").findAllSorted("phoneno");

        ArrayList<RealmResults<Bean>> xInvites = new ArrayList<>();
        xInvites.add(withName);
        xInvites.add(withOutName);

        return xInvites;
    }

and I used @Zhuinden link modified little for RealmAdapter

public abstract class RealmMultiAdapter<T extends RealmObject> extends RecyclerView.Adapter {

    protected List<RealmResults<T>> realmResults;
    protected Context context;
    private RealmBaseAdapter<T> realmBaseAdapter;

    public RealmMultiAdapter(Context context) {
        this.context = context;
    }

    public void setRealmList(List<RealmResults<T>> realmResults) {
        this.realmResults = realmResults;
    }

    /**
     * Returns how many items are in the data set.
     *
     * @return count of items.
     */
    public int getCount() {
        if (realmResults == null) {
            return 0;
        }
        int count = 0;
        for (RealmResults<T> realmResult : realmResults) {
            count += realmResult.size();
        }
        return count;
    }

    /**
     * Returns the item associated with the specified position.
     *
     * @param i index of item whose data we want.
     * @return the item at the specified position.
     */
    public T getItem(int i) {
        if (realmResults == null || realmResults.size() == 0) {
            return null;
        }
        int count = 0;
        for (RealmResults<T> realmResult : realmResults) {
            if (i < realmResult.size() + count) {
                return realmResult.get(i - count);
            }
            count += realmResult.size();
        }
        return null;
    }

    /**
     * Returns the current ID for an item. Note that item IDs are not stable so you cannot rely on the item ID being the
     * same after {@link #notifyDataSetChanged()} or {@link #updateRealmResults(List<RealmResults<T>>)} has been called.
     *
     * @param i index of item in the adapter.
     * @return current item ID.
     */
    @Override
    public long getItemId(int i) {
        // TODO: find better solution once we have unique IDs
        return i;
    }

    /**
     * Updates the RealmResults associated to the Adapter. Useful when the query has been changed.
     * If the query does not change you might consider using the automaticUpdate feature.
     *
     * @param queryResults the new RealmResults coming from the new query.
     */
    public void updateRealmResults(List<RealmResults<T>> queryResults) {

        this.realmResults = queryResults;
        notifyDataSetChanged();
    }

    public RealmBaseAdapter<T> getRealmAdapter() {

        return realmBaseAdapter;
    }

    public void setRealmAdapter(RealmBaseAdapter<T> realmAdapter) {

        realmBaseAdapter = realmAdapter;
    }
}

then after you can extend RealmMultiAdapter with your adapter.

public class BeanAdapter extends RealmMultiAdapter<Bean>{

after that same rule as you follow for other realm adapter.

PS: I was expecting something else, but I compromised with this solution.

DastanIqbal avatar May 23 '17 02:05 DastanIqbal

EDITED according to @Zhuinden I know this may look very hacky but this is how I managed to do it:

    public Observable<ArrayList<Project>> getUserProjects(int userId) {
        return getDatabase()
                .where(User.class)
                .equalTo("id", userId)
                .findFirstAsync()
                .asObservable()
                .doOnUnsubscribe(()->closeConnections(1))
                .filter(result -> result.isLoaded() && result.isValid())
                .cast(User.class)
                .map(user -> {
                    ArrayList<Project> projects= new ArrayList<>();
                    projects.addAll(user.getProjectsJoined());
                    projects.addAll(user.getProjects());
                    Collections.sort(projects, (o1, o2) -> -o1.getCreatedAt().compareTo(o2.getCreatedAt()));
                    return projects;
                });
    }

mhd-adeeb-masoud avatar Aug 08 '17 15:08 mhd-adeeb-masoud

I don't see why you would map them to a RealmList if it's not actually a managed link between managed objects.

It would make more sense for it to be

List<Project> projects = new ArrayList<>(user.getProjectsJoined().size() + user.getProjects().size());

Zhuinden avatar Aug 08 '17 16:08 Zhuinden

You're absolutely right. I just did it that way because all of my adapters inherit from RealmAdapter, I know ArrayList is lighter... I am not even using auto update... otherwise it wouldn't have made any sense.

mhd-adeeb-masoud avatar Aug 08 '17 16:08 mhd-adeeb-masoud

Another use case: showing user search results, with any matching at the start showing first, and any matching anywhere in the word showing second.

edmofro avatar May 13 '22 17:05 edmofro

➤ Edwin Monk-Fromont commented:

Another use case: showing user search results, with any matching at the start showing first, and any matching anywhere in the word showing second.

sync-by-unito[bot] avatar May 13 '22 17:05 sync-by-unito[bot]

I have a use case for this also; 'pinning' items to the top of an adapter.

Yes i have the same requirement, do we have any direct method available from realm other than the workarounds suggested by DastanIqbal commented on May 23, 2017

superbgv avatar Jan 24 '23 10:01 superbgv