Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merge two RealmResult #4301

Open
DastanIqbal opened this issue Mar 8, 2017 · 22 comments
Open

Merge two RealmResult #4301

DastanIqbal opened this issue Mar 8, 2017 · 22 comments

Comments

@DastanIqbal
Copy link

DastanIqbal commented Mar 8, 2017

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 help@realm.io)

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

@cmelchior
Copy link
Contributor

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.

@Zhuinden
Copy link
Contributor

Zhuinden commented Mar 9, 2017

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

@kneth
Copy link
Member

kneth commented Mar 9, 2017

Realm Core does not provide this functionality currently.

@DastanIqbal
Copy link
Author

DastanIqbal commented Mar 9, 2017

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?

@cmelchior
Copy link
Contributor

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.

@DastanIqbal
Copy link
Author

k thanks @cmelchior let me try what you explained

And realm is awesome. :)

@DastanIqbal
Copy link
Author

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

@cmelchior
Copy link
Contributor

cmelchior commented Mar 9, 2017

It was made non-final in 2.2.2

@Zhuinden
Copy link
Contributor

Zhuinden commented Mar 9, 2017

@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

@DastanIqbal
Copy link
Author

DastanIqbal commented Mar 9, 2017

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

@Zhuinden
Copy link
Contributor

Zhuinden commented Mar 9, 2017

That statement is false.

@DastanIqbal
Copy link
Author

DastanIqbal commented Mar 9, 2017

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

@DastanIqbal
Copy link
Author

DastanIqbal commented Mar 9, 2017

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

@rscottcarson
Copy link

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

@staber
Copy link

staber commented May 18, 2017

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

@DastanIqbal
Copy link
Author

DastanIqbal commented May 23, 2017

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.

@mhd-adeeb-masoud
Copy link

mhd-adeeb-masoud commented Aug 8, 2017

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;
                });
    }

@Zhuinden
Copy link
Contributor

Zhuinden commented Aug 8, 2017

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());

@mhd-adeeb-masoud
Copy link

mhd-adeeb-masoud commented Aug 8, 2017

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.

@edmofro
Copy link

edmofro commented May 13, 2022

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
Copy link

sync-by-unito bot commented May 13, 2022

➤ 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.

@superbgv
Copy link

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests