Merge pull request #11648 from virtualcitySYSTEMS/dwithin

Dwithin filter
This commit is contained in:
Andreas Hocevar
2020-10-19 11:18:44 +02:00
committed by GitHub
4 changed files with 319 additions and 89 deletions
+49 -89
View File
@@ -925,9 +925,10 @@ const GETFEATURE_SERIALIZERS = {
'Or': makeChildAppender(writeLogicalFilter), 'Or': makeChildAppender(writeLogicalFilter),
'Not': makeChildAppender(writeNotFilter), 'Not': makeChildAppender(writeNotFilter),
'BBOX': makeChildAppender(writeBboxFilter), 'BBOX': makeChildAppender(writeBboxFilter),
'Contains': makeChildAppender(writeContainsFilter), 'Contains': makeChildAppender(writeSpatialFilter),
'Intersects': makeChildAppender(writeIntersectsFilter), 'Intersects': makeChildAppender(writeSpatialFilter),
'Within': makeChildAppender(writeWithinFilter), 'Within': makeChildAppender(writeSpatialFilter),
'DWithin': makeChildAppender(writeDWithinFilter),
'PropertyIsEqualTo': makeChildAppender(writeComparisonFilter), 'PropertyIsEqualTo': makeChildAppender(writeComparisonFilter),
'PropertyIsNotEqualTo': makeChildAppender(writeComparisonFilter), 'PropertyIsNotEqualTo': makeChildAppender(writeComparisonFilter),
'PropertyIsLessThan': makeChildAppender(writeComparisonFilter), 'PropertyIsLessThan': makeChildAppender(writeComparisonFilter),
@@ -944,11 +945,12 @@ const GETFEATURE_SERIALIZERS = {
'Or': makeChildAppender(writeLogicalFilter), 'Or': makeChildAppender(writeLogicalFilter),
'Not': makeChildAppender(writeNotFilter), 'Not': makeChildAppender(writeNotFilter),
'BBOX': makeChildAppender(writeBboxFilter), 'BBOX': makeChildAppender(writeBboxFilter),
'Contains': makeChildAppender(writeContainsFilter), 'Contains': makeChildAppender(writeSpatialFilter),
'Disjoint': makeChildAppender(writeDisjointFilter), 'Disjoint': makeChildAppender(writeSpatialFilter),
'Intersects': makeChildAppender(writeIntersectsFilter), 'Intersects': makeChildAppender(writeSpatialFilter),
'ResourceId': makeChildAppender(writeResourceIdFilter), 'ResourceId': makeChildAppender(writeResourceIdFilter),
'Within': makeChildAppender(writeWithinFilter), 'Within': makeChildAppender(writeSpatialFilter),
'DWithin': makeChildAppender(writeDWithinFilter),
'PropertyIsEqualTo': makeChildAppender(writeComparisonFilter), 'PropertyIsEqualTo': makeChildAppender(writeComparisonFilter),
'PropertyIsNotEqualTo': makeChildAppender(writeComparisonFilter), 'PropertyIsNotEqualTo': makeChildAppender(writeComparisonFilter),
'PropertyIsLessThan': makeChildAppender(writeComparisonFilter), 'PropertyIsLessThan': makeChildAppender(writeComparisonFilter),
@@ -1048,54 +1050,6 @@ function writeBboxFilter(node, filter, objectStack) {
format.prototype.writeGeometryElement(node, filter.extent, objectStack); format.prototype.writeGeometryElement(node, filter.extent, objectStack);
} }
/**
* @param {Node} node Node.
* @param {import("./filter/Contains.js").default} filter Filter.
* @param {Array<*>} objectStack Node stack.
*/
function writeContainsFilter(node, filter, objectStack) {
const parent = /** @type {Object} */ (objectStack[objectStack.length - 1]);
const context = parent['context'];
const version = context['version'];
parent['srsName'] = filter.srsName;
const format = GML_FORMATS[version];
writePropertyName(version, node, filter.geometryName);
format.prototype.writeGeometryElement(node, filter.geometry, objectStack);
}
/**
* @param {Node} node Node.
* @param {import("./filter/Intersects.js").default} filter Filter.
* @param {Array<*>} objectStack Node stack.
*/
function writeIntersectsFilter(node, filter, objectStack) {
const parent = /** @type {Object} */ (objectStack[objectStack.length - 1]);
const context = parent['context'];
const version = context['version'];
parent['srsName'] = filter.srsName;
const format = GML_FORMATS[version];
writePropertyName(version, node, filter.geometryName);
format.prototype.writeGeometryElement(node, filter.geometry, objectStack);
}
/**
* @param {Node} node Node.
* @param {import("./filter/Disjoint.js").default} filter Filter.
* @param {Array<*>} objectStack Node stack.
*/
function writeDisjointFilter(node, filter, objectStack) {
const parent = /** @type {Object} */ (objectStack[objectStack.length - 1]);
const context = parent['context'];
const version = context['version'];
parent['srsName'] = filter.srsName;
const format = GML_FORMATS[version];
writePropertyName(version, node, filter.geometryName);
format.prototype.writeGeometryElement(node, filter.geometry, objectStack);
}
/** /**
* @param {Element} node Element. * @param {Element} node Element.
* @param {import("./filter/ResourceId.js").default} filter Filter. * @param {import("./filter/ResourceId.js").default} filter Filter.
@@ -1107,10 +1061,10 @@ function writeResourceIdFilter(node, filter, objectStack) {
/** /**
* @param {Node} node Node. * @param {Node} node Node.
* @param {import("./filter/Within.js").default} filter Filter. * @param {import("./filter/Spatial.js").default} filter Filter.
* @param {Array<*>} objectStack Node stack. * @param {Array<*>} objectStack Node stack.
*/ */
function writeWithinFilter(node, filter, objectStack) { function writeSpatialFilter(node, filter, objectStack) {
const parent = /** @type {Object} */ (objectStack[objectStack.length - 1]); const parent = /** @type {Object} */ (objectStack[objectStack.length - 1]);
const context = parent['context']; const context = parent['context'];
const version = context['version']; const version = context['version'];
@@ -1121,6 +1075,26 @@ function writeWithinFilter(node, filter, objectStack) {
format.prototype.writeGeometryElement(node, filter.geometry, objectStack); format.prototype.writeGeometryElement(node, filter.geometry, objectStack);
} }
/**
* @param {Node} node Node.
* @param {import("./filter/DWithin.js").default} filter Filter.
* @param {Array<*>} objectStack Node stack.
*/
function writeDWithinFilter(node, filter, objectStack) {
const parent = /** @type {Object} */ (objectStack[objectStack.length - 1]);
const context = parent['context'];
const version = context['version'];
writeSpatialFilter(node, filter, objectStack);
const distance = createElementNS(getFilterNS(version), 'Distance');
writeStringTextNode(distance, filter.distance.toString());
if (version === '2.0.0') {
distance.setAttribute('uom', filter.unit);
} else {
distance.setAttribute('units', filter.unit);
}
node.appendChild(distance);
}
/** /**
* @param {Node} node Node. * @param {Node} node Node.
* @param {import("./filter/During.js").default} filter Filter. * @param {import("./filter/During.js").default} filter Filter.
@@ -1130,11 +1104,8 @@ function writeDuringFilter(node, filter, objectStack) {
const parent = /** @type {Object} */ (objectStack[objectStack.length - 1]); const parent = /** @type {Object} */ (objectStack[objectStack.length - 1]);
const context = parent['context']; const context = parent['context'];
const version = context['version']; const version = context['version'];
const ns = FESNS[version];
const valueReference = createElementNS(ns, 'ValueReference');
writeStringTextNode(valueReference, filter.propertyName);
node.appendChild(valueReference);
writeExpression(FESNS[version], 'ValueReference', node, filter.propertyName);
const timePeriod = createElementNS(GMLNS, 'TimePeriod'); const timePeriod = createElementNS(GMLNS, 'TimePeriod');
node.appendChild(timePeriod); node.appendChild(timePeriod);
@@ -1202,12 +1173,11 @@ function writeComparisonFilter(node, filter, objectStack) {
const parent = /** @type {Object} */ (objectStack[objectStack.length - 1]); const parent = /** @type {Object} */ (objectStack[objectStack.length - 1]);
const context = parent['context']; const context = parent['context'];
const version = context['version']; const version = context['version'];
const ns = OGCNS[context['version']];
if (filter.matchCase !== undefined) { if (filter.matchCase !== undefined) {
node.setAttribute('matchCase', filter.matchCase.toString()); node.setAttribute('matchCase', filter.matchCase.toString());
} }
writePropertyName(version, node, filter.propertyName); writePropertyName(version, node, filter.propertyName);
writeOgcLiteral(ns, node, '' + filter.expression); writeLiteral(version, node, '' + filter.expression);
} }
/** /**
@@ -1231,17 +1201,17 @@ function writeIsBetweenFilter(node, filter, objectStack) {
const parent = /** @type {Object} */ (objectStack[objectStack.length - 1]); const parent = /** @type {Object} */ (objectStack[objectStack.length - 1]);
const context = parent['context']; const context = parent['context'];
const version = context['version']; const version = context['version'];
const ns = OGCNS[context['version']]; const ns = getFilterNS(version);
writePropertyName(version, node, filter.propertyName); writePropertyName(version, node, filter.propertyName);
const lowerBoundary = createElementNS(ns, 'LowerBoundary'); const lowerBoundary = createElementNS(ns, 'LowerBoundary');
node.appendChild(lowerBoundary); node.appendChild(lowerBoundary);
writeOgcLiteral(ns, lowerBoundary, '' + filter.lowerBoundary); writeLiteral(version, lowerBoundary, '' + filter.lowerBoundary);
const upperBoundary = createElementNS(ns, 'UpperBoundary'); const upperBoundary = createElementNS(ns, 'UpperBoundary');
node.appendChild(upperBoundary); node.appendChild(upperBoundary);
writeOgcLiteral(ns, upperBoundary, '' + filter.upperBoundary); writeLiteral(version, upperBoundary, '' + filter.upperBoundary);
} }
/** /**
@@ -1253,7 +1223,6 @@ function writeIsLikeFilter(node, filter, objectStack) {
const parent = /** @type {Object} */ (objectStack[objectStack.length - 1]); const parent = /** @type {Object} */ (objectStack[objectStack.length - 1]);
const context = parent['context']; const context = parent['context'];
const version = context['version']; const version = context['version'];
const ns = OGCNS[version];
node.setAttribute('wildCard', filter.wildCard); node.setAttribute('wildCard', filter.wildCard);
node.setAttribute('singleChar', filter.singleChar); node.setAttribute('singleChar', filter.singleChar);
node.setAttribute('escapeChar', filter.escapeChar); node.setAttribute('escapeChar', filter.escapeChar);
@@ -1261,7 +1230,7 @@ function writeIsLikeFilter(node, filter, objectStack) {
node.setAttribute('matchCase', filter.matchCase.toString()); node.setAttribute('matchCase', filter.matchCase.toString());
} }
writePropertyName(version, node, filter.propertyName); writePropertyName(version, node, filter.propertyName);
writeOgcLiteral(ns, node, '' + filter.pattern); writeLiteral(version, node, '' + filter.pattern);
} }
/** /**
@@ -1270,12 +1239,21 @@ function writeIsLikeFilter(node, filter, objectStack) {
* @param {Node} node Node. * @param {Node} node Node.
* @param {string} value Value. * @param {string} value Value.
*/ */
function writeOgcExpression(ns, tagName, node, value) { function writeExpression(ns, tagName, node, value) {
const property = createElementNS(ns, tagName); const property = createElementNS(ns, tagName);
writeStringTextNode(property, value); writeStringTextNode(property, value);
node.appendChild(property); node.appendChild(property);
} }
/**
* @param {string} version Version.
* @param {Node} node Node.
* @param {string} value PropertyName value.
*/
function writeLiteral(version, node, value) {
writeExpression(getFilterNS(version), 'Literal', node, value);
}
/** /**
* @param {string} version Version. * @param {string} version Version.
* @param {Node} node Node. * @param {Node} node Node.
@@ -1283,30 +1261,12 @@ function writeOgcExpression(ns, tagName, node, value) {
*/ */
function writePropertyName(version, node, value) { function writePropertyName(version, node, value) {
if (version === '2.0.0') { if (version === '2.0.0') {
writeFesValueReference(FESNS[version], node, value); writeExpression(FESNS[version], 'ValueReference', node, value);
} else { } else {
writeOgcExpression(OGCNS[version], 'PropertyName', node, value); writeExpression(OGCNS[version], 'PropertyName', node, value);
} }
} }
/**
* @param {string} ns Namespace.
* @param {Node} node Node.
* @param {string} value PropertyName value.
*/
function writeFesValueReference(ns, node, value) {
writeOgcExpression(ns, 'ValueReference', node, value);
}
/**
* @param {string} ns Namespace.
* @param {Node} node Node.
* @param {string} value PropertyName value.
*/
function writeOgcLiteral(ns, node, value) {
writeOgcExpression(ns, 'Literal', node, value);
}
/** /**
* @param {Node} node Node. * @param {Node} node Node.
* @param {string} time PropertyName value. * @param {string} time PropertyName value.
+18
View File
@@ -4,6 +4,7 @@
import And from './filter/And.js'; import And from './filter/And.js';
import Bbox from './filter/Bbox.js'; import Bbox from './filter/Bbox.js';
import Contains from './filter/Contains.js'; import Contains from './filter/Contains.js';
import DWithin from './filter/DWithin.js';
import Disjoint from './filter/Disjoint.js'; import Disjoint from './filter/Disjoint.js';
import During from './filter/During.js'; import During from './filter/During.js';
import EqualTo from './filter/EqualTo.js'; import EqualTo from './filter/EqualTo.js';
@@ -131,6 +132,23 @@ export function within(geometryName, geometry, opt_srsName) {
return new Within(geometryName, geometry, opt_srsName); return new Within(geometryName, geometry, opt_srsName);
} }
/**
* Create a `<DWithin>` operator to test whether a geometry-valued property
* is within a distance to a given geometry.
*
* @param {!string} geometryName Geometry name to use.
* @param {!import("../geom/Geometry.js").default} geometry Geometry.
* @param {!number} distance Distance.
* @param {!string} unit Unit.
* @param {string=} opt_srsName SRS name. No srsName attribute will be
* set on geometries when this is not provided.
* @returns {!DWithin} `<DWithin>` operator.
* @api
*/
export function dwithin(geometryName, geometry, distance, unit, opt_srsName) {
return new DWithin(geometryName, geometry, distance, unit, opt_srsName);
}
/** /**
* Creates a `<PropertyIsEqualTo>` comparison operator. * Creates a `<PropertyIsEqualTo>` comparison operator.
* *
+38
View File
@@ -0,0 +1,38 @@
/**
* @module ol/format/filter/DWithin
*/
import Spatial from './Spatial.js';
/**
* @classdesc
* Represents a `<DWithin>` operator to test whether a geometry-valued property
* is within a distance to a given geometry.
* @api
*/
class DWithin extends Spatial {
/**
* @param {!string} geometryName Geometry name to use.
* @param {!import("../../geom/Geometry.js").default} geometry Geometry.
* @param {!number} distance Distance.
* @param {!string} unit Unit.
* @param {string=} opt_srsName SRS name. No srsName attribute will be
* set on geometries when this is not provided.
*/
constructor(geometryName, geometry, distance, unit, opt_srsName) {
super('DWithin', geometryName, geometry, opt_srsName);
/**
* @public
* @type {!number}
*/
this.distance = distance;
/**
* @public
* @type {!string}
*/
this.unit = unit;
}
}
export default DWithin;
+214
View File
@@ -19,6 +19,7 @@ import {
contains as containsFilter, contains as containsFilter,
disjoint as disjointFilter, disjoint as disjointFilter,
during as duringFilter, during as duringFilter,
dwithin as dwithinFilter,
equalTo as equalToFilter, equalTo as equalToFilter,
greaterThan as greaterThanFilter, greaterThan as greaterThanFilter,
greaterThanOrEqualTo as greaterThanOrEqualToFilter, greaterThanOrEqualTo as greaterThanOrEqualToFilter,
@@ -686,6 +687,48 @@ describe('ol.format.WFS', function () {
expect(serialized.firstElementChild).to.xmleql(parse(text)); expect(serialized.firstElementChild).to.xmleql(parse(text));
}); });
it('creates a dwithin filter', function () {
const text =
'<wfs:Query xmlns:wfs="http://www.opengis.net/wfs" ' +
' typeName="area" srsName="EPSG:4326" ' +
' xmlns:topp="http://www.openplans.org/topp">' +
' <ogc:Filter xmlns:ogc="http://www.opengis.net/ogc">' +
' <ogc:DWithin>' +
' <ogc:PropertyName>the_geom</ogc:PropertyName>' +
' <gml:Polygon xmlns:gml="http://www.opengis.net/gml">' +
' <gml:exterior>' +
' <gml:LinearRing>' +
' <gml:posList srsDimension="2">' +
' 10 20 10 25 15 25 15 20 10 20' +
' </gml:posList>' +
' </gml:LinearRing>' +
' </gml:exterior>' +
' </gml:Polygon>' +
' <ogc:Distance units="m">10</ogc:Distance>' +
' </ogc:DWithin>' +
' </ogc:Filter>' +
'</wfs:Query>';
const serialized = new WFS().writeGetFeature({
srsName: 'EPSG:4326',
featureTypes: ['area'],
filter: dwithinFilter(
'the_geom',
new Polygon([
[
[10, 20],
[10, 25],
[15, 25],
[15, 20],
[10, 20],
],
]),
10,
'm'
),
});
expect(serialized.firstElementChild).to.xmleql(parse(text));
});
it('creates During property filter', function () { it('creates During property filter', function () {
const text = const text =
'<wfs:Query xmlns:wfs="http://www.opengis.net/wfs" ' + '<wfs:Query xmlns:wfs="http://www.opengis.net/wfs" ' +
@@ -1595,5 +1638,176 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
expect(serialized).to.xmleql(parse(text)); expect(serialized).to.xmleql(parse(text));
}); });
}); });
it('creates a dwithin filter', function () {
const text =
'<wfs:Query xmlns:wfs="http://www.opengis.net/wfs/2.0" ' +
' typeNames="area" srsName="EPSG:4326" ' +
' xmlns:topp="http://www.openplans.org/topp">' +
' <fes:Filter xmlns:fes="http://www.opengis.net/fes/2.0">' +
' <fes:DWithin>' +
' <fes:ValueReference>the_geom</fes:ValueReference>' +
' <gml:Polygon xmlns:gml="http://www.opengis.net/gml/3.2">' +
' <gml:exterior>' +
' <gml:LinearRing>' +
' <gml:posList srsDimension="2">' +
' 10 20 10 25 15 25 15 20 10 20' +
' </gml:posList>' +
' </gml:LinearRing>' +
' </gml:exterior>' +
' </gml:Polygon>' +
' <fes:Distance uom="m">10</fes:Distance>' +
' </fes:DWithin>' +
' </fes:Filter>' +
'</wfs:Query>';
const serialized = new WFS({version: '2.0.0'}).writeGetFeature({
srsName: 'EPSG:4326',
featureTypes: ['area'],
filter: dwithinFilter(
'the_geom',
new Polygon([
[
[10, 20],
[10, 25],
[15, 25],
[15, 20],
[10, 20],
],
]),
10,
'm'
),
});
expect(serialized.firstElementChild).to.xmleql(parse(text));
});
it('creates isLike property filter', function () {
const text =
'<wfs:Query xmlns:wfs="http://www.opengis.net/wfs/2.0" ' +
' typeNames="topp:states" srsName="urn:ogc:def:crs:EPSG::4326" ' +
' xmlns:topp="http://www.openplans.org/topp">' +
' <fes:Filter xmlns:fes="http://www.opengis.net/fes/2.0">' +
' <fes:PropertyIsLike wildCard="*" singleChar="." escapeChar="!">' +
' <fes:ValueReference>name</fes:ValueReference>' +
' <fes:Literal>New*</fes:Literal>' +
' </fes:PropertyIsLike>' +
' </fes:Filter>' +
'</wfs:Query>';
const serialized = new WFS({version: '2.0.0'}).writeGetFeature({
srsName: 'urn:ogc:def:crs:EPSG::4326',
featureNS: 'http://www.openplans.org/topp',
featurePrefix: 'topp',
featureTypes: ['states'],
filter: likeFilter('name', 'New*'),
});
expect(serialized.firstElementChild).to.xmleql(parse(text));
});
it('creates isBetween property filter', function () {
const text =
'<wfs:Query xmlns:wfs="http://www.opengis.net/wfs/2.0" ' +
' typeNames="topp:states" srsName="urn:ogc:def:crs:EPSG::4326" ' +
' xmlns:topp="http://www.openplans.org/topp">' +
' <fes:Filter xmlns:fes="http://www.opengis.net/fes/2.0">' +
' <fes:PropertyIsBetween>' +
' <fes:ValueReference>area</fes:ValueReference>' +
' <fes:LowerBoundary><fes:Literal>100</fes:Literal></fes:LowerBoundary>' +
' <fes:UpperBoundary><fes:Literal>1000</fes:Literal></fes:UpperBoundary>' +
' </fes:PropertyIsBetween>' +
' </fes:Filter>' +
'</wfs:Query>';
const serialized = new WFS({version: '2.0.0'}).writeGetFeature({
srsName: 'urn:ogc:def:crs:EPSG::4326',
featureNS: 'http://www.openplans.org/topp',
featurePrefix: 'topp',
featureTypes: ['states'],
filter: betweenFilter('area', 100, 1000),
});
expect(serialized.firstElementChild).to.xmleql(parse(text));
});
it('creates greater/less than property filters', function () {
const text =
'<wfs:Query xmlns:wfs="http://www.opengis.net/wfs/2.0" ' +
' typeNames="topp:states" srsName="urn:ogc:def:crs:EPSG::4326" ' +
' xmlns:topp="http://www.openplans.org/topp">' +
' <fes:Filter xmlns:fes="http://www.opengis.net/fes/2.0">' +
' <fes:Or>' +
' <fes:And>' +
' <fes:PropertyIsGreaterThan>' +
' <fes:ValueReference>area</fes:ValueReference>' +
' <fes:Literal>100</fes:Literal>' +
' </fes:PropertyIsGreaterThan>' +
' <fes:PropertyIsGreaterThanOrEqualTo>' +
' <fes:ValueReference>pop</fes:ValueReference>' +
' <fes:Literal>20000</fes:Literal>' +
' </fes:PropertyIsGreaterThanOrEqualTo>' +
' </fes:And>' +
' <fes:And>' +
' <fes:PropertyIsLessThan>' +
' <fes:ValueReference>area</fes:ValueReference>' +
' <fes:Literal>100</fes:Literal>' +
' </fes:PropertyIsLessThan>' +
' <fes:PropertyIsLessThanOrEqualTo>' +
' <fes:ValueReference>pop</fes:ValueReference>' +
' <fes:Literal>20000</fes:Literal>' +
' </fes:PropertyIsLessThanOrEqualTo>' +
' </fes:And>' +
' </fes:Or>' +
' </fes:Filter>' +
'</wfs:Query>';
const serialized = new WFS({version: '2.0.0'}).writeGetFeature({
srsName: 'urn:ogc:def:crs:EPSG::4326',
featureNS: 'http://www.openplans.org/topp',
featurePrefix: 'topp',
featureTypes: ['states'],
filter: orFilter(
andFilter(
greaterThanFilter('area', 100),
greaterThanOrEqualToFilter('pop', 20000)
),
andFilter(
lessThanFilter('area', 100),
lessThanOrEqualToFilter('pop', 20000)
)
),
});
expect(serialized.firstElementChild).to.xmleql(parse(text));
});
it('creates During property filter', function () {
const text =
'<wfs:Query xmlns:wfs="http://www.opengis.net/wfs/2.0" ' +
' typeNames="states" srsName="EPSG:4326">' +
' <fes:Filter xmlns:fes="http://www.opengis.net/fes/2.0">' +
' <fes:During>' +
' <fes:ValueReference>date_prop</fes:ValueReference>' +
' <gml:TimePeriod xmlns:gml="http://www.opengis.net/gml">' +
' <gml:begin>' +
' <gml:TimeInstant>' +
' <gml:timePosition>2010-01-20T00:00:00Z</gml:timePosition>' +
' </gml:TimeInstant>' +
' </gml:begin>' +
' <gml:end>' +
' <gml:TimeInstant>' +
' <gml:timePosition>2012-12-31T00:00:00Z</gml:timePosition>' +
' </gml:TimeInstant>' +
' </gml:end>' +
' </gml:TimePeriod>' +
' </fes:During>' +
' </fes:Filter>' +
'</wfs:Query>';
const serialized = new WFS({version: '2.0.0'}).writeGetFeature({
srsName: 'EPSG:4326',
featureTypes: ['states'],
filter: duringFilter(
'date_prop',
'2010-01-20T00:00:00Z',
'2012-12-31T00:00:00Z'
),
});
expect(serialized.firstElementChild).to.xmleql(parse(text));
});
}); });
}); });