Code Monkey home page Code Monkey logo

natural-search's Introduction

DEPRECATED

This project is deprecated and no longer maintained. The component Natural Search now lives in https://github.com/Ecodev/natural.

Natural Search

Build Status Total Downloads Latest Stable Version License Join the chat at https://gitter.im/Ecodev/natural-search

This is an Angular component to search for things via configurable facets. Facets may be configured to use one of the built-in component, or a custom component to input values.

See the component in action on the demo page.

Install

yarn add @ecodev/natural-search

Development

The most useful commands for development are:

  • yarn dev to start a development server
  • yarn build-docs to build the docs locally (it will be published automatically by Travis)
  • yarn release to publish the lib to npm

Prior work

While the implementation is entirely different, VisualSearch.js was an important inspiration.

natural-search's People

Contributors

angular-cli avatar powerkiki avatar sambaptista avatar

Stargazers

 avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

Forkers

gitter-badger

natural-search's Issues

Support multiple config per field via `alias`/`name`

Mixed up selections

Today natural-search let us configure the same field more than once, such as:

const config1: NaturalSearchConfiguration = [
    {
        field: 'date',
        display: 'Real date',
        component: TypeDateRangeComponent,  // This is the important difference, not shown in screenshot below but actually exist
    },
    {
        field: 'date',
        display: 'Textual date',
    },
];

Then if we attempt to set a selection for the textual version of the date, we would do it like so:

const selections: NaturalSearchSelections = [
    [
        {
            field: 'date',
            condition: {
                like: {value: 'foo'},
            },
        },
    ],
];

And because the selection has no way to specify if it is targeting the actual or textual date, then natural-search would incorrectly assume it is the an actual date. But the textual condition is obviously invalid for an actual date, so it will be incorrectly shown as an invalid actual date:

image

Mixed up transforms

Similar for transform functions. If configuration and selection is the following:

const config1: NaturalSearchConfiguration = [
    {
        field: 'date',
        display: 'Version A',
        transform: (s: Selection) => {
            if (s.condition.like) {
                s.condition.like.value = 'Version A';
            }
            return s;
        },
    },
    {
        field: 'date',
        display: 'Version B',
        transform: (s: Selection) => {
            if (s.condition.like) {
                s.condition.like.value = 'Version B';
            }
            return s;
        },
    },
];

const selections: NaturalSearchSelections = [
    [
        {
            field: 'date',
            condition: {
                like: {value: 'foo'},
            },
        },
    ],
];

Then we would have no way to correctly apply transform for Version B and we would always get a GraphQL selection like:

{
  "groups": [
    {
      "conditions": [
        {
          "date": {
            "like": {
              "value": "Version A"
            }
          }
        }
      ]
    }
  ]
}

Cause

The cause of this is the getConfigurationFromSelection function who is used for both cases and use only the field name to match a configuration and a selection. So of course if there are multiple identical field names, it cannot know which one we really need.

Possible solution

Enforce the uniqueness of field names by changing the configuration structure to have the field name as key for the ItemConfiguration like so:

/**
 * The key `field` is the field the item configuration should apply to.
 *
 * In most cases it should be the property name of the model. Something like:
 *
 * - name
 * - description
 * - artist.name
 */
export type NaturalSearchConfigurationVersion2 = { [field: string]: ItemConfiguration };


// No `field` anymore
export interface BasicConfigurationVersion2 {
    display: string;
    transform?: (Selection) => Selection;
}

And similar for selection:

export type GroupSelections = { [field: string]: FilterGroupConditionField };

// Probably entirely deleted
// export interface Selection {
//     field: string;
//     condition: FilterGroupConditionField;
// }

Alternatives ?

Could we find a way to somehow modify getConfigurationFromSelection, or remove its use entirely, to be able to link a selection with its configuration ?

Date range

Should have a default component for date range.

In a second pass we could consider having some easy-to-select ranges, such as "previous week" or "last year". Those preset might be in this lib or might be configured by lib consumer... to be defined...

Output (filtres de sortie)

Ce composant à pour seul but de transformer une configuration (qui peut changer à tout moment) vers une liste d'objets représentant les filtres.

Les attributs des objets répondent à une condition AND. Les listes répondent à une condition OR.

Filtre les résultats contenant 'McClane
[{search: 'McClane'}]

Filtre les résultats contenant 'asdf ou 'McClane'
[{search: 'McClane'}, {search: 'qwer'}]

Filtre les résultats contenant McClane et ayant un parent dont l'id = 1

[{
  search: 'McClane', 
  parents: ["1"]
}]

Filtre les résultats contenant McClane et ayant un parent dont l'id = 1 ou 2

[{
  search: 'McClane', 
  parents: ["1", "2"]
}]

Filtre les résultats contenant McClane et ayant un parent dont l'id = 1 ou 2, ou ceux contenant Rambo et ayant un parent dont l'id = 3 ou 4

[
{
  search: 'McClane', 
  parents: ["1", "2"]
},
{
  search: 'Rambo', 
  parents: ["3", "4"]
}
]

Filtre les résultats qui n'ont pas de relation
[{parents: []}]

Filtre les résultats dont l'attribut est évalué à null ou undefined ignorent le filtre. Ces deux résulalts sont égaux : [{parents: null}] [{}]

Filtre les résultats qui ont n'importe quelle relation (un avis sur cet aspect ?)
[{parents: "*"}]

Plages numériques

Filtre les résultats dans une plage donnée (numéros) :

[{
  size: { min: 2, max : 10 }
}]

Filtre les résultats dans une plage donnée (dates) :

[{
  size: { from: 2, to : 10 }
}]

Inputs (configuration du composant) et interfaces correspondantes

Recherche texte global

recherche 1 1

Recherche texte sur un attribut

recherche 2

La recherche texte est celle par défaut pour une config qui n'a pas de spécification :

[
  {
     display: 'Artiste',
     attribute: 'artiste',
  }
]

Une fois validé, un nouveau bloc apparaît.
recherche 3

Recherche dans une liste

recherche 3 1

Valeurs prédéfinies en tant que tableau de strings :

[
  {
     display: 'Artiste',
     attribute: 'artiste',
     values: ['Picasso', 'Van Gogh'],
  }
]

Valeurs prédéfinies en tant que tableau d'objets

[
  {
     display: 'Artiste',
     attribute: 'artiste',
     values: [{id: 1, name : 'Picasso'}, {id: 2, fullName : 'Van Gogh'}],
     valuesDisplayFn = (item) => item.fullName ? item.fullName : item.name,
  }
]

Valeurs prédéfinie, post-loadées :

[
  {
     display: 'Artiste',
     attribute: 'artiste',
     values: artistsDataSource,
     valuesDisplayFn = (item) => item.fullName ? item.fullName : item.name,
  }
]

Une DataSource reprend les principes de CDK / Material2. Elle dispose d'une fonction .connect() qui permet à natural-search de compléter les valeurs à afficher quand elles sont disponibles.

Valeurs prédéfinie auto-complétées :

[
  {
     display: 'Artiste',
     attribute: 'artiste',
     autocompleteValues: artistsAutocompleteDataSource,
     valuesDisplayFn = (item) => item.fullName ? item.fullName : item.name,
  }
]

Une AutocompleteDataSource étend une DataSource mais reçoit en plus un observable qui représente les variables de la query. Ainsi elle est autonome pour faire correspondre la recherche de l'utilisateur dans le natural-search et recevoir les résultats correspondant.

Recherche dans un champ numérique

recherche 3 2

Numéro simple :

[
  {
     display: 'Taille',
     attribute: 'size',
     type: 'numeric',
     min : 0,
     max : 100
  }
]

Plage :

[
  {
     display: 'Taille',
     attribute: 'size',
     type: 'range',
     min : 0,
     max : 100,
     minRequired : false,
     maxRequired: true,
  }
]

Tel que configuré ci-dessus, la plage autorise un maximum, mais pas un minimum ce qui signifie un numéro plus petit que 100 doit être entré pour valider le filtre.

Valeurs discrètes :

[
  {
     display: 'Taille',
     attribute: 'size',
     type: 'range', // si indéfini, n'acceptera qu'une valeur
     values : [0, 3, 7, 11, 13, 17],
     minRequired : false,
     maxRequired: true,
  }
]

Dans ce cas, si l'utilisateur saisie un numéro entre les valeurs fournies, il sera remplacé par le plus proche.

Mais encore ...

D'autres types pourront venir enrichir cette liste non exhaustive, p.ex un slider pourrait être l'une des prochaines étapes pour les valeurs numériques. Le slider d'okpilot, bien que trop complet a été conçu dans la lignée des composants et pourrait assez facilement être porté ici.

ENTER should close component

Number and range component should probably be closed when hitting ENTER and it has valid values. Or at the very least another way to close it via keyboard only.

Limit component creations

When a new search value is entered all previously existing search values are re-evaluated. That means a few components are created and destroyed only to get their rendered value.

This might be OK for local things, but it becomes problematic when custom component need to fetch things over the network to render the value. That means every single new addition to the search will potentially trigger a few network requests to re-render previously available rendered strings. This is clearly visible in the GUI with string disappearing for a short time and make the GUI "jumps".

We must find a way to not need to re-render previously available string. Maybe via a custom cache.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.