UNPKG

@phenixrts/aerospike

Version:
1,303 lines (1,141 loc) 54.1 kB
// ***************************************************************************** // Copyright 2013-2019 Aerospike, Inc. // // Licensed under the Apache License, Version 2.0 (the "License") // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // ***************************************************************************** 'use strict' /* eslint-env mocha */ const Aerospike = require('../lib/aerospike') const helper = require('./test_helper') const maps = Aerospike.maps const Context = Aerospike.cdt.Context const status = Aerospike.status const eql = require('deep-eql') const { assertError, assertRecordEql, assertResultEql, assertResultSatisfy, cleanup, createRecord, expectError, initState, operate } = require('./util/statefulAsyncTest') const orderMap = (bin, order, ctx) => { const policy = new maps.MapPolicy({ order }) const setMapPolicy = maps.setPolicy(bin, policy) if (ctx) setMapPolicy.withContext(ctx) return operate(setMapPolicy) } const orderByKey = (bin, ctx) => orderMap(bin, maps.order.KEY_ORDERED, ctx) const orderByKeyValue = (bin, ctx) => orderMap(bin, maps.order.KEY_VALUE_ORDERED, ctx) describe('client.operate() - CDT Map operations', function () { helper.skipUnlessSupportsFeature(Aerospike.features.CDT_MAP, this) describe('maps.setPolicy', function () { it('changes the map order', function () { return initState() .then(createRecord({ map: { c: 1, b: 2, a: 3 } })) .then(orderByKey('map')) .then(operate(maps.getByKeyRange('map', 'a', 'z', maps.returnType.KEY))) .then(assertResultEql({ map: ['a', 'b', 'c'] })) .then(cleanup()) }) }) describe('maps.put', function () { it('adds the item to the map and returns the size of the map', function () { return initState() .then(createRecord({ map: { a: 1, b: 2, c: 3 } })) .then(operate(maps.put('map', 'd', 99))) .then(assertResultEql({ map: 4 })) .then(assertRecordEql({ map: { a: 1, b: 2, c: 3, d: 99 } })) .then(cleanup()) }) it('replaces the item and returns the size of the map', function () { return initState() .then(createRecord({ map: { a: 1, b: 2, c: 3 } })) .then(operate(maps.put('map', 'b', 99))) .then(assertResultEql({ map: 3 })) .then(assertRecordEql({ map: { a: 1, b: 99, c: 3 } })) .then(cleanup()) }) it('creates a new map if it does not exist yet', function () { return initState() .then(createRecord({ i: 1 })) .then(operate(maps.put('map', 'a', 1))) .then(assertResultEql({ map: 1 })) .then(assertRecordEql({ i: 1, map: { a: 1 } })) .then(cleanup()) }) it('fails if the bin does not contain a map', function () { return initState() .then(createRecord({ map: 'this is not a map' })) .then(expectError()) .then(operate(maps.put('map', 'a', 1))) .then(assertError(status.ERR_BIN_INCOMPATIBLE_TYPE)) .then(cleanup()) }) context('update-only write mode', function () { const updateOnlyPolicy = new maps.MapPolicy({ writeMode: maps.writeMode.UPDATE_ONLY }) it('overwrites an existing key', function () { return initState() .then(createRecord({ map: { a: 1, b: 2, c: 3 } })) .then(operate(maps.put('map', 'b', 99, updateOnlyPolicy))) .then(assertResultEql({ map: 3 })) .then(assertRecordEql({ map: { a: 1, b: 99, c: 3 } })) .then(cleanup()) }) it('fails to write a non-existing key', function () { return initState() .then(createRecord({ map: { a: 1, b: 2, c: 3 } })) .then(expectError()) .then(operate(maps.put('map', 'd', 99, updateOnlyPolicy))) .then(assertError(status.ERR_FAIL_ELEMENT_NOT_FOUND)) .then(cleanup()) }) }) context('with update-only flag', function () { helper.skipUnlessVersion('>= 4.3.0', this) const policy = new maps.MapPolicy({ writeFlags: maps.writeFlags.UPDATE_ONLY }) it('overwrites an existing key', function () { return initState() .then(createRecord({ map: { a: 1, b: 2, c: 3 } })) .then(operate(maps.put('map', 'b', 99, policy))) .then(assertResultEql({ map: 3 })) .then(assertRecordEql({ map: { a: 1, b: 99, c: 3 } })) .then(cleanup()) }) it('fails to write a non-existing key', function () { return initState() .then(createRecord({ map: { a: 1, b: 2, c: 3 } })) .then(expectError()) .then(operate(maps.put('map', 'd', 99, policy))) .then(assertError(status.ERR_FAIL_ELEMENT_NOT_FOUND)) .then(cleanup()) }) context('with no-fail flag', function () { const policy = new maps.MapPolicy({ writeFlags: maps.writeFlags.UPDATE_ONLY | maps.writeFlags.NO_FAIL }) it('does not add the item but returns ok', function () { return initState() .then(createRecord({ map: { a: 1, b: 2, c: 3 } })) .then(operate(maps.put('map', 'd', 99, policy))) .then(assertResultEql({ map: 3 })) .then(assertRecordEql({ map: { a: 1, b: 2, c: 3 } })) .then(cleanup()) }) }) }) context('create-only write mode', function () { const createOnlyPolicy = new maps.MapPolicy({ writeMode: maps.writeMode.CREATE_ONLY }) it('fails to overwrite an existing key', function () { return initState() .then(createRecord({ map: { a: 1, b: 2, c: 3 } })) .then(expectError()) .then(operate(maps.put('map', 'b', 99, createOnlyPolicy))) .then(assertError(status.ERR_FAIL_ELEMENT_EXISTS)) .then(cleanup()) }) it('creates a new key if it does not exist', function () { return initState() .then(createRecord({ map: { a: 1, b: 2, c: 3 } })) .then(operate(maps.put('map', 'd', 99, createOnlyPolicy))) .then(assertResultEql({ map: 4 })) .then(assertRecordEql({ map: { a: 1, b: 2, c: 3, d: 99 } })) .then(cleanup()) }) }) context('with create-only flag', function () { helper.skipUnlessVersion('>= 4.3.0', this) const policy = new maps.MapPolicy({ writeFlags: maps.writeFlags.CREATE_ONLY }) it('fails to overwrite an existing key', function () { return initState() .then(createRecord({ map: { a: 1, b: 2, c: 3 } })) .then(expectError()) .then(operate(maps.put('map', 'b', 99, policy))) .then(assertError(status.ERR_FAIL_ELEMENT_EXISTS)) .then(cleanup()) }) it('creates a new key if it does not exist', function () { return initState() .then(createRecord({ map: { a: 1, b: 2, c: 3 } })) .then(operate(maps.put('map', 'd', 99, policy))) .then(assertResultEql({ map: 4 })) .then(assertRecordEql({ map: { a: 1, b: 2, c: 3, d: 99 } })) .then(cleanup()) }) context('with no-fail flag', function () { const policy = new maps.MapPolicy({ writeFlags: maps.writeFlags.CREATE_ONLY | maps.writeFlags.NO_FAIL }) it('does not update the item but returns ok', function () { return initState() .then(createRecord({ map: { a: 1, b: 2, c: 3 } })) .then(operate(maps.put('map', 'b', 99, policy))) .then(assertResultEql({ map: 3 })) .then(assertRecordEql({ map: { a: 1, b: 2, c: 3 } })) .then(cleanup()) }) }) }) context('with nested map context', function () { helper.skipUnlessVersion('>= 4.6.0', this) it('adds the item to the nested map', function () { return initState() .then(createRecord({ list: [{ a: 1, b: 2, c: 3 }] })) .then(operate(maps.put('list', 'd', 99).withContext(ctx => ctx.addListIndex(0)))) .then(assertResultEql({ list: 4 })) .then(assertRecordEql({ list: [{ a: 1, b: 2, c: 3, d: 99 }] })) .then(cleanup()) }) }) }) describe('maps.putItems', function () { it('adds each item to the map and returns the size of the map', function () { return initState() .then(createRecord({ map: { a: 1, b: 2, c: 3 } })) .then(operate(maps.putItems('map', { c: 99, d: 100 }))) .then(assertResultEql({ map: 4 })) .then(assertRecordEql({ map: { a: 1, b: 2, c: 99, d: 100 } })) .then(cleanup()) }) context('with update-only flag', function () { helper.skipUnlessVersion('>= 4.3.0', this) const policy = new maps.MapPolicy({ writeFlags: maps.writeFlags.UPDATE_ONLY }) it('fails if any of the items do not yet exist in the map', function () { return initState() .then(createRecord({ map: { a: 1, b: 2, c: 3, d: 4, e: 5 } })) .then(expectError()) .then(operate(maps.putItems('map', { c: 10, x: 100 }, policy))) .then(assertError(status.ERR_FAIL_ELEMENT_NOT_FOUND)) .then(cleanup()) }) context('with no-fail flag', function () { const policy = new maps.MapPolicy({ writeFlags: maps.writeFlags.UPDATE_ONLY | maps.writeFlags.NO_FAIL }) it('does not update the map but returns ok', function () { return initState() .then(createRecord({ map: { a: 1, b: 2, c: 3, d: 4, e: 5 } })) .then(operate(maps.putItems('map', { c: 10, x: 100 }, policy))) .then(assertResultEql({ map: 5 })) .then(assertRecordEql({ map: { a: 1, b: 2, c: 3, d: 4, e: 5 } })) .then(cleanup()) }) context('with partial flag', function () { const policy = new maps.MapPolicy({ writeFlags: maps.writeFlags.UPDATE_ONLY | maps.writeFlags.NO_FAIL | maps.writeFlags.PARTIAL }) it('updates only the existing items', function () { return initState() .then(createRecord({ map: { a: 1, b: 2, c: 3, d: 4, e: 5 } })) .then(operate(maps.putItems('map', { c: 10, x: 100 }, policy))) .then(assertResultEql({ map: 5 })) .then(assertRecordEql({ map: { a: 1, b: 2, c: 10, d: 4, e: 5 } })) .then(cleanup()) }) }) }) }) context('with create-only flag', function () { helper.skipUnlessVersion('>= 4.3.0', this) const policy = new maps.MapPolicy({ writeFlags: maps.writeFlags.CREATE_ONLY }) it('fails if any of the items already exist in the map', function () { return initState() .then(createRecord({ map: { a: 1, b: 2, c: 3, d: 4, e: 5 } })) .then(expectError()) .then(operate(maps.putItems('map', { c: 10, x: 100 }, policy))) .then(assertError(status.ERR_FAIL_ELEMENT_EXISTS)) .then(cleanup()) }) context('with no-fail flag', function () { const policy = new maps.MapPolicy({ writeFlags: maps.writeFlags.CREATE_ONLY | maps.writeFlags.NO_FAIL }) it('does not update the map but returns ok', function () { return initState() .then(createRecord({ map: { a: 1, b: 2, c: 3, d: 4, e: 5 } })) .then(operate(maps.putItems('map', { c: 10, x: 100 }, policy))) .then(assertResultEql({ map: 5 })) .then(assertRecordEql({ map: { a: 1, b: 2, c: 3, d: 4, e: 5 } })) .then(cleanup()) }) context('with partial flag', function () { const policy = new maps.MapPolicy({ writeFlags: maps.writeFlags.CREATE_ONLY | maps.writeFlags.NO_FAIL | maps.writeFlags.PARTIAL }) it('adds only the keys that do not exist yet', function () { return initState() .then(createRecord({ map: { a: 1, b: 2, c: 3, d: 4, e: 5 } })) .then(operate(maps.putItems('map', { c: 10, x: 100 }, policy))) .then(assertResultEql({ map: 6 })) .then(assertRecordEql({ map: { a: 1, b: 2, c: 3, d: 4, e: 5, x: 100 } })) .then(cleanup()) }) }) }) }) context('with nested map context', function () { helper.skipUnlessVersion('>= 4.6.0', this) it('adds each item to the nested map', function () { return initState() .then(createRecord({ map: { nested: { a: 1, b: 2, c: 3 } } })) .then(operate(maps.putItems('map', { c: 99, d: 100 }).withContext(ctx => ctx.addMapKey('nested')))) .then(assertResultEql({ map: 4 })) .then(assertRecordEql({ map: { nested: { a: 1, b: 2, c: 99, d: 100 } } })) .then(cleanup()) }) }) }) describe('maps.increment', function () { it('increments the value of the entry and returns the final value', function () { return initState() .then(createRecord({ map: { a: 1, b: 2, c: 3 } })) .then(operate(maps.increment('map', 'b', 10))) .then(assertResultEql({ map: 12 })) .then(assertRecordEql({ map: { a: 1, b: 12, c: 3 } })) .then(cleanup()) }) it('creates a new entry if the key does not exist yet', function () { return initState() .then(createRecord({ map: { a: 1, b: 2, c: 3 } })) .then(operate(maps.increment('map', 'd', 10))) .then(assertResultEql({ map: 10 })) .then(assertRecordEql({ map: { a: 1, b: 2, c: 3, d: 10 } })) .then(cleanup()) }) context('with nested map context', function () { helper.skipUnlessVersion('>= 4.6.0', this) it('increments the value of the entry and returns the final value', function () { return initState() .then(createRecord({ list: [[1, 2, 3], { a: 1, b: 2, c: 3 }] })) .then(operate(maps.increment('list', 'b', 10).withContext(ctx => ctx.addListIndex(1)))) .then(assertResultEql({ list: 12 })) .then(assertRecordEql({ list: [[1, 2, 3], { a: 1, b: 12, c: 3 }] })) .then(cleanup()) }) }) }) describe('maps.decrement', function () { it('decrements the value of the entry and returns the final value', function () { return initState() .then(createRecord({ map: { a: 1, b: 12, c: 3 } })) .then(operate(maps.decrement('map', 'b', 10))) .then(assertResultEql({ map: 2 })) .then(assertRecordEql({ map: { a: 1, b: 2, c: 3 } })) .then(cleanup()) }) it('creates a new entry if the key does not exist yet', function () { return initState() .then(createRecord({ map: { a: 1, b: 2, c: 3 } })) .then(operate(maps.decrement('map', 'd', 1))) .then(assertResultEql({ map: -1 })) .then(assertRecordEql({ map: { a: 1, b: 2, c: 3, d: -1 } })) .then(cleanup()) }) context('with nested map context', function () { helper.skipUnlessVersion('>= 4.6.0', this) it('decrements the value of the entry and returns the final value', function () { return initState() .then(createRecord({ list: [{ a: 1, b: 12, c: 3 }, ['a', 'b', 'c']] })) .then(operate(maps.decrement('list', 'b', 10).withContext(ctx => ctx.addListIndex(0)))) .then(assertResultEql({ list: 2 })) .then(assertRecordEql({ list: [{ a: 1, b: 2, c: 3 }, ['a', 'b', 'c']] })) .then(cleanup()) }) }) }) describe('maps.clear', function () { it('removes all entries from the map', function () { return initState() .then(createRecord({ map: { a: 1, b: 12, c: 3 } })) .then(operate(maps.clear('map'))) .then(assertResultEql({ map: null })) .then(assertRecordEql({ map: { } })) .then(cleanup()) }) context('with nested map context', function () { helper.skipUnlessVersion('>= 4.6.0', this) it('removes all entries from the map', function () { return initState() .then(createRecord({ map: { nested: { deepNested: { a: 1, b: 12, c: 3 } } } })) .then(operate( maps .clear('map') .withContext(ctx => ctx.addMapKey('nested').addMapKey('deepNested')) )) .then(assertRecordEql({ map: { nested: { deepNested: { } } } })) .then(cleanup()) }) }) }) describe('maps.removeByKey', function () { it('removes a map entry identified by key', function () { return initState() .then(createRecord({ map: { a: 1, b: 2, c: 3 } })) .then(operate(maps.removeByKey('map', 'b', maps.returnType.VALUE))) .then(assertResultEql({ map: 2 })) .then(assertRecordEql({ map: { a: 1, c: 3 } })) .then(cleanup()) }) it('does not fail when removing a non-existing key', function () { return initState() .then(createRecord({ map: { a: 1, b: 2, c: 3 } })) .then(operate(maps.removeByKey('map', 'd', maps.returnType.VALUE))) .then(assertResultEql({ map: null })) .then(assertRecordEql({ map: { a: 1, b: 2, c: 3 } })) .then(cleanup()) }) context('with nested map context', function () { helper.skipUnlessVersion('>= 4.6.0', this) it('removes a map entry identified by key', function () { return initState() .then(createRecord({ list: [{ a: 1, b: 2, c: 3 }, { a: 2, b: 3, c: 4 }] })) .then(operate(maps.removeByKey('list', 'b').withContext(ctx => ctx.addListIndex(-1)))) .then(assertRecordEql({ list: [{ a: 1, b: 2, c: 3 }, { a: 2, c: 4 }] })) .then(cleanup()) }) }) }) describe('maps.removeByKeyList', function () { it('removes map entries identified by one or more keys', function () { return initState() .then(createRecord({ map: { a: 1, b: 2, c: 3 } })) .then(operate(maps.removeByKeyList('map', ['a', 'c'], maps.returnType.VALUE))) .then(assertResultSatisfy(result => eql(result.map.sort(), [1, 3]))) .then(assertRecordEql({ map: { b: 2 } })) .then(cleanup()) }) it('skips non-existent keys', function () { return initState() .then(createRecord({ map: { a: 1, b: 2, c: 3 } })) .then(operate(maps.removeByKeyList('map', ['a', 'x', 'y', 'z', 'c'], maps.returnType.VALUE))) .then(assertResultSatisfy(result => eql(result.map.sort(), [1, 3]))) .then(assertRecordEql({ map: { b: 2 } })) .then(cleanup()) }) context('with nested map context', function () { helper.skipUnlessVersion('>= 4.6.0', this) it('removes map entries identified by one or more keys', function () { return initState() .then(createRecord({ maps: { a: { a: 1, b: 2, c: 3 }, b: { a: 2, b: 3, c: 4 } } })) .then(operate(maps.removeByKeyList('maps', ['a', 'c']).withContext(ctx => ctx.addMapKey('a')))) .then(assertRecordEql({ maps: { a: { b: 2 }, b: { a: 2, b: 3, c: 4 } } })) .then(cleanup()) }) }) }) describe('maps.removeByKeyRange', function () { it('removes map entries identified by key range', function () { return initState() .then(createRecord({ map: { a: 1, b: 2, c: 3, d: 4 } })) .then(orderByKey('map')) .then(operate(maps.removeByKeyRange('map', 'b', 'd', maps.returnType.VALUE))) .then(assertResultEql({ map: [2, 3] })) .then(assertRecordEql({ map: { a: 1, d: 4 } })) .then(cleanup()) }) it('removes all keys from the specified start key until the end', function () { return initState() .then(createRecord({ map: { a: 1, b: 2, c: 3, d: 4 } })) .then(orderByKey('map')) .then(operate(maps.removeByKeyRange('map', 'b', null, maps.returnType.VALUE))) .then(assertResultEql({ map: [2, 3, 4] })) .then(assertRecordEql({ map: { a: 1 } })) .then(cleanup()) }) it('removes all keys from the start to the specified end', function () { return initState() .then(createRecord({ map: { a: 1, b: 2, c: 3, d: 4 } })) .then(orderByKey('map')) .then(operate(maps.removeByKeyRange('map', null, 'b', maps.returnType.VALUE))) .then(assertResultEql({ map: [1] })) .then(assertRecordEql({ map: { b: 2, c: 3, d: 4 } })) .then(cleanup()) }) context('with nested map context', function () { helper.skipUnlessVersion('>= 4.6.0', this) it('removes map entries identified by key range', function () { const mapContext = new Context().addListIndex(0) return initState() .then(createRecord({ list: [{ a: 1, b: 2, c: 3, d: 4 }] })) .then(orderByKey('list', mapContext)) .then(operate(maps.removeByKeyRange('list', 'b', 'd').withContext(mapContext))) .then(assertRecordEql({ list: [{ a: 1, d: 4 }] })) .then(cleanup()) }) }) }) describe('maps.removeByKeyRelIndexRange', function () { helper.skipUnlessVersion('>= 4.3.0', this) context('with count', function () { it('removes map entries nearest to key and greater, by index', function () { return initState() .then(createRecord({ map: { a: 17, e: 2, f: 15, j: 10 } })) .then(orderByKey('map')) .then(operate(maps.removeByKeyRelIndexRange('map', 'f', 0, 1).andReturn(maps.returnType.KEY))) .then(assertResultEql({ map: ['f'] })) .then(assertRecordEql({ map: { a: 17, e: 2, j: 10 } })) .then(cleanup()) }) }) context('without count', function () { it('removes map entries nearest to key and greater, by index', function () { return initState() .then(createRecord({ map: { a: 17, e: 2, f: 15, j: 10 } })) .then(orderByKey('map')) .then(operate(maps.removeByKeyRelIndexRange('map', 'f', 0).andReturn(maps.returnType.KEY))) .then(assertResultEql({ map: ['f', 'j'] })) .then(assertRecordEql({ map: { a: 17, e: 2 } })) .then(cleanup()) }) }) context('with nested map context', function () { helper.skipUnlessVersion('>= 4.6.0', this) it('removes map entries nearest to key and greater, by index', function () { const mapContext = new Context().addListIndex(0) return initState() .then(createRecord({ list: [{ a: 17, e: 2, f: 15, j: 10 }, { a: 32, f: 14 }] })) .then(orderByKey('list', mapContext)) .then(operate(maps.removeByKeyRelIndexRange('list', 'f', 0, 1).withContext(mapContext))) .then(assertRecordEql({ list: [{ a: 17, e: 2, j: 10 }, { a: 32, f: 14 }] })) .then(cleanup()) }) }) }) describe('maps.removeByValue', function () { it('removes a map entry identified by value', function () { return initState() .then(createRecord({ map: { a: 1, b: 2, c: 3 } })) .then(operate(maps.removeByValue('map', 2, maps.returnType.RANK))) .then(assertResultEql({ map: [1] })) .then(assertRecordEql({ map: { a: 1, c: 3 } })) .then(cleanup()) }) context('with nested map context', function () { helper.skipUnlessVersion('>= 4.6.0', this) it('removes a map entry identified by value', function () { return initState() .then(createRecord({ list: [{ a: 1, b: 2 }, { a: 1, b: 2 }, { a: 2, b: 3 }] })) .then(operate( maps .removeByValue('list', 2) .withContext(ctx => ctx.addListValue({ a: 1, b: 2 })) // matches only the first list value .andReturn(maps.returnType.RANK) )) .then(assertResultEql({ list: [1] })) .then(assertRecordEql({ list: [{ a: 1 }, { a: 1, b: 2 }, { a: 2, b: 3 }] })) .then(cleanup()) }) }) }) describe('maps.removeByValueList', function () { it('removes map entries identified by one or more values', function () { return initState() .then(createRecord({ map: { a: 1, b: 2, c: 3 } })) .then(operate(maps.removeByValueList('map', [1, 3], maps.returnType.RANK))) .then(assertResultEql({ map: [0, 2] })) .then(assertRecordEql({ map: { b: 2 } })) .then(cleanup()) }) it('skips non-existent values', function () { return initState() .then(createRecord({ map: { a: 1, b: 2, c: 3 } })) .then(operate(maps.removeByValueList('map', [1, 99, 3], maps.returnType.RANK))) .then(assertResultEql({ map: [0, 2] })) .then(assertRecordEql({ map: { b: 2 } })) .then(cleanup()) }) context('with nested map context', function () { helper.skipUnlessVersion('>= 4.6.0', this) it('removes map entries identified by one or more values', function () { return initState() .then(createRecord({ map: { a: { a: 1, b: 2, c: 3 }, b: { b: 2, c: 3, d: 4 } } })) .then(operate(maps.removeByValueList('map', [1, 3]).withContext(ctx => ctx.addMapKey('a')))) .then(assertRecordEql({ map: { a: { b: 2 }, b: { b: 2, c: 3, d: 4 } } })) .then(cleanup()) }) }) }) describe('maps.removeByValueRange', function () { it('removes map entries identified by value range', function () { return initState() .then(createRecord({ map: { a: 1, b: 2, c: 2, d: 3 } })) .then(operate(maps.removeByValueRange('map', 2, 3, maps.returnType.RANK))) .then(assertResultEql({ map: [1, 2] })) .then(assertRecordEql({ map: { a: 1, d: 3 } })) .then(cleanup()) }) it('removes all keys from the specified start value until the end', function () { return initState() .then(createRecord({ map: { a: 1, b: 2, c: 3 } })) .then(operate(maps.removeByValueRange('map', 2, null, maps.returnType.RANK))) .then(assertResultEql({ map: [1, 2] })) .then(assertRecordEql({ map: { a: 1 } })) .then(cleanup()) }) it('removes all keys from the start to the specified end value', function () { return initState() .then(createRecord({ map: { a: 1, b: 2, c: 3 } })) .then(operate(maps.removeByValueRange('map', null, 2, maps.returnType.RANK))) .then(assertResultEql({ map: [0] })) .then(assertRecordEql({ map: { b: 2, c: 3 } })) .then(cleanup()) }) context('with nested map context', function () { helper.skipUnlessVersion('>= 4.6.0', this) it('removes map entries identified by value range', function () { return initState() .then(createRecord({ list: [['a', 'b', 'c'], { a: 1, b: 2, c: 2, d: 3 }] })) .then(operate(maps.removeByValueRange('list', 2, 3).withContext(ctx => ctx.addListIndex(-1)))) .then(assertRecordEql({ list: [['a', 'b', 'c'], { a: 1, d: 3 }] })) .then(cleanup()) }) }) }) describe('maps.removeByValueRelRankRange', function () { helper.skipUnlessVersion('>= 4.3.0', this) context('with count', function () { it('removes map entries nearest to value and greater by relative rank', function () { return initState() .then(createRecord({ map: { e: 2, j: 10, f: 15, a: 17 } })) .then(orderByKeyValue('map')) .then(operate(maps.removeByValueRelRankRange('map', 11, 1, 1).andReturn(maps.returnType.KEY))) .then(assertResultEql({ map: ['a'] })) .then(assertRecordEql({ map: { e: 2, j: 10, f: 15 } })) .then(cleanup()) }) }) context('without count', function () { it('removes map entries nearest to value and greater by relative rank', function () { return initState() .then(createRecord({ map: { e: 2, j: 10, f: 15, a: 17 } })) .then(orderByKeyValue('map')) .then(operate(maps.removeByValueRelRankRange('map', 11, -1).andReturn(maps.returnType.KEY))) .then(assertResultEql({ map: ['j', 'f', 'a'] })) .then(assertRecordEql({ map: { e: 2 } })) .then(cleanup()) }) }) context('with nested map context', function () { helper.skipUnlessVersion('>= 4.6.0', this) it('removes map entries nearest to value and greater by relative rank', function () { const mapContext = new Context() .addListIndex(2) .addListIndex(1) return initState() .then(createRecord({ list: ['a', 'b', ['c', { e: 2, j: 10, f: 15, a: 17 }], 'd', 'e'] })) .then(orderByKeyValue('list', mapContext)) .then(operate(maps.removeByValueRelRankRange('list', 11, 1, 1).withContext(mapContext))) .then(assertRecordEql({ list: ['a', 'b', ['c', { e: 2, j: 10, f: 15 }], 'd', 'e'] })) .then(cleanup()) }) }) }) describe('maps.removeByIndex', function () { it('removes a map entry identified by index', function () { return initState() .then(createRecord({ map: { a: 1, b: 2, c: 3 } })) .then(operate(maps.removeByIndex('map', 1, maps.returnType.KEY))) .then(assertResultEql({ map: 'b' })) .then(assertRecordEql({ map: { a: 1, c: 3 } })) .then(cleanup()) }) context('with nested map context', function () { helper.skipUnlessVersion('>= 4.6.0', this) it('removes a map entry identified by index', function () { return initState() .then(createRecord({ map: { nested: { a: 1, b: 2, c: 3 } } })) .then(operate(maps.removeByIndex('map', 1).withContext(ctx => ctx.addMapValue({ a: 1, b: 2, c: 3 })))) .then(assertRecordEql({ map: { nested: { a: 1, c: 3 } } })) .then(cleanup()) }) }) }) describe('maps.removeByIndexRange', function () { it('removes map entries identified by index range', function () { return initState() .then(createRecord({ map: { a: 1, b: 2, c: 2, d: 3 } })) .then(operate(maps.removeByIndexRange('map', 1, 2, maps.returnType.KEY))) .then(assertResultEql({ map: ['b', 'c'] })) .then(assertRecordEql({ map: { a: 1, d: 3 } })) .then(cleanup()) }) it('removes all map entries starting at the specified index if count is null', function () { return initState() .then(createRecord({ map: { a: 1, b: 2, c: 3 } })) .then(operate(maps.removeByIndexRange('map', 1, null, maps.returnType.KEY))) .then(assertResultEql({ map: ['b', 'c'] })) .then(assertRecordEql({ map: { a: 1 } })) .then(cleanup()) }) it('removes all map entries starting at the specified index if count is undefined', function () { return initState() .then(createRecord({ map: { a: 1, b: 2, c: 3 } })) .then(operate(maps.removeByIndexRange('map', 1))) .then(assertResultEql({ map: null })) .then(assertRecordEql({ map: { a: 1 } })) .then(cleanup()) }) context('with nested map context', function () { helper.skipUnlessVersion('>= 4.6.0', this) it('removes map entries identified by index range', function () { return initState() .then(createRecord({ map: { nested: { a: 1, b: 2, c: 2, d: 3 } } })) .then(operate(maps.removeByIndexRange('map', 1, 2).withContext(ctx => ctx.addMapKey('nested')))) .then(assertRecordEql({ map: { nested: { a: 1, d: 3 } } })) .then(cleanup()) }) }) }) describe('maps.removeByRank', function () { it('removes a map entry identified by rank', function () { return initState() .then(createRecord({ map: { a: 3, b: 2, c: 1 } })) .then(operate(maps.removeByRank('map', 0, maps.returnType.VALUE))) .then(assertResultEql({ map: 1 })) .then(assertRecordEql({ map: { a: 3, b: 2 } })) .then(cleanup()) }) context('with nested map context', function () { helper.skipUnlessVersion('>= 4.6.0', this) it('removes a map entry identified by rank', function () { return initState() .then(createRecord({ list: [{ a: 3, b: 2, c: 1 }] })) .then(operate(maps.removeByRank('list', 0).withContext(ctx => ctx.addListIndex(0)))) .then(assertRecordEql({ list: [{ a: 3, b: 2 }] })) .then(cleanup()) }) }) }) describe('maps.removeByRankRange', function () { it('removes map entries identified by rank range', function () { return initState() .then(createRecord({ map: { a: 3, b: 2, c: 1 } })) .then(operate(maps.removeByRankRange('map', 0, 2, maps.returnType.VALUE))) .then(assertResultEql({ map: [1, 2] })) .then(assertRecordEql({ map: { a: 3 } })) .then(cleanup()) }) it('removes all map entries starting at the specified rank until the end', function () { return initState() .then(createRecord({ map: { a: 3, b: 2, c: 1 } })) .then(operate(maps.removeByRankRange('map', 1, null, maps.returnType.VALUE))) .then(assertResultEql({ map: [2, 3] })) .then(assertRecordEql({ map: { c: 1 } })) .then(cleanup()) }) context('with nested map context', function () { helper.skipUnlessVersion('>= 4.6.0', this) it('removes map entries identified by rank range', function () { return initState() .then(createRecord({ list: [{ a: 3, b: 2, c: 1 }] })) .then(operate(maps.removeByRankRange('list', 0, 2).withContext(ctx => ctx.addListIndex(-1)))) .then(assertRecordEql({ list: [{ a: 3 }] })) .then(cleanup()) }) }) }) describe('maps.size', function () { it('returns the size of the map', function () { return initState() .then(createRecord({ map: { a: 1, b: 2, c: 3 } })) .then(operate(maps.size('map'))) .then(assertResultEql({ map: 3 })) .then(cleanup()) }) it('returns zero if the map is empty', function () { return initState() .then(createRecord({ map: { } })) .then(operate(maps.size('map'))) .then(assertResultEql({ map: 0 })) .then(cleanup()) }) it('returns null if the map does not exist', function () { return initState() .then(createRecord({ i: 1 })) .then(operate(maps.size('map'))) .then(assertResultEql({ map: null })) .then(cleanup()) }) context('with nested map context', function () { helper.skipUnlessVersion('>= 4.6.0', this) it('returns the size of the map', function () { return initState() .then(createRecord({ map: { nested: { a: 1, b: 2, c: 3 } } })) .then(operate(maps.size('map').withContext(ctx => ctx.addMapKey('nested')))) .then(assertResultEql({ map: 3 })) .then(cleanup()) }) }) }) describe('maps.getByKey', function () { it('fetches a map entry identified by key', function () { return initState() .then(createRecord({ map: { a: 1, b: 2, c: 3 } })) .then(operate(maps.getByKey('map', 'b', maps.returnType.KEY_VALUE))) .then(assertResultEql({ map: ['b', 2] })) .then(cleanup()) }) it('does not fail if the key does not exist', function () { return initState() .then(createRecord({ map: { a: 1, b: 2, c: 3 } })) .then(operate(maps.getByKey('map', 'z', maps.returnType.KEY_VALUE))) .then(assertResultEql({ map: [] })) .then(cleanup()) }) context('with nested map context', function () { helper.skipUnlessVersion('>= 4.6.0', this) it('fetches a map entry identified by key', function () { return initState() .then(createRecord({ list: [{ a: 1, b: 2, c: 3 }, { b: 3 }] })) .then(operate( maps .getByKey('list', 'b') .withContext(ctx => ctx.addListIndex(0)) .andReturn(maps.returnType.KEY_VALUE) )) .then(assertResultEql({ list: ['b', 2] })) .then(cleanup()) }) }) }) describe('maps.getByKeyRange', function () { it('fetches map entries identified by key range', function () { return initState() .then(createRecord({ map: { a: 1, b: 2, c: 3, d: 4 } })) .then(orderByKey('map')) .then(operate(maps.getByKeyRange('map', 'b', 'd', maps.returnType.KEY))) .then(assertResultEql({ map: ['b', 'c'] })) .then(cleanup()) }) it('fetches all keys from the specified start key until the end', function () { return initState() .then(createRecord({ map: { a: 1, b: 2, c: 3, d: 4 } })) .then(orderByKey('map')) .then(operate(maps.getByKeyRange('map', 'b', null, maps.returnType.KEY))) .then(assertResultEql({ map: ['b', 'c', 'd'] })) .then(cleanup()) }) it('fetches all keys from the start to the specified end', function () { return initState() .then(createRecord({ map: { a: 1, b: 2, c: 3 } })) .then(operate(maps.getByKeyRange('map', null, 'b', maps.returnType.KEY))) .then(assertResultEql({ map: ['a'] })) .then(cleanup()) }) context('with nested map context', function () { helper.skipUnlessVersion('>= 4.6.0', this) it('fetches map entries identified by key range', function () { const mapContext = new Context().addListIndex(-1) return initState() .then(createRecord({ list: [{ b: 3, c: 4 }, { a: 1, b: 2, c: 3, d: 4 }] })) .then(orderByKey('list', mapContext)) .then(operate( maps .getByKeyRange('list', 'b', 'd') .withContext(mapContext) .andReturn(maps.returnType.KEY) )) .then(assertResultEql({ list: ['b', 'c'] })) .then(cleanup()) }) }) }) describe('maps.getByKeyRelIndexRange', function () { helper.skipUnlessVersion('>= 4.3.0', this) context('with count', function () { it('retrieves map entries nearest to key and greater, by index', function () { return initState() .then(createRecord({ map: { a: 17, e: 2, f: 15, j: 10 } })) .then(orderByKey('map')) .then(operate(maps.getByKeyRelIndexRange('map', 'f', 0, 1).andReturn(maps.returnType.KEY))) .then(assertResultEql({ map: ['f'] })) .then(cleanup()) }) }) context('without count', function () { it('retrieves map entries nearest to key and greater, by index', function () { return initState() .then(createRecord({ map: { a: 17, e: 2, f: 15, j: 10 } })) .then(orderByKey('map')) .then(operate(maps.getByKeyRelIndexRange('map', 'f', 0).andReturn(maps.returnType.KEY))) .then(assertResultEql({ map: ['f', 'j'] })) .then(cleanup()) }) }) context('with nested map context', function () { helper.skipUnlessVersion('>= 4.6.0', this) it('retrieves map entries nearest to key and greater, by index', function () { const mapContext = new Context().addListIndex(-1) return initState() .then(createRecord({ list: [{ a: 17, e: 2, f: 15, j: 10 }] })) .then(orderByKey('list', mapContext)) .then(operate( maps .getByKeyRelIndexRange('list', 'f', 0) .withContext(mapContext) .andReturn(maps.returnType.KEY) )) .then(assertResultEql({ list: ['f', 'j'] })) .then(cleanup()) }) }) }) describe('maps.getByValue', function () { it('fetches a map entry identified by value', function () { return initState() .then(createRecord({ map: { a: 1, b: 2, c: 3 } })) .then(operate(maps.getByValue('map', 2, maps.returnType.VALUE))) .then(assertResultEql({ map: [2] })) .then(cleanup()) }) context('with nested map context', function () { helper.skipUnlessVersion('>= 4.6.0', this) it('fetches a map entry identified by value', function () { return initState() .then(createRecord({ map: { nested: { a: 1, b: 2, c: 2 } } })) .then(operate( maps .getByValue('map', 2) .withContext(ctx => ctx.addMapKey('nested')) .andReturn(maps.returnType.KEY) )) .then(assertResultSatisfy(result => eql(result.map.sort(), ['b', 'c']))) .then(cleanup()) }) }) }) describe('maps.getByValueRange', function () { it('fetches map entries identified by value range', function () { return initState() .then(createRecord({ map: { a: 1, b: 2, c: 2, d: 3 } })) .then(operate(maps.getByValueRange('map', 2, 3, maps.returnType.VALUE))) .then(assertResultEql({ map: [2, 2] })) .then(cleanup()) }) it('fetches all values from the specified start value until the end', function () { return initState() .then(createRecord({ map: { a: 1, b: 2, c: 3 } })) .then(operate(maps.getByValueRange('map', 2, null, maps.returnType.VALUE))) .then(assertResultSatisfy(result => eql(result.map.sort(), [2, 3]))) .then(cleanup()) }) it('fetches all values from the start to the specified end', function () { return initState() .then(createRecord({ map: { a: 1, b: 2, c: 3 } })) .then(operate(maps.getByValueRange('map', null, 2, maps.returnType.VALUE))) .then(assertResultEql({ map: [1] })) .then(cleanup()) }) context('with nested map context', function () { helper.skipUnlessVersion('>= 4.6.0', this) it('fetches map entries identified by value range', function () { return initState() .then(createRecord({ list: [{ a: 2, b: 3, c: 4 }, { a: 1, b: 2, c: 2, d: 3 }] })) .then(operate( maps .getByValueRange('list', 2, 3) .withContext(ctx => ctx.addListIndex(1)) .andReturn(maps.returnType.VALUE) )) .then(assertResultEql({ list: [2, 2] })) .then(cleanup()) }) }) }) describe('maps.getByValueRelRankRange', function () { helper.skipUnlessVersion('>= 4.3.0', this) context('with count', function () { it('retrieves map entries nearest to value and greater by relative rank', function () { return initState() .then(createRecord({ map: { e: 2, j: 10, f: 15, a: 17 } })) .then(orderByKeyValue('map')) .then(operate(maps.getByValueRelRankRange('map', 11, 1, 1).andReturn(maps.returnType.KEY))) .then(assertResultEql({ map: ['a'] })) .then(cleanup()) }) }) context('without count', function () { it('retrieves map entries nearest to value and greater by relative rank', function () { return initState() .then(createRecord({ map: { e: 2, j: 10, f: 15, a: 17 } })) .then(orderByKeyValue('map')) .then(operate(maps.getByValueRelRankRange('map', 11, -1).andReturn(maps.returnType.KEY))) .then(assertResultEql({ map: ['j', 'f', 'a'] })) .then(cleanup()) }) }) context('with nested map context', function () { helper.skipUnlessVersion('>= 4.6.0', this) it('retrieves map entries nearest to value and greater by relative rank', function () { const mapContext = new Context().addMapKey('nested') return initState() .then(createRecord({ map: { nested: { e: 2, j: 10, f: 15, a: 17 } } })) .then(orderByKeyValue('map', mapContext)) .then(operate( maps .getByValueRelRankRange('map', 11, 1, 1) .withContext(mapContext) .andReturn(maps.returnType.KEY) )) .then(assertResultEql({ map: ['a'] })) .then(cleanup()) }) }) }) describe('maps.getByIndex', function () { it('fetches a map entry identified by index', function () { return initState() .then(createRecord({ map: { a: 1, b: 2, c: 3 } })) .then(operate(maps.getByIndex('map', 1, maps.returnType.KEY_VALUE))) .then(assertResultEql({ map: ['b', 2] })) .then(cleanup()) }) it('fetches a map entry identified by negative index', function () { return initState() .then(createRecord({ map: { a: 1, b: 2, c: 3 } })) .then(operate(maps.getByIndex('map', -1, maps.returnType.KEY_VALUE))) .then(assertResultEql({ map: ['c', 3] })) .then(cleanup()) }) context('with nested map context', function () { helper.skipUnlessVersion('>= 4.6.0', this) it('fetches a map entry identified by index', function () { return initState() .then(createRecord({ list: [1, 2, 3, 4, { a: 1, b: 2, c: 3 }] })) .then(operate( maps .getByIndex('list', 1) .withContext(ctx => ctx.addListIndex(4)) .andReturn(maps.returnType.KEY_VALUE) )) .then(assertResultEql({ list: ['b', 2] })) .then(cleanup()) }) }) }) describe('maps.getByIndexRange', function () { it('fetches map entries identified by index range', function () { return initState() .then(createRecord({ map: { a: 1, b: 2, c: 2, d: 3 } })) .then(operate(maps.getByIndexRange('map', 1, 2, maps.returnType.KEY_VALUE))) .then(assertResultEql({ map: ['b', 2, 'c', 2] })) .then(cleanup()) }) it('fetches map entries identified by negative index range', function () { return initState() .then(createRecord({ map: { a: 1, b: 2, c: 2, d: 3 } })) .then(operate(maps.getByIndexRange('map', -2, 2, maps.returnType.KEY_VALUE))) .then(assertResultEql({ map: ['c', 2, 'd', 3] })) .then(cleanup()) }) it('fetches all map entries starting from the specified index until the end', function () { return initState() .then(createRecord({ map: { a: 1, b: 2, c: 3 } })) .then(operate(maps.getByIndexRange('map', 1, null, maps.returnType.KEY_VALUE))) .then(assertResultEql({ map: ['b', 2, 'c', 3] })) .then(cleanup()) }) context('with nested map context', function () { helper.skipUnlessVersion('>= 4.6.0', this) it('fetches map entries identified by index range', function () { return initState() .then(createRecord({ list: [{ a: 1, b: 2, c: 2, d: 3 }] })) .then(operate( maps .getByIndexRange('list', 1, 2) .withContext(ctx => ctx.addListIndex(0)) .andReturn(maps.returnType.KEY_VALUE) )) .then(assertResultEql({ list: ['b', 2, 'c', 2] })) .then(cleanup()) }) }) }) describe('maps.getByRank', function () { it('fetches a map entry identified by rank', function () { return initState() .then(createRecord({ map: { a: 3, b: 2, c: 1 } })) .then(operate(maps.getByRank('map', 0, maps.returnType.VALUE))) .then(assertResultEql({ map: 1 })) .then(cleanup()) }) it('fetches a map entry identified by negative rank', function () { return initState() .then(createRecord({ map: { a: 3, b: 2, c: 1 } })) .then(operate(maps.getByRank('map', -1, maps.returnType.VALUE))) .then(assertResultEql({ map: 3 })) .then(cleanup()) }) context('with nested map context', function () { helper.skipUnlessVersion('>= 4.6.0', this) it('fetches a map entry identified by rank', function () { return initState() .then(createRecord({ map: { nested: { a: 3, b: 2, c: 1 } } })) .then(operate( maps .getByRank('map', 0) .withContext(ctx => ctx.addMapKey('nested')) .andReturn(maps.returnType.VALUE) )) .then(assertResultEql({ map: 1 })) .then(cleanup()) }) }) }) describe('maps.getByRankRange', function () { it('fetches map entries identified by rank range', function () { return initState() .then(createRecord({ map: { a: 3, b: 2, c: 1 } })) .then(operate(maps.getByRankRange('map', 0, 2, maps.returnType.VALUE))) .then(assertResultEql({ map: [1, 2] })) .then(cleanup()) }) it('fetches map entries identified by negative rank range', function () { return initState() .then(createRecord({ map: { a: 3, b: 2, c: 1 } })) .then(operate(maps.getByRankRange('map', -2, 2, maps.returnType.VALUE))) .then(assertResultEql({ map: [2, 3] })) .then(cleanup()) }) it('fetches all map entries starting at the specified rank until the end', function () { return initState() .then(createRecord({ map: { a: 3, b: 2, c: 1 } })) .then(operate(maps.getByRankRange('map', 1, null, maps.returnType.VALUE))) .then(assertResultEql({ map: [2, 3] })) .then(cleanup()) }) context('with nested map context', function () { helper.skipUnlessVersion('>= 4.6.0', this) it('fetches map entries identified by rank range', function () { return initState() .then(createRecord({ list: [{ a: 3, b: 2, c: 1 }] })) .then(operate( maps .getByRankRange('list', 0, 2) .withContext(ctx => ctx.addListIndex(0)) .andReturn(maps.returnType.VALUE)