// Copyright Epic Games, Inc. All Rights Reserved. #include "MetasoundFrontendQuery.h" #include "Algo/IsSorted.h" #include "Algo/Sort.h" #include "Algo/Transform.h" #include "CoreMinimal.h" #include "MetasoundFrontendDocument.h" #include "Templates/TypeHash.h" namespace Metasound { namespace FrontendQueryPrivate { // Wrapper for step defined by function struct FStreamFunctionFrontendQueryStep: IFrontendQueryStreamStep { using FStreamFunction = FFrontendQueryStep::FStreamFunction; FStreamFunctionFrontendQueryStep(FStreamFunction&& InFunc) : Func(MoveTemp(InFunc)) { } void Stream(TArray& OutEntries) override { Func(OutEntries); } private: FStreamFunction Func; }; // Wrapper for step defined by function struct FTransformFunctionFrontendQueryStep: IFrontendQueryTransformStep { using FTransformFunction = FFrontendQueryStep::FTransformFunction; FTransformFunctionFrontendQueryStep(FTransformFunction InFunc) : Func(InFunc) { } void Transform(FFrontendQueryEntry::FValue& InValue) const override { Func(InValue); } FTransformFunction Func; }; // Wrapper for step defined by function struct FMapFunctionFrontendQueryStep: IFrontendQueryMapStep { using FMapFunction = FFrontendQueryStep::FMapFunction; FMapFunctionFrontendQueryStep(FMapFunction InFunc) : Func(InFunc) { } FFrontendQueryEntry::FKey Map(const FFrontendQueryEntry& InEntry) const override { return Func(InEntry); } FMapFunction Func; }; // Wrapper for step defined by function struct FReduceFunctionFrontendQueryStep: IFrontendQueryReduceStep { using FReduceFunction = FFrontendQueryStep::FReduceFunction; FReduceFunctionFrontendQueryStep(FReduceFunction InFunc) : Func(InFunc) { } void Reduce(FFrontendQueryEntry::FKey InKey, TArrayView& InEntries, FReduceOutputView& OutResult) const override { return Func(InKey, InEntries, OutResult); } FReduceFunction Func; }; // Wrapper for step defined by function struct FFilterFunctionFrontendQueryStep: IFrontendQueryFilterStep { using FFilterFunction = FFrontendQueryStep::FFilterFunction; FFilterFunctionFrontendQueryStep(FFilterFunction InFunc) : Func(InFunc) { } bool Filter(const FFrontendQueryEntry& InEntry) const override { return Func(InEntry); } FFilterFunction Func; }; // Wrapper for step defined by function struct FScoreFunctionFrontendQueryStep: IFrontendQueryScoreStep { using FScoreFunction = FFrontendQueryStep::FScoreFunction; FScoreFunctionFrontendQueryStep(FScoreFunction InFunc) : Func(InFunc) { } float Score(const FFrontendQueryEntry& InEntry) const override { return Func(InEntry); } FScoreFunction Func; }; // Wrapper for step defined by function struct FSortFunctionFrontendQueryStep: IFrontendQuerySortStep { using FSortFunction = FFrontendQueryStep::FSortFunction; FSortFunctionFrontendQueryStep(FSortFunction InFunc) : Func(InFunc) { } bool Sort(const FFrontendQueryEntry& InEntryLHS, const FFrontendQueryEntry& InEntryRHS) const override { return Func(InEntryLHS, InEntryRHS); } FSortFunction Func; }; // Wrapper for step defined by function struct FLimitFunctionFrontendQueryStep: IFrontendQueryLimitStep { using FLimitFunction = FFrontendQueryStep::FLimitFunction; FLimitFunctionFrontendQueryStep(FLimitFunction InFunc) : Func(InFunc) { } int32 Limit() const override { return Func(); } FLimitFunction Func; }; // Base implementation of a query step execution. struct FStepExecuterBase : public FFrontendQueryStep::IStepExecuter { // Appends incremental results to the output results. EResultModificationState Append(const FFrontendQuerySelection& InIncremental, FFrontendQuerySelection& InOutResult) const { EResultModificationState ResultState = EResultModificationState::Unmodified; if (InIncremental.GetSelection().Num() > 0) { InOutResult.AppendToStorageAndSelection(InIncremental); ResultState = EResultModificationState::Modified; } return ResultState; } }; // Implements execution of a source struct FSourceStepExecuter : public FStepExecuterBase { FSourceStepExecuter(TUniquePtr&& InSource) : Source(MoveTemp(InSource)) { } // Get the latest entries from the source virtual EResultModificationState Increment(FFrontendQuerySelection& InOutResult) override { if (Source.IsValid()) { TArray NewEntries; Source->Stream(NewEntries); if (NewEntries.Num() > 0) { InOutResult.AppendToStorageAndSelection(NewEntries); return EResultModificationState::Modified; } } return EResultModificationState::Unmodified; } virtual EResultModificationState Merge(const FFrontendQuerySelection& InIncremental, FFrontendQuerySelection& InOutResult) override { return Append(InIncremental, InOutResult); } virtual bool IsMergeRequiredForIncremental() const override { return false; } virtual EResultModificationState Execute(FFrontendQuerySelection& InOutResult) override { Reset(); // Reset source to original state. return Increment(InOutResult); } virtual void Reset() override { if (Source.IsValid()) { Source->Reset(); } } private: TUniquePtr Source; }; template struct TStepExecuter : public FStepExecuterBase { TStepExecuter(TUniquePtr&& InStep) : Step(MoveTemp(InStep)) { } virtual void Reset() override { } virtual bool IsMergeRequiredForIncremental() const override { return false; } protected: TUniquePtr Step; }; struct FStreamStepExecuter : TStepExecuter { using TStepExecuter::TStepExecuter; virtual EResultModificationState Increment(FFrontendQuerySelection& InOutResult) override { EResultModificationState ResultState = EResultModificationState::Unmodified; if (Step.IsValid()) { TArray NewEntries; Step->Stream(NewEntries); if (NewEntries.Num() > 0) { ResultState = EResultModificationState::Modified; InOutResult.AppendToStorageAndSelection(NewEntries); } } return ResultState; } virtual EResultModificationState Merge(const FFrontendQuerySelection& InIncremental, FFrontendQuerySelection& InOutResult) override { return Append(InIncremental, InOutResult); } virtual EResultModificationState Execute(FFrontendQuerySelection& InOutResult) override { return Increment(InOutResult); } }; struct FTransformStepExecuter : TStepExecuter { using TStepExecuter::TStepExecuter; virtual EResultModificationState Increment(FFrontendQuerySelection& InOutResult) override { EResultModificationState ResultState = EResultModificationState::Unmodified; if (Step.IsValid()) { if (InOutResult.GetSelection().Num() > 0) { ResultState = EResultModificationState::Modified; } for (FFrontendQueryEntry* Entry : InOutResult.GetSelection()) { if (nullptr != Entry) { Step->Transform(Entry->Value); } } } return ResultState; } virtual EResultModificationState Merge(const FFrontendQuerySelection& InIncremental, FFrontendQuerySelection& InOutResult) override { return Append(InIncremental, InOutResult); } virtual EResultModificationState Execute(FFrontendQuerySelection& InOutResult) override { return Increment(InOutResult); } }; struct FMapStepExecuter : TStepExecuter { using TStepExecuter::TStepExecuter; virtual EResultModificationState Increment(FFrontendQuerySelection& InOutResult) override { EResultModificationState ResultState = EResultModificationState::Unmodified; if (Step.IsValid()) { if (InOutResult.GetSelection().Num() > 0) { ResultState = EResultModificationState::Modified; } for (FFrontendQueryEntry* Entry : InOutResult.GetSelection()) { if (nullptr != Entry) { Entry->Key = Step->Map(*Entry); } } } return ResultState; } virtual EResultModificationState Merge(const FFrontendQuerySelection& InIncremental, FFrontendQuerySelection& InOutResult) override { return Append(InIncremental, InOutResult); } virtual EResultModificationState Execute(FFrontendQuerySelection& InOutResult) override { return Increment(InOutResult); } }; struct FReduceStepExecuter : TStepExecuter { using FKey = FFrontendQueryEntry::FKey; private: static const FKey InvalidKey; static const FFrontendQueryEntry::FKey& GetKey(const FFrontendQueryEntry* InEntry) { if (nullptr != InEntry) { return InEntry->Key; } else { return InvalidKey; } } // Reduce entries. // // @parma InKey - The key associated with InEntriesToReduce // @parma InEntriesToReduce - Entries to either append or reduce. // @param OutResult - The output query selection to append to. void Reduce(const FFrontendQueryEntry::FKey& InKey, TArrayView InEntriesToReduce, FFrontendQuerySelection& OutResult) { check(Step.IsValid()); // Note: InEntriesToReduce is has valid storage in OutResult. FFrontendQuerySelection::FReduceOutputView ReduceResult(InEntriesToReduce); Step->Reduce(InKey, InEntriesToReduce, ReduceResult); OutResult.AppendToStorageAndSelection(MoveTemp(ReduceResult)); } // Iterate over sorted entries. template void IterSortedEntries(TArrayView SortedSelection, FuncType InFunc) { // input selection must be sorted by key. check(Algo::IsSortedBy(SortedSelection, FReduceStepExecuter::GetKey)); if (SortedSelection.Num() > 0) { int32 StartIndex = 0; FFrontendQueryEntry::FKey CurrentKey = GetKey(SortedSelection[0]); const int32 Num = SortedSelection.Num(); for (int32 EndIndex = 1; EndIndex < Num; EndIndex++) { FFrontendQueryEntry::FKey ThisKey = GetKey(SortedSelection[EndIndex]); if (CurrentKey != ThisKey) { InFunc(CurrentKey, SortedSelection.Slice(StartIndex, EndIndex - StartIndex)); CurrentKey = ThisKey; StartIndex = EndIndex; } } InFunc(CurrentKey, SortedSelection.Slice(StartIndex, SortedSelection.Num() - StartIndex)); } } TArray GetSortedSelection(TArrayView InSelection) { TArray SortedSelection(InSelection.GetData(), InSelection.Num()); Algo::SortBy(SortedSelection, GetKey); return SortedSelection; } public: using TStepExecuter::TStepExecuter; virtual EResultModificationState Increment(FFrontendQuerySelection& InOutResult) override { check(IsMergeRequiredForIncremental()); return Execute(InOutResult); } virtual EResultModificationState Merge(const FFrontendQuerySelection& InIncremental, FFrontendQuerySelection& InOutResult) override { EResultModificationState ResultState = EResultModificationState::Unmodified; if (Step.IsValid()) { if (InIncremental.GetSelection().Num() > 0) { TSet NewKeys; Algo::Transform(InIncremental.GetSelection(), NewKeys, GetKey); InOutResult.AppendToStorageAndSelection(InIncremental); TArray SortedSelection = GetSortedSelection(InOutResult.GetSelection()); InOutResult.ResetSelection(); IterSortedEntries(SortedSelection, [&](const FKey& InKey, TArrayView InEntriesToReduce) { if (NewKeys.Contains(InKey)) { // Some of the entries re new. Need to re-reduce. this->Reduce(InKey, InEntriesToReduce, InOutResult); } else { // All these entries are owned by the result already. InOutResult.AppendToSelection(InEntriesToReduce); } }); ResultState = EResultModificationState::Modified; } } return ResultState; } virtual EResultModificationState Execute(FFrontendQuerySelection& InOutResult) override { EResultModificationState ResultState = EResultModificationState::Unmodified; if (Step.IsValid()) { if (InOutResult.GetSelection().Num() > 0) { TArray SortedSelection = GetSortedSelection(InOutResult.GetSelection()); InOutResult.ResetSelection(); IterSortedEntries(SortedSelection, [&](const FKey& InKey, TArrayView InEntriesToReduce) { this->Reduce(InKey, InEntriesToReduce, InOutResult); }); ResultState = EResultModificationState::Modified; } } return ResultState; } virtual bool IsMergeRequiredForIncremental() const override { // Merge is required during an incremental update because // existing results and new results may share the same key and // need re-reducing. return true; } }; const FReduceStepExecuter::FKey FReduceStepExecuter::InvalidKey; struct FFilterStepExecuter : TStepExecuter { using TStepExecuter::TStepExecuter; virtual EResultModificationState Increment(FFrontendQuerySelection& InOutResult) override { EResultModificationState ResultState = EResultModificationState::Unmodified; if (Step.IsValid()) { auto FilterFunc = [&](const FFrontendQueryEntry* Entry) { return (nullptr != Entry) && Step->Filter(*Entry); }; int32 NumRemoved = InOutResult.FilterSelection(FilterFunc); if (NumRemoved > 0) { ResultState = EResultModificationState::Modified; } } return ResultState; } virtual EResultModificationState Merge(const FFrontendQuerySelection& InIncremental, FFrontendQuerySelection& InOutResult) override { return Append(InIncremental, InOutResult); } virtual EResultModificationState Execute(FFrontendQuerySelection& InOutResult) override { return Increment(InOutResult); } }; struct FScoreStepExecuter : TStepExecuter { using TStepExecuter::TStepExecuter; virtual EResultModificationState Increment(FFrontendQuerySelection& InOutResult) override { EResultModificationState ResultState = EResultModificationState::Unmodified; if (Step.IsValid()) { if (InOutResult.GetSelection().Num() > 0) { ResultState = EResultModificationState::Modified; } for (FFrontendQueryEntry* Entry : InOutResult.GetSelection()) { if (nullptr != Entry) { Entry->Score = Step->Score(*Entry); } } } return ResultState; } virtual EResultModificationState Merge(const FFrontendQuerySelection& InIncremental, FFrontendQuerySelection& InOutResult) override { return Append(InIncremental, InOutResult); } virtual EResultModificationState Execute(FFrontendQuerySelection& InOutResult) override { return Increment(InOutResult); } }; struct FSortStepExecuter : TStepExecuter { using TStepExecuter::TStepExecuter; EResultModificationState Increment(FFrontendQuerySelection& InOutResult) override { check(IsMergeRequiredForIncremental()); return Execute(InOutResult); } virtual EResultModificationState Merge(const FFrontendQuerySelection& InIncremental, FFrontendQuerySelection& InOutResult) override { EResultModificationState ResultState = EResultModificationState::Unmodified; if (Step.IsValid()) { const bool bIncrementalHasNonEmptySelection = InIncremental.GetSelection().Num() > 0; if (bIncrementalHasNonEmptySelection) { // Append incremental data to this result InOutResult.AppendToStorageAndSelection(InIncremental); InOutResult.GetSelection().Sort( [&](const FFrontendQueryEntry& InLHS, const FFrontendQueryEntry& InRHS) { return Step->Sort(InLHS, InRHS); } ); ResultState = EResultModificationState::Modified; } } return ResultState; } virtual EResultModificationState Execute(FFrontendQuerySelection& InOutResult) override { EResultModificationState ResultState = EResultModificationState::Unmodified; if (Step.IsValid()) { if (InOutResult.GetSelection().Num() > 0) { InOutResult.GetSelection().Sort( [&](const FFrontendQueryEntry& InLHS, const FFrontendQueryEntry& InRHS) { return Step->Sort(InLHS, InRHS); } ); ResultState = EResultModificationState::Modified; } } return ResultState; } virtual bool IsMergeRequiredForIncremental() const { // Merging is required during incremental update because sort // order may change after merge. return true; } }; struct FLimitStepExecuter : TStepExecuter { using TStepExecuter::TStepExecuter; EResultModificationState Increment(FFrontendQuerySelection& InOutResult) override { check(IsMergeRequiredForIncremental()); return Execute(InOutResult); } virtual EResultModificationState Merge(const FFrontendQuerySelection& InIncremental, FFrontendQuerySelection& InOutResult) override { EResultModificationState ResultState = EResultModificationState::Unmodified; if (Step.IsValid()) { int32 Limit = Step->Limit(); if (InIncremental.GetSelection().Num() > 0) { ResultState = EResultModificationState::Modified; InOutResult.AppendToStorageAndSelection(InIncremental); if (InOutResult.GetSelection().Num() > Limit) { InOutResult.SetSelection(InOutResult.GetSelection().Slice(0, Limit)); } } } return ResultState; } virtual EResultModificationState Execute(FFrontendQuerySelection& InOutResult) override { EResultModificationState ResultState = EResultModificationState::Unmodified; if (Step.IsValid()) { int32 Limit = Step->Limit(); if (InOutResult.GetSelection().Num() > Limit) { ResultState = EResultModificationState::Modified; InOutResult.SetSelection(InOutResult.GetSelection().Slice(0, Limit)); } } return ResultState; } virtual bool IsMergeRequiredForIncremental() const { // Merge is required because values under limit may be different // after merge. return true; } }; } FFrontendQueryKey::FFrontendQueryKey() : Key(TInPlaceType()) , Hash(INDEX_NONE) {} FFrontendQueryKey::FFrontendQueryKey(int32 InKey) : Key(TInPlaceType(), InKey) , Hash(GetTypeHash(InKey)) { } FFrontendQueryKey::FFrontendQueryKey(const FString& InKey) : Key(TInPlaceType(), InKey) , Hash(GetTypeHash(InKey)) { } FFrontendQueryKey::FFrontendQueryKey(const FName& InKey) : Key(TInPlaceType(), InKey) , Hash(GetTypeHash(InKey)) { } bool FFrontendQueryKey::IsValid() const { return Key.GetIndex() != FKeyType::IndexOfType(); } bool operator==(const FFrontendQueryKey& InLHS, const FFrontendQueryKey& InRHS) { if (InLHS.Hash == InRHS.Hash) { if (InLHS.IsValid() && InRHS.IsValid()) { if (InLHS.Key.GetIndex() == InRHS.Key.GetIndex()) { switch(InLHS.Key.GetIndex()) { case FFrontendQueryKey::FKeyType::IndexOfType(): return InLHS.Key.Get() == InRHS.Key.Get(); case FFrontendQueryKey::FKeyType::IndexOfType(): return InLHS.Key.Get() == InRHS.Key.Get(); case FFrontendQueryKey::FKeyType::IndexOfType(): return InLHS.Key.Get() == InRHS.Key.Get(); default: // Unhandled case type. checkNoEntry(); } } } } return false; } bool operator!=(const FFrontendQueryKey& InLHS, const FFrontendQueryKey& InRHS) { return !(InLHS == InRHS); } bool operator<(const FFrontendQueryKey& InLHS, const FFrontendQueryKey& InRHS) { if (InLHS.Hash != InRHS.Hash) { return InLHS.Hash < InRHS.Hash; } if (InLHS.Key.GetIndex() != InRHS.Key.GetIndex()) { return InLHS.Key.GetIndex() < InRHS.Key.GetIndex(); } switch(InLHS.Key.GetIndex()) { case FFrontendQueryKey::FKeyType::IndexOfType(): return InLHS.Key.Get() < InRHS.Key.Get(); case FFrontendQueryKey::FKeyType::IndexOfType(): return InLHS.Key.Get() < InRHS.Key.Get(); case FFrontendQueryKey::FKeyType::IndexOfType(): return InLHS.Key.Get().FastLess(InRHS.Key.Get()); default: // Unhandled case type. checkNoEntry(); } return false; } uint32 GetTypeHash(const FFrontendQueryKey& InKey) { return InKey.Hash; } FFrontendQuerySelection::FFrontendQuerySelection(const FFrontendQuerySelection& InOther) { AppendToStorageAndSelection(InOther); } FFrontendQuerySelection& FFrontendQuerySelection::operator=(const FFrontendQuerySelection& InOther) { ResetStorageAndSelection(); AppendToStorageAndSelection(InOther); return *this; } FFrontendQuerySelection::FReduceOutputView::FReduceOutputView(TArrayView InEntries) : InitialEntries(InEntries) { } void FFrontendQuerySelection::FReduceOutputView::Add(FFrontendQueryEntry& InResult) { if (!InitialEntries.Contains(&InResult)) { SelectedNewEntries.Add(MakeUnique(InResult)); } else { SelectedExistingEntries.Add(&InResult); } } TArrayView FFrontendQuerySelection::GetSelection() { return Selection; } TArrayView FFrontendQuerySelection::GetSelection() const { return MakeArrayView(Selection); } void FFrontendQuerySelection::AppendToStorageAndSelection(const FFrontendQuerySelection& InSelection) { TArrayView InEntries = InSelection.GetSelection(); AppendToStorageAndSelection(InSelection.GetSelection()); } void FFrontendQuerySelection::AppendToStorageAndSelection(FFrontendQuerySelection&& InSelection) { InSelection.ShrinkStorageToSelection(); AppendToStorageAndSelection(MoveTemp(InSelection.Storage)); } void FFrontendQuerySelection::AppendToStorageAndSelection(TArrayView InEntries) { const int32 Num = InEntries.Num(); if (Num > 0) { int32 StorageIndex = Storage.Num(); int32 SelectionIndex = Selection.Num(); for (const FFrontendQueryEntry& Entry : InEntries) { Storage.Add(MakeUnique(Entry)); } Selection.AddZeroed(Num); for (int32 i = 0; i < Num; i++) { Selection[SelectionIndex] = Storage[StorageIndex].Get(); SelectionIndex++; StorageIndex++; } } } void FFrontendQuerySelection::AppendToStorageAndSelection(TArrayView InEntries) { if (InEntries.Num() > 0) { int32 StorageIndex = Storage.Num(); int32 SelectionIndex = Selection.Num(); int32 NumAdded = 0; for (const FFrontendQueryEntry* Entry : InEntries) { if (nullptr != Entry) { Storage.Add(MakeUnique(*Entry)); NumAdded++; } } if (NumAdded > 0) { Selection.AddZeroed(NumAdded); for (int32 i = 0; i < NumAdded; i++) { Selection[SelectionIndex] = Storage[StorageIndex].Get(); SelectionIndex++; StorageIndex++; } } } } void FFrontendQuerySelection::AppendToStorageAndSelection(FReduceOutputView&& InReduceView) { AppendToStorageAndSelection(MoveTemp(InReduceView.SelectedNewEntries)); AppendToSelection(InReduceView.SelectedExistingEntries); } void FFrontendQuerySelection::AppendToStorageAndSelection(TArray>&& InEntries) { if (InEntries.Num() > 0) { int32 StorageIndex = Storage.Num(); int32 SelectionIndex = Selection.Num(); int32 NumAdded = 0; for (TUniquePtr& Entry : InEntries) { if (Entry.IsValid()) { Storage.Add(MoveTemp(Entry)); NumAdded++; } } if (NumAdded > 0) { Selection.AddZeroed(NumAdded); for (int32 i = 0; i < NumAdded; i++) { Selection[SelectionIndex] = Storage[StorageIndex].Get(); SelectionIndex++; StorageIndex++; } } } } void FFrontendQuerySelection::ResetStorageAndSelection() { Storage.Reset(); Selection.Reset(); } void FFrontendQuerySelection::ResetSelection() { Selection.Reset(); } void FFrontendQuerySelection::SetSelection(TArrayView InEntries) { Selection = InEntries; } void FFrontendQuerySelection::AddToSelection(FFrontendQueryEntry* InEntry) { if (nullptr != InEntry) { Selection.Add(InEntry); } } void FFrontendQuerySelection::AppendToSelection(TArrayView InEntries) { Selection.Append(InEntries.GetData(), InEntries.Num()); } void FFrontendQuerySelection::ShrinkStorageToSelection() { TSet SelectionSet(Selection); Storage.RemoveAll([&](const TUniquePtr& InEntry) { return !SelectionSet.Contains(InEntry.Get()); }); } FFrontendQuerySelectionView::FFrontendQuerySelectionView(TSharedRef InResult) : Result(InResult) { } TArrayView FFrontendQuerySelectionView::GetSelection() const { return Result->GetSelection(); } FFrontendQueryStep::FFrontendQueryStep(FStreamFunction&& InFunc) : StepExecuter(MakeUnique(MakeUnique(MoveTemp(InFunc)))) { } FFrontendQueryStep::FFrontendQueryStep(FTransformFunction&& InFunc) : StepExecuter(MakeUnique(MakeUnique(MoveTemp(InFunc)))) { } FFrontendQueryStep::FFrontendQueryStep(FMapFunction&& InFunc) : StepExecuter(MakeUnique(MakeUnique(MoveTemp(InFunc)))) { } FFrontendQueryStep::FFrontendQueryStep(FReduceFunction&& InFunc) : StepExecuter(MakeUnique(MakeUnique(MoveTemp(InFunc)))) { } FFrontendQueryStep::FFrontendQueryStep(FFilterFunction&& InFunc) : StepExecuter(MakeUnique(MakeUnique(MoveTemp(InFunc)))) { } FFrontendQueryStep::FFrontendQueryStep(FScoreFunction&& InFunc) : StepExecuter(MakeUnique(MakeUnique(MoveTemp(InFunc)))) { } FFrontendQueryStep::FFrontendQueryStep(FSortFunction&& InFunc) : StepExecuter(MakeUnique(MakeUnique(MoveTemp(InFunc)))) { } FFrontendQueryStep::FFrontendQueryStep(FLimitFunction&& InFunc) : StepExecuter(MakeUnique(MakeUnique(MoveTemp(InFunc)))) { } FFrontendQueryStep::FFrontendQueryStep(TUniquePtr&& InSource) : StepExecuter(MakeUnique(MoveTemp(InSource))) { } FFrontendQueryStep::FFrontendQueryStep(TUniquePtr&& InStep) : StepExecuter(MakeUnique(MoveTemp(InStep))) { } FFrontendQueryStep::FFrontendQueryStep(TUniquePtr&& InStep) : StepExecuter(MakeUnique(MoveTemp(InStep))) { } FFrontendQueryStep::FFrontendQueryStep(TUniquePtr&& InStep) : StepExecuter(MakeUnique(MoveTemp(InStep))) { } FFrontendQueryStep::FFrontendQueryStep(TUniquePtr&& InStep) : StepExecuter(MakeUnique(MoveTemp(InStep))) { } FFrontendQueryStep::FFrontendQueryStep(TUniquePtr&& InStep) : StepExecuter(MakeUnique(MoveTemp(InStep))) { } FFrontendQueryStep::FFrontendQueryStep(TUniquePtr&& InStep) : StepExecuter(MakeUnique(MoveTemp(InStep))) { } FFrontendQueryStep::FFrontendQueryStep(TUniquePtr&& InStep) : StepExecuter(MakeUnique(MoveTemp(InStep))) { } FFrontendQueryStep::FFrontendQueryStep(TUniquePtr&& InStep) : StepExecuter(MakeUnique(MoveTemp(InStep))) { } FFrontendQueryStep::EResultModificationState FFrontendQueryStep::Execute(FFrontendQuerySelection& InOutResult) { if (StepExecuter.IsValid()) { return StepExecuter->Execute(InOutResult); } return EResultModificationState::Unmodified; } FFrontendQueryStep::EResultModificationState FFrontendQueryStep::Increment(FFrontendQuerySelection& InOutResult) { if (StepExecuter.IsValid()) { return StepExecuter->Increment(InOutResult); } return EResultModificationState::Unmodified; } FFrontendQueryStep::EResultModificationState FFrontendQueryStep::Merge(const FFrontendQuerySelection& InIncremental, FFrontendQuerySelection& InOutResult) { if (StepExecuter.IsValid()) { return StepExecuter->Merge(InIncremental, InOutResult); } return EResultModificationState::Unmodified; } bool FFrontendQueryStep::IsMergeRequiredForIncremental() const { if (StepExecuter.IsValid()) { return StepExecuter->IsMergeRequiredForIncremental(); } return false; } void FFrontendQueryStep::Reset() { if (StepExecuter.IsValid()) { StepExecuter->Reset(); } } FFrontendQuery::FFrontendQuery() : Result(MakeShared()) { } const TArray>& FFrontendQuery::GetSteps() const { return Steps; } FFrontendQuery& FFrontendQuery::AddStreamLambdaStep(FStreamFunction&& InFunc) { return AddFunctionStep(MoveTemp(InFunc)); } FFrontendQuery& FFrontendQuery::AddTransformLambdaStep(FTransformFunction&& InFunc) { return AddFunctionStep(MoveTemp(InFunc)); } FFrontendQuery& FFrontendQuery::AddMapLambdaStep(FMapFunction&& InFunc) { return AddFunctionStep(MoveTemp(InFunc)); } FFrontendQuery& FFrontendQuery::AddReduceLambdaStep(FReduceFunction&& InFunc) { return AddFunctionStep(MoveTemp(InFunc)); } FFrontendQuery& FFrontendQuery::AddFilterLambdaStep(FFilterFunction&& InFunc) { return AddFunctionStep(MoveTemp(InFunc)); } FFrontendQuery& FFrontendQuery::AddScoreLambdaStep(FScoreFunction&& InFunc) { return AddFunctionStep(MoveTemp(InFunc)); } FFrontendQuery& FFrontendQuery::AddSortLambdaStep(FSortFunction&& InFunc) { return AddFunctionStep(MoveTemp(InFunc)); } FFrontendQuery& FFrontendQuery::AddLimitLambdaStep(FLimitFunction&& InFunc) { return AddFunctionStep(MoveTemp(InFunc)); } FFrontendQuery& FFrontendQuery::AddStep(TUniquePtr&& InStep) { if (ensure(InStep.IsValid())) { int32 StepIndex = Steps.Num(); if (InStep->IsMergeRequiredForIncremental()) { StepResultCache.Add(StepIndex); } Steps.Add(MoveTemp(InStep)); } return *this; } FFrontendQuerySelectionView FFrontendQuery::Execute() { UpdateResult(); return FFrontendQuerySelectionView(Result); } FFrontendQuerySelectionView FFrontendQuery::Reset() { Result->ResetStorageAndSelection(); for (int32 StepIndex = 0; StepIndex < Steps.Num(); StepIndex++) { if (Steps[StepIndex].IsValid()) { Steps[StepIndex]->Reset(); } } return FFrontendQuerySelectionView(Result); } FFrontendQuerySelectionView FFrontendQuery::GetSelection() const { return FFrontendQuerySelectionView(Result); } void FFrontendQuery::UpdateResult() { FFrontendQuerySelection IncrementalResult; EResultModificationState IncrementalResultState = EResultModificationState::Unmodified; // Perform incremental update sequentially for (int32 StepIndex = 0; StepIndex < Steps.Num(); StepIndex++) { if (ensure(Steps[StepIndex].IsValid())) { FFrontendQueryStep& Step = *Steps[StepIndex]; IncrementalResultState = Step.Increment(IncrementalResult); const bool bMergeIncrementalResults = Step.IsMergeRequiredForIncremental() && (EResultModificationState::Modified == IncrementalResultState); if (bMergeIncrementalResults) { const bool bIsResultCacheEmpty = StepResultCache[StepIndex].GetSelection().Num() == 0; if (bIsResultCacheEmpty) { // If the prior result for this step is empty, we can continue // using the incremental path. StepResultCache[StepIndex] = IncrementalResult; } else { Step.Merge(IncrementalResult, StepResultCache[StepIndex]); *Result = StepResultCache[StepIndex]; // After results are merged, downstream steps can no longer // work on incremental data. Run downstream steps on full // result set. ExecuteSteps(StepIndex + 1); return; } } } } if (Steps.Num() > 0) { if (ensure(Steps.Last().IsValid())) { FFrontendQueryStep& Step = *Steps.Last(); if (EResultModificationState::Modified == IncrementalResultState) { // If all incremental steps are performed and result is modified, // merge the incremental result with the output result. Step.Merge(IncrementalResult, *Result); } } } } void FFrontendQuery::ExecuteSteps(int32 InStartStepIndex) { for (int32 StepIndex = InStartStepIndex; StepIndex < Steps.Num(); StepIndex++) { if (ensure(Steps[StepIndex].IsValid())) { FFrontendQueryStep& Step = *Steps[StepIndex]; EResultModificationState ExecuteResultState = Step.Execute(*Result); const bool bNeedsStepResultCached = (EResultModificationState::Modified == ExecuteResultState) && Step.IsMergeRequiredForIncremental(); if (bNeedsStepResultCached) { StepResultCache[StepIndex] = *Result; } } } } }