Google Cloud Datastore nie pozwala na wykonanie zapytań wyszukiwania SQL Style
SELECT * FROM Person WHERE Name LIKE "Rob*"
To powróci Rob, Robert, Roberto, Roberta, Roby
i tak dalej.
GCP Datastore Pozwala tylko filtrować na polach za pomocą operatorów: >, >=, <, <=
z przepisami takimi jak:
- Dowolna wielka litera jest mniejsza niż jakakolwiek dolna litera
a < b < c < ... < z
Z tymi zasadami zapytanie
query := datastore.NewQuery("Person").Filter("Name >= ", "Rob").Order("Name")
Nie tylko zwróciłby wszystkie osobę, której imię zaczyna się od Rob
, ale także wszystkie osoby, których nazwa jest większa niż Rob
(Ruben, Sarah, Zoe) i wszystkie osoby, których nazwy zaczynają się od małej litery.
Niniejszy Post to hack, który znalazłem w Idź na emulację zapytania SQL Style Wyszukaj.
2 odpowiedzi
Poniższy rozwiązanie rozwiązuje problem programatycznie. Jest kodowany w go
, ale uważam, że można go łatwo dostosować do dowolnego języka.
Najpierw wyprodukuję cały fragment, a potem złamę go.
func (store *PersonStore) Search(name string) ([]Person, error) {
context := context.Background()
persons := make([]*Person, 0)
query := datastore.NewQuery("Person").Filter("Name >= ", strings.ToLower(name)).Order("Name")
keys, err := store.client.GetAll(context, query, &persons)
if err != nil {
return nil, fmt.Errorf("unable to search for persons w/ names containing %s - %v", name, err)
}
filteredPersons := make([]*Perons, 0)
for i, p := range persons {
p.ID = keys[i].ID
if !strings.Contains(p.Name, strings.ToLower(name)) {
break
} else {
filteredPersons = append(filteredPersons, p)
}
}
}
1 - małe założenie
Aby ten kod był do pracy, najpierw musimy wykonać bardzo silne założenie, że wszystkie nazwy są małymi literami . Jeśli z jakiegoś powodu nie możesz zrobić tego założenia, nadal można częściowo korzystać z tego kodu, ale będzie mniej wydajny.
2 - Zapytanie Datastore
Pierwsza część kodu jest przeznaczona do pobierania danych dla osób, których nazwa pasuje do pożądanego wzoru.
context := context.Background()
persons := make([]*Person, 0)
query := datastore.NewQuery("Person").Filter("Name >= ", strings.ToLower(name)).Order("Name")
keys, err := store.client.GetAll(context, query, &persons)
if err != nil {
return nil, fmt.Errorf("unable to search for persons w/ names containing %s - %v", name, err)
}
Upewniamy się, że używamy strings.ToLower(name)
, aby upewnić się, że nie będziemy pobrać wszystkie Person
s. Idealnie name
powinno być również przycięte, ale zwykle jest to wykonane w front-end, więc omowaliśmy to tutaj.
Po raz kolejny jest to podstawa przy założeniu, że wszystkie Name
s są małymi literami. Jeśli nie możesz tego zakładać, zawsze możesz użyć
query := datastore.NewQuery("Person").Filter("Name >= ", name).Order("Name")
Po prostu otrzymasz początkową listę z ( ewentualnie dużo ) więcej Person
s.
Wreszcie zamawiamy naszą pobieraną listę za pomocą .Order("Name")
, ponieważ będzie to punkt bazowy drugiej części kodu.
3 - Filtruj nazwiska
Do tej pory był prostym kodu kodu GetAll
. Nadal musimy włożyć klucze do struktur Person
. Musimy znaleźć sposób na zoptymalizowanie tego. W tym celu możemy odpocząć na fakcie, że persons
i keys
jest dokładna długość i kolejność. Tak więc nadchodzący Loop for
rozpoczyna dokładnie jak zwykły klucz wkładki do bitów struktury.
for i, p := range persons {
p.ID = keys[i].ID
Następny bit jest tam, gdzie jest optymalizacja: ponieważ wiemy, że Person
s zostaną zamawiane przez Name
Jesteśmy pewni, że jak tylko strings.Contains(p.Name, strings.ToLower(name))
nie jest prawdą wybraliśmy wszystkie {{ X3}} S, którego Name
pasuje do naszych kryteriów, czyli, tak szybko jak p.Name
nie zaczyna się już z Rob
, ale z Roc
lub Rod
lub wszystko leksykograficznie większe niż to.
if !strings.Contains(p.Name, strings.ToLower(name)) {
break
Możemy wtedy uciec naszej pętli za pomocą instrukcji break
, ma nadzieję, że mając tylko kilka pierwszych elementów {x1}} listy, która pasuje do naszych kryteriów. One spadają do else
oświadczenie:
} else {
filteredPersons = append(filteredPersons, p)
}
4 - Filtruj nazwiska bez małego założenia
Jak powiedziałem wcześniej, jeśli nie można zakładać, że wszystkie nazwy są w małych literach, nadal możesz używać tego kodu, ale nie zostanie zoptymalizowany, ponieważ zamazanisz Listę, aby skanować pełną listę {X0}} zwróconą przez zapytanie.
Kod powinien wyglądać tak
filteredPersons := make([]*Perons, 0)
for i, p := range persons {
p.ID = keys[i].ID
if strings.Contains(p.Name, strings.ToLower(name)) {
filteredPersons = append(filteredPersons, p)
}
}
Specjalnie dla dopasowań prefiksu można używać wielu filtrów nierówności na tej samej nieruchomości. To znaczy. z https://cloud.google.com/datastore/docs/concepts/queries9/ A> "Jeśli zapytanie ma wiele filtrów nierówności na danej nieruchomości, podmiot pasuje do zapytania tylko wtedy, gdy co najmniej jedna z jego indywidualnych wartości dla nieruchomości spełnia wszystkie filtry".
Przykładem na tej stronie jest SELECT * FROM Task WHERE tag > 'learn' AND tag < 'math'
. Lub dla twojego przypadku query := datastore.NewQuery("Person").Filter("Name >= ", "Rob").Filter("Name <= Rob~").Order("Name")
Podobne pytania
Nowe pytania
go
Go to język programowania typu open source. Jest zapisywany statycznie, ze składnią luźno pochodzącą z języka C, dodającą automatyczne zarządzanie pamięcią, bezpieczeństwo typów, niektóre możliwości dynamicznego pisania, dodatkowe typy wbudowane, takie jak tablice o zmiennej długości (zwane odcinkami) i mapy klucz-wartość oraz duża biblioteka standardowa.