I am using the elasticsearch-rails and elasticsearch-model gems for my Ruby on Rails app, which is like a question-and-answer site.
My main question is: how do you tell Elasticsearch which documents to update when there was a change to a nested object that's nested in multiple documents?
I have one index my_index and mappings for question, and answer. In particular, question has a nested object with a user:
"question": {
"properties": {
"user": {
"type": "nested",
"properties": {
"created_at": {
"type": "date",
"format": "dateOptionalTime"
},
"name": {
"type": "string"
},
"id": {
"type": "long"
},
"email": {
"type": "string"
}
}
}
...
}
}
It's possible for a user to change his name, and I have hooks to update the user in Elasticsearch:
after_commit lambda { __elasticsearch__.index_document}, on: :update
But this isn't updating the appropriate question objects correctly, and I don't know what to pass to the index_document call to make sure it updates all the corresponding questions with the new user name. Does anyone know? It might even help me to see what a RESTful/curl request should look like?
Any help would be appreciated!
There are a couple of different ways you can go about this. They are all probably going to require some code changes, though. I don't think there is a way to do what you are asking directly, with your current setup.
You can read about the various options here. If you can set things up as a one-to-many relationship, then the parent/child relationship is probably the way to go. Then you could set up something like this:
PUT my_index
{
"mappings": {
"user": {
"properties": {...}
},
"question": {
"_parent": {
"type": "user"
},
"properties": {...}
}
}
}
And in that case you would be able to update users independently of questions. But it makes querying more complicated, which may or may not be a problem in your application code.
Given that you already have nested documents set up, you could simply query for all the documents that have that particular user as a nested document, with something like:
POST /test_index/question/_search
{
"filter": {
"nested": {
"path": "user",
"filter": {
"term": {
"user.id": 2
}
}
}
}
}
and once you have all the affected question documents you can modify the user name in each one and update all the documents with a bulk index request.
Here is some code I used to play around with that last bit:
http://sense.qbox.io/gist/d2a319c6b4e7da0d5ff910b4118549228d90cba0
Related
The API I'm trying to describe has a structure where the root object can contain an arbitrary number of child objects (properties that are themselves objects). The "key", or property in the root object, is the unique identifier of the child object, and the value is the rest of the child object's data.
{
"child1": { ... bunch of stuff ... },
"child2": { ... bunch of stuff ... },
...
}
This could similarly be modeled as an array, e.g.:
[
{ "id": "child1", ... bunch of stuff ... },
{ "id": "child2", ... bunch of stuff ... },
...
]
but this both makes it structurally less clear what the identifying property is and makes uniqueness among the children's ID implicit rather than explicit, so we want to use an object, or a map.
I've seen the Swagger documentation for Model with Map/Dictionary Properties, but that doesn't adequately suit my use case. Writing something like:
"Parent": {
"additionalProperties": {
"$ref": "#/components/schemas/Child",
}
Yields something like this:
This adequately communicates the descriptiveness of the value in the property, but how do I document what the restrictions are for the "key" in the object? Ideally I'd like to say something like "it's not just any arbitrary string, it's the ID that corresponds to the child". Is this supported in any way?
Your example is correct.
how do I document what the restrictions are for the "key" in the object? Ideally I'd like to say something like "it's not just any arbitrary string, it's the ID that corresponds to the child". Is this supported in any way?
OpenAPI 3.1
OAS 3.1 fully supports JSON Schema 2020-12, including patternProperties. This keyword lets you define the format of dictionary keys by using a regular expression:
"Parent": {
"type": "object",
"patternProperties": {
"^child\d+$": {
"$ref": "#/components/schemas/Child"
}
},
"description": "A map of `Child` schemas, where the keys are IDs that correspond to the child"
}
Or, if the property names are defined by an enum, you can use propertyNames to define that enum:
"Parent": {
"type": "object",
"propertyNames": {
"enum": ["foo", "bar"]
},
"additionalProperties": {
"$ref": "#/components/schemas/Child"
}
}
OpenAPI 3.0 and 2.0
Dictionary keys are assumed to be strings, but there's no way to limit the contents/format of keys. You can document any restrictions and specifics verbally in the schema description. Adding schema examples could help illustrate what your dictionary/map might look like.
"Parent": {
"type": "object",
"additionalProperties": {
"$ref": "#/components/schemas/Child"
},
"description": "A map of `Child` schemas, where the keys are IDs that correspond to the child",
"example": {
"child1": { ... bunch of stuff ... },
"child2": { ... bunch of stuff ... },
}
If the possible key names are known (for example, they are part of an enum), you can define your dictionary as a regular object and the keys as individual object properties:
// Keys can be: key1, key2, key3
"Parent": {
"type": "object",
"properties": {
"key1": { "$ref": "#/components/schemas/Child" },
"key2": { "$ref": "#/components/schemas/Child" },
"key3": { "$ref": "#/components/schemas/Child" }
}
}
Then you can add "additionalProperties": false to really ensure that only those keys are used.
I have a realtime db all setup and working. The data structure is very simple:
Item
some: info
some: other info
Item 2
some: info
some: other info
My rules are also super simple:
{
"rules": {
".read":"auth.uid != null",
".write":"auth.uid != null"
}
}
The issue (obviously) is that while I am forcing a user to be authenticated, that's all I am requiring and any user can access all the items in the db.
What I want is a way to limit a user to an item.
something like:
Item1
some: info
some: other info
user_1: auth.uid
user_2: auth.uid2
Item2
some: info
some: other info
user_1: auth.uid3
user_2: auth.uid4
I can store that data but I am not sure how to structure my rules to limit that.
My actual json looks like:
{
"annotations": {
"8df0309f-dc62-821e-dd65-f0ad46396937": {
"author": "1OXVKN3Y5Z-11",
"xfdf": "LONG STRING"
}
},
"complete": false,
"created_at": "2020-09-01T17:52:25.653Z",
"field_values": {
"field_name": {
"name": "copy",
"value": "TEsting",
"widgetID": "e61e3abf-7cdd-7d07-daec-6c3d3a55d667"
}
},
"stamp_count": 0
}
What I plan to implement is:
{
"annotations": {
"8df0309f-dc62-821e-dd65-f0ad46396937": {
"author": "1OXVKN3Y5Z-11",
"xfdf": "LONG STRING"
}
},
"complete": false,
"created_at": "2020-09-01T17:52:25.653Z",
"field_values": {
"field_name": {
"name": "copy",
"value": "TEsting",
"widgetID": "e61e3abf-7cdd-7d07-daec-6c3d3a55d667"
}
},
"stamp_count": 0,
"users": [ "CFX4I0PTM9-11", "CFX4I0PTM9-7"]
}
One I implement that json structure, how can I setup rules to support?
From reading your question and the comment thread I think your requirement is:
Allow a user to access an item if their UID is associated with that item.
In that case, you'll first need to ensure that the UIDs are in keys, as you can't search across multiple values, as your proposed users array would require. So you'd end up with:
"items": {
"item1": {
...
"users": {
"CFX4I0PTM9-11": true,
"CFX4I0PTM9-7": true
}
}
}
Now with this structure, you can ensure a user can only update items where their UID is in the users map with rules like this:
{
"rules": {
"items": {
"$itemid": {
".write": "data.child('users').child(auth.uid).exists()"
}
}
}
}
For reading the specific item you could use a similar rule. That will allow the user to read an item once they know its complete path, and when their UID is in the users map.
But you won't be able to query this structure, as you can only index on named properties. For more on this, and the alternative data structure to still implement you use-case, see Firebase query if child of child contains a value
I have data saved to a Firebase Database with complex relationships indexed as outlined here similar to the example below.
{
"groups": {
"alpha": {
"name": "Alpha Group",
"members": {
"mchen": true,
"hmadi": true
}
},
...
},
"users": {
"mchen": {
"name": "Mary Chen",
// index Mary's groups in her profile
"groups": {
// the value here doesn't matter, just that the key exists
"alpha": true
},
},
...
},
"animals": {
"dog": {
"users": {
// the value here doesn't matter, just that the key exists
"mchen": true
},
},
...
},
}
For my project, what I need to do is I need to get a list of group members for all users who like dogs. This means means getting one reference, then another, then another. If I want to get the group members of people who like dogs, I'd Fetch
animals>dog>mchen
user>mchen>groups>alpha
groups>alpha ... members(final result)
Given that this is all done asynchronously, how would I conduct these data retrievals with conditions of completion, chaining one to the next? I've already tried nesting completion handlers, and this doesn't seem to work.
So I've been looking into the surveymonkey v3 api documentation for formatting questions types. What I want to do is create a multiple choice question that has an "other" option, which if selected has a text field that a user can fill in to be more specific. Is there a way to accomplish this with the api?
You should be able to do that when creating/updating a question.
Example:
POST /v3/surveys/<id>/pages/<id>/questions
{
"family": "single_choice",
"subtype": "vertical",
"answers": {
"other": [{
"text": "Other (please specify)",
"is_answer_choice": true,
"num_lines": 1,
"num_chars": 50
}],
"choices": [
{
"text": "Apples"
},
{
"text": "Oranges"
},
{
"text": "Bananas"
}
]
},
"headings": [
{
"heading": "What is your favourite fruit?"
}
]
}
This is_answer_choice field seems to not be accepted currently. That is a bug, you can watch the docs to potentially get notified on updates, or try it again later.
Edit: This method should work now, give it a try and let me know if it solves your problem!
I want to define an object with a key and a value but can't figure out how to define it as a property in a swagger 2.0 definition. Frankly, I am not sure if this is even possible if one wants to be able to define strongly typed clients. However, I figure I'd ask to see if this is even possible/allowable.
Basically, I want to be able to store the following object:
{ "currencies":
{ "usd" : 1.10,
"eur" : 2.25
}
}
But I don't want usd, and eur to have to be defined in the schema. I can do the below but I don't want usd or eur to be predefined. I want it to accept any value for the keys of the object.
"Product": {
"properties": {
"currencies": {
"type": "currencyObj"
},
}
},
},
"currencyObj": {
"id": "currencyObj",
"properties": {
"eur": {
"type": "float"
},
"usd": {
"type": "float"
}
}
}
Swagger 2.0 allows you to define a string to type mapping, you need to use additionalProperties. Use following notation to define a map :
"currencyObj": {
"type": "object",
"additionalProperties": {
"type": "number"
}
}