DoneJS StealJS jQuery++ FuncUnit DocumentJS
5.33.3
6.0.0 4.3.0 3.14.1 2.3.35
  • About
  • Guides
  • API Docs
  • Community
  • Contributing
  • Bitovi
    • Bitovi.com
    • Blog
    • Design
    • Development
    • Training
    • Open Source
    • About
    • Contact Us
  • About
  • Guides
  • API Docs
    • Observables
      • can-bind
      • can-compute
      • can-debug
      • can-define
      • can-define/list/list
      • can-define/map/map
      • can-define-backup
      • can-define-stream
      • can-define-stream-kefir
      • can-event-queue
      • can-kefir
      • can-list
      • can-map
      • can-map-compat
      • can-map-define
      • can-observable-array
      • can-observable-object
      • can-observation
      • can-observation-recorder
      • can-observe
      • can-simple-map
      • can-simple-observable
      • can-stream
      • can-stream-kefir
      • can-value
    • Views
      • can-attribute-observable
      • can-component
      • can-stache
      • can-stache-bindings
      • can-stache-converters
      • can-stache-element
      • can-stache-route-helpers
      • can-view-autorender
      • can-view-callbacks
      • can-view-import
      • can-view-live
      • can-view-model
      • can-view-nodelist
      • can-view-parser
      • can-view-scope
      • can-view-target
      • steal-stache
    • Data Modeling
      • can-connect
      • can-connect-feathers
      • can-connect-ndjson
      • can-connect-tag
      • can-fixture
      • can-fixture-socket
      • can-local-store
      • can-memory-store
      • can-ndjson-stream
      • can-query-logic
        • prototype
          • count
          • difference
          • filterMembers
          • filterMembersAndGetCount
          • identityKeys
          • index
          • intersection
          • isDefinedAndHasMembers
          • isEqual
          • isMember
          • isProperSubset
          • isSpecial
          • isSubset
          • union
          • unionMembers
        • query format
          • Query
          • Comparison Operators
        • static methods
          • defineComparison
          • set.difference
          • set.intersection
          • set.union
        • static types
          • EMPTY
          • KeysAnd
          • UNDEFINABLE
          • UNIVERSAL
          • UNKNOWABLE
          • makeEnum
      • can-realtime-rest-model
      • can-rest-model
      • can-set-legacy
      • can-super-model
    • Routing
      • can-deparam
      • can-param
      • can-route
      • can-route-hash
      • can-route-mock
      • can-route-pushstate
    • JS Utilities
      • can-assign
      • can-define-lazy-value
      • can-diff
      • can-globals
      • can-join-uris
      • can-key
      • can-key-tree
      • can-make-map
      • can-parse-uri
      • can-queues
      • can-string
      • can-string-to-any
      • can-zone-storage
    • DOM Utilities
      • can-ajax
      • can-attribute-encoder
      • can-child-nodes
      • can-control
      • can-dom-data
      • can-dom-events
      • can-dom-mutate
      • can-event-dom-enter
      • can-event-dom-radiochange
      • can-fragment
    • Data Validation
      • can-define-validate-validatejs
      • can-type
      • can-validate
      • can-validate-interface
      • can-validate-legacy
      • can-validate-validatejs
    • Typed Data
      • can-cid
      • can-construct
      • can-construct-super
      • can-data-types
      • can-namespace
      • can-reflect
      • can-reflect-dependencies
      • can-reflect-promise
      • can-types
    • Polyfills
      • can-symbol
      • can-vdom
    • Core
    • Infrastructure
      • can-global
      • can-test-helpers
    • Ecosystem
    • Legacy
  • Community
  • Contributing
  • GitHub
  • Twitter
  • Chat
  • Forum
  • News
Bitovi

defineComparison

  • Edit on GitHub

QueryLogic.defineComparison(TypeA, TypeB, comparisons)

This registers comparison operators between the TypeA and TypeB types. For example, the following might define how to compare the GreaterThan type to the LessThan type:

import {QueryLogic} from "can";

QueryLogic.defineComparison(GreaterThan, LessThan, {
  union(greaterThan, lessThan){
    if(greaterThan.value < lessThan.value) {
      return QueryLogic.UNIVERSAL
    } else {
      return new QueryLogic.ValueOr([greaterThan, lessThan]);
    }
  },
  intersection(greaterThan, lessThan){
    if(greaterThan.value < lessThan.value) {
      return new QueryLogic.ValueAnd([greaterThan, lessThan]);
    } else {
      return QueryLogic.EMPTY;
    }
  },
  difference(greaterThan, lessThan){
    if(greaterThan.value < lessThan.value) {
      return new QueryLogic.GreaterThanEqual(lessThan.value)
    } else {
      return greaterThan;
    }
  }
});

Note, for comparisons of two different types, you will also want to define the reverse difference like:

import {QueryLogic} from "can";

QueryLogic.defineComparison(LessThan, GreaterThan, {
  difference(lessThan, greaterThan) {
    if(greaterThan.value < lessThan.value) {
      return new QueryLogic.LessThanEqual(lessThan.value);
    } else {
      return lessThan;
    }
  }
});

union and intersection isn't necessary because they are symmetrical (Ex: a U b = b U a).

Also, for many types you will want to define the difference between the UNIVERSAL set and the type:

import {QueryLogic} from "can";
QueryLogic.defineComparison(QueryLogic.UNIVERSAL, GreaterThan, {
  difference(universe, greaterThan) {
    return new QueryLogic.LessThanEqual(greaterThan.value);
  }
})

Parameters

  1. TypeA {function}:

    A constructor function.

  2. TypeB {function}:

    A constructor function.

  3. comparisons {Object}:

    An object of one or more of the following comparison functions:

    • union(typeAInstance, typeBInstance) - Returns a representation of the union of the two sets.
    • intersection(typeAInstance, typeBInstance) - Returns a representation of the intersection of the two sets.
    • difference(typeAInstance, typeBInstance) - Returns a representation of the difference of the two sets.

    The special sets can be returned from these functions to indicate:

    • EMPTY - The empty set.
    • UNDEFINABLE - A representation of the result of the operation exists, but there is no way to express it.
    • UNKNOWABLE - It is unknown if a representation of the operations result exists.

Use

If you want total control over filtering logic, you can create a SetType that provides the following:

  • methods:
    • can.isMember - A function that returns if an object belongs to the query.
    • can.serialize - A function that returns the serialized form of the type for the query.
  • comparisons:
    • union - The result of taking a union of two SetTypes.
    • intersection - The result of taking an intersection of two SetTypes.
    • difference - The result of taking a difference of two SetTypes.

The following creates a SearchableStringSet that is able to perform searches that match the provided text like:

import {canReflect, QueryLogic} from "can";

// Takes the value of `name` (ex: `"chicken"`)
function SearchableStringSet(value) {
  this.value = value;
}

canReflect.assignSymbols(SearchableStringSet.prototype,{
  // Returns if the name on a todo is actually a member of the set.
  "can.isMember": function(value){
    return value.includes(this.value);
  },
  // Converts back to a value that can be in a query.
  "can.serialize": function(){
    return this.value;
  }
});

// Specify how to do the fundamental set comparisons.
QueryLogic.defineComparison(SearchableStringSet,SearchableStringSet,{
  // Return a set that would load all records in searchA and searchB.
  union(searchA, searchB){
    // If searchA's text contains searchB's text, then
    // searchB will include searchA's results.
    if(searchA.value.includes(searchB.value)) {
      // A:`food` ∪ B:`foo` => `foo`
      return searchB;
    }
    if(searchB.value.includes(searchA.value)) {
      // A:`foo` ∪ B:`food` => `foo`
      return searchA;
    }
    // A:`ice` ∪ B:`cream` => `ice` || `cream`
    return new QueryLogic.ValueOr([searchA, searchB]);
  },
  // Return a set that would load records shared by searchA and searchB.
  intersection(searchA, searchB){
    // If searchA's text contains searchB's text, then
    // searchA is the shared search results.
    if(searchA.value.includes(searchB.value)) {
        // A:`food` ∩ B:`foo` => `food`
        return searchA;
    }
    if(searchB.value.includes(searchA.value)) {
        // A:`foo` ∩ B:`food` => `food`
      return searchB;
    }
    // A:`ice` ∩ B:`cream` => `ice` && `cream`
    // But suppose AND isn't supported,
    // So we return `UNDEFINABLE`.
    return QueryLogic.UNDEFINABLE;
  },
  // Return a set that would load records in searchA that are not in
  // searchB.
  difference(searchA, searchB){
    // if searchA's text contains searchB's text, then
    // searchA has nothing outside what searchB would return.
    if(searchA.value.includes(searchB.value)) {
      // A:`food` \ B:`foo` => ∅
      return QueryLogic.EMPTY;
    }
    // If searchA has results outside searchB's results
    // then there are records, but we aren't able to
    // create a string that represents this.
    if(searchB.value.includes(searchA.value)) {
      // A:`foo` \ B:`food` => UNDEFINABLE
      return QueryLogic.UNDEFINABLE;
    }

    // A:`ice` \ B:`cream` => `ice` && !`cream`
    // If there's another situation, we
    // aren't able to express the difference
    // so we return UNDEFINABLE.
    return QueryLogic.UNDEFINABLE;
  }
});

const recipes = [
  {id: 1, name: "garlic chicken"},
  {id: 2, name: "ice cream"},
  {id: 3, name: "chicken kiev"}
];

const queryLogic = new QueryLogic({ keys: {
  name : {[Symbol.for("can.SetType")]: SearchableStringSet}
}});

const result = queryLogic.filterMembers({
  filter: {name: "chicken"}
}, recipes);

console.log( result ); //-> [
  // {id: 1, name: "garlic chicken"},
  // {id: 3, name: "chicken kiev"}
  // ]

Notice how all values that match chicken are returned.

import {canReflect, QueryLogic} from "can";

// Takes the value of `name` (ex: `"chicken"`)
function SearchableStringSet(value) {
  this.value = value;
}

canReflect.assignSymbols(SearchableStringSet.prototype,{
  // Returns if the name on a todo is actually a member of the set.
  "can.isMember": function(value){
    return value.includes(this.value);
  },
  // Converts back to a value that can be in a query.
  "can.serialize": function(){
    return this.value;
  }
});

// Specify how to do the fundamental set comparisons.
QueryLogic.defineComparison(SearchableStringSet,SearchableStringSet,{
  // Return a set that would load all records in searchA and searchB.
  union(searchA, searchB){
    // If searchA's text contains searchB's text, then
    // searchB will include searchA's results.
    if(searchA.value.includes(searchB.value)) {
      // A:`food` ∪ B:`foo` => `foo`
      return searchB;
    }
    if(searchB.value.includes(searchA.value)) {
      // A:`foo` ∪ B:`food` => `foo`
      return searchA;
    }
    // A:`ice` ∪ B:`cream` => `ice` || `cream`
    return new QueryLogic.ValueOr([searchA, searchB]);
  },
  // Return a set that would load records shared by searchA and searchB.
  intersection(searchA, searchB){
    // If searchA's text contains searchB's text, then
    // searchA is the shared search results.
    if(searchA.value.includes(searchB.value)) {
        // A:`food` ∩ B:`foo` => `food`
        return searchA;
    }
    if(searchB.value.includes(searchA.value)) {
        // A:`foo` ∩ B:`food` => `food`
      return searchB;
    }
    // A:`ice` ∩ B:`cream` => `ice` && `cream`
    // But suppose AND isn't supported,
    // So we return `UNDEFINABLE`.
    return QueryLogic.UNDEFINABLE;
  },
  // Return a set that would load records in searchA that are not in
  // searchB.
  difference(searchA, searchB){
    // if searchA's text contains searchB's text, then
    // searchA has nothing outside what searchB would return.
    if(searchA.value.includes(searchB.value)) {
      // A:`food` \ B:`foo` => ∅
      return QueryLogic.EMPTY;
    }
    // If searchA has results outside searchB's results
    // then there are records, but we aren't able to
    // create a string that represents this.
    if(searchB.value.includes(searchA.value)) {
      // A:`foo` \ B:`food` => UNDEFINABLE
      return QueryLogic.UNDEFINABLE;
    }

    // A:`ice` \ B:`cream` => `ice` && !`cream`
    // If there's another situation, we
    // aren't able to express the difference
    // so we return UNDEFINABLE.
    return QueryLogic.UNDEFINABLE;
  }
});

const recipes = [
  {id: 1, name: "garlic chicken"},
  {id: 2, name: "ice cream"},
  {id: 3, name: "chicken kiev"}
];

const queryLogic = new QueryLogic({ keys: {
  name : {[Symbol.for("can.SetType")]: SearchableStringSet}
}});

const result = queryLogic.filterMembers({
  filter: {name: "chicken"}
}, recipes);

console.log( result ); //-> [
  // {id: 1, name: "garlic chicken"},
  // {id: 3, name: "chicken kiev"}
  // ]

Testing

To test SearchableStringSet, you can use QueryLogic.set.union, QueryLogic.set.intersection and QueryLogic.set.difference as follows:

test("SearchableStringSet", function(assert){

  assert.deepEqual(
    QueryLogic.set.union(
      new SearchableStringSet("foo"),
      new SearchableStringSet("food")
    ),
    new SearchableStringSet("foo"),
    "union works"
  );
})

CanJS is part of DoneJS. Created and maintained by the core DoneJS team and Bitovi. Currently 5.33.3.

On this page

Get help

  • Chat with us
  • File an issue
  • Ask questions
  • Read latest news