MongoDB Realm - Filter Queries - mongodb

In the scheme section where I connect the realm to the MongoDB collections there is a tab to add a filter query to filter the results being synced. I added a filter but every time I query on the app to load the data I get all the documents instead of filtered documents.
How can I filter the data the app receives?
Collection Schema:
{
"title": "Group",
"required": [
"_id",
"cDate",
"name",
"info",
"isPublic",
"joinWithRequest",
"partition"
],
"properties": {
"_id": {
"bsonType": "objectId"
},
"admins": {
"bsonType": "array",
"items": {
"bsonType": "objectId"
}
},
"members": {
"bsonType": "array",
"items": {
"bsonType": "objectId"
}
},
"photoItems": {
"bsonType": "array",
"items": {
"bsonType": "object",
"title": "Item",
"properties": {
"id": {
"type": "string"
},
"cDate": {
"bsonType": "date"
}
},
"required": [
"id",
"cDate"
]
}
},
"videoItems": {
"bsonType": "array",
"items": {
"bsonType": "object",
"title": "Item",
"properties": {
"id": {
"type": "string"
},
"cDate": {
"bsonType": "date"
}
},
"required": [
"id",
"cDate"
]
}
},
"cDate": {
"bsonType": "date"
},
"partition": {
"bsonType": "string"
},
"name": {
"bsonType": "string"
},
"info": {
"bsonType": "string"
},
"icon": {
"bsonType": "string"
},
"isPublic": {
"bsonType": "bool"
},
"joinWithRequest": {
"bsonType": "bool"
}
}
}
Filter:
Client Query:
let groups = realm.objects(Group.self).sorted(byKeyPath: "cDate")

What you're asking about is (was) called a query based sync. While it limited the amount of data sync'd it also was limited by the number of users, system resources etc.
At this time query based sync is no longer supported in MongoDB Realm
However, you can get much of the same functionality by leveraging partitions - which are required for sync'ing anyway. Let me give a high level example.
Suppose you have a 'posts' app with users and then posts done by users of specific groups. For this example, this user belongs to Group_0 and Group_1. Your user object may look like this
class UserClass: Object {
#objc dynamic var _id: //a users uid
#objc dynamic var user_name = ""
let groupPartitionList = List<GroupClass>() //will populate with Group_0 and Group_1
}
and a post may look like this
class PostClass: Object {
#objc dynamic var _id: ObjectId = ObjectId.generate()
#objc dynamic var _partitionKey: GroupPartition = ""
#objc dynamic var title = ""
#obcj dynamic var post = ""
override static func primaryKey() -> String? {
return "_id"
}
}
Suppose there are 50 different groups.
Upon the user authenticating and the user object populating, you would then know the user belongs to Group_0 and Group_1 which could be displayed in a popup menu - defaulting to the first group, Group_0.
Once the user logs in, get just the Group_0 posts
let whichPartition = myUserObject.groupPartitionList[0] //object at index 0 = Group_0
let config = user.configuration(partitionValue: whichPartition)
Realm.asyncOpen(configuration: config) { result in
...
let group0Posts = realm.objects(PostClass.self) //loads just the posts Group_0
}
As you can see, there is no query: the data is limited by the partition key e.g this will not read posts from any other group - only posts with a partition key of Group_0
Likewise, if the user changes the popup to Group_1, only posts with partition key of Group_1 will sync and no other. So even though there are 50 groups and thousands of posts, you will only download and sync Group_0's and Group_1's via the partition value.
You can augment this further by using a server filter (as mentioned in the comments to the question) to withhold certain data so the amount of data being queried at the server level is smaller.
For example, you could add a filter to ignore any posts that are older than 5 years. That will significantly speed up queries by reducing the amount of data being queried (which effectively could reduce the number of results because those are ignored)
There's some excellent reading in the MongoDB Realm Docs Partition Atlas Data Into Realms.

Related

JSON Schema one-to-many relation, in MongoDB Atlas?

I'm stumped trying to get a one-to-many relationship established in MongoDB Atlas, via JSON Schema (for use in a GraphQL API).
This is in the Atlas App Services > Data Access > Schema > "Collections" tab.
(It's possible I'm thinking about this wrong, any ideas appreciated.)
So, I have two collections: Node and URL. Each Node can have one or more URLs.
Each URL has three properties: _id (primary key), url, and node:
// URLs (JSON Schema)
{
"title": "url",
"properties": {
"_id": {
"bsonType": "objectId"
},
"node": {
"bsonType": "string"
},
"url": {
"bsonType": "string"
}
}
}
Each URL is matched to a Node (URL.node === Node._id):
// URLs relationships (JSON Schema)
{
"node": {
"ref": "#/relationship/db_cluster/db_name/nodes",
"foreignKey": "_id",
"isList": false
}
}
Each Node has two properties of its own, _id (primary key) and name, and urls which should be an array of linked documents (from the URLs Collection, of course).
// Nodes (JSON Schema)
{
"title": "node",
"properties": {
"_id": {
"bsonType": "string"
},
"name": {
"bsonType": "string"
},
"urls": {
"bsonType": "array",
"items": {
"bsonType": "object",
"properties": {
"_id": {
"bsonType": "objectId"
},
"node": {
"bsonType": "string"
},
"url": {
"bsonType": "string"
}
}
}
}
}
}
So far so good. The above seems to work OK.
The trouble begins when I try and link the two Collections together. I am attempting to link a few URLs to each Node, with this relationship:
// Nodes relationships (JSON Schema)
{
"urls": {
"ref": "#/relationship/db_cluster/db_name/urls",
"foreignKey": "_id",
"isList": true
}
}
However, I can't save the above relationship. I get an error
relationship source property type must match foreign property type
Any ideas? Thanks.

MongoDB Stitch GraphQL Custom Mutation Resolver returning null

GraphQL is a newer feature for MongoDB Stitch, and I know it is in beta, so thank you for your help in advance. I am excited about using GraphQL directly in Stitch so I am hoping that maybe I just overlooked something.
The documentation for the return Payload displays the use of bsonType, but when actually entering the JSON Schema for the payload type it asks for you to use "type" instead of "bsonType". It still works using "bsonType" to me which is odd as long as at least one of the properties uses "type".
Below is the function:
const mongodb = context.services.get("mongodb-atlas");
const collection = mongodb.db("<database>").collection("<collection>");
const query = { _id: BSON.ObjectId(input.id) }
const update = {
"$push": {
"notes": {
"createdBy": context.user.id,
"createdAt": new Date,
"text": input.text
}
}
};
const options = { returnNewDocument: true }
collection.findOneAndUpdate(query, update, options).then(updatedDocument => {
if(updatedDocument) {
console.log(`Successfully updated document: ${updatedDocument}.`)
} else {
console.log("No document matches the provided query.")
}
return {
_id: updatedDocument._id,
notes: updatedDocument.notes
}
})
.catch(err => console.error(`Failed to find and update document: ${err}`))
}
Here is the Input Type in the customer resolver:
"type": "object",
"title": "AddNoteToLeadInput",
"required": [
"id",
"text"
],
"properties": {
"id": {
"type": "string"
},
"text": {
"type": "string"
}
}
}
Below is the Payload Type:
{
"type": "object",
"title": "AddNoteToLeadPayload",
"properties": {
"_id": {
"type": "objectId"
},
"notes": {
"type": "array",
"items": {
"type": "object",
"properties": {
"createdAt": {
"type": "string"
},
"createdBy": {
"type": "string"
},
"text": {
"type": "string"
}
}
}
}
}
}
When entering the wrong "type" the error states:
Expected valid values are:[array boolean integer number null object string]
When entering the wrong "bsonType" the error states:
Expected valid values are:[string object array objectId boolean bool null regex date timestamp int long decimal double number binData]
I've tried every combination I can think of including changing all "bsonType" to "type". I also tried changing the _id to a string when using "type" or objectId when "bsonType". No matter what combination I try when I use the mutation it does what it is supposed to and adds the note into the lead, but the return payload always displays null. I need it to return the _id and note so that it will update the InMemoryCache in Apollo on the front end.
I noticed that you might be missing a return before your call to collection.findOneAndUpdate()
I tried this function (similar to yours) and got GraphiQL to return values (with String for all the input and payload types)
exports = function(input){
const mongodb = context.services.get("mongodb-atlas");
const collection = mongodb.db("todo").collection("dreams");
const query = { _id: input.id }
const update = {
"$push": {
"notes": {
"createdBy": context.user.id,
"createdAt": "6/10/10/10",
"text": input.text
}
}
};
const options = { returnNewDocument: true }
return collection.findOneAndUpdate(query, update, options).then(updatedDocument => {
if(updatedDocument) {
console.log(`Successfully updated document: ${updatedDocument}.`)
} else {
console.log("No document matches the provided query.")
}
return {
_id: updatedDocument._id,
notes: updatedDocument.notes
}
})
.catch(err => console.error(`Failed to find and update document: ${err}`))
}
Hi Bernard – There is an unfortunate bug in the custom resolver form UI at the moment which doesn't allow you to only use bsonType in the input/payload types – we are working on addressing this. In actually you should be able to use either type/bsonType or a mix of the two as long as they agree with your data. I think that the payload type definition you want is likely:
{
"type": "object",
"title": "AddNoteToLeadPayload",
"properties": {
"_id": {
"bsonType": "objectId"
},
"notes": {
"type": "array",
"items": {
"type": "object",
"properties": {
"createdAt": {
"bsonType": "date"
},
"createdBy": {
"type": "string"
},
"text": {
"type": "string"
}
}
}
}
}
}
If that doesn't work, it might be helpful to give us a sample of the data that you would like returned.

How do I add custom queries in GraphQL using Strapi?

I'm using graphQL to query a MongoDB database in React, using Strapi as my CMS. I'm using Apollo to handle the GraphQL queries. I'm able to get my objects by passing an ID argument, but I want to be able to pass different arguments like a name.
This works:
{
course(id: "5eb4821d20c80654609a2e0c") {
name
description
modules {
title
}
}
}
This doesn't work, giving the error "Unknown argument \"name\" on field \"course\" of type \"Query\"
{
course(name: "course1") {
name
description
modules {
title
}
}
}
From what I've read, I need to define a custom query, but I'm not sure how to do this.
The model for Course looks like this currently:
"kind": "collectionType",
"collectionName": "courses",
"info": {
"name": "Course"
},
"options": {
"increments": true,
"timestamps": true
},
"attributes": {
"name": {
"type": "string",
"unique": true
},
"description": {
"type": "richtext"
},
"banner": {
"collection": "file",
"via": "related",
"allowedTypes": [
"images",
"files",
"videos"
],
"plugin": "upload",
"required": false
},
"published": {
"type": "date"
},
"modules": {
"collection": "module"
},
"title": {
"type": "string"
}
}
}
and the
Any help would be appreciated.
Referring to Strapi GraphQL Query API
You can use where with the query courses to filter your fields. You will get a list of courses instead of one course
This should work:
{
courses(where: { name: "course1" }) {
name
description
modules {
title
}
}
}

Elastic Search Fuzzy Search root and nested fields

I am new to Elastic Search and facing a couple of issues when querying. I have a simple Mongodb database with collections of cities and places of interest. Each collection has a cityName and other details like website etc, and also a places object array. This is my mapping;
{
"mappings": {
"properties": {
"cityName": {
"type": "text"
},
"phone": {
"type": "keyword"
},
"email": {
"type": "keyword"
},
"website": {
"type": "keyword"
},
"notes": {
"type": "keyword"
},
"status": {
"type": "keyword"
},
"places": {
"type": "nested",
"properties": {
"name": {
"type": "text"
},
"status": {
"type": "keyword"
},
"category": {
"type": "keyword"
},
"reviews": {
"properties": {
"rating": {
"type": "long"
},
"comment": {
"type": "keyword"
},
"user": {
"type": "nested"
}
}
}
}
}
}
}
}
I need a fuzzy query where user can search both cityName and places.name, however I get results when I search a single word, adding multiple words return 0 hits. I am sure I am missing something here because I started learning elastic search 2 days ago. The following query returns results because I have a document with cityName: Islamabad and places array having objects that have the keyword Islamabad in their name, in some places the keyword Islamabad is at the beginning of the place.name and in some places objects it might be in the middle or end
This is what I am using : Returns results when only one word
{
"query": {
"bool": {
"should": [
{
"fuzzy": {
"cityName": "Islamabad"
}
},
{
"nested": {
"path": "places",
"query": {
"fuzzy": {
"places.name": "Islamabad"
}
}
}
}
]
}
}
}
Adding another word, say, club, to the above query returns 0 hits when I actually do have places having names Islamabad club and Islamabad Golf club
Problem
The search query is sent from an app and so it is dynamic, so the term to search is same for both cityName and places.name AND places.name doesn't always have the cityName in it.
What do I need exactly??
I need a query where I can search cityName and the array of places (only searching places.name). The query should be of Fuzzy type so that it still returns results if the word Islamabad is spelled like Islambad or even return results for Islam or Abad. And the query should also return results for multiple words, I am sure am I doing something wrong there. Any help would be appreciated.
**P.S : ** I am actually using MongoDB as my database but migrating to Elastic Search ONLY for improving our search feature. I tried different ways with MongoDB, used the mongoose-fuzzy-searching npm module but that didn't work, so if there's a simpler solution for MongoDB please share that too.
Thanks.
EDIT 1:
I had to change the structure (mapping) of my data. Now I have 2 separate indices, one for cities with city details and a cityId and another index for all places, each place has a cityId which will be used for joining later if needed. Each place also has a cityName key so I will only be searching the places index because it has all the details (place name and city name).
I have a city including the word Welder's in it's name and also the some places inside the same location have the word Welder's in their name, which have a type:text. However when searched for welder both of the following queries don't return these documents, a search for welders OR welder's does return these documents. I am not sure why welder won't match with Welder's*. I didn't specify any analyzer during the creation of both the indices and neither am I explicitly defining it in the query can anyone help me out with this query so it behaves as expected:
Query 1 :
{
"query": {
"bool": {
"should": [
{
"match": {
"name": {
"query": "welder",
"fuzziness": 20
}
}
},
{
"match": {
"cityName": {
"query": "welder",
"fuzziness": 20
}
}
}
]
}
}
}
Query 2 :
{
"query": {
"match": {
"name": {
"query": "welder",
"fuzziness": 20
}
}
}
}
the fuzzy query is meant to be used to find approximations of your complete query within a certain distance :
To find similar terms, the fuzzy query creates a set of all possible
variations, or expansions, of the search term within a specified edit
distance. The query then returns exact matches for each expansion.
If you you cant to allow fuzzy matching of individual terms in your query your need to use a match query with the fuzziness activated.
POST <your_index>/_search
{
"query": {
"bool": {
"should": [
{
"match": {
"cityName": {
"query": "Islamabad golf",
"fuzziness": "AUTO"
}
}
},
{
"nested": {
"path": "places",
"query": {
"match": {
"places.name": {
"query": "Islamabad golf",
"fuzziness": "AUTO"
}
}
}
}
}
]
}
}
}
Reminder: Fuzziness in elasticsearch allow at max 2 corrections per term. SO you will never be able to match Islam with Islamabad since there are 4 changes between those terms.
For more information on distance and fuzziness parameters please refer to this documentation page fuzziness parameters

How to update the Embedded Data which is inside of another Embedded Data?

I have document like below in MongoDB:
{
"_id": "test",
"tasks": [
{
"Name": "Task1",
"Parameter": [
{
"Name": "para1",
"Type": "String",
"Value": "*****"
},
{
"Name": "para2",
"Type": "String",
"Value": "*****"
}
]
},
{
"Name": "Task2",
"Parameter": [
{
"Name": "para1",
"Type": "String",
"Value": "*****"
},
{
"Name": "para2",
"Type": "String",
"Value": "*****"
}
]
}
]
}
There is Embedded Data Structure (Parameter) inside of another Embedded Data Structure (Tasks). Now I want to update the para1 in Task1's Parameter.
I have tried many ways but I can only use query tasks.Parameter.name to find the para1 but cannot update it. the example in the doc are using .$. to update the value in a Embedded Data Structure but it doesn't work in my case.
Anyone have any ideas ?
MongoDB currently only supports the positional operator once, and only for the top level array. There is a ticket SERVER-831 to change this behavior for your use case. You can follow the issue there and up vote it.
However, you might be able to change your approach to accomplish what you want to do. One way is to change your schema. Collapse the tasks name into the array so the document looks like this:
{
_id:test,
tasks:
[
{
Task:1
Name:para1,
Type:String,
Value:*****
},
{
Task:1
Name:para2,
Type:String,
Value:*****
},
{
Task:2
Name:para1,
Type:String,
Value:*****
},
{
Task:2
Name:para2,
Type:String,
Value:*****
}
]
}
Another approach that may work for you is to use $pull and $push. For instance something like this to replace a task (this assumes that tasks.Parameter.Name is unique to an array of Parameters):
db.test2.update({$and: [{"tasks.Name": "Task3"}, {"tasks.Parameter.Name":"para1"}]}, {$pull: {"tasks.$.Parameter": {"Name": "para1"}}})
db.test2.update({"tasks.Name": "Task3"}, {$push: {"tasks.$.Parameter": {"Name": "para3", Type: "String", Value: 1}}})
With this solution you need to be careful with regard to concurrency, as there will be a brief moment where the document doesn't exist.