Improve recursive query performance

gtask_related_email
Alex Baker 6 years ago
parent 3fac672f1e
commit 83642e25d7

File diff suppressed because it is too large Load Diff

@ -58,7 +58,7 @@ import org.tasks.notifications.NotificationDao;
CaldavAccount.class, CaldavAccount.class,
GoogleTaskAccount.class GoogleTaskAccount.class
}, },
version = 65) version = 66)
public abstract class Database extends RoomDatabase { public abstract class Database extends RoomDatabase {
public static final String NAME = "database"; public static final String NAME = "database";

@ -5,11 +5,19 @@ import static com.todoroo.astrid.helper.UUIDHelper.newUUID;
import androidx.room.ColumnInfo; import androidx.room.ColumnInfo;
import androidx.room.Entity; import androidx.room.Entity;
import androidx.room.Ignore; import androidx.room.Ignore;
import androidx.room.Index;
import androidx.room.PrimaryKey; import androidx.room.PrimaryKey;
import com.todoroo.andlib.data.Property; import com.todoroo.andlib.data.Property;
import com.todoroo.andlib.data.Table; import com.todoroo.andlib.data.Table;
@Entity(tableName = "caldav_tasks") @Entity(
tableName = "caldav_tasks",
indices = {
@Index(name = "cd_task", value = "cd_task"),
@Index(
name = "cd_calendar_parent",
value = {"cd_calendar", "cd_parent"})
})
public class CaldavTask { public class CaldavTask {
public static final String KEY = "caldav"; public static final String KEY = "caldav";

@ -7,16 +7,22 @@ import android.os.Parcelable;
import androidx.room.ColumnInfo; import androidx.room.ColumnInfo;
import androidx.room.Entity; import androidx.room.Entity;
import androidx.room.Ignore; import androidx.room.Ignore;
import androidx.room.Index;
import androidx.room.PrimaryKey; import androidx.room.PrimaryKey;
import com.todoroo.andlib.data.Property.LongProperty;
import com.todoroo.andlib.data.Property.StringProperty;
import com.todoroo.andlib.data.Table; import com.todoroo.andlib.data.Table;
import java.io.Serializable; import java.io.Serializable;
@Entity(tableName = TABLE_NAME) @Entity(tableName = TABLE_NAME, indices = @Index(name = "geo_task", value = "task"))
public class Geofence implements Serializable, Parcelable { public class Geofence implements Serializable, Parcelable {
public static final String TABLE_NAME = "geofences"; public static final String TABLE_NAME = "geofences";
public static final Table TABLE = new Table(TABLE_NAME); public static final Table TABLE = new Table(TABLE_NAME);
public static final LongProperty TASK = new LongProperty(TABLE, "task");
public static final StringProperty PLACE = new StringProperty(TABLE, "place");
public static final Parcelable.Creator<Geofence> CREATOR = public static final Parcelable.Creator<Geofence> CREATOR =
new Parcelable.Creator<Geofence>() { new Parcelable.Creator<Geofence>() {
@Override @Override

@ -3,11 +3,19 @@ package org.tasks.data;
import androidx.room.ColumnInfo; import androidx.room.ColumnInfo;
import androidx.room.Entity; import androidx.room.Entity;
import androidx.room.Ignore; import androidx.room.Ignore;
import androidx.room.Index;
import androidx.room.PrimaryKey; import androidx.room.PrimaryKey;
import com.todoroo.andlib.data.Property; import com.todoroo.andlib.data.Property;
import com.todoroo.andlib.data.Table; import com.todoroo.andlib.data.Table;
@Entity(tableName = "google_tasks") @Entity(
tableName = "google_tasks",
indices = {
@Index(name = "gt_task", value = "gt_task"),
@Index(
name = "gt_list_parent",
value = {"gt_list_id", "gt_parent"})
})
public class GoogleTask { public class GoogleTask {
public static final String KEY = "gtasks"; // $NON-NLS-1$ public static final String KEY = "gtasks"; // $NON-NLS-1$

@ -10,9 +10,11 @@ import android.os.Parcelable;
import androidx.room.ColumnInfo; import androidx.room.ColumnInfo;
import androidx.room.Entity; import androidx.room.Entity;
import androidx.room.Ignore; import androidx.room.Ignore;
import androidx.room.Index;
import androidx.room.PrimaryKey; import androidx.room.PrimaryKey;
import com.google.common.base.Strings; import com.google.common.base.Strings;
import com.mapbox.api.geocoding.v5.models.CarmenFeature; import com.mapbox.api.geocoding.v5.models.CarmenFeature;
import com.todoroo.andlib.data.Property.StringProperty;
import com.todoroo.andlib.data.Table; import com.todoroo.andlib.data.Table;
import com.todoroo.astrid.helper.UUIDHelper; import com.todoroo.astrid.helper.UUIDHelper;
import java.io.Serializable; import java.io.Serializable;
@ -21,12 +23,14 @@ import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import org.tasks.location.MapPosition; import org.tasks.location.MapPosition;
@Entity(tableName = TABLE_NAME) @Entity(tableName = TABLE_NAME, indices = @Index(name = "place_uid", value = "uid", unique = true))
public class Place implements Serializable, Parcelable { public class Place implements Serializable, Parcelable {
public static final String TABLE_NAME = "places"; public static final String TABLE_NAME = "places";
public static final Table TABLE = new Table(TABLE_NAME); public static final Table TABLE = new Table(TABLE_NAME);
public static final StringProperty UID = new StringProperty(TABLE, "uid");
public static final Parcelable.Creator<Place> CREATOR = public static final Parcelable.Creator<Place> CREATOR =
new Parcelable.Creator<Place>() { new Parcelable.Creator<Place>() {
@Override @Override

@ -4,18 +4,19 @@ import androidx.annotation.NonNull;
import androidx.room.ColumnInfo; import androidx.room.ColumnInfo;
import androidx.room.Entity; import androidx.room.Entity;
import androidx.room.Ignore; import androidx.room.Ignore;
import androidx.room.Index;
import androidx.room.PrimaryKey; import androidx.room.PrimaryKey;
import com.todoroo.andlib.data.Property.StringProperty; import com.todoroo.andlib.data.Property.StringProperty;
import com.todoroo.andlib.data.Table; import com.todoroo.andlib.data.Table;
import com.todoroo.astrid.data.Task; import com.todoroo.astrid.data.Task;
import org.tasks.backup.XmlReader; import org.tasks.backup.XmlReader;
@Entity(tableName = "tags") @Entity(tableName = "tags", indices = @Index(name = "tag_task", value = "task"))
public class Tag { public class Tag {
public static final String KEY = "tags-tag"; // $NON-NLS-1$ public static final String KEY = "tags-tag"; // $NON-NLS-1$
@Deprecated public static final Table TABLE = new Table("tags"); public static final Table TABLE = new Table("tags");
public static final StringProperty TASK_UID = new StringProperty(TABLE, "task_uid"); public static final StringProperty TASK_UID = new StringProperty(TABLE, "task_uid");
public static final StringProperty NAME = new StringProperty(TABLE, "name"); public static final StringProperty NAME = new StringProperty(TABLE, "name");

@ -315,6 +315,20 @@ public class Migrations {
} }
}; };
private static final Migration MIGRATION_65_66 =
new Migration(65, 66) {
@Override
public void migrate(@NonNull SupportSQLiteDatabase database) {
database.execSQL("CREATE UNIQUE INDEX `place_uid` ON `places` (`uid`)");
database.execSQL("CREATE INDEX `geo_task` ON `geofences` (`task`)");
database.execSQL("CREATE INDEX `tag_task` ON `tags` (`task`)");
database.execSQL("CREATE INDEX `gt_list_parent` ON `google_tasks` (`gt_list_id`, `gt_parent`)");
database.execSQL("CREATE INDEX `gt_task` ON `google_tasks` (`gt_task`)");
database.execSQL("CREATE INDEX `cd_calendar_parent` ON `caldav_tasks` (`cd_calendar`, `cd_parent`)");
database.execSQL("CREATE INDEX `cd_task` ON `caldav_tasks` (`cd_task`)");
}
};
public static final Migration[] MIGRATIONS = public static final Migration[] MIGRATIONS =
new Migration[] { new Migration[] {
MIGRATION_35_36, MIGRATION_35_36,
@ -337,7 +351,8 @@ public class Migrations {
MIGRATION_61_62, MIGRATION_61_62,
MIGRATION_62_63, MIGRATION_62_63,
MIGRATION_63_64, MIGRATION_63_64,
MIGRATION_64_65 MIGRATION_64_65,
MIGRATION_65_66
}; };
private static Migration NOOP(int from, int to) { private static Migration NOOP(int from, int to) {

@ -33,6 +33,7 @@ import com.todoroo.astrid.api.TagFilter;
import com.todoroo.astrid.core.SortHelper; import com.todoroo.astrid.core.SortHelper;
import com.todoroo.astrid.dao.Database; import com.todoroo.astrid.dao.Database;
import com.todoroo.astrid.dao.TaskDao; import com.todoroo.astrid.dao.TaskDao;
import com.todoroo.astrid.dao.TaskDao.TaskCriteria;
import com.todoroo.astrid.data.Task; import com.todoroo.astrid.data.Task;
import io.reactivex.Single; import io.reactivex.Single;
import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.android.schedulers.AndroidSchedulers;
@ -41,9 +42,11 @@ import io.reactivex.schedulers.Schedulers;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import javax.inject.Inject; import javax.inject.Inject;
import org.tasks.data.CaldavCalendar;
import org.tasks.data.CaldavTask; import org.tasks.data.CaldavTask;
import org.tasks.data.Geofence; import org.tasks.data.Geofence;
import org.tasks.data.GoogleTask; import org.tasks.data.GoogleTask;
import org.tasks.data.GoogleTaskList;
import org.tasks.data.Place; import org.tasks.data.Place;
import org.tasks.data.Tag; import org.tasks.data.Tag;
import org.tasks.data.TaskContainer; import org.tasks.data.TaskContainer;
@ -72,7 +75,7 @@ public class TaskListViewModel extends ViewModel implements Observer<PagedList<T
.as("tags"); .as("tags");
private static final StringProperty TAGS_RECURSIVE = private static final StringProperty TAGS_RECURSIVE =
new StringProperty(null, "(SELECT group_concat(distinct(tag_uid))\n" + new StringProperty(null, "(SELECT group_concat(distinct(tag_uid))\n" +
"FROM tags WHERE tags.task = recursive_tasks.task\n" + "FROM tags WHERE tags.task = tasks._id\n" +
"GROUP BY tags.task)") "GROUP BY tags.task)")
.as("tags"); .as("tags");
@ -133,8 +136,8 @@ public class TaskListViewModel extends ViewModel implements Observer<PagedList<T
String joins = String joins =
Join.left(GoogleTask.TABLE.as(GTASK_METADATA_JOIN), gtaskJoinCriterion).toString() Join.left(GoogleTask.TABLE.as(GTASK_METADATA_JOIN), gtaskJoinCriterion).toString()
+ Join.left(CaldavTask.TABLE.as(CALDAV_METADATA_JOIN), caldavJoinCriterion) + Join.left(CaldavTask.TABLE.as(CALDAV_METADATA_JOIN), caldavJoinCriterion)
+ Join.left(Geofence.TABLE, field(Geofence.TABLE_NAME + ".task").eq(Task.ID)) + Join.left(Geofence.TABLE, Geofence.TASK.eq(Task.ID))
+ Join.left(Place.TABLE, field(Place.TABLE_NAME + ".uid").eq(field("geofences.place"))); + Join.left(Place.TABLE, Place.UID.eq(Geofence.PLACE));
if (atLeastLollipop() if (atLeastLollipop()
&& (filter instanceof CaldavFilter && (filter instanceof CaldavFilter
@ -150,31 +153,59 @@ public class TaskListViewModel extends ViewModel implements Observer<PagedList<T
fields.add(TAGS_RECURSIVE); fields.add(TAGS_RECURSIVE);
fields.add(INDENT); fields.add(INDENT);
String joinedQuery = Join.left(Task.TABLE, Task.ID.eq(RECURSIVE_TASK)) + joins; String joinedQuery = Join.inner(Task.TABLE, Task.ID.eq(RECURSIVE_TASK)) + joins;
String parentQuery; String parentQuery;
QueryTemplate subtaskQuery = new QueryTemplate(); QueryTemplate subtaskQuery = new QueryTemplate();
if (filter instanceof CaldavFilter) { if (filter instanceof CaldavFilter) {
Criterion criterion = CaldavFilter.getCriterion(((CaldavFilter) filter).getCalendar()); CaldavCalendar calendar = ((CaldavFilter) filter).getCalendar();
parentQuery = parentQuery =
new QueryTemplate() new QueryTemplate()
.join(Join.left(CaldavTask.TABLE, Task.ID.eq(CaldavTask.TASK))) .join(
.where(Criterion.and(criterion, CaldavTask.PARENT.eq(0))) Join.inner(
CaldavTask.TABLE,
Criterion.and(
CaldavTask.CALENDAR.eq(calendar.getUuid()),
CaldavTask.PARENT.eq(0),
CaldavTask.TASK.eq(Task.ID),
CaldavTask.DELETED.eq(0))))
.where(TaskCriteria.activeAndVisible())
.toString(); .toString();
subtaskQuery subtaskQuery
.join(Join.inner(RECURSIVE, RECURSIVE_TASK.eq(CaldavTask.PARENT))) .join(Join.inner(RECURSIVE, CaldavTask.PARENT.eq(RECURSIVE_TASK)))
.join(Join.left(CaldavTask.TABLE, Task.ID.eq(CaldavTask.TASK))) .join(
.where(criterion); Join.inner(
CaldavTask.TABLE,
Criterion.and(
CaldavTask.CALENDAR.eq(calendar.getUuid()),
CaldavTask.PARENT.gt(0),
CaldavTask.TASK.eq(Task.ID),
CaldavTask.DELETED.eq(0))))
.where(TaskCriteria.activeAndVisible());
} else { } else {
Criterion criterion = GtasksFilter.getCriterion(((GtasksFilter) filter).getList()); GoogleTaskList list = ((GtasksFilter) filter).getList();
parentQuery = parentQuery =
new QueryTemplate() new QueryTemplate()
.join(Join.left(GoogleTask.TABLE, Task.ID.eq(GoogleTask.TASK))) .join(
.where(Criterion.and(criterion, GoogleTask.PARENT.eq(0))) Join.inner(
GoogleTask.TABLE,
Criterion.and(
GoogleTask.LIST.eq(list.getRemoteId()),
GoogleTask.PARENT.eq(0),
GoogleTask.TASK.eq(Task.ID),
GoogleTask.DELETED.eq(0))))
.where(TaskCriteria.activeAndVisible())
.toString(); .toString();
subtaskQuery subtaskQuery
.join(Join.inner(RECURSIVE, RECURSIVE_TASK.eq(GoogleTask.PARENT))) .join(Join.inner(RECURSIVE, GoogleTask.PARENT.eq(RECURSIVE_TASK)))
.join(Join.left(GoogleTask.TABLE, Task.ID.eq(GoogleTask.TASK))) .join(
.where(criterion); Join.inner(
GoogleTask.TABLE,
Criterion.and(
GoogleTask.LIST.eq(list.getRemoteId()),
GoogleTask.PARENT.gt(0),
GoogleTask.TASK.eq(Task.ID),
GoogleTask.DELETED.eq(0))))
.where(TaskCriteria.activeAndVisible());
} }
String sortSelect = SortHelper.orderSelectForSortTypeRecursive(preferences.getSortMode()); String sortSelect = SortHelper.orderSelectForSortTypeRecursive(preferences.getSortMode());

Loading…
Cancel
Save