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.

0
avi.elkharrat 6 marzec 2020, 00:35

2 odpowiedzi

Najlepsza odpowiedź

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)
        }
    }
0
avi.elkharrat 5 marzec 2020, 21:35