Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Set Datatype #3669

Merged
merged 48 commits into from
Apr 19, 2021
Merged
Show file tree
Hide file tree
Changes from 34 commits
Commits
Show all changes
48 commits
Select commit Hold shift + click to select a range
b1607b1
Set datatype implementation. Initial work
Mar 29, 2021
4ee3c54
Updated to newer 'Stitch' Docker image
Mar 30, 2021
796f559
Updated to newer 'Stitch' Docker image
Mar 30, 2021
535d662
Merge branch 'ff/new-set-datatype' of github.com:realm/realm-js into …
Mar 30, 2021
9b25bbc
Fixed tslint inclusion error
Mar 30, 2021
f184b04
Set Tests WiP
Apr 2, 2021
ecfe0f7
Fixed return value in .get(). Disabled linked object tests awaiting …
Apr 2, 2021
fa2449e
Add listener methods. Documentation. Minor cleanup.
Apr 6, 2021
3ed9b64
Added UUID selector to schema parsing
Apr 6, 2021
ad71162
Update tests/js/set-tests.js
Apr 7, 2021
df46206
Update tests/js/set-tests.js
Apr 7, 2021
ba726a3
Update tests/js/set-tests.js
Apr 7, 2021
2505340
Cleanup tests according to PR comments
Apr 7, 2021
b22457e
Fixed merge conflict
Apr 7, 2021
d592a61
Tests cleanup
Apr 7, 2021
be3830c
Moved Set's property handling when parsing schemas into js_set.hpp
Apr 7, 2021
b8a8e82
Added exception documentation for derive_property_type
Apr 7, 2021
b1f9ca1
Addressing review comments. Minor cleanups
Apr 8, 2021
924f515
Fixed formatting for Sets
Apr 8, 2021
429be82
Removed property on Set. Obfuscated function. *Note*: Unit tests…
Apr 9, 2021
8a8a1c8
Added test of aggregate functions. Genereal cleanup in tests.
Apr 9, 2021
0339258
Added Set iteration methods. Removed Set's index-based accessor by a…
Apr 9, 2021
9825e19
Added iteration and serialization unit tests
Apr 9, 2021
5b4c781
Fixed test TODOs
Apr 13, 2021
651620f
Cleaned up the Set class
Apr 13, 2021
2c499bf
Added TS and JSDoc documentation
Apr 13, 2021
27b999a
Minor typos fixed
Apr 13, 2021
f5e0dd8
Merge branch 'develop' into ff/new-set-datatype
Apr 13, 2021
03af89a
Updated changelog
Apr 13, 2021
0e60c64
Attempted fix for Android build error
Apr 13, 2021
4fbb64b
Inlined derive_property_type on Set to avoid double linkage.
Apr 13, 2021
f0a1781
Updated JSDoc
Apr 13, 2021
60e2873
Cleaned up set method exports
Apr 14, 2021
71e30f2
Added explanatory comment to Set docs
Apr 14, 2021
751b3b6
Update tests/js/set-sync-tests.js
Apr 14, 2021
e2d05aa
Added database cleanup in testSetSyncedDownstream
Apr 15, 2021
c8e68f4
Merge branch 'ff/new-set-datatype' of github.com:realm/realm-js into …
Apr 15, 2021
0248330
Backed out changes to Realm App Importer
Apr 15, 2021
b43c72b
Simplified syntax in set-tests.js
Apr 15, 2021
0b599fc
Fixed typo
Apr 15, 2021
e0c3f2a
Cleaned up tests
Apr 15, 2021
b7b55c6
Merged from develop
Apr 15, 2021
e88bb2e
Removed obsolete config file
Apr 15, 2021
3646046
Fixed sync tests for Set
Apr 16, 2021
bfa90ad
Fixed conflict markers
Apr 16, 2021
459dfe2
Disabled Set's aggregate functions tests on node
Apr 16, 2021
d741d44
Disabled Set's aggregate functions tests on non-node environments
Apr 16, 2021
2fc861a
Merge branch 'ff/new-set-datatype' of github.com:realm/realm-js into …
Apr 16, 2021
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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ NOTE: Bump file format version to 21. NO DOWNGRADE PATH IS AVAILABLE.

### Enhancements
* Adding Mixed types. ([#3389](https://github.com/realm/realm-js/issues/3389))
* Added Set type ([#3378](https://github.com/realm/realm-js/issues/3378)).
* Array of primitive lists will not be `snapshot()`'ed.
* Added `ssl` option to `Realm.App.Sync` configuration.

Expand Down
2 changes: 1 addition & 1 deletion dependencies.list
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,4 @@ REALM_CORE_VERSION=10.1.4
REALM_SYNC_VERSION=10.1.6
NAPI_VERSION=4
OPENSSL_VERSION=1.1.1g
MDBREALM_TEST_SERVER_TAG=2021-02-10
MDBREALM_TEST_SERVER_TAG=2021-03-28
30 changes: 19 additions & 11 deletions docs/realm.js
Original file line number Diff line number Diff line change
Expand Up @@ -425,6 +425,9 @@ class Realm {
* linkToObject: 'MyClass',
* listOfObjects: 'MyClass[]', // or {type: 'list', objectType: 'MyClass'}
* objectsLinkingToThisObject: {type: 'linkingObjects', objectType: 'MyClass', property: 'linkToObject'}
*
* setOfStrings: 'string<>',
* setOfOptionalStrings: 'string?<>', // or {type: 'set', objectType: 'string'}
* }
* };
*/
Expand All @@ -433,16 +436,16 @@ class Realm {
* @typedef Realm~ObjectSchemaProperty
* @type {Object}
* @property {Realm~PropertyType} type - The type of this property.
* @property {Realm~PropertyType} [objectType] - **Required** when `type` is `"list"` or `"linkingObjects"`,
* and must match the type of an object in the same schema, or, for `"list"`
* only, any other type which may be stored as a Realm property.
* @property {Realm~PropertyType} [objectType] - **Required** when `type` is `"list"`, `"set"` or `"linkingObjects"`,
* and must match the type of an object in the same schema, or, for `"list"` or `"set"`,
* other type which may be stored as a Realm property.
* @property {string} [property] - **Required** when `type` is `"linkingObjects"`, and must match
* the name of a property on the type specified in `objectType` that links to the type this property belongs to.
* @property {any} [default] - The default value for this property on creation when not
* otherwise specified.
* @property {boolean} [optional] - Signals if this property may be assigned `null` or `undefined`.
* For `"list"` properties of non-object types, this instead signals whether the values inside the list may be assigned `null` or `undefined`.
* This is not supported for `"list"` properties of object types and `"linkingObjects"` properties.
* For `"list"` or `"set"` properties of non-object types, this instead signals whether the values inside the list may be assigned `null` or `undefined`.
* This is not supported for `"list"` or `"set"` properties of object types and `"linkingObjects"` properties.
* @property {boolean} [indexed] - Signals if this property should be indexed. Only supported for
* `"string"`, `"int"`, and `"bool"` properties.
* @property {string} [mapTo] - Set this to the name of the underlying property in the Realm file if the Javascript property
Expand All @@ -465,15 +468,18 @@ class Realm {
*
* When specifying property types in an {@linkplain Realm~ObjectSchema object schema}, you
* may append `?` to any of the property types to indicate that it is optional
* (i.e. it can be `null` in addition to the normal values) and `[]` to
* indicate that it is instead a list of that type. For example,
* `optionalIntList: 'int?[]'` would declare a property which is a list of
* nullable integers. The property types reported by {@linkplain Realm.Collection
* collections} and in a Realm's schema will never
* (i.e. it can be `null` in addition to the normal values).
* Given a type, _T_, the following postfix operators may be used:
* * _T_ `[]` indicates that the property is a {@linkplain Realm.List} of values with of type _T_
* * _T_ `<>` indicated that the property is a {@linkplain Realm.Set} of values with type _T_
*
* For example, `optionalIntList: 'int?[]'` declares a property which is a list of
* nullable integers, while `optionalStringSet: 'string?<>'` declares a set of nullable strings.
* The property types reported by {@linkplain Realm.Collection collections} and in a Realm's schema will never
* use these forms.
*
* @typedef Realm~PropertyType
* @type {("bool"|"int"|"float"|"double"|"string"|"decimal128"|"objectId"|"date"|"data"|"list"|"linkingObjects"|"<ObjectType>")}
* @type {("bool"|"int"|"float"|"double"|"string"|"decimal128"|"objectId"|"date"|"data"|"list"|"set"|"linkingObjects"|"<ObjectType>")}
*
* @property {Mixed} "mixed" - Property value that allow any of the following types (`"bool","int","float","double","string","decimal128","objectId","date","data"`), this type is nullable by default.
* @property {boolean} "bool" - Property value may either be `true` or `false`.
Expand All @@ -493,6 +499,8 @@ class Realm {
* @property {Realm.List} "list" - Property may be assigned any ordered collection
* (e.g. `Array`, {@link Realm.List}, {@link Realm.Results}) of objects all matching the
* `objectType` specified in the {@link Realm~ObjectSchemaProperty ObjectSchemaProperty}.
* @property {Realm.Set} "set" - Prpperty may be assigned an array (e.g., `Array`) of objects all matching the
* `objectType` specified in the {@link Realm~ObjectSchemaProperty ObjectSchemaProperty}.
* @property {Realm.Results} "linkingObjects" - Property is read-only and always returns a {@link Realm.Results}
* of all the objects matching the `objectType` that are linking to the current object
* through the `property` relationship specified in {@link Realm~ObjectSchemaProperty ObjectSchemaProperty}.
Expand Down
77 changes: 77 additions & 0 deletions docs/set.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
////////////////////////////////////////////////////////////////////////////
//
// Copyright 2021 Realm 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.
//
////////////////////////////////////////////////////////////////////////////

/**
* Instances of this class will be returned when accessing object properties whose type is `"Set"`
* (see {@linkplain Realm~ObjectSchemaProperty ObjectSchemaProperty}).
*

* Sets mostly behave like normal JavaScript Sets, with a few exceptions:
* They can only store values of a single type (indicated by the `type`
* and `optional` properties of the Set).
* They can only be modified inside a {@linkplain Realm#write write} transaction.
* Unlike JavaScript's Set, Realm~Set does NOT make any guarantees about the
* traversal order of `values()`, `entries()`, `keys()`, or `forEach` iterations.
* If values in a Set are required to have some order, it must be implemented
* by the developer by, for example, wrapping values in an object that holds
* a user-supplied insertion order.
*
* @extends Realm.Collection
* @memberof Realm
*/
class Set extends Collection {
/**
* Remove a value from the Set
* @param {T} value Value to delete from the Set
* @throws {Error} If not inside a write transaction.
* @returns {boolean}: true if the value existed in the Set, false otherwise
*/
delete(value) { }

/**
* Remove all values from the Set
* @throws {Error} If not inside a write transaction.
* @returns {void}
*/
clear() { }

/**
* Add a value to the Set
*
* @param {T} value Value to add to the Set
* @throws {TypeError} If a `value` is not of a type which can be stored in
* the Set, or if an object being added to the Set does not match the
* {@linkcode Realm~ObjectSchema object schema} for the Set.
*
* @throws {Error} If not inside a write transaction.
* @returns {Realm.Set}: The Set itself, after adding the element
*/
add(value) { }

/**
* Check for the existence of a value in the Set
*
* @param {T} value Value to to search for in the Set
* @throws {TypeError} If a `value` is not of a type which can be stored in
* the Set, or if an object being added to the Set does not match the
* {@linkcode Realm~ObjectSchema object schema} for the Set.
*
* @returns {boolean}: True if the value exists in the Set, false otherwise
*/
has(value) { }
}
14 changes: 10 additions & 4 deletions lib/collection-methods.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
////////////////////////////////////////////////////////////////////////////
//
// Copyright 2016 Realm Inc.
// Copyright 2021 Realm Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -55,7 +55,8 @@ Object.defineProperty(iteratorPrototype, Symbol.iterator, {

['entries', 'keys', 'values'].forEach(function(methodName) {
var method = function() {
var self = (this.type === "object") ? this.snapshot() : this;
const isSet = this instanceof Realm.Set;
var self = (this.type === "object" || isSet) ? this.snapshot() : this;
var index = 0;

return Object.create(iteratorPrototype, {
Expand All @@ -69,13 +70,18 @@ Object.defineProperty(iteratorPrototype, Symbol.iterator, {
var value;
switch (methodName) {
case 'entries':
value = [index, self[index]];
value = isSet ?
[self[index], self[index]]
: [index, self[index]];
break;
case 'keys':
value = index;
value = isSet ?
undefined
: index;
break;
default:
value = self[index];
break;
}

index++;
Expand Down
5 changes: 4 additions & 1 deletion lib/extensions.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
////////////////////////////////////////////////////////////////////////////
//
// Copyright 2016 Realm Inc.
// Copyright 2021 Realm Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -66,11 +66,13 @@ function openLocalRealm(realmConstructor, config) {
module.exports = function(realmConstructor, environment) {
// Add the specified Array methods to the Collection prototype.
Object.defineProperties(realmConstructor.Collection.prototype, require('./collection-methods'));
Object.defineProperties(realmConstructor.Set.prototype, require('./set-methods')(realmConstructor));

setConstructorOnPrototype(realmConstructor.Collection);
setConstructorOnPrototype(realmConstructor.List);
setConstructorOnPrototype(realmConstructor.Results);
setConstructorOnPrototype(realmConstructor.Object);
setConstructorOnPrototype(realmConstructor.Set);

realmConstructor.BSON = require('bson');
realmConstructor._Decimal128 = realmConstructor.BSON.Decimal128;
Expand All @@ -90,6 +92,7 @@ module.exports = function(realmConstructor, environment) {
enumerable: false
});


const getInternalCacheId = (realmObj) => {
const { name, primaryKey } = realmObj.objectSchema();
const id = primaryKey ? realmObj[primaryKey] : realmObj._objectId();
Expand Down
37 changes: 37 additions & 0 deletions lib/set-methods.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
////////////////////////////////////////////////////////////////////////////
//
// Copyright 2021 Realm 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';

module.exports = function(realmConstructor) {
var forEachMethod = function(callback) {
const elements = Array.from(this.values());
elements.forEach(element => callback(element));
};

var toJSONMethod = function(_, cache = new Map()) {
const elementArray = Array.from(this.values());
return elementArray.map((item, index) =>
item instanceof realmConstructor.Object ? item.toJSON(index.toString(), cache) : item);
};

return {
forEach: {value: forEachMethod, configurable: true, writable: true},
toJSON: {value: toJSONMethod, configurable: true, writable: true}
};
}
2 changes: 1 addition & 1 deletion packages/realm-app-importer/src/RealmAppImporter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ export class RealmAppImporter {
}

private loadStichJson(appTemplatePath: string) {
const stitchJsonPath = path.resolve(appTemplatePath, "stitch.json");
const stitchJsonPath = path.resolve(appTemplatePath, "config.json");
return this.loadJson(stitchJsonPath);
}

Expand Down
11 changes: 8 additions & 3 deletions src/js_object_accessor.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@

#include "js_mixed.hpp"
#include "js_list.hpp"
#include "js_set.hpp"
#include "js_realm_object.hpp"
#include "js_schema.hpp"

Expand Down Expand Up @@ -165,7 +166,7 @@ class NativeAccessor {
return ResultsClass<JSEngine>::create_instance(m_ctx, std::move(results));
}
ValueType box(realm::object_store::Set set) {
throw std::runtime_error("'Set' type support is not implemented yet");
return SetClass<JSEngine>::create_instance(m_ctx, std::move(set));
}
ValueType box(realm::object_store::Dictionary dictionart) {
throw std::runtime_error("'Dictionary' type support is not implemented yet");
Expand Down Expand Up @@ -233,8 +234,12 @@ class NativeAccessor {
return false;
}

bool is_same_set(realm::object_store::Set const& set, ValueType const& value) const {
throw std::runtime_error("'Set' type support is not implemented yet");
bool is_same_set(realm::object_store::Set const &set, ValueType const &value) const {
auto object = Value::validated_to_object(m_ctx, value);
if (js::Object<JSEngine>::template is_instance<SetClass<JSEngine>>(m_ctx, object)) {
return set == *get_internal<JSEngine, SetClass<JSEngine>>(m_ctx, object);
}
return false;
}

bool is_same_dictionary(realm::object_store::Dictionary const& dictionary, ValueType const& value) const {
Expand Down
3 changes: 3 additions & 0 deletions src/js_realm.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
#include "js_util.hpp"
#include "js_realm_object.hpp"
#include "js_list.hpp"
#include "js_set.hpp"
#include "js_results.hpp"
#include "js_schema.hpp"
#include "js_observable.hpp"
Expand Down Expand Up @@ -481,12 +482,14 @@ inline typename T::Function RealmClass<T>::create_constructor(ContextType ctx) {
FunctionType realm_constructor = ObjectWrap<T, RealmClass<T>>::create_constructor(ctx);
FunctionType collection_constructor = ObjectWrap<T, CollectionClass<T>>::create_constructor(ctx);
FunctionType list_constructor = ObjectWrap<T, ListClass<T>>::create_constructor(ctx);
FunctionType set_constructor = ObjectWrap<T, SetClass<T>>::create_constructor(ctx);
FunctionType realm_object_constructor = ObjectWrap<T, RealmObjectClass<T>>::create_constructor(ctx);
FunctionType results_constructor = ObjectWrap<T, ResultsClass<T>>::create_constructor(ctx);

PropertyAttributes attributes = ReadOnly | DontEnum | DontDelete;
Object::set_property(ctx, realm_constructor, "Collection", collection_constructor, attributes);
Object::set_property(ctx, realm_constructor, "List", list_constructor, attributes);
Object::set_property(ctx, realm_constructor, "Set", set_constructor, attributes);
Object::set_property(ctx, realm_constructor, "Results", results_constructor, attributes);
Object::set_property(ctx, realm_constructor, "Object", realm_object_constructor, attributes);

Expand Down
15 changes: 12 additions & 3 deletions src/js_schema.hpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
////////////////////////////////////////////////////////////////////////////
//
// Copyright 2016 Realm Inc.
// Copyright 2021 Realm Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
Expand All @@ -19,7 +19,6 @@
#pragma once

#include <map>

#include "js_types.hpp"
#include <realm/object-store/schema.hpp>

Expand Down Expand Up @@ -80,6 +79,11 @@ static inline void parse_property_type(StringData object_name, Property& prop, S
type = type.substr(0, type.size() - 2);
}

if (type.ends_with("<>")) {
prop.type |= PropertyType::Set;
type = type.substr(0, type.size() - 2);
}

if (type.ends_with("?")) {
prop.type |= PropertyType::Nullable;
type = type.substr(0, type.size() - 1);
Expand Down Expand Up @@ -159,6 +163,7 @@ static inline void parse_property_type(StringData object_name, Property& prop, S
prop.type |= PropertyType::UUID | PropertyType::Array;
prop.object_type = "";
}

else {
if (is_nullable(prop.type)) {
throw std::logic_error(util::format("List property '%1.%2' cannot be optional", object_name, prop.name));
Expand All @@ -169,6 +174,10 @@ static inline void parse_property_type(StringData object_name, Property& prop, S
prop.type |= PropertyType::Object | PropertyType::Array;
}
}
else if (type == "set") {
// apply the correct properties for sets
realm::js::set::derive_property_type(object_name, prop); // may throw std::logic_error
}
else if (type == "linkingObjects") {
prop.type |= PropertyType::LinkingObjects | PropertyType::Array;
}
Expand All @@ -182,7 +191,7 @@ static inline void parse_property_type(StringData object_name, Property& prop, S
}

// Object properties are implicitly optional
if (prop.type == PropertyType::Object && !is_array(prop.type)) {
if (prop.type == PropertyType::Object && !is_array(prop.type) && !is_set(prop.type)) {
prop.type |= PropertyType::Nullable;
}
}
Expand Down
Loading