Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 14 additions & 11 deletions src/AnkiNet/CollectionFile/Database/CardRepository.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,19 +11,22 @@ public CardRepository(SqliteConnection connection) : base(connection)

protected override string TableName => "[cards]";

protected override string Columns =>
"[id], [nid], [did], [ord], [mod], " +
"[usn], [type], [queue], [due], [ivl], " +
"[factor], [reps], [lapses], [left], [odue]," +
"[odid], [flags], [data]";
protected override IReadOnlyList<string> Columns { get; } =
[
"[id]", "[nid]", "[did]", "[ord]", "[mod]",
"[usn]", "[type]", "[queue]", "[due]", "[ivl]",
"[factor]", "[reps]", "[lapses]", "[left]", "[odue]",
"[odid]", "[flags]", "[data]"
];

protected override string GetValues(card i)
protected override IReadOnlyList<object> GetValues(card i)
{
return
$"{i.id},{i.nid},{i.did},{i.ord},{i.mod}," +
$"{i.usn},{i.type},{i.queue},{i.due},{i.ivl}," +
$"{i.factor},{i.reps},{i.lapses},{i.left},{i.odue}," +
$"{i.odid},{i.flags},'{i.data}'";
return [
i.id, i.nid, i.did, i.ord, i.mod,
i.usn, i.type, i.queue, i.due, i.ivl,
i.factor, i.reps, i.lapses, i.left, i.odue,
i.odid, i.flags, i.data
];
}

protected override card Map(SqliteDataReader reader)
Expand Down
21 changes: 12 additions & 9 deletions src/AnkiNet/CollectionFile/Database/ColRepository.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,17 +11,20 @@ public ColRepository(SqliteConnection connection) : base(connection)

protected override string TableName => "[col]";

protected override string Columns =>
"[id], [crt], [mod], [scm], [ver], " +
"[dty], [usn], [ls], [conf], [models], " +
"[decks], [dconf], [tags]";
protected override IReadOnlyList<string> Columns { get; } =
[
"[id]", "[crt]", "[mod]", "[scm]", "[ver]",
"[dty]", "[usn]", "[ls]", "[conf]", "[models]",
"[decks]", "[dconf]", "[tags]"
];

protected override string GetValues(col i)
protected override IReadOnlyList<object> GetValues(col i)
{
return
$"{i.id},{i.crt},{i.mod},{i.scm},{i.ver}," +
$"{i.dty},{i.usn},{i.ls},'{i.conf}','{i.models}'," +
$"'{i.decks}','{i.dconf}','{i.tags}'";
return [
i.id, i.crt, i.mod, i.scm, i.ver,
i.dty, i.usn, i.ls, i.conf, i.models,
i.decks, i.dconf, i.tags
];
}

protected override col Map(SqliteDataReader reader)
Expand Down
7 changes: 4 additions & 3 deletions src/AnkiNet/CollectionFile/Database/GraveRepository.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,12 @@ public GraveRepository(SqliteConnection connection) : base(connection)

protected override string TableName => "[graves]";

protected override string Columns => "[usn], [oid], [type]";
protected override IReadOnlyList<string> Columns { get; } =
["[usn]", "[oid]", "[type]"];

protected override string GetValues(grave i)
protected override IReadOnlyList<object> GetValues(grave i)
{
return $"{i.usn},{i.oid},{i.type}";
return [i.usn, i.oid, i.type];
}

protected override grave Map(SqliteDataReader reader)
Expand Down
25 changes: 14 additions & 11 deletions src/AnkiNet/CollectionFile/Database/NoteRepository.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,19 +11,22 @@ public NoteRepository(SqliteConnection connection) : base(connection)

protected override string TableName => "[notes]";

protected override string Columns =>
"[id], [guid], [mid], " +
"[mod], [usn], [tags], " +
"[flds], [sfld], [csum], " +
"[flags], [data]";
protected override IReadOnlyList<string> Columns { get; } =
[
"[id]", "[guid]", "[mid]",
"[mod]", "[usn]", "[tags]",
"[flds]", "[sfld]", "[csum]",
"[flags]", "[data]"
];

protected override string GetValues(note i)
protected override IReadOnlyList<object> GetValues(note i)
{
return
$"{i.id},'{i.guid}',{i.mid}," +
$"{i.mod},{i.usn},'{i.tags}'," +
$"'{i.flds}','{i.sfld}',{i.csum}," +
$"{i.flags},'{i.data}'";
return [
i.id, i.guid, i.mid,
i.mod, i.usn, i.tags,
i.flds, i.sfld, i.csum,
i.flags, i.data
];
}

protected override note Map(SqliteDataReader reader)
Expand Down
17 changes: 10 additions & 7 deletions src/AnkiNet/CollectionFile/Database/RevLogRepository.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,18 @@ public RevLogRepository(SqliteConnection connection) : base(connection)

protected override string TableName => "[revlog]";

protected override string Columns =>
"[id], [cid], [usn], [ease], [ivl], " +
"[lastIvl], [factor], [time], [type]";
protected override IReadOnlyList<string> Columns { get; } =
[
"[id]", "[cid]", "[usn]", "[ease]", "[ivl]",
"[lastIvl]", "[factor]", "[time]", "[type]"
];

protected override string GetValues(revLog i)
protected override IReadOnlyList<object> GetValues(revLog i)
{
return
$"{i.id},{i.cid},{i.usn},{i.ease},{i.ivl}," +
$"{i.lastIvl},{i.factor},{i.time},{i.type}";
return [
i.id, i.cid, i.usn, i.ease, i.ivl,
i.lastIvl, i.factor, i.time, i.type
];
}

protected override revLog Map(SqliteDataReader reader)
Expand Down
53 changes: 38 additions & 15 deletions src/AnkiNet/CollectionFile/Database/SqliteRepository.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,10 @@ internal abstract class SqliteRepository<T>
private readonly SqliteConnection _connection;

protected abstract string TableName { get; }
protected abstract string Columns { get; }
protected abstract string GetValues(T item);
protected abstract IReadOnlyList<string> Columns { get; }

protected abstract IReadOnlyList<object> GetValues(T item);


protected abstract T Map(SqliteDataReader reader);

Expand All @@ -21,12 +23,12 @@ public async Task<List<T>> ReadAll()
{
var result = new List<T>();

var readAllSqlQuery = $"SELECT {Columns} FROM {TableName}";
var readAllSqlQuery = $"SELECT {string.Join(",", Columns)} FROM {TableName}";

try
{
using var command = new SqliteCommand(readAllSqlQuery, _connection);
using var reader = await command.ExecuteReaderAsync();
await using var command = new SqliteCommand(readAllSqlQuery, _connection);
await using var reader = await command.ExecuteReaderAsync();

while (await reader.ReadAsync())
{
Expand All @@ -42,29 +44,50 @@ public async Task<List<T>> ReadAll()
return result;
}

public async Task Add(List<T> items)
public async Task Add(IReadOnlyList<T> items)
{
if (!items.Any())
if (items.Count == 0)
{
return;
}

var writeSqlQuery = $@"
INSERT INTO {TableName}
({Columns})
VALUES ";
string writeSqlQuery;
{
var values = Enumerable.Range(0, items.Count).Select(itemIndex =>
{
var @params = Enumerable.Range(0, Columns.Count).Select(paramIndex => ParamName(itemIndex, paramIndex));

var values = items.Select(i => $"({GetValues(i)})");
writeSqlQuery += string.Join(',', values);
return $"({string.Join(',', @params)})";
});

writeSqlQuery = $"INSERT INTO {TableName} ({string.Join(",", Columns)}) VALUES {string.Join(',', values)}";
}

try
{
using var command = new SqliteCommand(writeSqlQuery, _connection);
var i = await command.ExecuteNonQueryAsync();
await using var command = new SqliteCommand(writeSqlQuery, _connection);

foreach (var (item, itemIndex) in items.Select((item, itemIndex) => (item, itemIndex)))
{
var itemValues = GetValues(item);
foreach (var (itemValue, paramIndex) in itemValues.Select((itemValue, paramIndex) => (itemValue, paramIndex)))
{
var paramName = ParamName(itemIndex, paramIndex);
command.Parameters.AddWithValue(paramName, itemValue);
}
}

var numberOfItemsInserted = await command.ExecuteNonQueryAsync();
}
catch (Exception e)
{
throw new IOException($"Cannot Add {typeof(T).Name}", e);
}

#region Helper function(s)

static string ParamName(int itemIndex, int paramIndex) => $"@p{itemIndex}_{paramIndex}";

#endregion
}
}
22 changes: 14 additions & 8 deletions src/AnkiNet/CollectionFile/Mapper/CollectionMapper.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Text.Json;
using System.Text.Json.Serialization.Metadata;
using AnkiNet.CollectionFile.Database.Model;
using AnkiNet.CollectionFile.Model;
using AnkiNet.CollectionFile.Model.Json;
Expand All @@ -7,12 +8,17 @@ namespace AnkiNet.CollectionFile.Mapper;

internal static class CollectionMapper
{
private static readonly JsonSerializerOptions SerializerOptions = new()
{
TypeInfoResolver = new DefaultJsonTypeInfoResolver(),
};

public static Collection FromDb(col col)
{
var configuration = JsonSerializer.Deserialize<JsonConfiguration>(col.conf);
var models = JsonSerializer.Deserialize<Dictionary<long, JsonModel>>(col.models);
var decks = JsonSerializer.Deserialize<Dictionary<long, JsonDeck>>(col.decks);
var decksConfiguration = JsonSerializer.Deserialize<Dictionary<long, JsonDeckConfguration>>(col.dconf);
var configuration = JsonSerializer.Deserialize<JsonConfiguration>(col.conf, SerializerOptions);
var models = JsonSerializer.Deserialize<Dictionary<long, JsonModel>>(col.models, SerializerOptions);
var decks = JsonSerializer.Deserialize<Dictionary<long, JsonDeck>>(col.decks, SerializerOptions);
var decksConfiguration = JsonSerializer.Deserialize<Dictionary<long, JsonDeckConfguration>>(col.dconf, SerializerOptions);

return new Collection(
col.id,
Expand All @@ -33,10 +39,10 @@ public static Collection FromDb(col col)

public static col ToDb(Collection collection)
{
var conf = JsonSerializer.Serialize(collection.Configuration);
var models = JsonSerializer.Serialize(collection.Models);
var decks = JsonSerializer.Serialize(collection.Decks);
var dconf = JsonSerializer.Serialize(collection.DecksConfiguration);
var conf = JsonSerializer.Serialize(collection.Configuration, SerializerOptions);
var models = JsonSerializer.Serialize(collection.Models, SerializerOptions);
var decks = JsonSerializer.Serialize(collection.Decks, SerializerOptions);
var dconf = JsonSerializer.Serialize(collection.DecksConfiguration, SerializerOptions);

return new col(
collection.Id,
Expand Down