Is there a better way to create a dynamic match on mongo? - mongodb

I'm in the process of creating a view which shows all types of data. Currently I'm using tons of if statements to true and create a match parameter for all the different type of request. I really don't think writing out 120 possible if statements is the best way.. plus its getting hard to keep off. I'm hoping someone can point in the right direction. This is what I have so far.
func GetAllHourly(dbsession *mgo.Session, year, month, day, site, size, network, region string, code int) (items []MassAggregation, err error) {
defer dbsession.Close()
var match bson.M
if network == "openx3" {
network = "openx"
}
group := bson.M{"$group": bson.M{"_id": bson.M{"aws_region": "$aws_region", "http_request_status": "$http_request_status", "hour": "$hour", "network": "$network", "site": "$site", "size": "$size", "zone": "$zone", "extra": "$extra"}, "total": bson.M{"$sum": "$count"}}}
if site == "" && size == "" && network == "" && region == "" && code == -1 {
match = bson.M{"$match": bson.M{"year": year, "month": month, "day": day}}
} else if site != "" && size == "" && network == "" && region == "" && code == -1 {
match = bson.M{"$match": bson.M{"year": year, "month": month, "day": day, "site": site}}
} else if site != "" && size != "" && network == "" && region == "" && code == -1 {
match = bson.M{"$match": bson.M{"year": year, "month": month, "day": day, "site": site, "size": size}}
} else if site != "" && size != "" && network != "" && region == "" && code == -1 {
match = bson.M{"$match": bson.M{"year": year, "month": month, "day": day, "site": site, "size": size, "network": &bson.RegEx{Pattern: network, Options: "i"}}}
} else if site != "" && size != "" && network != "" && region != "" && code == -1 {
match = bson.M{"$match": bson.M{"year": year, "month": month, "day": day, "site": site, "size": size, "network": &bson.RegEx{Pattern: network, Options: "i"}, "aws_region": region}}
} else if site != "" && size != "" && network != "" && region != "" && code != -1 {
match = bson.M{"$match": bson.M{"year": year, "month": month, "day": day, "site": site, "size": size, "network": &bson.RegEx{Pattern: network, Options: "i"}, "aws_region": region, "http_request_status": code}}
} else if site == "" && size != "" && network == "" && region == "" && code == -1 {
match = bson.M{"$match": bson.M{"year": year, "month": month, "day": day, "site": site, "size": size}}
} else if site == "" && size != "" && network != "" && region == "" && code == -1 {
match = bson.M{"$match": bson.M{"year": year, "month": month, "day": day, "size": size, "network": &bson.RegEx{Pattern: network, Options: "i"}}}
} else if site == "" && size != "" && network != "" && region != "" && code == -1 {
match = bson.M{"$match": bson.M{"year": year, "month": month, "day": day, "site": site, "size": size, "network": &bson.RegEx{Pattern: network, Options: "i"}, "aws_region": region}}
} else if site == "" && size == "" && network != "" && region == "" && code == -1 {
match = bson.M{"$match": bson.M{"year": year, "month": month, "day": day, "network": &bson.RegEx{Pattern: network, Options: "i"}}}
} else if site == "" && size == "" && network != "" && region != "" && code == -1 {
match = bson.M{"$match": bson.M{"year": year, "month": month, "day": day, "network": &bson.RegEx{Pattern: network, Options: "i"}, "aws_region": region}}
} else if site == "" && size == "" && network != "" && region != "" && code != -1 {
match = bson.M{"$match": bson.M{"year": year, "month": month, "day": day, "network": &bson.RegEx{Pattern: network, Options: "i"}, "aws_region": region, "http_request_status": code}}
} else if site == "" && size == "" && network == "" && region != "" && code == -1 {
match = bson.M{"$match": bson.M{"year": year, "month": month, "day": day, "aws_region": region}}
} else if site == "" && size == "" && network == "" && region != "" && code != -1 {
match = bson.M{"$match": bson.M{"year": year, "month": month, "day": day, "aws_region": region, "http_request_status": code}}
} else if site == "" && size == "" && network == "" && region == "" && code != -1 {
match = bson.M{"$match": bson.M{"year": year, "month": month, "day": day, "http_request_status": code}}
} else if site != "" && size == "" && network == "" && region == "" && code != -1 {
match = bson.M{"$match": bson.M{"year": year, "month": month, "day": day, "site": site, "http_request_status": code}}
} else if site != "" && size == "" && network == "" && region != "" && code == -1 {
match = bson.M{"$match": bson.M{"year": year, "month": month, "day": day, "site": site, "aws_region": region}}
} else if site != "" && size == "" && network != "" && region == "" && code == -1 {
match = bson.M{"$match": bson.M{"year": year, "month": month, "day": day, "site": site, "network": &bson.RegEx{Pattern: network, Options: "i"}}}
} else if site == "" && size != "" && network == "" && region == "" && code != -1 {
match = bson.M{"$match": bson.M{"year": year, "month": month, "day": day, "size": size, "http_request_status": code}}
} else if site == "" && size != "" && network == "" && region != "" && code == -1 {
match = bson.M{"$match": bson.M{"year": year, "month": month, "day": day, "size": size, "aws_region": region}}
} else if site == "" && size != "" && network != "" && region == "" && code == -1 {
match = bson.M{"$match": bson.M{"year": year, "month": month, "day": day, "size": size, "network": &bson.RegEx{Pattern: network, Options: "i"}}}
} else if site == "" && size == "" && network != "" && region == "" && code != -1 {
match = bson.M{"$match": bson.M{"year": year, "month": month, "day": day, "network": &bson.RegEx{Pattern: network, Options: "i"}, "http_request_status": code}}
} else if site == "" && size == "" && network != "" && region != "" && code == -1 {
match = bson.M{"$match": bson.M{"year": year, "month": month, "day": day, "network": &bson.RegEx{Pattern: network, Options: "i"}, "aws_region": region}}
} else if site != "" && size != "" && network != "" && region == "" && code != -1 {
match = bson.M{"$match": bson.M{"year": year, "month": month, "day": day, "network": &bson.RegEx{Pattern: network, Options: "i"}, "size": size, "http_request_status": code}}
} else if site != "" && size != "" && network == "" && region == "" && code != -1 {
match = bson.M{"$match": bson.M{"year": year, "month": month, "day": day, "site": site, "size": size, "http_request_status": code}}
} else if site == "" && size != "" && network != "" && region == "" && code != -1 {
match = bson.M{"$match": bson.M{"year": year, "month": month, "day": day, "network": &bson.RegEx{Pattern: network, Options: "i"}, "size": size, "http_request_status": code}}
} else if site != "" && size == "" && network != "" && region != "" && code == -1 {
match = bson.M{"$match": bson.M{"year": year, "month": month, "day": day, "network": &bson.RegEx{Pattern: network, Options: "i"}, "aws_region": region, "site": site}}
} else if site != "" && size == "" && network != "" && region != "" && code != -1 {
match = bson.M{"$match": bson.M{"year": year, "month": month, "day": day, "network": &bson.RegEx{Pattern: network, Options: "i"}, "aws_region": region, "site": site, "http_request_status": code}}
} else if site != "" && size == "" && network == "" && region != "" && code != -1 {
match = bson.M{"$match": bson.M{"year": year, "month": month, "day": day, "aws_region": region, "site": site, "http_request_status": code}}
} else if site == "" && size != "" && network != "" && region != "" && code != -1 {
match = bson.M{"$match": bson.M{"year": year, "month": month, "day": day, "network": &bson.RegEx{Pattern: network, Options: "i"}, "aws_region": region, "size": size, "http_request_status": code}}
}
operations := []bson.M{match, group}
err = dbsession.DB("logs").C("prod").Pipe(operations).All(&items)
return
}
As you can see it's unruly.. but i haven't found an alternative yet. I'm hoping I just haven't found the answer.
UPDATE: I've changed my approach a bit. I'm still curious if this is the best way.
func GetAllHourly(dbsession *mgo.Session, year, month, day, site, size, network, region string, code int) (items []MassAggregation, err error) {
defer dbsession.Close()
matches := []bson.M{bson.M{"$match": bson.M{"year": year, "month": month, "day": day}}}
if network == "openx3" {
network = "openx"
}
if site != "" {
matches = append(matches, bson.M{"$match": bson.M{"site": site}})
}
if size != "" {
matches = append(matches, bson.M{"$match": bson.M{"size": size}})
}
if region != "" {
matches = append(matches, bson.M{"$match": bson.M{"aws_region": region}})
}
if code != -1 {
matches = append(matches, bson.M{"$match": bson.M{"http_request_status": code}})
}
if network != "" {
matches = append(matches, bson.M{"$match": bson.M{"network": &bson.RegEx{Pattern: network, Options: "i"}}})
}
group := bson.M{"$group": bson.M{"_id": bson.M{"aws_region": "$aws_region", "http_request_status": "$http_request_status", "hour": "$hour", "network": "$network", "site": "$site", "size": "$size", "zone": "$zone", "extra": "$extra"}, "total": bson.M{"$sum": "$count"}}}
var operations []bson.M
for _, match := range matches {
operations = append(operations, match)
}
operations = append(operations, group)
err = dbsession.DB("logs").C("prod").Pipe(operations).All(&items)
return
}

bson.M{} is just named type for map[string]interface{} as you can see in docs: http://godoc.org/labix.org/v2/mgo/bson#M
So, why don't you just use it as dictionary to build custom query? You'll have much less code:
query := bson.M{}
if site != "" {
query["site"] = site
}
if size != "" {
query["size"] = size
}
}
// Then use query variable for querying mongodb

Related

Is it possible to use a variable object inside collection.find() in mongoose?

Context:
I am trying to code the back end of an "Advanced Search" option
It is a project I'm coding to practice JS, MongoDB(Mongoose), Next, Node, Etc.
The Schema I'm using is the following:
House Schema:{
name:String,
address:{
street:String
city:String,
state:String
},
operations:{
rent:Boolean,
shortRent:Boolean,
purchase:Boolean,
agentRentPrice:Number,
agentShortRentPrice:Number,
agentSellingPrice:Number},
features:{
bathrooms:Number,
bedrooms:Number,
beds:Number,
amenities:[String],
},
}
Now, the FrontEnd sends the following info (in req.body):
query = {
name: null,
address: { city: null, state: null },
operations: {
rentAvailable: false,
purchaseAvailable: false,
shortRentAvailable: false,
agentRentPriceMin: null,
agentSellingPriceMin: null,
agentShortRentPriceMin: null,
agentRentPriceMax: null,
agentSellingPriceMax: null,
agentShortRentPriceMax: null,
},
features: {
bathroomsMin: null,
dormsMin: null,
bedsMin: null,
amenities: null,
},
}
Of course, those "null" values will be replaced with numbers or strings with the parameters introduced by the user.
With this object, I then declare another:
let queryObj = {
name: query.name,
address: { city: query.address.city, state: query.address.state },
operations: {
rentAvailable: query.operations.rentAvailable,
purchaseAvailable: query.operations.purchaseAvailable,
shortRentAvailable: query.operations.shortRentAvailable,
agentRentPrice: {
$gte: query.operations.agentRentPriceMin,
$lte: query.operations.agentRentPriceMax,
},
agentSellingPrice: {
$gte: query.operations.agentSellingPriceMin,
$lte: query.operations.agentSellingPriceMax,
},
agentShortRentPrice: {
$gte: query.operations.agentShortRentPriceMin,
$lte: query.operations.agentShortRentPriceMax,
},
},
features: {
bathrooms: { $gte: query.features.bathroomsMin },
dorms: { $gte: query.features.dormsMin },
beds: { $gte: query.features.bedsMin },
amenities: { $in: query.features.amenities },
},
}
Finally I reduce this object, removing any "null", "false", "" and "{}" values.
For example, the user searches for: house available for renting, in New York, with 2 bedrooms, with a pool, and max Rent price of $10.000
So, req.body.query will be
{
name: null,
address: { city: "New York", state: null },
operations: {
rentAvailable: true,
purchaseAvailable: false,
shortRentAvailable: false,
agentRentPriceMin: null,
agentSellingPriceMin: null,
agentShortRentPriceMin: null,
agentRentPriceMax: 10000,
agentSellingPriceMax: null,
agentShortRentPriceMax: null,
},
features: {
bathroomsMin: null,
dormsMin: 2,
bedsMin: null,
amenities: ["pool"],
},
}
Next, I declare
let queryObj = {
name: null,
address: { city: "New York", state: null },
operations: {
rentAvailable: true,
purchaseAvailable: false,
shortRentAvailable: false,
agentRentPrice: {
$gte: null,
$lte: 10000,
},
agentSellingPrice: {
$gte: null,
$lte: null,
},
agentShortRentPrice: {
$gte: null,
$lte: null,
},
},
features: {
bathrooms: { $gte: null },
dorms: { $gte: 2 },
beds: { $gte: null },
amenities: { $in: ["pool"] },
},
};
I have a function here that reduces this object (removing "false" "null" "" and "{}" values):
queryObj = {
address: { city: "New York" },
operations: {
rentAvailable: true,
agentRentPrice: {
$lte: 10000,
},
},
features: {
dorms: { $gte: 2 },
amenities: { $in: ["pool"] },
},
};
As you can see, "query" (and therefore "queryObj") will vary a lot; the user may or may not use any of the available search parameters, so (as i see it) it is not possible to "hard-code" the queryObj structure
I've tried using
Home.aggregate([{$match:queryObj}])
without success (returns no results).
Is it possible what I'm trying to do?
Try to use a function to build the filter:
const body = {
name: null,
address: { city: 'New York', state: null },
operations: {
rentAvailable: true,
purchaseAvailable: false,
shortRentAvailable: false,
agentRentPriceMin: null,
agentSellingPriceMin: null,
agentShortRentPriceMin: null,
agentRentPriceMax: 10000,
agentSellingPriceMax: null,
agentShortRentPriceMax: null,
},
features: {
bathroomsMin: null,
dormsMin: 2,
bedsMin: null,
amenities: ['pool'],
},
};
const buildFilter = (body) => {
const filter = {};
if (body.name != null) {
filter['name'] = body.name;
}
if (body.address.city != null) {
filter['address.city'] = body.address.city;
}
if (body.address.state != null) {
filter['address.state'] = body.address.state;
}
if (body.operations.rentAvailable != null) {
filter['operations.rentAvailable'] = body.operations.rentAvailable;
}
if (body.operations.purchaseAvailable != null) {
filter['operations.purchaseAvailable'] = body.operations.purchaseAvailable;
}
if (body.operations.shortRentAvailable != null) {
filter['operations.shortRentAvailable'] =
body.operations.shortRentAvailable;
}
let agentRentPrice = {};
if (body.operations.agentRentPriceMin != null) {
agentRentPrice = { $gte: body.operations.agentRentPriceMin };
}
if (body.operations.agentRentPriceMax != null) {
agentRentPrice = {
...agentRentPrice,
$lte: body.operations.agentRentPriceMax,
};
}
if (Object.keys(agentRentPrice).length > 0) {
filter['operations.agentRentPrice'] = agentRentPrice;
}
let agentSellingPrice = {};
if (body.operations.agentSellingPriceMin != null) {
agentSellingPrice = { $gte: body.operations.agentSellingPriceMin };
}
if (body.operations.agentSellingPriceMax != null) {
agentSellingPrice = {
...agentSellingPrice,
$lte: body.operations.agentSellingPriceMax,
};
}
if (Object.keys(agentSellingPrice).length > 0) {
filter['operations.agentSellingPrice'] = agentSellingPrice;
}
let agentShortRentPrice = {};
if (body.operations.agentShortRentPriceMin != null) {
agentShortRentPrice = { $gte: body.operations.agentShortRentPriceMin };
}
if (body.operations.agentShortRentPriceMax != null) {
agentShortRentPrice = {
...agentShortRentPrice,
$lte: body.operations.agentShortRentPriceMax,
};
}
if (Object.keys(agentShortRentPrice).length > 0) {
filter['operations.agentShortRentPrice'] = agentShortRentPrice;
}
if (body.features.bathrooms != null) {
filter['features.bathrooms'] = { $gte: body.features.bathrooms };
}
if (body.features.dorms != null) {
filter['features.dorms'] = { $gte: body.features.dorms };
}
if (body.features.beds != null) {
filter['features.beds'] = { $gte: body.features.beds };
}
if (body.features.amenities != null) {
filter['features.amenities'] = { $in: body.features.amenities };
}
return filter;
};
console.log(buildFilter(body));
A little verbose, but it is due to the fact that you don't have a 1-on-1 correspondence between the attributes of the body and your filter's properties.
You can solve this by creating the filter dynamically on the backend. Basically, you will start with an empty filter, and then you will check each user input, and add it to the filter if exists. That way, you don't have to worry if the user will send multiple filters, or if it will not send filters at all. Everything will be added dynamically.
const queryObj = {
address: { city: "New York" },
operations: {
rentAvailable: true,
agentRentPrice: {
$lte: 10000,
},
},
features: {
dorms: { $gte: 2 },
amenities: { $in: ["pool"] },
},
};
// Initialize empty filter.
const filter = {};
if (queryObj?.address?.city) filter['address.city'] = queryObj.address.city;
if (typeof queryObj?.operations?.rentAvailable === boolean) filter['operations.rentAvailable'] = queryObj.operations.rentAvailable;
if (queryObj?.operations?.agentRentPrice?.$lte) filter['operations.agentRentPrice'] = { $lte: queryObj.operations.agentRentPrice.$lte };

Date range filter and One-to-Many relation query syntax

I am trying to filter booked rooms to find all available rooms:
model Room {
id Int #id #default(autoincrement())
roomNumber String #unique
hotel String
bookings Booking[]
}
model Booking {
id Int #id #default(autoincrement())
hotel String
startDate DateTime
endDate DateTime
roomId Int
room Room #relation(fields: [roomId], references: [id])
##map("bookings")
}
I don't get the syntax of a prisma findMany query that would check, given two booking dates, all available rooms.
The mathematical logic to find if a room is not available is (i.e. if one booking interval crosses or contains the dates given) :
(startDate <= dto.startDate && dto.startDate < endDate) ||
(startDate < dto.endDate && dto.endDate <= endDate) ||
(dto.startDate <= startDate && dto.endDate >= endDate)
I would appreciate any help as I don't understand quite well the order of logic filters in prisma...
You can use none:
prisma.room.findMany({
where: {
bookings: {
none: {
// ...
},
},
},
});
and specify the condition for a booking that would case a conflict (i.e. make the room not available) or use every:
prisma.room.findMany({
where: {
bookings: {
every: {
//...
},
},
},
});
and specify the condition that bookings have to meet to not cause a conflict.
The condition you've specified seems unnecessary complex to me. Every booking that meets this condition should be a conflict:
(dto.startDate <= endDate && dto.endDate >= startDate)
(might need some adjustment depending on how you want to treat edge cases)
All together:
prisma.room.findMany({
where: {
bookings: {
none: {
endDate: { lte: dto.startDate },
startDate: { gte: dto.endDate },
},
},
},
});
Just in case you want to specify more complex conditions using OR you can do like this:
prisma.room.findMany({
where: {
bookings: {
none: {
OR: [
{
// (startDate <= dto.startDate && dto.startDate < endDate)
startDate: { lte: dto.startDate },
endDate: { gt: dto.startDate },
},
{
// (startDate < dto.endDate && dto.endDate <= endDate)
startDate: { lt: dto.endDate },
endDate: { gte: dto.endDate },
},
{
// (dto.startDate <= startDate && dto.endDate >= endDate)
startDate: { gte: dto.startDate },
endDate: { lte: dto.endDate },
},
],
},
},
},
});

How query nested array by time range mongodb with golang driver?

A have booking system with apoinments and need to get all booking objects with apoinments filtering with special time.
{
"id": "6295001bef176110cb52076c",
"companyID": "6294deeed16a4491020a6f3a",
"createdAt": "2022-05-30T17:34:19.458Z",
"updatedAt": "2022-05-31T12:23:58.805Z",
"apoinments": [
{
"id": "62a349a1fe907103f8d33111",
"startTime": "2022-06-10T12:00:00Z",
"endTime": "2022-06-10T14:00:00Z",
"createdAt": "2022-06-10T13:39:45.057Z",
"companyID": "6294deeed16a4491020a6f3a",
"bookingItemID": "6295001bef176110cb52076c",
},
{
"id": "62a349c0fe907103f8d33112",
"startTime": "2022-06-10T14:00:00Z",
"endTime": "2022-06-10T16:00:00Z",
"createdAt": "2022-06-10T13:40:16.927Z",
"companyID": "6294deeed16a4491020a6f3a",
"bookingItemID": "6295001bef176110cb52076c",
}
],
"title": "One",
}
I need to get all booking objects with their apoinments with startTime and endTime with special range so i do:
func (companyRepository *companyRepositoryImpl) GetCompanyBookingItems(companyID string) (*model.CompanyBookingItems, error) {
var existingCompany model.Company
objectId, _ := primitive.ObjectIDFromHex(companyID)
filter := bson.M{"_id": objectId}
err := companyRepository.Connection.Collection("companies").FindOne(cntx, filter).Decode(&existingCompany)
if err != nil {
return nil, exception.ResourceNotFoundException("Company", "id", companyID)
}
switch hour := time.Now().Hour(); { // missing expression means "true"
// dev
case hour >= 8 && hour <= 23:
// prod
// case hour >= 3 && hour <= 18:
// log.Println("hour", hour)
startDate := time.Date(time.Now().Year(), time.Now().Month(), time.Now().Day(), 11, 0, 0, 0, time.UTC)
endDate := time.Date(time.Now().Year(), time.Now().Month(), time.Now().Day()+1, 05, 0, 0, 0, time.UTC)
bookingItems := *existingCompany.BookingItems
for i := range bookingItems {
bookingItem := bookingItems[i]
var newApoinments []model.Apoinment
for _, apoinment := range *bookingItem.Apoinments {
if apoinment.StartTime.Local().After(startDate.Local()) && apoinment.EndTime.Local().Before(endDate.Local()) {
newApoinments = append(newApoinments, apoinment)
// log.Println("10 - 23", apoinment.ClientName)
}
}
*bookingItems[i].Apoinments = newApoinments
}
// dev
case hour >= 0 && hour <= 7:
// prod
// case hour >= 19 && hour <= 2:
startDate := time.Date(time.Now().Year(), time.Now().Month(), time.Now().Day()-1, 11, 0, 0, 0, time.UTC)
endDate := time.Date(time.Now().Year(), time.Now().Month(), time.Now().Day(), 05, 0, 0, 0, time.UTC)
// log.Println("start", startDate)
// log.Println("end", endDate)
bookingItems := *existingCompany.BookingItems
for i := range bookingItems {
bookingItem := bookingItems[i]
var newApoinments []model.Apoinment
for _, apoinment := range *bookingItem.Apoinments {
if apoinment.StartTime.Local().After(startDate.Local()) && apoinment.EndTime.Local().Before(endDate.Local()) {
newApoinments = append(newApoinments, apoinment)
// log.Println("10 - 23", apoinment.ClientName)
}
}
*bookingItems[i].Apoinments = newApoinments
}
}
return &model.CompanyBookingItems{
Data: *existingCompany.BookingItems,
}, nil
}
but get error runtime error: invalid memory address or nil pointer dereference
How i can filter and mongodb return me only document with special ranged time apoinments of booking object?

Data not updated on Production DB

I have a weird situation on my project. I have 2 entities: children and users. Users create children, so on children, I have a reference to their owners and on users I have a list of the children they have created.
When I add a new child to firebase, the user owner of that child has his list of children updated to add that new child.
The thing is: on the staging environment everything works as it should be. But on the production environment, the child is added to the children, but not to the list of children on users.
Both databases have the same rules.
I've got to this project when it was already partially developed, so I can't even find where specifically the user should be updated. It seems to me that when you update a child, the users entity is automagically updated (using breakpoints I could verify it actually happens on the same step). I'm kind of new to Firebase so I am really lost here.
Can anyone try to help me?
Here are the database rules:
{"rules": {
"children": {
"$uid": {
".validate": "newData.hasChildren(['first_name'])",
"date_of_birth": {
".validate": "newData.isString() && newData.val().matches(/^(19|20)\\d\\d-(0[1-9]|1[012])-(0[1-9]|[12][0-9]|3[01])$/)"
},
"first_name": {
".validate": "newData.isString() && newData.val().length > 0"
},
"users": {
"$key2": {
".validate": "root.child('users').child($key2).val() != null && newData.isString() && newData.val() == 'parent'"
},
".validate": "newData.hasChildren()"
},
"$other": {
".validate": "false"
},
".read": "data.child('users').child(auth.uid).val() != null",
".write": "data.child('users').child(auth.uid).val() == 'parent' || newData.child('users').child(auth.uid).val() == 'parent'"
}
},
"users": {
".indexOn": [
"email"
],
"$uid": {
".validate": "newData.hasChildren()",
"email": {
".validate": "newData.isString() && newData.val() == auth.token.email"
},
"first_name": {
".validate": "newData.isString()"
},
"is_subscribed": {
".validate": "newData.isBoolean()"
},
"children": {
"$key5": {
".validate": "root.child('children').child($key5).val() != null && newData.isString() && newData.val() == 'parent'"
},
".validate": "newData.hasChildren()"
},
"$other": {
".validate": "false"
},
".read": "auth != null && auth.uid == $uid",
".write": "auth != null && auth.uid == $uid"
}
}
}
}
and the code that adds children to it.
var entityDatabase: DatabaseReference = Database.database().reference().child(“children”).child(did)
entityDatabase.keepSynced(true)
var firstName: String?
var users: [String: String] = [:]
var params: [String: Any] {
var strings: [String: String] = [:]
strings["first_name"] = firstName
let keysToRemove = strings.keys.filter { strings[$0] == "" }
for key in keysToRemove {
strings.removeValue(forKey: key)
}
var dict: [String: Any] = strings
dict["users"] = users
return dict
}
guard let user = Firebase.Auth.auth().currentUser else {
return
}
guard firstName?.isEmpty == false else {
return
}
users[user.uid] = "parent"
entityDatabase.updateChildValues(params) { [weak self] (error, _) in
self?.observe()
guard error == nil else {
return
}
switch self?.updatedImage {
case let image?:
self?.save(image: image, completion: completion)
case nil:
}
}

Creating Mongo/mapReduce data source in icCube

I would like to create a MongoDB/mapReduce data source into icCube (http://www.iccube.com/support/documentation/user_guide/schemas_cubes/ds_mongodb.php#mapReduce), The below script works fine from Mongo shell, how it should be formatted to be accepted by icCube, when I paste the same code into icCube datasource builder, I get this error message:
MongoDB : invalid JSON (table:Test.mapReduce) : [var location_map = function() { if (this.companyId = "1234" && this.Parent_Location !== undefined && this.Parent_Location.value.length > 0 && this.Parent_Location.value[0].id !== undefined && this.Parent_Location.value[0].id !== null) { emit(this.Parent_Location.value[0].id, {Location_NameOfLocation: this.Location_NameOfLocation, LocationID: this._id, CountryName: "", companyId : 0}); } } var country_map = function() { if (this.companyId = "1234") { emit(this._id, { CountryName: this.CountryName, Location_NameOfLocation: "", LocationID: 0, companyId : this.companyId }); } } var r = function(key, values) { var result = {LocationID: 0, CountryName: "", Location_NameOfLocation: "", companyId : 0}; values.forEach(function(value) { if (result.LocationID === 0 && value.LocationID !== null ) { result.LocationID = value.LocationID; } if (result.companyId === 0 && value.companyId !== null ) { result.companyId = value.companyId; } if (result.CountryName === "" && value.CountryName !== null ) { result.CountryName = value.CountryName; } if (result.Location_NameOfLocation === "" && value.Location_NameOfLocation !== null ) { result.Location_NameOfLocation = value.Location_NameOfLocation; } }); return result; } db.runCommand( { mapReduce: Location, map: location_map, reduce: r, out: { replace: LocationsCountries }, query: {companyId : "1234"} } ) db.runCommand( { mapReduce: Countries, map: country_map, reduce: r, out: { reduce: LocationsCountries }, query: {companyId : "1234" } } )] ^
Mongo mapReduce script:
var location_map = function() {
if (this.companyId = "1234" && this.Parent_Location !== undefined && this.Parent_Location.value.length > 0 && this.Parent_Location.value[0].id !== undefined && this.Parent_Location.value[0].id !== null) {
emit(this.Parent_Location.value[0].id, {Location_NameOfLocation: this.Location_NameOfLocation, LocationID: this._id, CountryName: "", companyId : 0});
}
}
var country_map = function() {
if (this.companyId = "1234") {
emit(this._id, { CountryName: this.CountryName, Location_NameOfLocation: "", LocationID: 0, companyId : this.companyId });
}
}
var r = function(key, values) {
var result = {LocationID: 0, CountryName: "", Location_NameOfLocation: "", companyId : 0};
values.forEach(function(value) {
if (result.LocationID === 0 && value.LocationID !== null ) { result.LocationID = value.LocationID; }
if (result.companyId === 0 && value.companyId !== null ) { result.companyId = value.companyId; }
if (result.CountryName === "" && value.CountryName !== null ) { result.CountryName = value.CountryName; }
if (result.Location_NameOfLocation === "" && value.Location_NameOfLocation !== null ) { result.Location_NameOfLocation = value.Location_NameOfLocation; }
});
return result;
}
db.runCommand(
{
mapReduce: "Location",
map: location_map,
reduce: r,
out: { replace: "LocationsCountries" },
query: {companyId : "1234"}
}
)
db.runCommand(
{
mapReduce: "Countries",
map: country_map,
reduce: r,
out: { reduce: "LocationsCountries" },
query: {companyId : "1234" }
}
)
Thanks,
Balint
You'll have to define your functions in strings ( and Javascript escape if required strings within these strings ). This is described here in the icCube documentation ( www ). Here is an example combining single quoted strings with double-quoted strings.
{
"mapReduce": "Location",
"map": 'function() {
if (this.companyId = "1234" && this.Parent_Location !== undefined && this.Parent_Location.value.length > 0 && this.Parent_Location.value[0].id !== undefined && this.Parent_Location.value[0].id !== null) {
emit(this.Parent_Location.value[0].id, {Location_NameOfLocation: this.Location_NameOfLocation, LocationID: this._id, CountryName: "", companyId : 0});
}
}',
"reduce": 'function(key, values) {
var result = {LocationID: 0, CountryName: "", Location_NameOfLocation: "", companyId : 0};
values.forEach(function(value) {
if (result.LocationID === 0 && value.LocationID !== null ) { result.LocationID = value.LocationID; }
if (result.companyId === 0 && value.companyId !== null ) { result.companyId = value.companyId; }
if (result.CountryName === "" && value.CountryName !== null ) { result.CountryName = value.CountryName; }
if (result.Location_NameOfLocation === "" && value.Location_NameOfLocation !== null ) { result.Location_NameOfLocation = value.Location_NameOfLocation; }
});
return result;
}',
"out": {
"replace": "LocationsCountries"
},
"query": {
"companyId" : "1234"
}
}