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
Add support for relative filtering #4740
Conversation
} | ||
|
||
Position previous = Context.getDataManager().getPrevPosition(position.getDeviceId(), newfixTime); | ||
Position next = Context.getDataManager().getNextPosition(position.getDeviceId(), newfixTime); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you please provide details on why we need "next" position.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The backend has configured MinPeriod filter set to 2 seconds. Imagine that these Positions were received by the server:
- Position A:
fixTime = 18:18:01
- Position C:
fixTime = 18:18:05
Then after 10 seconds, server receives position B with fixTime = 18:18:04
. As it turns out, it was not delivered immediately because device was offline for 3 seconds, because it was switching between networks.
Time-wise position B is far enough from position A (previous location), but it's too close to position C (next position). Hence, we need to take into account next position as well.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It makes sense in theory, but I'm not sure if it makes sense to actually implement it. You basically make 3 expensive SQL requests for every location report. It will cripple pretty much any server that runs any significant number of devices.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree that with thousands of tracking devices this will have deteriorating impact. But for private use it will not be a problem. No doubt, that we should make this option disabled by default.
What do you say, if I introduce this optimization: let's load next and prev positions only in case any of these filters are turned active:
- filterDistance
- filterMaxSpeed
- filterMinPeriod
So if we only duplicate filter is active, than we have only 1 request instead of 3.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm mostly worried about value of this feature vs cost of maintaining it going forward.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can also make lazy loading of last / previous position. What do you say? Only in the case when at least one of the corresponding filters is activated.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Makes sense to me.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks fine, but please update PR so I can review it more thoroughly.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK, I have pushed a new commit.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good. Add a few minor comments, mostly about formatting.
0f8c43a
to
fd91d6b
Compare
It is. I force pushed the commit.
|
setup/default.xml
Outdated
@@ -48,6 +48,10 @@ | |||
SELECT * FROM tc_positions WHERE deviceId = :deviceId AND fixTime BETWEEN :from AND :to ORDER BY fixTime | |||
</entry> | |||
|
|||
<entry key='database.selectPrecedingPosition'> | |||
SELECT * FROM tc_positions WHERE deviceId = :deviceId AND fixTime lte :time ORDER BY fixTime DESC LIMIT 1 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
One last thing. I noticed that you are using lte
. Is it a standard SQL syntax that will work in every SQL server? Can you please provide a reference because I couldn't find any information about it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Honestly, I thought that you were using some kind of library to parse SQL stored in XML. Did you figure out correctly, that you wrote it yourself. I couldn't use <
sign since it's forbidden inside XML tag.
We may use this:
SELECT * FROM tc_positions WHERE deviceId = :deviceId AND fixTime <= :time ORDER BY fixTime DESC LIMIT 1
or
SELECT * FROM tc_positions WHERE deviceId = :deviceId AND NOT (fixTime > :time) ORDER BY fixTime DESC LIMIT 1
What do you think?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think just <=
should be fine, so let's go with option 1.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, just to confirm. Have you tested the latest to make sure everything works?
.executeQuery(Position.class); | ||
if (positions.isEmpty()) { | ||
return null; | ||
} else { | ||
return positions.iterator().next(); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry, just noticed another thing. Can we use executeQuerySingle
here instead?
I have tested the code and I found a serious problem (as it seems to me), that have existed in traccar even before my pull request. As it turns out, traccar doesn't save fractional TIMESTAMP values, it saves only seconds (not milliseconds). Which means that duplicate comparison didn't work for protocols that send milliseconds:
Compare:
Honestly, I really think that traccar should save milliseconds in the database. Some usecases demand location data more frequent than once per second. Something like this: |
What database are you using? |
MariaDB.
|
Is there a universal solution? |
What do you mean?
|
I mean any ideas how to fix it via liquibase, so it's supported for add databases? |
Looks like this should do the thing:
How do check that it works? |
You have to check if it works with different database types. At least H2, MySQL, MS SQL and PostgreSQL. |
Let's switch to another PR? (since it's a different bug fix) How do I "launch" this new SQL changeset? |
Yep, makes sense to do a separate PR. |
Besides this separate time issue, I would say everything works for me. |
} | ||
|
||
private boolean filterZero(Position position) { | ||
return filterZero && position.getLatitude() == 0.0 && position.getLongitude() == 0.0; | ||
} | ||
|
||
private boolean filterDuplicate(Position position, Position last) { | ||
if (filterDuplicate && last != null && position.getFixTime().equals(last.getFixTime())) { | ||
if (filterDuplicate && last != null && position.getFixTime().getTime() == last.getFixTime().getTime()) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is this needed?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we're going to store milli/micro/nanoseconds in the TIMESTAMP field in the DB, then it means we would in fact compare float numbers. So maybe to filter out duplicates we should compare getTime()
with a specified precision.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry, where did you get floating numbers and nanoseconds? As far as I can tell, equals
does exactly the same thing as you do manually:
https://docs.oracle.com/javase/8/docs/api/java/util/Date.html#equals-java.lang.Object-
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, currently it does the same thing. But, if we make this change #4746, then database would store time with milliseconds/nanosecond precision (as float). Hence, comparing new position (converted from text) and preceding (from DB) might not be equal in theory. That is why I suggest to do comparison with a certain precision. I can do additional commit for that. Do you agree?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I thought you planned to fix database accuracy?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's leave it as it is now. And deal with it in the separate PR.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agree but this change should be reverted because it doesn't really do anything.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
Note with moving to fractional seconds you may need to account for In some apps at work we just use https://docs.oracle.com/javase/8/docs/api/java/time/LocalDateTime.html#MAX in code to always push max microseconds to account for this. |
Position last = null; | ||
if (Context.getIdentityManager() != null) { | ||
last = Context.getIdentityManager().getLastPosition(position.getDeviceId()); | ||
if (skipAttributes(position)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks like this actually changes the filtering logic. We only want to apply skipLimit
and skipAttributes
for certain types of filters.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Okay. Could you please elaborate? Why should those 2 skips work only for some filters?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a forum discussion about it if you want more context. Basically there are two categories of filtering. The ones that filter definitely invalid data (e.g. zero coordinates) and the ones that filter excessive data (e.g. duplicates). We always want to filter former, but want to keep latter if it has some important information (e.g. alarms).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done. I reverted to the original logic and also preserved almost the same order of filter checks (as it is in current master branch).
|
||
// filter out excessive data | ||
long deviceId = position.getDeviceId(); | ||
if (filterDuplicate || filterDistance > 0 || filterMaxSpeed > 0 || filterMinPeriod > 0) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What about filterStatic
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed.
Merged, thank you. |
@tananaev: Could you please add the new |
The plan is to replace that documentation with something that's generated automatically. |
This PR tries to address problem #4735.