Skip to content

Commit

Permalink
Use InsertManyAsync() on import to reduce connections to mongo. (#1054)
Browse files Browse the repository at this point in the history
  • Loading branch information
imnasnainaec authored Mar 5, 2021
1 parent b4c4416 commit 2926273
Show file tree
Hide file tree
Showing 6 changed files with 81 additions and 11 deletions.
19 changes: 18 additions & 1 deletion Backend.Tests/Mocks/WordRepositoryMock.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
using System.Threading.Tasks;
using BackendFramework.Interfaces;
using BackendFramework.Models;
using BackendFramework.Services;

namespace Backend.Tests.Mocks
{
Expand Down Expand Up @@ -45,6 +44,15 @@ public Task<Word> Create(Word word)
return Task.FromResult(word.Clone());
}

public Task<List<Word>> Create(List<Word> words)
{
foreach (var w in words)
{
Create(w);
}
return Task.FromResult(words);
}

public Task<bool> DeleteAllWords(string projectId)
{
_words.Clear();
Expand All @@ -63,6 +71,15 @@ public Task<Word> AddFrontier(Word word)
return Task.FromResult(word.Clone());
}

public Task<List<Word>> AddFrontier(List<Word> words)
{
foreach (var w in words)
{
AddFrontier(w);
}
return Task.FromResult(words);
}

public Task<bool> DeleteFrontier(string projectId, string wordId)
{
var origLength = _frontier.Count;
Expand Down
1 change: 1 addition & 0 deletions Backend/Controllers/LiftController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,7 @@ public async Task<IActionResult> UploadLiftFile(string projectId, [FromForm] Fil

// Import words from lift file
var resp = parser.ReadLiftFile(extractedLiftPath.FirstOrDefault());
await liftMerger.SaveImportEntries();

// Add character set to project from ldml file
var proj = await _projectService.GetProject(projectId);
Expand Down
12 changes: 9 additions & 3 deletions Backend/Interfaces/ILiftService.cs
Original file line number Diff line number Diff line change
@@ -1,21 +1,27 @@
using System.Threading.Tasks;
using System.Collections.Generic;
using System.Threading.Tasks;
using BackendFramework.Models;
using SIL.Lift.Parsing;

namespace BackendFramework.Interfaces
{
public interface ILiftService
{
ILexiconMerger<LiftObject, LiftEntry, LiftSense, LiftExample> GetLiftImporterExporter(
ILiftMerger GetLiftImporterExporter(
string projectId, IProjectService projectService, IWordRepository wordRepo);
void LdmlImport(string filePath, string langTag, IProjectService projectService, Project project);
Task<string> LiftExport(string projectId, IWordRepository wordRepo, IProjectService projectService);

// Methods to store, retrieve, and delete an export string in a common dictionary
// Methods to store, retrieve, and delete an export string in a common dictionary.
void StoreExport(string key, string filePath);
string? RetrieveExport(string key);
bool DeleteExport(string key);
void SetExportInProgress(string key, bool isInProgress);
bool IsExportInProgress(string key);
}

public interface ILiftMerger : ILexiconMerger<LiftObject, LiftEntry, LiftSense, LiftExample>
{
Task<List<Word>> SaveImportEntries();
}
}
2 changes: 2 additions & 0 deletions Backend/Interfaces/IWordRepository.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,12 @@ public interface IWordRepository
Task<List<Word>> GetAllWords(string projectId);
Task<Word?> GetWord(string projectId, string wordId);
Task<Word> Create(Word word);
Task<List<Word>> Create(List<Word> words);
Task<Word> Add(Word word);
Task<bool> DeleteAllWords(string projectId);
Task<List<Word>> GetFrontier(string projectId);
Task<Word> AddFrontier(Word word);
Task<List<Word>> AddFrontier(List<Word> word);
Task<bool> DeleteFrontier(string projectId, string wordId);
}
}
29 changes: 22 additions & 7 deletions Backend/Services/LiftApiServices.cs
Original file line number Diff line number Diff line change
Expand Up @@ -474,7 +474,7 @@ private static void LdmlExport(string filePath, string vernacularBcp47, List<str
return SecurityElement.Escape(sInput);
}

public ILexiconMerger<LiftObject, LiftEntry, LiftSense, LiftExample> GetLiftImporterExporter(
public ILiftMerger GetLiftImporterExporter(
string projectId, IProjectService projectService, IWordRepository wordRepo)
{
return new LiftMerger(projectId, projectService, wordRepo);
Expand Down Expand Up @@ -511,12 +511,12 @@ private static void WriteRangeElement(
liftRangesWriter.WriteEndElement(); //end range element
}


private sealed class LiftMerger : ILexiconMerger<LiftObject, LiftEntry, LiftSense, LiftExample>
private sealed class LiftMerger : ILiftMerger
{
private readonly string _projectId;
private readonly IProjectService _projectService;
private readonly IWordRepository _wordRepo;
private readonly List<Word> _importEntries = new List<Word>();

public LiftMerger(string projectId, IProjectService projectService, IWordRepository wordRepo)
{
Expand All @@ -526,9 +526,23 @@ public LiftMerger(string projectId, IProjectService projectService, IWordReposit
}

/// <summary>
/// The meat of lift import is done here.
/// This reads in all necessary attributes of a word and adds it to the database.
/// <see cref="Word"/>s are added to the private field <see cref="_importEntries"/>
/// during lift import. This saves the contents of _importEntries to the database.
/// </summary>
/// <returns> The words saved. </returns>
public async Task<List<Word>> SaveImportEntries()
{
var savedWords = new List<Word>(await _wordRepo.Create(_importEntries));
_importEntries.Clear();
return savedWords;
}

/// <summary>
/// A significant portion of lift import is done here. This reads in all necessary
/// attributes to create a word from <paramref name="entry"/>, adding the result
/// to <see cref="_importEntries"/> to be saved to the database.
/// </summary>
/// <param name="entry"></param>
public async void FinishEntry(LiftEntry entry)
{
var newWord = new Word
Expand Down Expand Up @@ -616,7 +630,8 @@ public async void FinishEntry(LiftEntry entry)
{
// Splits on the space between the number and name of the semantic domain
var splitSemDom = semanticDomainString.Split(" ", 2);
newSense.SemanticDomains.Add(new SemanticDomain { Id = splitSemDom[0], Name = splitSemDom[1] });
newSense.SemanticDomains.Add(
new SemanticDomain { Id = splitSemDom[0], Name = splitSemDom[1] });
}

newWord.Senses.Add(newSense);
Expand Down Expand Up @@ -652,7 +667,7 @@ public async void FinishEntry(LiftEntry entry)
}

newWord.ProjectId = _projectId;
await _wordRepo.Create(newWord);
_importEntries.Add(newWord);
}

/// <summary> Creates the object to transfer all the data from a word </summary>
Expand Down
29 changes: 29 additions & 0 deletions Backend/Services/WordRepository.cs
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ private static void PopulateBlankWordTimes(Word word)
/// If the Created or Modified time fields are blank, they will automatically calculated using the current
/// time. This allows services to set or clear the values before creation to control these fields.
/// </remarks>
/// <param name="word"></param>
/// <returns> The word created </returns>
public async Task<Word> Create(Word word)
{
Expand All @@ -85,6 +86,24 @@ public async Task<Word> Create(Word word)
return word;
}

/// <summary> Adds a list of <see cref="Word"/>s to the WordsCollection and Frontier </summary>
/// <remarks>
/// If the Created or Modified time fields are blank, they will automatically calculated using the current
/// time. This allows services to set or clear the values before creation to control these fields.
/// </remarks>
/// <param name="words"></param>
/// <returns> The words created </returns>
public async Task<List<Word>> Create(List<Word> words)
{
foreach (var w in words)
{
PopulateBlankWordTimes(w);
}
await _wordDatabase.Words.InsertManyAsync(words);
await AddFrontier(words);
return words;
}

/// <summary> Adds a <see cref="Word"/> only to the WordsCollection </summary>
/// <remarks>
/// If the Created or Modified time fields are blank, they will automatically calculated using the current
Expand All @@ -105,13 +124,23 @@ public async Task<List<Word>> GetFrontier(string projectId)
}

/// <summary> Adds a <see cref="Word"/> only to the Frontier </summary>
/// <param name="word"></param>
/// <returns> The word created </returns>
public async Task<Word> AddFrontier(Word word)
{
await _wordDatabase.Frontier.InsertOneAsync(word);
return word;
}

/// <summary> Adds a list of <see cref="Word"/>s only to the Frontier </summary>
/// <param name="words"></param>
/// <returns> The words created </returns>
public async Task<List<Word>> AddFrontier(List<Word> words)
{
await _wordDatabase.Frontier.InsertManyAsync(words);
return words;
}

/// <summary> Removes <see cref="Word"/> from the Frontier with specified wordId and projectId </summary>
/// <returns> A bool: success of operation </returns>
public async Task<bool> DeleteFrontier(string projectId, string wordId)
Expand Down

0 comments on commit 2926273

Please sign in to comment.