Skip to content

Commit

Permalink
Merge pull request #62 from caetano-dev/nameTable
Browse files Browse the repository at this point in the history
New database table for unique app names
  • Loading branch information
jasonjmcghee authored Jan 5, 2024
2 parents de85760 + 75e0f13 commit b696809
Show file tree
Hide file tree
Showing 2 changed files with 123 additions and 50 deletions.
113 changes: 92 additions & 21 deletions rem/DB.swift
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ class DatabaseManager {

private let videoChunks = Table("video_chunks")
private let frames = Table("frames")
private let uniqueAppNames = Table("unique_application_names")

let allText = VirtualTable("allText")

private let id = Expression<Int64>("id")
Expand Down Expand Up @@ -59,6 +61,7 @@ class DatabaseManager {
try db.run(videoChunks.drop(ifExists: true))
try db.run(frames.drop(ifExists: true))
try db.run(allText.drop(ifExists: true))
try db.run(uniqueAppNames.drop(ifExists: true))
} catch {
print("Failed to delete tables")
}
Expand All @@ -83,6 +86,28 @@ class DatabaseManager {
t.column(activeApplicationName)
})

try! db.run(uniqueAppNames.create(ifNotExists: true) { t in
t.column(id, primaryKey: .autoincrement)
t.column(activeApplicationName, unique: true)
})
// Seed the `uniqueAppNames` table if empty
do {
if try db.scalar(uniqueAppNames.count) == 0 {
let query = frames.select(distinct: activeApplicationName)
var appNames: [String] = []
for row in try db.prepare(query) {
if let appName = row[activeApplicationName] {
appNames.append(appName)
}
}
let insert = uniqueAppNames.insertMany(
appNames.map { name in [activeApplicationName <- name] }
)
try db.run(insert)
}
} catch {
print("Error seeding database with app names: \(error)")
}
let config = FTS4Config()
.column(frameId, [.unindexed])
.column(text)
Expand Down Expand Up @@ -137,14 +162,41 @@ class DatabaseManager {
}

func insertFrame(activeApplicationName: String?) -> Int64 {
// logger.debug("inserting frame: \(self.lastFrameId + 1) at offset: \(self.currentFrameOffset)")
let insert = frames.insert(chunkId <- currentChunkId, timestamp <- Date(), offsetIndex <- currentFrameOffset, self.activeApplicationName <- activeApplicationName)
let id = try! db.run(insert)
currentFrameOffset += 1
lastFrameId = id

if let appName = activeApplicationName {
//will check if the app name is already in the database.
insertUniqueApplicationNamesIfNeeded(appName)
}

return id
}

private func insertUniqueApplicationNamesIfNeeded(_ appName: String) {
let query = uniqueAppNames.filter(activeApplicationName == appName)

do {
let count = try db.scalar(query.count)
if count == 0 {
insertUniqueApplicationNames(appName)
}
} catch {
print("Error checking existence of app name: \(error)")
}
}

func insertUniqueApplicationNames(_ appName: String) {
let insert = uniqueAppNames.insert(activeApplicationName <- appName)
do {
try db.run(insert)
} catch {
print("Error inserting unique application name: \(error)")
}
}

func insertTextForFrame(frameId: Int64, text: String) {
let insert = allText.insert(self.frameId <- frameId, self.text <- text)
try! db.run(insert)
Expand Down Expand Up @@ -243,13 +295,27 @@ class DatabaseManager {
return 0
}

func search(searchText: String, limit: Int = 9, offset: Int = 0) -> [(frameId: Int64, fullText: String, applicationName: String?, timestamp: Date, filePath: String, offsetIndex: Int64)] {
let query = allText
func search(appName: String = "", searchText: String, limit: Int = 9, offset: Int = 0) -> [(frameId: Int64, fullText: String, applicationName: String?, timestamp: Date, filePath: String, offsetIndex: Int64)] {
var partialQuery = allText
.join(frames, on: frames[id] == allText[frameId])
.join(videoChunks, on: frames[chunkId] == videoChunks[id])
.filter(text.match("*\(searchText)*"))
.select(allText[frameId], text, frames[activeApplicationName], frames[timestamp], videoChunks[filePath], frames[offsetIndex])
.limit(limit, offset: offset)

if !appName.isEmpty {
partialQuery = partialQuery.join(uniqueAppNames, on: uniqueAppNames[activeApplicationName] == frames[activeApplicationName])
}

var query = partialQuery
.filter(text.match("*\(searchText)*"))

if !appName.isEmpty && searchText.isEmpty {
query = partialQuery
.filter(uniqueAppNames[activeApplicationName].lowercaseString == appName.lowercased())
} else if !appName.isEmpty && !searchText.isEmpty {
query = query.filter(uniqueAppNames[activeApplicationName].lowercaseString == appName.lowercased())
}

query = query.select(allText[frameId], text, frames[activeApplicationName], frames[timestamp], videoChunks[filePath], frames[offsetIndex])
.limit(limit, offset: offset)

var results: [(Int64, String, String?, Date, String, Int64)] = []
do {
Expand All @@ -268,12 +334,18 @@ class DatabaseManager {
return results
}

func getRecentResults(limit: Int = 9, offset: Int = 0) -> [(frameId: Int64, fullText: String?, applicationName: String?, timestamp: Date, filePath: String, offsetIndex: Int64)] {
let query = frames
func getRecentResults(selectedFilterApp: String = "", limit: Int = 9, offset: Int = 0) -> [(frameId: Int64, fullText: String?, applicationName: String?, timestamp: Date, filePath: String, offsetIndex: Int64)] {
var query = frames
.join(videoChunks, on: frames[chunkId] == videoChunks[id])
.select(frames[id], frames[activeApplicationName], frames[timestamp], videoChunks[filePath], frames[offsetIndex])
.order(frames[timestamp].desc)
.limit(limit, offset: offset)

if !selectedFilterApp.isEmpty {
query = query
.join(uniqueAppNames, on: uniqueAppNames[activeApplicationName] == frames[activeApplicationName])
.filter(uniqueAppNames[activeApplicationName].lowercaseString == selectedFilterApp.lowercased())
}

var results: [(Int64, String?, String?, Date, String, Int64)] = []
do {
Expand All @@ -292,23 +364,22 @@ class DatabaseManager {
}

func getAllApplicationNames() -> [String] {
var applicationNames: [String] = []

do {
let distinctAppsQuery = frames.select(distinct: activeApplicationName)
for row in try db.prepare(distinctAppsQuery) {
if let appName = row[activeApplicationName] {
applicationNames.append(appName)
}
var applicationNames: [String] = []

do {
let distinctAppsQuery = uniqueAppNames.select(activeApplicationName)
for row in try db.prepare(distinctAppsQuery) {
if let appName = row[activeApplicationName] {
applicationNames.append(appName)
}
} catch {
print("Error fetching application names: \(error)")
}
return applicationNames
} catch {
print("Error fetching application names: \(error)")
}


return applicationNames
}

func getImage(index: Int64, maxSize: CGSize? = nil) -> CGImage? {
guard let frameData = DatabaseManager.shared.getFrame(forIndex: index) else { return nil }

Expand Down
60 changes: 31 additions & 29 deletions rem/Search.swift
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,9 @@ struct SearchBar: View {
@Namespace var nspace
@FocusState var focused: Bool?
var debounceSearch = Debouncer(delay: 0.3)
var applicationNameFilter: [String]
@Binding var selectedFilterAppIndex: Int
@Binding var selectedFilterApp: String
@State private var applicationFilterArray: [String] = []

var body: some View {
HStack(spacing: 16) {
Expand Down Expand Up @@ -76,7 +76,7 @@ struct SearchBar: View {
}

FilterPicker(
applicationNameFilter: applicationNameFilter,
applicationFilterArray: applicationFilterArray,
selectedFilterAppIndex: $selectedFilterAppIndex,
selectedFilterApp: $selectedFilterApp,
debounceSearch: debounceSearch,
Expand All @@ -87,7 +87,7 @@ struct SearchBar: View {
}

struct FilterPicker: View {
var applicationNameFilter: [String]
@State var applicationFilterArray: [String]
@Binding var selectedFilterAppIndex: Int
@Binding var selectedFilterApp: String
var debounceSearch: Debouncer
Expand All @@ -96,22 +96,34 @@ struct FilterPicker: View {
var body: some View {
VStack(alignment: .leading) {
Picker("Application", selection: $selectedFilterAppIndex) {
ForEach(applicationNameFilter.indices, id: \.self) { index in
Text(applicationNameFilter[index])
ForEach(applicationFilterArray.indices, id: \.self) { index in
Text(applicationFilterArray[index])
.tag(index)
}
}
.onHover(perform: { hovering in
updateAppFilterData()
})
.onAppear{
updateAppFilterData()
}
.pickerStyle(.menu)
.onChange(of: selectedFilterAppIndex) { newIndex in
guard newIndex >= 0 && newIndex < applicationNameFilter.count else {
guard newIndex >= 0 && newIndex < applicationFilterArray.count else {
return
}
selectedFilterApp = applicationNameFilter[selectedFilterAppIndex]
selectedFilterApp = applicationFilterArray[selectedFilterAppIndex]
onSearch()
}
.frame(width: 200)
}
}
private func updateAppFilterData() {
var appFilters = ["All apps"]
let allAppNames = DatabaseManager.shared.getAllApplicationNames()
appFilters.append(contentsOf: allAppNames)
applicationFilterArray = appFilters
}
}


Expand Down Expand Up @@ -179,7 +191,6 @@ class SearchResult: ObservableObject, Identifiable {
// .replacingOccurrences(of: "\\s+", with: "\\s*", options: .regularExpression)
.replacingOccurrences(of: "(", with: "\\(")
.replacingOccurrences(of: ")", with: "\\)")
.replacingOccurrences(of: #"!([^ ]*) "#, with: "", options: .regularExpression)

if let regex = try? NSRegularExpression(pattern: pattern, options: .caseInsensitive),
let match = regex.firstMatch(in: text, options: [], range: NSRange(location: 0, length: text.utf16.count)) {
Expand Down Expand Up @@ -309,7 +320,6 @@ struct ResultsView: View {
SearchBar(
text: $searchText,
onSearch: performSearch,
applicationNameFilter: getAppFilterData(),
selectedFilterAppIndex: $selectedFilterAppIndex,
selectedFilterApp: $selectedFilterApp
)
Expand Down Expand Up @@ -355,31 +365,23 @@ struct ResultsView: View {
private func getSearchResults() -> [SearchResult] {
var results: [(frameId: Int64, fullText: String?, applicationName: String?, timestamp: Date, filePath: String, offsetIndex: Int64)] = []

if searchText.isEmpty {
results = DatabaseManager.shared.getRecentResults(limit: limit, offset: offset)
} else {
results = DatabaseManager.shared.search(searchText: searchText, limit: limit, offset: offset)
}

if selectedFilterAppIndex == 0 {
return mapResultsToSearchResult(results)
if searchText.isEmpty {
results = DatabaseManager.shared.getRecentResults(limit: limit, offset: offset)
} else {
results = DatabaseManager.shared.search(searchText: searchText, limit: limit, offset: offset)
}
} else {
let filteredResults = results.filter { result in
if let appName = result.applicationName {
return appName.lowercased() == selectedFilterApp.lowercased()
}
return false
if searchText.isEmpty {
results = DatabaseManager.shared.getRecentResults(selectedFilterApp: selectedFilterApp, limit: limit, offset: offset)
} else {
results = DatabaseManager.shared.search(appName: selectedFilterApp, searchText: searchText, limit: limit, offset: offset)
}
return mapResultsToSearchResult(filteredResults)
}

return mapResultsToSearchResult(results)
}

private func getAppFilterData() -> [String] {
var appFilters = ["All apps"]
let allAppNames = DatabaseManager.shared.getAllApplicationNames()
appFilters.append(contentsOf: allAppNames)
return appFilters
}


private func mapResultsToSearchResult(_ data: [(frameId: Int64, fullText: String?, applicationName: String?, timestamp: Date, filePath: String, offsetIndex: Int64)]) -> [SearchResult] {
let searchResults = data.map { item in
Expand Down

0 comments on commit b696809

Please sign in to comment.