Skip to content

Commit

Permalink
Merge pull request #1349 from terrestris/wfs-util-ol-filter
Browse files Browse the repository at this point in the history
Feature: allow OpenLayers Filter instances in WfsFilterUtil
  • Loading branch information
ahennr authored Mar 5, 2024
2 parents 7919e59 + 1e01896 commit c0ea690
Show file tree
Hide file tree
Showing 2 changed files with 91 additions and 20 deletions.
57 changes: 55 additions & 2 deletions src/WfsFilterUtil/WfsFilterUtil.spec.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
/* eslint-env jest*/
import EqualTo from 'ol/format/filter/EqualTo';
import GreaterThanOrEqualTo from 'ol/format/filter/GreaterThanOrEqualTo';
import IsLike from 'ol/format/filter/IsLike';
import Or from 'ol/format/filter/Or';

Expand Down Expand Up @@ -58,9 +59,9 @@ describe('WfsFilterUtil', () => {
expect(WfsFilterUtil.createWfsFilter).toBeDefined();
});

it('returns null if no search attributes for the provided feature type are found', () => {
it('returns undefined if no search attributes for the provided feature type are found', () => {
const got = WfsFilterUtil.createWfsFilter(featureType, stringSearchTerm, {});
expect(got).toBeNull();
expect(got).toBeUndefined();
});

it('returns simple LIKE filter if only one attribute is provided and ' +
Expand Down Expand Up @@ -144,6 +145,58 @@ describe('WfsFilterUtil', () => {
expect(query.children[2].tagName).toBe('Filter');
expect(query.children[2].getElementsByTagName('Literal')[0].innerHTML).toBe(`*${searchTerm}*`);
});
filterSpy.mockRestore();
});

it('use OL filter instance if olFilterOnly property is set to true', () => {
const filterSpy = jest.spyOn(WfsFilterUtil, 'createWfsFilter');
const searchTerm: string = 'peter';
const searchTerm2: number = 5;

const olFilter = new GreaterThanOrEqualTo('testProperty', searchTerm2);
const testConfig: SearchConfig = {
...searchConfig,
filter: olFilter,
olFilterOnly: true
};

const res = WfsFilterUtil.getCombinedRequests(testConfig, searchTerm);
expect(res?.tagName).toBe('GetFeature');
expect(res.querySelectorAll('Query').length).toBe(searchConfig.featureTypes!.length);
expect(filterSpy).toHaveBeenCalledTimes(0);
res.querySelectorAll('Query').forEach(query => {
expect(query.children[2].tagName).toBe('Filter');
expect(query.children[2].getElementsByTagName('Literal')[0].innerHTML).toEqual(`${searchTerm2}`);
expect(query.getElementsByTagName('PropertyIsGreaterThanOrEqualTo')[0]).toBeDefined();
});
filterSpy.mockRestore();
});

it('creates WFS GetFeature request body containing queries and combined filters for each feature type', () => {
const filterSpy = jest.spyOn(WfsFilterUtil, 'createWfsFilter');
const searchTerm: string = 'peter';
const searchTerm2: number = 5;
const olFilter = new GreaterThanOrEqualTo('anotherTestAttribute', searchTerm2);
const testConfig: SearchConfig = {
...searchConfig,
filter: olFilter
};
testConfig.attributeDetails.featureType[attrName] = stringExactFalse;
const got = WfsFilterUtil.getCombinedRequests(testConfig, searchTerm) as Element;
expect(got?.tagName).toBe('GetFeature');
expect(got.querySelectorAll('Query').length).toBe(searchConfig.featureTypes!.length);
expect(filterSpy).toHaveBeenCalledTimes(searchConfig.featureTypes!.length);
got.querySelectorAll('Query').forEach(query => {
expect(query.children[2].tagName).toBe('Filter');
expect(query.children[2].getElementsByTagName('Literal')[0].innerHTML).toBe(`*${searchTerm}*`);
expect(query.children[2].getElementsByTagName('And')[0]).toBeDefined();
expect(
query.children[2].getElementsByTagName('And')[0].
getElementsByTagName('PropertyIsGreaterThanOrEqualTo')[0].
getElementsByTagName('Literal')[0].innerHTML
).toEqual(`${searchTerm2}`);
});
filterSpy.mockRestore();
});
});
});
Expand Down
54 changes: 36 additions & 18 deletions src/WfsFilterUtil/WfsFilterUtil.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import _isNil from 'lodash/isNil';
import { equalTo, like, or } from 'ol/format/filter';
import OlFormatFilter from 'ol/format/filter/Filter';
import { and, equalTo, like, or } from 'ol/format/filter';
import OlFilter from 'ol/format/filter/Filter';
import OlFormatWFS, { WriteGetFeatureOptions } from 'ol/format/WFS';

export type AttributeSearchSettings = {
type: 'number' | 'int' | 'string';
exactSearch?: boolean;
matchCase?: boolean;
type: 'number' | 'int' | 'string';
};

/**
Expand Down Expand Up @@ -38,16 +38,18 @@ export type AttributeDetails = {
};

export type SearchConfig = {
attributeDetails: AttributeDetails;
featureNS: string;
featureTypes?: string[];
featurePrefix: string;
featureTypes?: string[];
filter?: OlFilter;
geometryName?: string;
maxFeatures?: number;
olFilterOnly?: boolean;
outputFormat?: string;
propertyNames?: string[];
srsName?: string;
wfsFormatOptions?: string;
attributeDetails: AttributeDetails;
propertyNames?: string[];
};

/**
Expand All @@ -61,31 +63,32 @@ class WfsFilterUtil {
* Creates a filter for a given feature type considering configured
* search attributes, mapped features types to an array of attribute details and the
* current search term.
* Currently, supports EQUALTO and LIKE filters only, which can be combined with
* Currently, supports EQUAL_TO and LIKE filters only, which can be combined with
* OR filter if searchAttributes array contains multiple values though.
*
* @param featureType
* @param {string} searchTerm Search value.
* @param attributeDetails
* attributes that should be searched through.
* @return {OlFormatFilter} Filter to be used with WFS GetFeature requests.
* @return {OlFilter} Filter to be used with WFS GetFeature requests.
* @private
*/
static createWfsFilter(
featureType: string,
searchTerm: string,
attributeDetails: AttributeDetails
): OlFormatFilter | null {
): OlFilter | undefined {

const details = attributeDetails[featureType];

if (!details) {
return null;
if (_isNil(details)) {
return;
}

const attributes = Object.keys(details);

if (attributes.length === 0) {
return null;
return;
}

const propertyFilters = attributes
Expand Down Expand Up @@ -120,19 +123,34 @@ class WfsFilterUtil {
*/
static getCombinedRequests(searchConfig: SearchConfig, searchTerm: string): Element | undefined {
const {
attributeDetails,
featureNS,
featurePrefix,
featureTypes,
filter,
geometryName,
maxFeatures,
olFilterOnly,
outputFormat,
srsName,
attributeDetails,
propertyNames
propertyNames,
srsName
} = searchConfig;

const requests = featureTypes?.map((featureType: string): any => {
const filter = WfsFilterUtil.createWfsFilter(featureType, searchTerm, attributeDetails);
let combinedFilter: OlFilter | undefined;

// existing OlFilter should be applied to attribute
if (olFilterOnly && !_isNil(filter)) {
combinedFilter = filter;
} else {
const attributeFilter = WfsFilterUtil.createWfsFilter(featureType, searchTerm, attributeDetails);
if (!_isNil(filter) && !_isNil(attributeFilter)) {
combinedFilter = and(attributeFilter, filter);
} else {
combinedFilter = attributeFilter;
}
}

const wfsFormatOpts: WriteGetFeatureOptions = {
featureNS,
featurePrefix,
Expand All @@ -146,8 +164,8 @@ class WfsFilterUtil {
if (!_isNil(propertyNames)) {
wfsFormatOpts.propertyNames = propertyNames;
}
if (!_isNil(filter)) {
wfsFormatOpts.filter = filter;
if (!_isNil(combinedFilter)) {
wfsFormatOpts.filter = combinedFilter;
}

const wfsFormat: OlFormatWFS = new OlFormatWFS(wfsFormatOpts);
Expand Down

0 comments on commit c0ea690

Please sign in to comment.