2

Erweiterte Suchfunktion mit Operatoren und Klammerausdrücken

Hallo Community,

wir nutzen viele Ansichten in Kombination mit Suchfeldern, nun hatte ich mir vorgenommen, dass es nur noch ein Textfeld sein soll, welches als Suchfeld fungiert. Aber halt erweiterte Suche möglich sein sollte mit Operatoren und Klammern. Dies hatte ich jetzt soweit umgesetzt bekommen.

Die Suchfunktion unterstützt folgende Operatoren:

  • + (UND): Kombiniert zwei Suchbegriffe, beide müssen vorkommen
  • / (ODER): Findet Einträge, die mindestens einen der Suchbegriffe enthalten
  • - (AUSSER): Schließt Einträge aus, die einen bestimmten Begriff enthalten
  • ( ) (Klammern): Ermöglicht verschachtelte Suchausdrücke

Dachte wäre eventuell auch für andere Nutzer interessant, daher habe ich eine Beispiel Datenbank angehängt.

Anpassung für eigene Datenbanken

Die Lösung lässt sich an eure eigenen Tabellen und Felder anpassen:

  1. Ändert projekt : 'Filterung Ansicht' auf eure Tabelle
  2. Passt projekt.Material auf eure Relation an
  3. Passt die Suchfelder an (Hersteller, Oberbegriff, Herstellernummer)

 

und es würde mich interessieren, ob es dafür eine bessere Lösung geben würde, als den Weg den ich genommen habe.

5 Antworten

null
    • mirko3
    • vor 9 Tagen
    • Gemeldet - anzeigen

    Hi  , tolle (und viel) Arbeit. Das alles in einzelne Funktionen unterzubringen ist eine kluge Idee bei längerem Code, um ihn übersichtlicher zu machen. Scheint auch alles perfekt zu funktionieren. Danke. Mirko

    • Josef_Koenig
    • vor 9 Tagen
    • Gemeldet - anzeigen

    Sehr schönes Projekt! Vielen Dank!

    Ich habe folgendes ausprobiert: 
    Eingabe: test - siemens

    Das erwartete Ergebnis wären - wie auch Deine TextInfo-Box anzeigt - die Datensätze 2, 4, 5, 6
    Angezeigt werden aber die Datensätze 1 und 3 (so als hätte man nicht - (AUSSER), sondern + (UND) eingetippt.
     

      • Lamping & Reisig Gmbh & Co.KG
      • LuRLorenz
      • vor 8 Tagen
      • Gemeldet - anzeigen

       das stimmt, hatte nicht alle Fälle ganz betrachtet. Konnte den Code jetzt aber noch etwas kürzen und sollte jetzt auch noch etwas effizienter sein und dazu richtig.

      Das hier ist jetzt der Code aus dem Button, für die Ansicht die Zeile 164 entfernen.

      function getMaterialOverNr(projekt : 'Filterung Ansicht',filterNr : text) do
          let textNrs := split(filterNr, ", ");
          let numbersNrs := [0];
          for x in textNrs do
              if x != "" then
                  numbersNrs := array(numbersNrs, [number(x)])
              end
          end;
          projekt.'Alle Vorhandenen Materialien'[contains(numbersNrs, number(this.Nr))]
      end;
      function filterMaterial(projekt : 'Filterung Ansicht',suchText : text,filterTextNrs : text) do
          if suchText = "x§x" then
              getMaterialOverNr(projekt, filterTextNrs)
          else
              projekt.'Alle Vorhandenen Materialien'[Hersteller like suchText or Oberbegriff like suchText or
                  Herstellernummer like suchText]
          end
      end;
      function getProjektMaterial(projekt : 'Filterung Ansicht',suchText : text,filterTextNrs : text) do
          let ziel := projekt.'Alle Vorhandenen Materialien';
          if suchText = "" then
              projekt.'Alle Vorhandenen Materialien'
          else
              if suchText = "x$x" then
                  getMaterialOverNr(projekt, filterTextNrs)
              else
                  if contains(suchText, " + ") then
                      let splitSuche := split(suchText, " + ");
                      let ergebnis := projekt.'Alle Vorhandenen Materialien';
                      for x in splitSuche do
                          let temp := filterMaterial(projekt, x, filterTextNrs);
                          ergebnis := ergebnis[contains(temp.Nr, this.Nr)]
                      end;
                      ergebnis
                  else
                      if contains(suchText, " / ") then
                          let splitSuche := split(suchText, " / ");
                          let item1 := item(splitSuche, 0);
                          let ergebnis := filterMaterial(projekt, item1, filterTextNrs);
                          for x in splitSuche[this != item1] do
                              let temp := filterMaterial(projekt, x, filterTextNrs);
                              ergebnis := projekt.'Alle Vorhandenen Materialien'[contains(array(ergebnis.Nr, temp.Nr), this.Nr)]
                          end;
                          ergebnis
                      else
                          if contains(suchText, " - ") then
                              let splitSuche := split(suchText, " - ");
                              let item1 := item(splitSuche, 0);
                              let ergebnis := filterMaterial(projekt, item1, filterTextNrs);
                              for x in splitSuche[this != item1 and this != "x§x"] do
                                  ergebnis := ergebnis[not Hersteller like x and not Oberbegriff like x and not Herstellernummer like x]
                              end;
                              ergebnis
                          else
                              filterMaterial(projekt, suchText, filterTextNrs)
                          end
                      end
                  end
              end
          end
      end;
      function fusionMaterial(project : 'Filterung Ansicht',filter1 : text,filter2 : text,operator : text) do
          let material1 := getMaterialOverNr(project, filter1);
          let material2 := getMaterialOverNr(project, filter2);
          let result := project.'Alle Vorhandenen Materialien';
          switch operator do
          case "and":
              (result := material1[contains(material2.Nr, this.Nr)])
          case "or":
              (result := project.'Alle Vorhandenen Materialien'[contains(material1.Nr, this.Nr) or contains(material2.Nr, this.Nr)])
          case "except":
              (result := material1[not contains(material2.Nr, this.Nr)])
          end;
          concat(result.Nr)
      end;
      function findKlammerStart(text : text) do
          let chars := split(text, "");
          let found := -1;
          for i from 0 to length(text) - 1 do
              if item(chars, i) = "(" then found := i end
          end;
          found
      end;
      function findKlammerEnde(text : text,startPos : number) do
          let tiefe := 1;
          let chars := split(text, "");
          let found := -1;
          for i from startPos + 1 to length(text) do
              if item(chars, i) = "(" then
                  tiefe := tiefe + 1
              else
                  if item(chars, i) = ")" then
                      if tiefe = 1 then found := i end;
                      tiefe := tiefe - 1
                  end
              end
          end;
          found
      end;
      function findOperator(text : text,start : number,pos : number) do
          let restText := substring(text, start, pos);
          if contains(restText, " + ") then
              "and"
          else
              if contains(restText, " / ") then
                  "or"
              else
                  if contains(restText, " - ") then
                      "except"
                  else
                      ""
                  end
              end
          end
      end;
      function klammerAufteilung(project : 'Filterung Ansicht',suche : text) do
          let startSuche := suche;
          let filterTextNrs := "";
          let weiterSuchen := true;
          let zeilenumbruch := "
      ";
          let DebugText := "Start: " + startSuche + zeilenumbruch;
          while weiterSuchen do
              if not (contains(startSuche, "(") and contains(startSuche, ")")) then
                  weiterSuchen := false;
                  DebugText := DebugText + "Keine Klammern mehr gefunden" + zeilenumbruch
              else
                  let klammerStart := findKlammerStart(startSuche);
                  DebugText := DebugText + "Klammer-Start gefunden bei: " + klammerStart + zeilenumbruch;
                  if klammerStart < 0 then
                      weiterSuchen := false;
                      DebugText := DebugText + "Kein Klammer-Start gefunden" + zeilenumbruch
                  else
                      let klammerEnde := findKlammerEnde(startSuche, klammerStart);
                      DebugText := DebugText + "Klammer-Ende gefunden bei: " + klammerEnde + zeilenumbruch;
                      if klammerEnde < 0 then
                          weiterSuchen := false;
                          DebugText := DebugText + "Kein Klammer-Ende gefunden" + zeilenumbruch
                      else
                          let inKlammerText := substring(startSuche, klammerStart + 1, klammerEnde);
                          DebugText := DebugText + "Klammerinhalt: '" + inKlammerText + "'" + zeilenumbruch;
                          let material := getProjektMaterial(project, inKlammerText, filterTextNrs);
                          let materialNrs := concat(material.Nr);
                          DebugText := DebugText + "Material Nummern: " + materialNrs + zeilenumbruch;
                          let klammerMitInhalt := substring(startSuche, klammerStart, klammerEnde + 1);
                          startSuche := replace(startSuche, klammerMitInhalt, "x§x");
                          DebugText := DebugText + "Suchtext nach Ersetzung: '" + startSuche + "'" + zeilenumbruch;
                          filterTextNrs := materialNrs
                      end
                  end
              end
          end
      ;
          DebugText := DebugText + "Endgültiger Suchtext: '" + startSuche + "'" + zeilenumbruch;
          DebugText := DebugText + "Gespeicherte Nummern: " + filterTextNrs + zeilenumbruch;
          let materialNrs := "";
          if startSuche != "x§x" then
              let material := getProjektMaterial(project, startSuche, filterTextNrs);
              materialNrs := concat(material.Nr);
              DebugText := DebugText + "Material aus Resttext: " + materialNrs + zeilenumbruch
          else
              materialNrs := filterTextNrs
          end;
          project.(DebugTextInfo := DebugText);
          getMaterialOverNr(project, materialNrs)
      end;
      let me := this;
      let suche := Suche;
      klammerAufteilung(me, suche)
      
    • Josef_Koenig
    • vor 8 Tagen
    • Gemeldet - anzeigen

    Ich kann den Code leider nicht überprüfen. Versuche ich, ihn auszutauschen, erhalte ich eine Fehlermeldung: "Die Funktion darf keine Datenänderungen vornehmen."

    Aber eine ganz andere Frage an die NINOX-Community bzw. an die Firma NINOX:
    Lässt sich diese geniale Suchfunktion nicht auch für die globale Suche implementieren?
    Das wäre eine große Bereicherung der Suchmöglichkeiten!

      • Lamping & Reisig Gmbh & Co.KG
      • LuRLorenz
      • vor 6 Tagen
      • Gemeldet - anzeigen

      Für die Ansicht müsstest du die Zeile 164 "project.(DebugTextInfo := DebugText);" löschen.

      Zu der anderen Frage, kann nicht ganz einschätzen wie effizient die Suche über sehr große Datenmengen wäre. Bei uns sind es glaube höchstens 500 Datensätze. Aber wenn das Ninox intern gemacht werden würde, dann müsste es ja auch nochmal effizienter möglich sein.