Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
92 changes: 92 additions & 0 deletions documentation/asciidoc/topics/code_examples/multimap-basic.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
var infinispan = require('infinispan');

var connected = infinispan.client(
{port: 11222, host: '127.0.0.1'},
{
authentication: {
enabled: true,
saslMechanism: 'SCRAM-SHA-256',
userName: 'admin',
password: 'changeme'
}
}
);

connected.then(function (client) {

// Store multiple values under the same key.
var put = client.multimapPut('colors', 'red');
put = put.then(function() {
return client.multimapPut('colors', 'green');
});
put = put.then(function() {
return client.multimapPut('colors', 'blue');
});

// Retrieve all values for a key.
var get = put.then(function() {
return client.multimapGet('colors');
});
get.then(function(values) {
console.log('Colors: ' + values); // ['red', 'green', 'blue']
});

// Check if a key exists in the multimap.
var containsKey = get.then(function() {
return client.multimapContainsKey('colors');
});
containsKey.then(function(exists) {
console.log('Contains key: ' + exists); // true
});

// Check if a specific value exists anywhere in the multimap.
var containsValue = containsKey.then(function() {
return client.multimapContainsValue('red');
});
containsValue.then(function(exists) {
console.log('Contains value: ' + exists); // true
});

// Check if a specific key-value pair exists.
var containsEntry = containsValue.then(function() {
return client.multimapContainsEntry('colors', 'green');
});
containsEntry.then(function(exists) {
console.log('Contains entry: ' + exists); // true
});

// Get the total number of entries across all keys.
var size = containsEntry.then(function() {
return client.multimapSize();
});
size.then(function(count) {
console.log('Total entries: ' + count); // 3
});

// Remove a single value from a key.
var removeEntry = size.then(function() {
return client.multimapRemoveEntry('colors', 'red');
});
removeEntry.then(function(removed) {
console.log('Removed entry: ' + removed); // true
});

// Remove all values for a key.
var removeKey = removeEntry.then(function() {
return client.multimapRemoveKey('colors');
});
removeKey.then(function(removed) {
console.log('Removed key: ' + removed); // true
});

// Disconnect from {brandname} Server.
return removeKey.then(function() {
return client.disconnect();
});

}).catch(function(error) {

// Log any errors.
console.log("Got error: " + error.message);

});
40 changes: 40 additions & 0 deletions documentation/asciidoc/topics/ref_client_usage.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,46 @@ include::code_examples/distributed-counters.js[]
|Removes the counter from the cluster.
|===

== Working with multimaps

Multimaps allow you to associate multiple values with a single key.
The {hr_js} client uses Set semantics by default, which means duplicate values for the same key are not stored.

[source,javascript,options="nowrap",subs=attributes+]
----
include::code_examples/multimap-basic.js[]
----

.Available multimap operations
[cols="1,3",options="header"]
|===
|Method |Description

|`multimapPut(key, value)`
|Adds a value to the collection for the given key.

|`multimapGet(key)`
|Returns an array of all values for the given key, or an empty array if the key does not exist.

|`multimapContainsKey(key)`
|Returns `true` if the key exists in the multimap.

|`multimapContainsValue(value)`
|Returns `true` if the value exists for any key in the multimap.

|`multimapContainsEntry(key, value)`
|Returns `true` if the key-value pair exists in the multimap.

|`multimapSize()`
|Returns the total number of entries across all keys.

|`multimapRemoveEntry(key, value)`
|Removes a single value from the given key. Returns `true` if the entry existed.

|`multimapRemoveKey(key)`
|Removes all values for the given key. Returns `true` if the key existed.
|===

== Working with queries

Use the `query` method to perform queries on your caches.
Expand Down
116 changes: 116 additions & 0 deletions lib/infinispan.js
Original file line number Diff line number Diff line change
Expand Up @@ -1096,6 +1096,122 @@
logger.debugf('Invoke counterGetAndSet(msgId=%d,name=%s,value=%d)', ctx.id, name, value);
return future(ctx, 0x7F, p.encodeCounterNameValue(name, value), p.decodeCounterValue);
},
/**
* Get all values associated with a key in the multimap.
*
* @param {(String|Object)} k Key to retrieve.
* @returns {Promise.<Array>}
* A promise that will be completed with an array of values,
* or an empty array if the key does not exist.
* @memberof Client#
* @since 0.16
*/
multimapGet: function(k) {
var ctx = transport.context(SMALL);
logger.debugf('Invoke multimapGet(msgId=%d,key=%s)', ctx.id, u.str(k));
return futureKey(ctx, 0x67, k, p.encodeMultimapKey(k), p.decodeMultimapCollection());
},
/**
* Add a value to the collection associated with a key in the multimap.
*
* @param {(String|Object)} k Key.
* @param {(String|Object)} v Value to add.
* @param {Object=} opts Optional store options (lifespan, maxIdle).
* @returns {Promise}
* A promise that will be completed when the value has been added.
* @memberof Client#
* @since 0.16
*/
multimapPut: function(k, v, opts) {
var ctx = transport.context(MEDIUM);
logger.debugf('Invoke multimapPut(msgId=%d,key=%s,value=%s)', ctx.id, u.str(k), u.str(v));
return futureKey(ctx, 0x6B, k, p.encodeMultimapPut(k, v), p.complete(_.constant(undefined)), opts);
},
/**
* Remove all values associated with a key in the multimap.
*
* @param {(String|Object)} k Key to remove.
* @returns {Promise.<Boolean>}
* A promise that will be completed with true if the key existed.
* @memberof Client#
* @since 0.16
*/
multimapRemoveKey: function(k) {
var ctx = transport.context(SMALL);
logger.debugf('Invoke multimapRemoveKey(msgId=%d,key=%s)', ctx.id, u.str(k));
return futureKey(ctx, 0x6D, k, p.encodeMultimapKey(k), p.decodeMultimapBoolean);
},
/**
* Remove a specific value from the collection associated with a key.
*
* @param {(String|Object)} k Key.
* @param {(String|Object)} v Value to remove.
* @returns {Promise.<Boolean>}
* A promise that will be completed with true if the entry existed.
* @memberof Client#
* @since 0.16
*/
multimapRemoveEntry: function(k, v) {
var ctx = transport.context(SMALL);
logger.debugf('Invoke multimapRemoveEntry(msgId=%d,key=%s,value=%s)', ctx.id, u.str(k), u.str(v));
return futureKey(ctx, 0x6F, k, p.encodeMultimapKeyValue(k, v), p.decodeMultimapBoolean);
},
/**
* Get the total number of key-value pairs in the multimap.
*
* @returns {Promise.<Number>}
* A promise that will be completed with the total size.
* @memberof Client#
* @since 0.16
*/
multimapSize: function() {
var ctx = transport.context(SMALL);
logger.debugf('Invoke multimapSize(msgId=%d)', ctx.id);
return future(ctx, 0x71, p.encodeMultimapSupportsDuplicates(), p.decodeMultimapSize);
},
/**
* Check whether a specific key-value pair exists in the multimap.
*
* @param {(String|Object)} k Key.
* @param {(String|Object)} v Value.
* @returns {Promise.<Boolean>}
* A promise that will be completed with true if the entry exists.
* @memberof Client#
* @since 0.16
*/
multimapContainsEntry: function(k, v) {
var ctx = transport.context(SMALL);
logger.debugf('Invoke multimapContainsEntry(msgId=%d,key=%s,value=%s)', ctx.id, u.str(k), u.str(v));
return futureKey(ctx, 0x73, k, p.encodeMultimapKeyValue(k, v), p.decodeMultimapBoolean);
},
/**
* Check whether a key exists in the multimap.
*
* @param {(String|Object)} k Key to check.
* @returns {Promise.<Boolean>}
* A promise that will be completed with true if the key exists.
* @memberof Client#
* @since 0.16
*/
multimapContainsKey: function(k) {
var ctx = transport.context(SMALL);
logger.debugf('Invoke multimapContainsKey(msgId=%d,key=%s)', ctx.id, u.str(k));
return futureKey(ctx, 0x75, k, p.encodeMultimapKey(k), p.decodeMultimapBoolean);
},
/**
* Check whether any key contains the given value in the multimap.
*
* @param {(String|Object)} v Value to check.
* @returns {Promise.<Boolean>}
* A promise that will be completed with true if any key contains the value.
* @memberof Client#
* @since 0.16
*/
multimapContainsValue: function(v) {
var ctx = transport.context(SMALL);
logger.debugf('Invoke multimapContainsValue(msgId=%d,value=%s)', ctx.id, u.str(v));
return future(ctx, 0x77, p.encodeMultimapValue(v), p.decodeMultimapBoolean);
},
/**
* Get server topology related information.
*
Expand Down
78 changes: 78 additions & 0 deletions lib/protocols.js
Original file line number Diff line number Diff line change
Expand Up @@ -1323,6 +1323,81 @@
};
}());

var MultimapMixin = (function() {
var SUPPORTS_DUPLICATES = codec.encodeUByte(0x00);

return {
encodeMultimapKey: function(k) {
var outer = this;
return function() {
return [outer.encodeMediaKey(k), SUPPORTS_DUPLICATES];
};
},
encodeMultimapPut: function(k, v) {
var outer = this;
return function(opts) {
return f.cat(
[outer.encodeMediaKey(k)],
outer.encodeExpiry(opts),
[outer.encodeMediaValue(v), SUPPORTS_DUPLICATES]
);
};
},
encodeMultimapKeyValue: function(k, v) {
var outer = this;
return function() {
return f.cat(
[outer.encodeMediaKey(k)],
[codec.encodeUByte(0x77)],
[outer.encodeMediaValue(v), SUPPORTS_DUPLICATES]
);
};
},
encodeMultimapValue: function(v) {
var outer = this;
return function() {
return [codec.encodeUByte(0x77), outer.encodeMediaValue(v), SUPPORTS_DUPLICATES];
};
},
encodeMultimapSupportsDuplicates: function() {
return function() {
return [SUPPORTS_DUPLICATES];
};
},
decodeMultimapCollection: function() {
var decoderValue = decoderMedia(this.valueMediaType);
var decodeSingleValue = decodeSingle(decoderValue);
return function(header, bytebuf) {
if (header.status !== 0x00 && header.status !== 0x03)
return {result: [], continue: true};
var count = DECODE_VINT(bytebuf);
if (!f.existy(count))
return {continue: false};
var values = [];
for (var i = 0; i < count; i++) {
var v = decodeSingleValue(bytebuf);
if (!f.existy(v))
return {continue: false};
values.push(v);
}
return {result: values, continue: true};
};
},
decodeMultimapBoolean: function(header, bytebuf) {
var b = DECODE_UBYTE(bytebuf);
if (!f.existy(b))
return {continue: false};
return {result: b !== 0, continue: true};
},
decodeMultimapSize: function(header, bytebuf) {
var size = f.actions([codec.decodeVLong()], codec.lastDecoded)(bytebuf);
if (!f.existy(size) && size !== 0)
return {continue: false};
return {result: size, continue: true};
}
};
}());

/**
* Protocol 4.0+ requires an 'otherParams' map after media types in the header.
* This mixin overrides stepsHeader to append the count (0 = no params).
Expand Down Expand Up @@ -1595,6 +1670,7 @@
, SASLMixin
, Ping30Mixin
, CounterMixin
, MultimapMixin
, ProtostreamType
, ProtobufRoot
, TransactionMixin
Expand All @@ -1614,6 +1690,7 @@
, SASLMixin
, Ping30Mixin
, CounterMixin
, MultimapMixin
, ProtostreamType
, ProtobufRoot
, TransactionMixin
Expand All @@ -1633,6 +1710,7 @@
, SASLMixin
, Ping30Mixin
, CounterMixin
, MultimapMixin
, ProtostreamType
, ProtobufRoot
, TransactionMixin
Expand Down
Loading
Loading