Files
core/search/Text 2 Hash.go
Akilan Selvacoumar b34b2e2b7c Change directory route and document change (#107)
* changed route to view files added by a certain node

* gofmt my changes

* added fix for blockchain/explore to also accept localnodes as a parameter.

* added local blockchain search
2023-03-10 09:47:18 +00:00

177 lines
5.1 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/*
File Name: Text 2 Hash.go
Copyright: 2021 Peernet s.r.o.
Author: Peter Kleissner
*/
package search
import (
"strings"
"unicode"
"lukechampine.com/blake3"
)
// Words must be the minimum length to be indexed.
const wordMinLength = 3
// text2Hashes creates hashes from words in the text. Text may be CamelCased.
// Text must be already validated for valid UTF8 when calling this function.
func text2Hashes(text string, hashes map[[32]byte]string) {
words := strings.FieldsFunc(text, func(char rune) bool {
if unicode.IsSpace(char) {
return true
}
return strings.ContainsAny(string(char), "+-._()[],")
})
for _, word := range words {
// remove hash tag prefix
word = strings.TrimPrefix(word, "#")
hashWordMap(word, hashes)
// CamelCase word detection
for _, word2 := range CamelCaseSplit(word) {
if word2 != word {
hashWordMap(word2, hashes)
}
}
}
}
// filename2Hashes creates hashes based on the filename and folder.
func filename2Hashes(filename, folder string, hashes map[[32]byte]string) {
if len(filename) < wordMinLength {
return
}
// First hash is created on the entire filename including extension. This is done for perfect matches.
hashWordMap(filename, hashes)
// Hash the filename without extension (and proceed without extension)
filename = filenameRemoveExtension(filename)
hashWordMap(filename, hashes)
// Hash each individual word of the filename and directory
text2Hashes(filename, hashes)
folder = strings.ReplaceAll(folder, "\\", " ")
folder = strings.ReplaceAll(folder, "/", " ")
text2Hashes(folder, hashes)
}
// hashWordMap hashes a word and stores it on the map. This immediately deduplicated hashes. It always lowercases the word.
func hashWordMap(word string, hashes map[[32]byte]string) {
word = strings.TrimSpace(strings.ToLower(word))
if len(word) < wordMinLength {
return
}
hashes[blake3.Sum256([]byte(word))] = word
}
// hashMapDelete deletes a hash from the list if the hash is valid
func hashMapDelete(hash []byte, hashes map[[32]byte]string) {
if len(hash) == 0 {
return
}
var hashB [32]byte
copy(hashB[:], hash)
delete(hashes, hashB)
}
// hashWord hashes a single word. It returns nil if not suitable. It always lowercases the word.
func hashWord(word string) (hash []byte, wordHashed string) {
word = strings.TrimSpace(strings.ToLower(word))
if len(word) < wordMinLength {
return
}
hashB := blake3.Sum256([]byte(word))
return hashB[:], word
}
// fork from https://github.com/fatih/camelcase/blob/master/camelcase.go
// Split splits the camelcase word and returns a list of words. It also
// supports digits. Both lower camel case and upper camel case are supported.
// For more info please check: http://en.wikipedia.org/wiki/CamelCase
//
// Examples
//
// "" => [""]
// "lowercase" => ["lowercase"]
// "Class" => ["Class"]
// "MyClass" => ["My", "Class"]
// "MyC" => ["My", "C"]
// "HTML" => ["HTML"]
// "PDFLoader" => ["PDF", "Loader"]
// "AString" => ["A", "String"]
// "SimpleXMLParser" => ["Simple", "XML", "Parser"]
// "vimRPCPlugin" => ["vim", "RPC", "Plugin"]
// "GL11Version" => ["GL", "11", "Version"]
// "99Bottles" => ["99", "Bottles"]
// "May5" => ["May", "5"]
// "BFG9000" => ["BFG", "9000"]
// "BöseÜberraschung" => ["Böse", "Überraschung"]
// "Two spaces" => ["Two", " ", "spaces"]
// "BadUTF8\xe2\xe2\xa1" => ["BadUTF8\xe2\xe2\xa1"]
//
// Splitting rules
//
// 1. If string is not valid UTF-8, return it without splitting as -> removed in this fork.
// single item array.
// 2. Assign all unicode characters into one of 4 sets: lower case
// letters, upper case letters, numbers, and all other characters.
// 3. Iterate through characters of string, introducing splits
// between adjacent characters that belong to different sets.
// 4. Iterate through array of split strings, and if a given string
// is upper case:
// if subsequent string is lower case:
// move last character of upper case string to beginning of
// lower case string
func CamelCaseSplit(src string) (entries []string) {
entries = []string{}
var runes [][]rune
lastClass := 0
class := 0
// split into fields based on class of unicode character
for _, r := range src {
switch true {
case unicode.IsLower(r):
class = 1
case unicode.IsUpper(r):
class = 2
case unicode.IsDigit(r):
class = 3
default:
class = 4
}
if class == lastClass {
runes[len(runes)-1] = append(runes[len(runes)-1], r)
} else {
runes = append(runes, []rune{r})
}
lastClass = class
}
// handle upper case -> lower case sequences, e.g.
// "PDFL", "oader" -> "PDF", "Loader"
for i := 0; i < len(runes)-1; i++ {
if unicode.IsUpper(runes[i][0]) && unicode.IsLower(runes[i+1][0]) {
runes[i+1] = append([]rune{runes[i][len(runes[i])-1]}, runes[i+1]...)
runes[i] = runes[i][:len(runes[i])-1]
}
}
// construct []string from results
for _, s := range runes {
if len(s) > 0 {
entries = append(entries, string(s))
}
}
return
}