Update
I finally figured this one out:
The key is to create the group with an account name and account type, something that is not really apparent looking at the documentation (there are no fields with those name in ContactsContract.Groups they are in the SyncColumns). When you create the group with those two values, the sync process will generate the source_id for you, at which point you can add member using either the group row id or the source_id.
Here is some sample code if anyone needs it.
ContentValues values = new ContentValues();
values.put(ContactsContract.Groups.TITLE,"yourGroupName");
values.put(ContactsContract.Groups.ACCOUNT_TYPE,"com.google");
values.put(ContactsContract.Groups.ACCOUNT_NAME,"someuser#gmail.com");
values.put(ContactsContract.Groups.GROUP_VISIBLE,1);
context.getContentResolver().insert(ContactsContract.Groups.CONTENT_URI, values);
then to add members:
values = new ContentValues();
values.put(ContactsContract.CommonDataKinds.GroupMembership.MIMETYPE,ContactsContract.CommonDataKinds.GroupMembership.CONTENT_ITEM_TYPE);
values.put(ContactsContract.CommonDataKinds.GroupMembership.RAW_CONTACT_ID, 22);
//values.put(ContactsContract.CommonDataKinds.GroupMembership.GROUP_ROW_ID, 56);
// the above or
values.put(ContactsContract.CommonDataKinds.GroupMembership.GROUP_SOURCE_ID,"sourceIdthatIsFilledInAfterSync");
//context.getContentResolver().insert(ContactsContract.Data.CONTENT_URI,values);
Original question:
I have been struggling with an issue related to syncing group memberships and think I figured out the cause, but I don't know how to fix it.
After creating 2 groups , one via code the the other manually and then query the groups using these columns.
private final static String[] GROUP_COLUMNS = {
ContactsContract.Groups._ID,
ContactsContract.Groups.DATA_SET,
ContactsContract.Groups.NOTES,
ContactsContract.Groups.SYSTEM_ID,
ContactsContract.Groups.GROUP_VISIBLE,
ContactsContract.Groups.DELETED,
ContactsContract.Groups.SHOULD_SYNC,
ContactsContract.Groups.SOURCE_ID,
ContactsContract.Groups.TITLE
};
I can dump out the results as this.
: --- begin ---
: key = title , value = myNewTestGroup
: key = data_set , value = null
: key = _id , value = 45
: key = sourceid , value = null
: key = group_visible , value = 1
: key = system_id , value = null
: key = should_sync , value = 1
: key = notes , value = myNewTestGroup
: key = deleted , value = 0
: --- end ---
: --- begin ---
: key = title , value = Mytest2
: key = data_set , value = null
: key = _id , value = 46
: key = sourceid , value = 144c8b8d0cca8a52
: key = group_visible , value = 1
: key = system_id , value = null
: key = should_sync , value = 1
: key = notes , value = Mytest2
: key = deleted , value = 0
: --- end ---
The manually create group (Mytest2) has a souceid which is listed as a column in ContactsContract.SyncColumns, while the code generated group has null.
I see references to source_id in may places in the android docs but I can't see how to obtain one.
I think somehow i would get this if i associate the group with an account.
Does anyone know how to associate at group with an account, or otherwise get this source id field set?
I finally figured this one out: The key is to create the group with an account name and account type, something that is not really apparent looking at the documentation (there are no fields with those name in ContactsContract.Groups they are in the SyncColumns). When you create the group with those two values, the sync process will generate the source_id for you, at which point you can add member using either the group row id or the source_id.
Here is some sample code if anyone needs it.
ContentValues values = new ContentValues();
values.put(ContactsContract.Groups.TITLE,"yourGroupName");
values.put(ContactsContract.Groups.ACCOUNT_TYPE,"com.google");
values.put(ContactsContract.Groups.ACCOUNT_NAME,"someuser#gmail.com");
values.put(ContactsContract.Groups.GROUP_VISIBLE,1);
context.getContentResolver().insert(ContactsContract.Groups.CONTENT_URI, values);
then to add members:
values = new ContentValues();
values.put(ContactsContract.CommonDataKinds.GroupMembership.MIMETYPE,ContactsContract.CommonDataKinds.GroupMembership.CONTENT_ITEM_TYPE);
values.put(ContactsContract.CommonDataKinds.GroupMembership.RAW_CONTACT_ID, 22);
//values.put(ContactsContract.CommonDataKinds.GroupMembership.GROUP_ROW_ID, 56);
// the above or
values.put(ContactsContract.CommonDataKinds.GroupMembership.GROUP_SOURCE_ID,"sourceIdthatIsFilledInAfterSync");
context.getContentResolver().insert(ContactsContract.Data.CONTENT_URI,values);
The data in sourceid still needs to be set in the second example before you are able to retrieve it.
here is what the docs have to say about sourceid:
String SOURCE_ID read/write:
String that uniquely identifies this row to its source account.
Typically it is set at the time the raw contact is inserted and never
changed afterwards. The one notable exception is a new raw contact: it
will have an account name and type (and possibly a data set), but no
source id. This indicates to the sync adapter that a new contact needs
to be created server-side and its ID stored in the corresponding
SOURCE_ID field on the phone.
Related
I have a DynamoDB Table "Music". On this it has a GSI with partition key "Category" and sort key "UserRating".
I can query easily as an example for songs that are in "Category" = "Rap" and "UserRating" = 1
How ever what I would like to do for example is query songs that are in "Category" = "Rap + Rock + Jazz" and "UserRating" = 1.
Is this possible or do I need to make multiple queries and join them on the client side?
This is the code I am currently querying with:
SongDatabaseMappingAdapter songs = new SongDatabaseMappingAdapter();
songs.setCategory("Rap");
String userRatingQueryString = "1";
Condition rangeKeyCondition = new Condition()
.withComparisonOperator(ComparisonOperator.EQ)
.withAttributeValueList(new AttributeValue().withN(userRatingQueryString));
DynamoDBQueryExpression queryExpression = new DynamoDBQueryExpression()
.withHashKeyValues(songs)
.withIndexName("Category-UserRating-index")
.withRangeKeyCondition("UserRating", rangeKeyCondition)
.withConsistentRead(false);
return mapper.query(SongDatabaseMappingAdapter.class, queryExpression);
You'll have to make 3 different calls (i.e. one each for 'Rap, 1', 'Rock, 1' and 'Jazz, 1') and then merge the response on the client side.
If Category and UserRating were partition-key and sort-key on your table (and not on the GSI, as is your case), then you could've used the BatchGetItem API to get your data in one go. Unfortunately, the API doesn't work on GSIs.
Is it possible to use an alias (AS) in a query for ORMLite in Android? I am trying to use it with the following code:
String query =
"SELECT *, (duration - elapsed) AS remaining FROM KitchenTimer ORDER BY remaining";
GenericRawResults<KitchenTimer> rawResults =
getHelper().getKitchenTimerDao().queryRaw(
query, getHelper().getKitchenTimerDao().getRawRowMapper());
But when this codes gets executed it gives the following error:
java.lang.IllegalArgumentException: Unknown column name 'remaining' in table kitchentimer
java.lang.IllegalArgumentException: Unknown column name 'remaining' in table kitchentimer
The raw-row-mapper associated with your KitchenTimerDao expects the results to correspond directly with the KitchenTimer entity columns. However, since you are adding your remaining column, it doesn't no where to put that result column, hence the exception. This is a raw-query so you will need to come up with your own results mapper -- you can't use the DAO's. See the docs on raw queries.
For instance, if you want to map the results into your own object Foo then you could do something like:
String query =
"SELECT *, (duration - elapsed) AS remaining FROM KitchenTimer ORDER BY remaining";
GenericRawResults<Foo> rawResults =
orderDao.queryRaw(query, new RawRowMapper<Foo>() {
public Foo mapRow(String[] columnNames, String[] resultColumns) {
// assuming 0th field is the * and 1st field is remaining
return new Foo(resultColumns[0], Integer.parseInt(resultColumns[1]));
}
});
// page through the results
for (Foo foo : rawResults) {
System.out.println("Name " + foo.name + " has " + foo.remaining + " remaining seconds");
}
rawResults.close();
I had the same problem. I wanted to get a list of objects but adding a new attribute with an alias.
To continue using the object mapper from OrmLite I used a RawRowMapper to receive columns and results. But instead of convert all columns manually I read the alias first and remove its reference in the column arrays. Then it is possible to use the OrmLite Dao mapper.
I write it in Kotlin code:
val rawResults = dao.queryRaw<Foo>(sql, RawRowMapper { columnNames, resultColumns ->
// convert array to list
val listNames = columnNames.toMutableList()
val listResults = resultColumns.toMutableList()
// get the index of the column not included in dao
val index = listNames.indexOf(ALIAS)
if (index == -1) {
// There is an error in the request because Alias was not received
return#RawRowMapper Foo()
}
// save the result
val aliasValue = listResults[index]
// remove the name and column
listNames.removeAt(index)
listResults.removeAt(index)
// map row
val foo = dao.rawRowMapper.mapRow(
listNames.toTypedArray(),
listResults.toTypedArray()
) as Foo
// add alias value. In my case I save it in the same object
// but another way is to create outside of mapping a list and
// add this value in the list if you don't want value and object together
foo.aliasValue = aliasValue
// return the generated object
return#RawRowMapper foo
})
It is not the shortest solution but for me it is very important to keep using the same mappers. It avoid errors when an attribute is added to a table and you don't remember to update the mapping.
How can I get Dynamo's UpdateItemRequest to either ignore the range key, or do a coarse test such as not null?
I am using a range key which contains the time of a record update. Separately, I update rows which I select using an ID field which is not a key. I update the ID field using an expectedAttribute in an UpdateItemRequest. Dynamo's UpdateItemRequest forces me to specify a range key value. My ID update code will not know the range key value. Can I somehow not specify a range key and not get an error? Or can I provide a simple range key test like not null?
When I remove the range key, AWS throws an error, "The provided key element does not match the schema"
// construct the update map
HashMap<String, AttributeValueUpdate> updates = new HashMap<String, AttributeValueUpdate>();
AttributeValue av = new AttributeValue().withN("1");
AttributeValueUpdate avu = new AttributeValueUpdate().withValue(av).withAction(AttributeAction.PUT);
updates.put("Column1", avu);
AttributeValue av2 = new AttributeValue().withN("2");
AttributeValueUpdate avu2 = new AttributeValueUpdate().withValue(av2).withAction(
AttributeAction.PUT);
updates.put("Column2", avu2);
// construct the key map
HashMap<String, AttributeValue> keyMap = new HashMap<String, AttributeValue>();
AttributeValue hashValue = new AttributeValue().withN("10");
keyMap.put("hashKey", hashValue);
AttributeValue lastModKeyValue = new AttributeValue().withN("1404175127074");
// ****** I want to remove this key but Dynamo throws an error
keyMap.put("problemRangeKey", lastModKeyValue);
// expected value comparison
AttributeValue idValue = new AttributeValue().withN("100");
ExpectedAttributeValue expected = new ExpectedAttributeValue(idValue);
UpdateItemRequest request = new UpdateItemRequest().withTableName(DatabaseConstants.CHART_TABLE)
.withKey(keyMap).withAttributeUpdates(updates).addExpectedEntry(ID, expected);
ddb.updateItem(request);
For clarity, here is how I use the range key to find rows after a specific time. If I remove this time range, then I would do a big scan every time to find rows after a specific time.
Map keyConditions = new HashMap();
AttributeValue attribute = new AttributeValue().withN("10");
Condition hashKeyCondition = new Condition().withComparisonOperator(ComparisonOperator.EQ.toString())
.withAttributeValueList(attribute);
keyConditions.put("hashKey", hashKeyCondition);
// specify new records
long lastQueryTime = PrefsActivity.getLastDynamoQueryTime(activity);
String lastTimeString = String.valueOf(lastQueryTime);
Log.v(TAG, "get Charts from dynamo that are after " + lastTimeString);
// update the query time to now
long now = System.currentTimeMillis();
PrefsActivity.setLastDynamoQueryTime(activity, now);
AttributeValue timeAttribute = new AttributeValue().withN(lastTimeString);
Condition timeCondition = new Condition().withComparisonOperator(ComparisonOperator.GT.toString())
.withAttributeValueList(timeAttribute);
keyConditions.put("problemRangeKey", timeCondition);
List<Map<String, AttributeValue>> ChartsInMaps = new ArrayList<Map<String, AttributeValue>>();
Map lastEvaluatedKey = null;
QueryRequest request = new QueryRequest().withTableName("tableName")
.withKeyConditions(keyConditions).withExclusiveStartKey(lastEvaluatedKey);
QueryResult result = ddb.query(request);
I'm working with the latest AWS Android SDK. In the sample above, I'm setting the hashKey to "10". In practice, that will vary.
Thanks in advance for any help!
Jeff
The question is, you want to update all items with a specific hashKey value and a specific column value (column name is id).
The table is Hash-Range schema. Since you don't know the range key, you have to fetch all the rows with that hashKey and then do a filter based on the column value (ID == value). You can use query filter to do that efficiently. Then for each row in your query, do a conditional put with expected value (ID == value) just to be sure.
QueryRequest request = new QueryRequest();
// set up query request (table name, etc)
// set up query filter (ID == value)
QueryResponse response = client.query(request)
for (item in response.items):
PutItemRequest req = new PutItemRequest();
// set up put item request
// set up primary key for item to update
// hashkey = item.hashkey
// rangekey = item.rangeKey
// set up expected value (ID == value)
client.updateItem(req)
On the other hand, since you want to efficiently query all the rows after a specific time, you can create a Local Secondary Index with "time" as a range key instead of making "time" the range key of your base table. You can still do efficient query using LSI (compared with scanning the table and filter by time). Whats more important is, if your "ID" attribute can uniquely identify a row with your hashkey, you can make "ID" the range key of your base table. This way you can also update the item efficiently since you already know the hashkey value and range key value (id value).
Of course it only truly benefits you when your hashkey and "ID" can uniquely identify a row :)
I want to know how can i use MAX MIN command with ORMLITE.
For example lets say we have this table
Table Name = Example
Column 1 = id
Column 2 = name
In ORMLITE how can i get max id ? I looked here but i didnt't understand exactly..
Can someone show me example about Max min in ORMLITE ?
QueryBuilder<Account, Integer> qb = accountDao.queryBuilder();
qb.selectRaw("MIN(orderCount)", "MAX(orderCount)");
// the results will contain 2 string values for the min and max
results = accountDao.queryRaw(qb.prepareStatementString());
String[] values = results.getFirstResult();
I found this from documentation
This is how I query for max ID in my code:
QueryBuilder<Example, String> builder = dao.queryBuilder();
builder.orderBy("id", false); // true or false for ascending so change to true to get min id
Example example = dao.queryForFirst(builder.prepare());
String id = null;
if (example == null)
id = "-1";
else
id = example.getId();
A couple of alternative answers can also be found here:
ORMLite - return item w/ maximum ID (or value)
You can use:
dao.queryRawValue("select MAX(columnName) from tableName")
It will directly return long value.
refer: http://ormlite.com/javadoc/ormlite-core/doc-files/ormlite_5.html#DAO-Methods
queryRawValue(String query, String... arguments)
Perform a raw query that returns a single value (usually an aggregate function like MAX or COUNT). If the query does not return a single long value then it will throw a SQLException.
First, please forgive me for my approximative English.
I have 2 tables, one (tb_facture) with a foreign key referencing the second (tb_garage)
My problem is that I can't INSERT in the tb_facture without giving a foreign key that references tb_garage.
If I give a valid foreign key, there is no problem. But if I try to give -1 (or something else), it doesn't insert.
I must be able to do this because, in my app, it is not an obligation to link a garage to the invoice.
Here is the insertion code :
private void saveState(){
String libelleFac = etLibelleFac.getText().toString();
String dateFac = tvDateAfficheeFac.getText().toString();
String kmFac = etKmFac.getText().toString();
String remarqueFac = etRemarqueFac.getText().toString();
float montantFac = Float.parseFloat(etMontantFac.getText().toString());
String datePaieFac = tvDateAfficheePaiement.getText().toString();
if(mRowId == null){
if (cbGarageFac.isChecked()){
db.insererFacture(itemSpListeVhc, itemSpListeGar, dateFac, libelleFac, remarqueFac, kmFac, montantFac, datePaieFac);
}
else{
db.insererFacture(itemSpListeVhc, -1, dateFac, libelleFac, remarqueFac, kmFac, montantFac, datePaieFac);
}
}
}
If I replace -1 by a valid foreign key or by itemSpListeGar, it works.
Here is my query :
public long insererFacture(long fkVhcFac, long fkGarFac, String dateFac, String libelleFac, String remarqueFac, String kmFac, float montantFac, String datePaieFac){
ContentValues values = new ContentValues();
values.put("fk_vhc_fa", fkVhcFac);
values.put("fk_gar_fa", fkGarFac);
values.put("date_fa", dateFac);
values.put("libelle_fa", libelleFac);
values.put("remarque_fa", remarqueFac);
values.put("kilometrage_fa", kmFac);
values.put("montant_fa", montantFac);
values.put("date_paie_fa", datePaieFac);
return db.insert("TB_FACTURE", null, values);
}
Thanks for your help!
PS : I specify that I don't use real FOREIGN KEYS, but the trigger method which is like this :
"fk_gar_fa INTEGER CONSTRAINT ct_fk_gar_fa REFERENCES TB_GARAGE(_id),"
You cannot bypass this, enforcing referential integrity is at the basis of relational databases!
In your case I'd suggest making a Garage record that says: "no Garage". And use it's primary key as the foreign key when adding a record in the Facture table that does not have an associated Garage.
It's a bit ugly, but either this, or you change your relational database to no longer enforce integrity and you'll open a new can of worms. :)