Filter table by field names specified with RealmField.
No crashes.
App crashes:
Caused by: java.lang.IllegalArgumentException: Invalid query: field '_id' not found in class 'cards'.
at io.realm.internal.fields.CachedFieldDescriptor.compileFieldDescription(CachedFieldDescriptor.java:80)
at io.realm.internal.fields.FieldDescriptor.compileIfNecessary(FieldDescriptor.java:292)
at io.realm.internal.fields.FieldDescriptor.getColumnIndices(FieldDescriptor.java:185)
at io.realm.RealmQuery.equalToWithoutThreadValidation(RealmQuery.java:309)
at io.realm.RealmQuery.in(RealmQuery.java:551)
at io.realm.RealmQuery.in(RealmQuery.java:531)
...
Constants replaced with values.
@RealmClass(name = "cards")
public class CardModel implements RealmModel {
@PrimaryKey
@Required
@RealmField(name = "_id")
private String mKey;
// getters and setters
}
...
try (final Realm realm = mRealmProvider.get()) {
final RealmResults<CardModel> all1 = realm.where(CardModel.class)
.in("mKey", map(keys)) // will NOT crash here
.findAll();
final RealmResults<CardModel> all2 = realm.where(CardModel.class)
.in("_id", map(keys)) // will crash here
.findAll();
}
Realm version(s): 5.0.0
Realm sync feature enabled: no
Android Studio version: 3.0.1
Which Android version and device: API 24, Emulator
ColumnInfo has both custom field name and Java field name:
I think typed realm uses field names for the queries, while dynamic realm uses internal names.
@Iojjj This is working as intended. Queries on typed Realms are done using the name defined in Java, not the name in @RealmField, which is how it is stored on disk. This should also be described in https://github.com/realm/realm-java/blob/master/realm-annotations/src/main/java/io/realm/annotations/RealmNamingPolicy.java#L51
Like @Zhuinden correctly says, the internal name is only used in the dynamic API which has no knowledge about the Java model class.
We, unfortunately, haven't written that up for the website yet.
Hi @Zhuinden @cmelchior
thanks for you replies. So this is an expected behavior.
Are you planning to allow Realm instances to use internal names instead of Java ones? Right now if I try to refactor field names (change mKey to mId IDE will not find any usages of string literal mKey). Because ColumnInfo has both mappings of internal and Java names it can do conversion between internal and Java names in the way it can be used by Query.
For example:
class TestModel implements RealmModel {
@RealmField(name = "id")
private long mId;
@RealmField(name = "not_id")
private String mNotId;
}
...
// somewhere in ColumnInfo
// check if passed name is an internal name. If 'yes' return column info
// check if passed name is a Java field name. If 'yes' return column info
// thrown an exception otherwise
...
final TestModel r1 = mRealm.where(TestModel.class).equalsTo("id", 1).findFirst();
final TestModel r2 = mRealm.where(TestModel.class).equalsTo("mId", 1).findFirst();
so that r1 and r2 will represent the same object. From my point of view this is a valid way of accessing table row values with typed Realm instance.
What do you think about this approach?
No, there is no such plans as that wasn't a use case we considered when implementing this. Also, I'm not sure what you are trying to accomplish? If you want to use the internal name for queries, why use the internal name at all? Why not just call your fields not_id in Java?
We cannot allow using both internal and Java queries because there might be conflicts, e.g:
public class Foo extends RealmObject {
@RealmField(name = "foo")
public String bar;
@RealmField(name = "bar")
public boolean foo;
}
You can use use the DynamicRealm API to use the internal name, but that will make accessing properties much more annoying, e.g. obj.getLong("id") vs. obj.getId()
If it is because you are in the middle of a refactoring searching for the string "mKey" will do the trick (but of course you need to check it isn't mKey from another class).
This is also why we in a lot of internal tests have the best practice of static fields in the model class:
public class Person extends RealmObject {
public static final FIELD_NAME = "name";
public String name;
}
realm.where(Person.class).equalTo(Person.FIELD_NAME, "Jane").findAll();
Why not just call your fields not_id in Java?
Our naming convention allows only camel case with Hungarian notation.
We cannot allow using both internal and Java queries because there might be conflicts, e.g:
Yes, I though about it as well. But annotation processor can handle this:
RealmField annotation or with an empty name in annotation. Use Java names as keys.In this case you'll have a single map with field mappings.
I'm not sure what you are trying to accomplish
I have an implementation of generic storage class that has basic functionality for CRUD operations (get, put, delete, simple filter):
public abstract class Storage<T extends RealmModel> {
private final Class<T> mTClass;
public void deleteByIds(final List<String> ids) {
// assume that deletion is performed in transaction
mRealm.where(mTClass).in(ModelContract.ID, ids).deleteAllFromRealm();
}
}
All my models have an ID field (mId in Java, "id" as internal Realm field name). This is my model's Contract. In case if at some point of time I decide to rename mId field, I don't need to rename Contract name and fix all of my queries. This is similar to what you proposed with FIELD_NAME. But:
BaseModel class where I can define a single entry point for an ID field and its internal name constant. You're not allowing models classes to extend any other classes even if base classes extend RealmObject or implement RealmModel.ClassA, I need also rename them in all of my models classes so that my generic storage continue working without any issues.Yes, I can see the problem. Honestly, I think a better solution to your problem would be if we started allowing non-polymorphic inheritance, which would allow for a base class with shared fields. So far we have not done that because we feared that not having polymorphism would really trip people up but both our Cocoa binding and ObjectBox do this, so we should probably revisit that decision.
The reason I'm hesitant about "just" allowing queries on both internal and Java names is that it muddles the layers between Java defined names and internal names and we need to enforce that you cannot use an internal name that is already being used in the Java model class.
I'll need to think a little about this.
If it is because you are in the middle of a refactoring searching for the string
"mKey"will do the trick (but of course you need to check it isn't mKey from another class).This is also why we in a lot of internal tests have the best practice of static fields in the model class:
public class Person extends RealmObject { public static final FIELD_NAME = "name"; public String name; } realm.where(Person.class).equalTo(Person.FIELD_NAME, "Jane").findAll();
This is exactly the reason why it should be used in typed queries as well, because when you (accidentally) rename the java field name to e.g. firstName, you'll still have the proper FIELD_NAME for realm queries.
If you have it this way, then there's no IDE help and you may easily end up having FIELD_NAME different than java name.
Also another use case for using it in queries is, that then you can have (in kotlin) something like this:
const val TYPE_FIELD = "type"
@RealmField(TYPE_FIELD)
private var _type: String
var type: TypeEnum
get() = _type.toString()
set(value) {
_type = TypeEnum.valueOf(value)
}
And you can still use:
realm
.where<MyClass>()
.equals(TYPE_FIELD, TypeEnum.TYPE_A)
.findAll()
Which then provides more type-safety, because from code you may access whatever name you added, but in queries you use fields you defined in annotation.
Most helpful comment
This is exactly the reason why it should be used in typed queries as well, because when you (accidentally) rename the java field
nameto e.g.firstName, you'll still have the properFIELD_NAMEfor realm queries.If you have it this way, then there's no IDE help and you may easily end up having
FIELD_NAMEdifferent than javaname.Also another use case for using it in queries is, that then you can have (in kotlin) something like this:
And you can still use:
Which then provides more type-safety, because from code you may access whatever name you added, but in queries you use fields you defined in annotation.