Large USN value in Evernote initial sync - java

I am trying to sync an evernote account using Java program, below is the code sample for it
NoteStoreClient noteStoreClient = clientFactory.createNoteStoreClient();
SyncChunk syncChunk = noteStoreClient.getSyncChunk(0, 200, true);
while (true) {
List<Note> noteListforCurrentChunk = syncChunk.getNotes();
//Sync to DB
syncChunk = noteStoreClient.getSyncChunk(syncChunk.getChunkHighUSN(), 200, true);
if (syncChunk.getChunkHighUSN() == syncChunk.getUpdateCount()) {
return;
}
}
The first call to syncChunk.getChunkHighUSN() returns 1187 for my user, which results in no notes being retrieved. This is happening to some accounts only
Can anyone help on this ?

Here's some quote on USN from the doc.
The USNs within an account start at “1” (for the first object created in the account) and then increase
monotonically every time an object is created, modified, or deleted. The server keeps track of the
“update count” for each account, which is identical to the highest USN that has been assigned.
So, the higher number of USN doesn't always mean you have many notes. It just indicates that the user has done some operations on its account.

I had to create a filter to make it working. Now i am able to retrieve all the notes under this notebook
SyncChunkFilter filter = new SyncChunkFilter();
filter.setIncludeNotes(true);
NoteStoreClient noteStoreClient = clientFactory.createNoteStoreClient();
SyncChunk syncChunk = noteStoreClient.getFilteredSyncChunk(0, 200, filter);
while (true) {
List<Note> noteListforCurrentChunk = syncChunk.getNotes();
//Sync to DB
syncChunk = noteStoreClient.getFilteredSyncChunk(syncChunk.getChunkHighUSN(), 200, filter);
if (syncChunk.getChunkHighUSN() == syncChunk.getUpdateCount()) {
return;
}
}

Related

Loading all contacts using the Microsoft Graph API sometimes looses/skips pages

We have an application that loads all contacts stored in an account using the Microsoft Graph API. The initial call we issue is https://graph.microsoft.com/v1.0/users/{userPrincipalName}/contacts$count=true&$orderBy=displayName%20ASC&$top=100, but we use the Java JDK to do that. Then we iterate over all pages and store all loaded contacts in a Set (local cache).
We do this every 5 minutes using an account with over 3000 contacts and sometimes, the count of contacts we received due to using $count does not match the number of contacts we loaded and stored in the local cache.
Verifying the numbers manually we can say, that the count was always correct, but there are contacts missing.
We use the following code to achieve this.
public List<Contact> loadContacts() {
Set<Contact> contacts = new TreeSet<>((contact1, contact2) -> StringUtils.compare(contact1.id, contact2.id));
List<QueryOption> requestOptions = List.of(
new QueryOption("$count", true),
new QueryOption("$orderBy", "displayName ASC"),
new QueryOption("$top", 100)
);
ContactCollectionRequestBuilder pageRequestBuilder = null;
ContactCollectionRequest pageRequest;
boolean hasNextPage = true;
while (hasNextPage) {
// initialize page request
if (pageRequestBuilder == null) {
pageRequestBuilder = graphClient.users(userId).contacts();
pageRequest = pageRequestBuilder.buildRequest(requestOptions);
} else {
pageRequest = pageRequestBuilder.buildRequest();
}
// load
ContactCollectionPage contactsPage = pageRequest.get();
if (contactsPage == null) {
throw new IllegalStateException("request returned a null page");
} else {
contacts.addAll(contactsPage.getCurrentPage());
}
// handle next page
hasNextPage = contactsPage.getNextPage() != null;
if (hasNextPage) {
pageRequestBuilder = contactsPage.getNextPage();
} else if (contactsPage.getCount() != null && !Objects.equals(contactsPage.getCount(), (long) contacts.size())) {
throw new IllegalStateException(String.format("loaded %d contacts but response indicated %d contacts", contacts.size(), contactsPage.getCount()));
} else {
// done
}
}
log.info("{} contacts loaded using graph API", contacts.size());
return new ArrayList<>(contacts);
}
Initially, we did not put the loaded contacts in a Set by ID but just in a List. With the List we very often got more contacts than $count. My idea was, that there is some caching going on and some pages get fetched multiple times. Using the Set we can make sure, that we only have unique contacts in our local cache.
But using the Set, we sometimes have less contacts than $count, meaning some pages got skipped and we end up in the condition that throws the IllegalStateException.
Currently, we use microsoft-graph 5.8.0 and azure-identiy 1.4.2.
Have you experienced similar issues and can help us solve this problem?
Or do you have any idea what could be causing these inconsistent results?
Your help is very much appreciated!

How to decide when to update, delete and insert

I am using spring data jpa for creating services. I have to do insert, update and delete operation on one save button. for save and update I am using repository save method in my code. For deciding need to do update or insert I am checking count of records.
If I am sending one record then I am successfully able to do save and update operations.
But my problem is that when I am sending two record which already present is db
that need to go for update. but In my situation I am checking count of record so its going for save instead of update.
Can any one tell me what condition need to check more then it will go for update ? Or
Tell me any another way for to decide when to go for update,when to go for insert and when to go for delete?
RoomInvestigatorMappingService class
public String updatePiDetails(List<PiDetails> roomInvestMapping) {
List<RoomInvestigatorMapping> currentRecord = new ArrayList<RoomInvestigatorMapping>();
for (PiDetails inputRecorObj : roomInvestMapping) {
currentRecord = roomInvestigatorMappingRepo.findByNRoomAllocationId(inputRecorObj.getnRoomAllocationId());
}
int currentRecordCount = currentRecord.size();
int inputRecordCount = roomInvestMapping.size();
// update existing record
if (inputRecordCount == currentRecordCount) {
for (PiDetails inputObject : roomInvestMapping) {
for (RoomInvestigatorMapping currentRecordObj : currentRecord) {
currentRecordObj.nInvestigatorId = inputObject.getnInvestigatorId();
currentRecordObj.nPercentageAssigned = inputObject.getnPercentageAssigned();
currentRecordObj.nRoomAllocationId = inputObject.getnRoomAllocationId();
roomInvestigatorMappingRepo.saveAll(currentRecord);
}
}
}
//insert new record
if (inputRecordCount > currentRecordCount) {
for (PiDetails inputObject : roomInvestMapping) {
RoomInvestigatorMapping investObj = new RoomInvestigatorMapping();
investObj.nInvestigatorId = inputObject.getnInvestigatorId();
investObj.nRoomAllocationId = inputObject.getnRoomAllocationId();
investObj.nPercentageAssigned = inputObject.getnPercentageAssigned();
roomInvestigatorMappingRepo.save(investObj);
}
}
return "sucessfully";
}
RoomInvestigatorMappingRepository interface
#Query("select roomInvestMapping from RoomInvestigatorMapping as roomInvestMapping where nRoomAllocationId=?1")
List<RoomInvestigatorMapping> findByNRoomAllocationId(Integer nRoomAllocationId);
Json Input
[
{
"nInvestigatorId": 911294,
"nPercentageAssigned": 50,
"nRoomAllocationId": 1
},
{
"nInvestigatorId": 911294,
"nPercentageAssigned": 50,
"nRoomAllocationId": 2
}
]
Just use CrudRepository.existsById(ID id)
The documentation says:
Returns whether an entity with the given id exists.

neo4j Java beginner. Cypher Execution Engine

The user logs in on the website and creates different events. This event is saved into the neo4j database as a node and I make the "EVENT_CREATOR" realtionship between the user and the event node.
I am trying to implement pagination for all the user's events on my website (using Play2 framework) and I need for example if user accesses the first page, I load the first ten events; 2nd page to load the 10th- 20th events, and so on...
this is my query:
match(n);
...
skip k;
limit 10;
return n;
At the moment I am getting all the events created by the user and add them to the array list.
private static List<PublicEvent> getEvents(int page, int pageSize) {
List<PublicEvent> events = new ArrayList<PublicEvent>();
GraphDatabaseService db = Neo4JHelper.getDatabase();
try (Transaction tx = db.beginTx()) {
Index<Node> userIndex = db.index().forNodes(ModelIndex.Users);
IndexHits<Node> userNodes = userIndex.get(ModelGraphProperty.UserProfile.UserName, SessionUtilities.getCurrentUser());
Node me = userNodes.next(); //current logged in user
PagingIterator paginator = new PagingIterator(me.getRelationships(GraphRelation.RelTypes.EVENT_CREATOR).iterator(), pageSize); // get all the events that were created by this user
paginator.page(page);
// adding all the created events by this user to an array
if (paginator.hasNext()) {
Relationship eventCreator = (Relationship)paginator.next();
Node event = eventCreator.getOtherNode(me);
events.add(new PublicEvent(event));
}
tx.success();
}
db.shutdown();
return events;
}
I want to update the code to run Cypher queries and I add the following lines of code (using the example https://www.tutorialspoint.com/neo4j/neo4j_cypher_api_example.htm )
GraphDatabaseService db = Neo4JHelper.getDatabase();
ExecutionEngine execEngine = new ExecutionEngine(db); //HERE I GET AN ERROR
ExecutionResult execResult = execEngine.execute("MATCH (n) RETURN n");
String results = execResult.dumpToString();
System.out.println(results);
it is expecting a second parameter: logger. What is the error or is there anything I am doing wrong?
RestGraphDatabase db= (RestGraphDatabase)Neo4JHelper.getDatabase();
RestCypherQueryEngine engine=new RestCypherQueryEngine(db.getRestAPI());
Map<String, Object> params = new HashMap<String, Object>();
params.put( "id", eventId );
String query="match (s) where id(s) = {id} return s;";
QueryResult result=engine.query(query,params);
if(result.iterator().hasNext()) {
//HERE PUT WHATEVER YOU NEED
}
Take a look at the documentation:
https://neo4j.com/docs/java-reference/current/

Aerospike: how do I get record key?

Aerospike client has scanAll method for reading all rows from it's store.
I use it in the folowing code:
ScanPolicy policy = new ScanPolicy();
policy.concurrentNodes = true;
policy.priority = Priority.DEFAULT;
policy.includeBinData = true;
policy.scanPercent = 100;
client.scanAll(policy, "namespaceName", "setName", new ScanCallback() {
#Override
public void scanCallback(Key key, Record record) throws AerospikeException {
STORE.put(key.userKey.toLong(), record.getValue("binName").toString());
}
});
But it is finished with NullPointerException, because userKey is null. All other fields are valid as expected. User key is the Long value, that was used for saving data:
client.put(writePolicy, new Key("namespaceName", "setName", userKey), new Bin("binName", value));
All is fine, if I do single request like this:
client.get(readPolicy, new Key("namespaceName", "setName", userKey));
What may be wrong? Why userKey is null?
Aerospike uses key and set name to generate unique digest, So it stores only digest.
While inserting one record if you set writePolicy.sendKey = true then key will be stored as metadata of record.
If one record is inserted with writePolicy.sendKey = true then only you will get key in scanCallback().
By default writePolicy.sendKey is false, so by default scanCallback() gets null as key. Thats why your key.userKey.toLong() gives NullPointerException.
I too have faced this problem even at the time of writing we were setting the WritePolicy.sendkeys=true.
After 2-3 days debugging found that there were some issue with aerospike client version. Initially i were using 3.0.25 but after upgrading it to 3.0.35 it started working fine.

Push Notification condition

Good day,
I am using Parse Push Notification, and below are my difficulties:
In brief, I would like to "merge" these two conditions:
query.whereEqualTo("Gender", userLookingGender);
pushQuery.whereEqualTo("the gender column of the ParseQuery", the user gender of the ParseQuery column);
In other words, I would like to send out a message to the user that falls within that gender. The gender column along with the genders are found in the parse query called "User".
Update:
userLookingGender is the following:
String userLookingGender = ParseUser.getCurrentUser().getString(
"Looking_Gender");
If you need any clarification let me know.
Update 2:
I use one condition, gender, to make easier to understand. Now Imagine if I had multiple condition, and is trying to send a push message only the recipient who fulfill all of the below criteria, and where upon button click it would take them to particular activity page.
ParseQuery<ParseObject> query = ParseQuery.getQuery("User");
query.whereNotEqualTo("objectId", ParseUser.getCurrentUser()
.getObjectId());
// users with Gender = currentUser.Looking_Gender
query.whereEqualTo("Gender", userLookingGender);
// users with Looking_Gender = currentUser.Gender
query.whereEqualTo("Looking_Gender", userGender);
query.setLimit(1) ;
ParseGeoPoint point = ParseUser.getCurrentUser().getParseGeoPoint("location");
query.whereWithinKilometers("location", point, mMax_Distance.doubleValue());
query.whereEqualTo("ActivityName", activityName);
query.whereGreaterThanOrEqualTo("UserAge", minimumAge);
query.whereLessThanOrEqualTo("UserAge", maximumAge);
Update 3:
Android Code
ParseCloud.callFunctionInBackground("sendPushToNearbyAndMatching", new HashMap<String, Object>(), new FunctionCallback<String>() {
public void done(String result, ParseException e) {
if (e == null) {
// success
}
}
});
Parse Cloud JavaScript code (found in cloud/main.js)
The owner column in this case is users
// Use Parse.Cloud.define to define as many cloud functions as you want.
// For example:
Parse.Cloud.define("hello", function(request, response) {
response.success("Hello world!");
});
Parse.Cloud.define("sendPushToNearbyAndMatching", function(request, response) {
Parse.Cloud.useMasterKey();
// the authenticated user on the device calling this function
var user = request.user;
// the complex query matching users
var query = new Parse.Query(Parse.User);
query.whereNotEqualTo("objectId", user.id);
// users with Gender = currentUser.Looking_Gender
query.equalTo("Gender", user.get("Gender"));
// users with Looking_Gender = currentUser.Gender
query.equalTo("Looking_Gender", user.get("Looking_Gender"));
query.equalTo("ActivityName", user.get("ActivityName"));
query.greaterThanOrEqualTo("UserAge", user.get("Minimum_Age"));
query.lessThanOrEqualTo("UserAge", user.get("Maximum_Age"));
query.limit(1);
query.each(function(user) {
// sendPushNotification is added in next code section
return sendPushNotification(user);
}).then(function() {
response.success("success!");
}, function(err) {
response.error(err);
});
});
var sendPushNotification = function(user) {
var query = new Parse.Query(Parse.Installation);
query.equalTo('users', user);
return Parse.Push.send({
where : query, // send to installations matching query
expiration_interval : 600, // optional - expires after 10 minutes
data : {
alert: "App says hello!",
}
})
}
Now that I have a bit more insight I think I'm ready with an answer:
I do Push from Cloud Code, and there the query matches against Installation objects, which is why having the values there as well would be useful.
It looks like you are sending directly from the app, so I would suggest creating a channel for each gender: https://parse.com/docs/push_guide#sending-channels/Android
Then you just need to:
String userLookingGender = ParseUser.getCurrentUser().getString(
"Looking_Gender");
ParsePush push = new ParsePush();
push.setChannel(userLookingGender);
push.setMessage("Your message");
push.sendInBackground();
Update:
Ok. the multiple queries indeed make matters more complicated.
I think you would have to move on to Cloud Code to perform such an advanced query push (which is by the way recommended for security reasons).
Cloud Code guide: https://parse.com/docs/cloud_code_guide
Embrasing the fact that users can have multiple devices, you need to be able to fetch all the installations associated with a user. To do this I would suggest saving a pointer to User on each installation. You can do this as part of the first login of your app.
Assuming you have a, say, owner column in your installation pointing to the respective User owning the device, then you can do something like this in Cloud Code:
Parse.Cloud.define("sendPushToNearbyAndMatching", function(request, response) {
Parse.Cloud.useMasterKey();
// the authenticated user on the device calling this function
var user = request.user;
// the complex query matching users
var query = new Parse.Query(Parse.User);
query.whereNotEqualTo("objectId", user.id);
// users with Gender = currentUser.Looking_Gender
query.equalTo("Gender", user.get("Gender"));
// users with Looking_Gender = currentUser.Gender
query.equalTo("Looking_Gender", user.get("Looking_Gender"));
query.limit(1);
... etc
// execute the query
// i am using each just to show an convenient way to iterate the results
// instead of setting limit(1) consider executing the query using first() instead
// android SDK has a getFirstInBackground() as well
query.each(function(user) {
// sendPushNotification is added in next code section
return sendPushNotification(user);
}).then(function() {
response.success("success!");
}, function(err) {
response.error(err);
});
});
About querying User in javascript: https://parse.com/docs/js_guide#users-querying
How to call this Cloud function from Android: https://parse.com/docs/android_guide#cloudfunctions
Now it is time to send out the notifications to the devices owned by the user:
var sendPushNotification = function(user) {
var promise = new Parse.Promise();
var query = new Parse.Query(Parse.Installation);
query.equalTo('owner', user);
query.count().then(function(count) {
console.log("sending push to " + count + " devices");
Parse.Push.send({
where : query, // send to installations matching query
expiration_interval : 600, // optional - expires after 10 minutes
data : {
alert: "App says hello!",
}
}).then(function() {
// success
console.log("push success");
promise.resolve();
}, function(error) {
console.error(error.message);
promise.reject(error);
});
});
return promise;
}
For more advanced pushes (if you for instance want to receive a broadcast to handle some data) see: https://parse.com/docs/push_guide#sending-queries/JavaScript
Also, if you decide to fiddle with Cloud Code and thereby javascript, I would highly recommend having a look at how promises work. This makes your life so much easier when handling asynchronous calls, for instance when issuing queries: https://parse.com/docs/js_guide#promises
This is all a lot of information and probably a lot to take in all at once if it is new to you, but I think it will be all worth it, I know it was for me.

Categories