Skip to content
GitLab
Projects
Groups
Snippets
/
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Menu
Open sidebar
nomad-lab
encyclopedia-gui
Commits
196bffa4
Commit
196bffa4
authored
Feb 21, 2021
by
Iker Hurtado
Browse files
Rewrite the SearchBox component making the search query representation semantic
parent
5fe5eec5
Pipeline
#93973
skipped with stage
Changes
3
Pipelines
1
Hide whitespace changes
Inline
Side-by-side
client/conf.js
View file @
196bffa4
window
.
nomadEnv
=
{
//apiRoot: "https://nomad-lab.eu/dev/nomad/enc-search/api/encyclopedia/",
//
apiRoot: "https://nomad-lab.eu/dev/rae/enc-search/api/encyclopedia/",
apiRoot
:
"
https://nomad-lab.eu/dev/rae/enc-search/api/encyclopedia/
"
,
//apiRoot: "https://nomad-lab.eu/prod/rae/beta/api/encyclopedia/",
//apiRoot: "https://nomad-lab.eu/prod/rae/api/encyclopedia/",
apiRoot
:
"
http://localhost:8000/fairdi/nomad/latest/api/encyclopedia/
"
,
//apiRoot: "http://localhost:8000/fairdi/nomad/latest/api/encyclopedia/",
keycloakBase
:
'
https://nomad-lab.eu/fairdi/keycloak/auth/
'
,
keycloakRealm
:
'
fairdi_nomad_test
'
,
keycloakClientId
:
'
nomad_gui_dev
'
...
...
client/src/search-mod/NewSearchMod.js
View file @
196bffa4
...
...
@@ -206,7 +206,7 @@ class NewSearchMod {
this
.
formulaBox
=
new
FormulaBox
();
this
.
formulaBox
.
setAddFormulaListener
(
formula
=>
{
if
(
formula
.
trim
()
!==
''
){
this
.
searchBox
.
addTag
(
formula
,
'
F
'
);
this
.
searchBox
.
addTag
(
formula
,
'
formula
'
);
//this.formulaBox.disable(true);
this
.
addElementButton
.
disabled
=
true
;
this
.
addMatNameButton
.
disabled
=
true
;
...
...
@@ -217,7 +217,7 @@ class NewSearchMod {
this
.
materialNameBox
=
new
MaterialNameBox
();
this
.
materialNameBox
.
setAddMaterialNameListener
(
name
=>
{
if
(
name
.
trim
()
!==
''
){
this
.
searchBox
.
addTag
(
name
,
'
MN
'
);
// this.addTag(name, 'MN');
this
.
searchBox
.
addTag
(
name
,
'
material
'
);
// this.addTag(name, 'MN');
this
.
addElementButton
.
disabled
=
true
;
this
.
addFormulaButton
.
disabled
=
true
;
// this.formulaBox.disableInput(); It doesn't seem needed
...
...
client/src/search-mod/SearchBox.view.js
View file @
196bffa4
...
...
@@ -44,10 +44,21 @@ class SearchBox{
constructor
()
{
this
.
searchQuery
=
[];
this
.
queryTypes
=
[];
//**** Types associated to query elements
//
this.searchQuery = [];
//
this.queryTypes = []; //**** Types associated to query elements
// Types: element (E), formula (F), symbol (S) and prop names
this
.
currentOperator
;
//
//
this
.
currentConnector
;
this
.
notItem
;
// It signals next item is preceding for a NOT
this
.
inSubquery
;
// the current query pointer is in a subquery, paretheses open
this
.
rootQuery
;
// Root query representation. Root item object (type="query")
this
.
_resetState
();
/* Two type of object representing the expresions:
- item (node): {type: 'element'|'formula'|'query', value: "ElementName"|"Formula"| Another (sub)query structure, not: true|false }
- boolean connector: { type: 'connector', value: 'AND'|'OR'}
*/
this
.
element
=
document
.
createElement
(
'
div
'
);
this
.
element
.
className
=
'
search-box
'
;
...
...
@@ -66,9 +77,13 @@ class SearchBox{
this
.
cleanButton
=
this
.
element
.
querySelector
(
'
.clean-btn
'
);
this
.
cleanButton
.
addEventListener
(
"
click
"
,
(
e
)
=>
{
/*
this.searchQuery = [];
this.queryTypes = [];
this
.
updateSearchQuery
();
this.updateSearchQuery();*/
// init search query
this
.
_resetState
();
this
.
renderQuery
();
this
.
cleanSearchQueryListener
();
});
...
...
@@ -83,7 +98,75 @@ class SearchBox{
}
_resetState
(){
this
.
notItem
=
false
;
this
.
inSubquery
=
false
;
this
.
rootQuery
=
{
type
:
'
query
'
,
value
:
[],
not
:
false
};
// no prop open indicates that is the first
}
getOldQueryFormat
(){
const
oldFormat
=
[];
this
.
rootQuery
.
value
.
forEach
(
item
=>
{
oldFormat
.
push
(...
getItemInOldFormat
(
item
));
})
const
searchQuery
=
[];
const
queryTypes
=
[];
oldFormat
.
forEach
(
item
=>
{
searchQuery
.
push
(
item
.
split
(
'
:
'
)[
0
]);
queryTypes
.
push
(
item
.
split
(
'
:
'
)[
1
]);
})
console
.
log
(
'
OLD F:
'
,
oldFormat
,
searchQuery
,
queryTypes
);
return
[
searchQuery
,
queryTypes
]
function
getItemInOldFormat
(
item
){
const
oldFormat
=
[];
if
(
item
.
not
){
oldFormat
.
push
(
'
NOT:S
'
);
}
if
(
item
.
type
===
'
query
'
){
oldFormat
.
push
(
'
(:S
'
);
item
.
value
.
forEach
(
i
=>
{
oldFormat
.
push
(...
getItemInOldFormat
(
i
));
})
oldFormat
.
push
(
'
):S
'
);
}
else
{
// types: element, formula, material and connector
let
itemValue
=
item
.
value
+
'
:
'
;
if
(
item
.
type
===
'
element
'
)
itemValue
+=
'
E
'
;
else
if
(
item
.
type
===
'
formula
'
)
itemValue
+=
'
F
'
;
else
if
(
item
.
type
===
'
material
'
)
itemValue
+=
'
MN
'
;
else
itemValue
+=
'
S
'
;
// connector
oldFormat
.
push
(
itemValue
);
}
return
oldFormat
;
}
}
getOptimadeQuery2
(
allowOtherElements
)
{
// Empty query
if
(
this
.
rootQuery
.
length
===
0
)
{
return
''
;
}
// Material name query. Does not allow combinations.
if
(
this
.
rootQuery
.
length
===
1
&&
this
.
rootQuery
.
value
[
0
].
type
===
'
material
'
)
{
// material case
return
`material_name="
${
this
.
rootQuery
.
value
[
0
].
value
}
"`
;
}
const
[
searchQuery
,
queryTypes
]
=
this
.
getOldQueryFormat
();
// Translate element/formula queries into OptimadeSyntax
const
translator
=
new
OptimadeTranslator
();
return
translator
.
translate
(
searchQuery
,
queryTypes
,
allowOtherElements
);
/*
// Empty query
if (this.searchQuery.length === 0) {
return '';
...
...
@@ -94,9 +177,11 @@ class SearchBox{
return `material_name="${this.searchQuery[0]}"`;
}
// Translate element/formula queries into OptimadeSyntax
const translator = new OptimadeTranslator();
return translator.translate(this.searchQuery, this.queryTypes, allowOtherElements);
*/
}
...
...
@@ -223,26 +308,6 @@ class SearchBox{
return
util
.
ELEMENTS
.
includes
(
item
)
}
/* NOt used anymore??
function getOptimadeElementExclusiveANDSubquery(subquery){
const elements = []
subquery.forEach( (item) => {
if (isElement(item)) elements.push(`"${item}"`);
});
return 'elements HAS ONLY '+elements.join(', ');
}
function getOptimadeFormulaExclusiveANDSubquery(subquery){
const fragments = [];
subquery.forEach( (item) => {
if (!isElement(item) && item !== 'AND' && item !== 'NOT'){// Is formula
fragments.push(new Formula(item).getFragments());
}
});
return 'formula HAS ONLY '+fragments.join(', ');
}
*/
function
isQueryWellFormed
(
searchQuery
){
const
openingParIndex
=
searchQuery
.
indexOf
(
'
(
'
)
...
...
@@ -257,73 +322,120 @@ class SearchBox{
setBoolOperator
(
operator
){
this
.
current
Opera
tor
=
operator
;
this
.
current
Connec
tor
=
operator
;
}
updateSearchQuery
(){
let
html
=
''
;
for
(
let
i
=
0
;
i
<
this
.
searchQuery
.
length
;
i
++
)
{
let
type
=
this
.
queryTypes
[
i
];
renderQuery
(){
if
(
type
===
'
S
'
||
type
===
'
P
'
)
html
+=
`<span class="search-query-symbol" >
${
this
.
searchQuery
[
i
]}
</span>`
;
else
html
+=
getTagHtml
(
this
.
searchQuery
[
i
],
(
type
===
'
F
'
?
true
:
false
));
}
console
.
log
(
'
this.updateSearchQuery:
'
,
this
.
searchQuery
,
this
.
queryTypes
);
this
.
searchQueryBox
.
innerHTML
=
html
;
this
.
searchQueryBox
.
innerHTML
=
getQueryStructHTML
(
this
.
rootQuery
);
if
(
this
.
searchQueryChangeListener
)
this
.
searchQueryChangeListener
();
function
getQueryStructHTML
(
queryObj
){
if
(
queryObj
.
type
===
'
connector
'
)
return
getSymbolHTML
(
queryObj
.
value
);
else
if
(
!
queryObj
.
type
&&
queryObj
.
not
)
// NOT item wrapper, the item is not defined yet
return
getSymbolHTML
(
'
NOT
'
);
else
if
(
queryObj
.
type
===
'
element
'
||
queryObj
.
type
===
'
formula
'
||
queryObj
.
type
===
'
material
'
)
// element and formula
return
(
queryObj
.
not
?
getSymbolHTML
(
'
NOT
'
)
:
''
)
+
getTagHtml
(
queryObj
.
value
,
(
queryObj
.
type
===
'
formula
'
?
true
:
false
));
else
{
// the object represents a query
let
html
=
''
;
queryObj
.
value
.
forEach
(
subqueryObj
=>
{
html
+=
getQueryStructHTML
(
subqueryObj
);
});
//console.log('getQueryStructHTML', queryObj, queryObj.value[queryObj.length-1])
const
notHTML
=
(
queryObj
.
not
?
getSymbolHTML
(
'
NOT
'
)
:
''
)
const
isOpen
=
queryObj
.
open
;
return
notHTML
+
(
isOpen
===
undefined
?
html
:
getSymbolHTML
(
'
(
'
)
+
html
+
(
isOpen
?
''
:
getSymbolHTML
(
'
)
'
)));
}
}
function
getSymbolHTML
(
s
){
return
`<span class="search-query-symbol">
${
s
}
</span>`
;
}
}
addItemInSearchQuery
(
item
,
type
){
this
.
searchQuery
.
push
(
item
);
this
.
queryTypes
.
push
(
type
);
// Add an item (with a preceding connector if needed) to the query
// The item could be an NOT wrapper for a tag or subquery
_addItem
(
item
){
// It can be a tag item or a NOT (wrapping an item, added later)
const
queryContent
=
this
.
rootQuery
.
value
;
const
tempQueryContent
=
this
.
inSubquery
?
queryContent
[
queryContent
.
length
-
1
].
value
:
queryContent
;
// If the item is not at the beginning of the query (or subquery) and
if
(
tempQueryContent
.
length
>
0
&&
// the last item is not a connector (AND|OR)
tempQueryContent
[
tempQueryContent
.
length
-
1
].
type
!==
'
connector
'
){
// Add a connector (before the item)
tempQueryContent
.
push
(
{
type
:
'
connector
'
,
value
:
this
.
currentConnector
}
);
}
if
(
item
===
'
NOT
'
)
// Add an item wrapper with the NOT prop to true
tempQueryContent
.
push
({
not
:
true
});
// type and value props are undefined
else
if
(
item
===
'
SUBQUERY
'
)
queryContent
.
push
({
type
:
'
query
'
,
value
:
[],
open
:
true
,
not
:
false
});
else
{
// Regular tags: element|formula|material
const
[
type
,
tag
]
=
item
.
split
(
'
:
'
);
tempQueryContent
.
push
({
type
:
type
,
value
:
tag
,
not
:
false
});
}
console
.
log
(
'
ROOT QUERY
'
,
this
.
rootQuery
)
this
.
renderQuery
();
}
addTag
(
tag
,
type
){
// If the it's an element and is already in the query it's not inserted
if
(
type
===
'
E
'
&&
this
.
searchQuery
.
indexOf
(
tag
)
>=
0
)
return
;
// Public method - type = element|formula|material
addTag
(
tag
,
type
){
// add an element, formula or material
if
(
this
.
searchQuery
.
length
>
0
&&
this
.
searchQuery
[
this
.
searchQuery
.
length
-
1
]
!==
'
(
'
&&
this
.
searchQuery
[
this
.
searchQuery
.
length
-
1
]
!==
'
NOT
'
)
this
.
addItemInSearchQuery
(
this
.
currentOperator
,
'
S
'
);
this
.
addItemInSearchQuery
(
tag
,
type
);
this
.
updateSearchQuery
();
if
(
this
.
notItem
){
// The tag to add is preceded for a NOT, the item object is already created
const
queryContent
=
this
.
rootQuery
.
value
;
const
tempQueryContent
=
this
.
inSubquery
?
queryContent
[
queryContent
.
length
-
1
].
value
:
queryContent
;
tempQueryContent
[
tempQueryContent
.
length
-
1
].
type
=
type
;
tempQueryContent
[
tempQueryContent
.
length
-
1
].
value
=
tag
;
this
.
notItem
=
false
;
this
.
renderQuery
();
}
else
this
.
_addItem
(
type
+
'
:
'
+
tag
);
}
addElements
(
elementArray
){
let
index
=
elementArray
.
length
;
while
(
index
--
)
{
this
.
addTag
(
elementArray
[
index
],
'
E
'
);
this
.
addTag
(
elementArray
[
index
],
'
element
'
);
}
return
true
;
}
addParentheses
(
isOpen
){
if
(
isOpen
&&
this
.
searchQuery
.
length
>
0
&&
this
.
searchQuery
[
this
.
searchQuery
.
length
-
1
]
!==
'
NOT
'
){
this
.
addItemInSearchQuery
(
this
.
currentOperator
,
'
S
'
);
const
queryContent
=
this
.
rootQuery
.
value
;
// ** Not checking if the it's inside a subquery
if
(
isOpen
){
if
(
this
.
notItem
){
queryContent
[
queryContent
.
length
-
1
].
type
=
'
query
'
;
queryContent
[
queryContent
.
length
-
1
].
value
=
[];
this
.
notItem
=
false
;
}
else
this
.
_addItem
(
'
SUBQUERY
'
);
}
this
.
addItemInSearchQuery
(
(
isOpen
?
'
(
'
:
'
)
'
),
'
P
'
);
this
.
updateSearchQuery
();
queryContent
[
queryContent
.
length
-
1
].
open
=
isOpen
;
this
.
renderQuery
();
this
.
inSubquery
=
isOpen
;
}
addNOT
(){
if
(
this
.
searchQuery
.
length
>
0
&&
this
.
searchQuery
[
this
.
searchQuery
.
length
-
1
]
!==
'
(
'
){
this
.
addItemInSearchQuery
(
this
.
currentOperator
,
'
S
'
);
}
this
.
addItemInSearchQuery
(
'
NOT
'
,
'
S
'
);
this
.
updateSearchQuery
();
this
.
_addItem
(
'
NOT
'
);
this
.
notItem
=
true
;
// The next item will be in a not expression
}
...
...
@@ -333,69 +445,43 @@ class SearchBox{
// NOT being used let isMaterialName = (this.queryTypes[0] === 'MN');
// spot the item and remove the item and the bool operator(s) related
let
itemIndex
=
this
.
searchQuery
.
indexOf
(
item
);
if
(
itemIndex
>=
0
){
let
i
,
elementsToRemove
;
/*
if (this.queryTypes[itemIndex+1] === 'S'){ // bool operator on the right
console.log('itemIndex', itemIndex, this.searchQuery[itemIndex+1])
if (this.searchQuery[itemIndex-1] === 'NOT'){
i = itemIndex-1; elementsToRemove = 3;
}else{ i = itemIndex; elementsToRemove = 2; }
}else*/
if
(
this
.
queryTypes
[
itemIndex
-
1
]
===
'
S
'
){
// bool operator on the left
// console.log('itemIndex', itemIndex, this.searchQuery[itemIndex-1])
if
(
this
.
searchQuery
[
itemIndex
-
1
]
===
'
NOT
'
){
i
=
itemIndex
-
2
;
elementsToRemove
=
3
;
}
else
{
i
=
itemIndex
-
1
;
elementsToRemove
=
2
;
}
}
else
{
// case: (item)
i
=
itemIndex
;
elementsToRemove
=
1
;
}
removeItemsFromSearchQuery
(
this
,
i
,
elementsToRemove
);
}
// Remove the symbols before the first element/formula
if
(
this
.
queryTypes
[
0
]
===
'
S
'
)
{
// AND , OR
let
elementsToRemove
=
1
;
if
(
this
.
queryTypes
[
1
]
===
'
S
'
)
elementsToRemove
=
2
;
// NOT
removeItemsFromSearchQuery
(
this
,
0
,
elementsToRemove
);
}
// Travese the array removing the unnecessary parethesis (only tested for one level nested)
if
(
this
.
searchQuery
.
indexOf
(
'
(
'
)
>=
0
){
// Recursion
for
(
let
i
=
0
;
i
<
this
.
searchQuery
.
length
;
i
++
)
{
// dangerous: modifing a array being traversed
if
(
this
.
searchQuery
[
i
]
===
'
(
'
){
if
(
this
.
searchQuery
[
i
+
1
]
===
'
)
'
){
// '()' case
removeItemsFromSearchQuery
(
this
,
i
,
2
);
// this.searchQuery.splice(i, 2); this.queryTypes.splice(i, 2);
}
else
if
(
this
.
searchQuery
[
i
+
2
]
===
'
)
'
){
// '(item)' case
this
.
searchQuery
.
splice
(
i
,
3
,
this
.
searchQuery
[
i
+
1
]);
this
.
queryTypes
.
splice
(
i
,
3
,
this
.
queryTypes
[
i
+
1
]);
removeItem
(
this
.
rootQuery
,
item
);
function
removeItem
(
queryObj
,
item
){
const
queryContent
=
queryObj
.
value
;
for
(
let
i
=
0
;
i
<
queryContent
.
length
;
i
++
)
{
if
(
queryContent
[
i
].
value
===
item
)
{
if
(
queryContent
[
i
+
1
]
&&
queryContent
[
i
+
1
].
type
===
'
connector
'
){
queryContent
.
splice
(
i
,
2
);
}
else
queryContent
.
splice
(
i
,
1
);
break
;
}
else
if
(
queryContent
[
i
].
type
===
'
query
'
){
removeItem
(
queryContent
[
i
],
item
);
if
(
queryContent
[
i
].
value
.
length
==
0
){
if
(
queryContent
[
i
+
1
]
&&
queryContent
[
i
+
1
].
type
===
'
connector
'
){
queryContent
.
splice
(
i
,
2
);
}
else
queryContent
.
splice
(
i
,
1
);
}
}
}
}
this
.
updateSearchQuery
();
this
.
renderQuery
();
console
.
log
(
'
ROOT QUERY REMOVING
'
,
this
.
rootQuery
)
if
(
util
.
ELEMENTS
.
indexOf
(
item
)
>=
0
){
// It's an element (being removed)
this
.
removeElementListener
(
item
);
}
if
(
this
.
queryTypes
.
length
===
0
){
// The search query gets blank
if
(
this
.
rootQuery
.
value
.
length
===
0
){
// The search query gets blank
this
.
cleanSearchQueryListener
();
}
return
true
;
function
removeItemsFromSearchQuery
(
self
,
start
,
delCount
){
self
.
searchQuery
.
splice
(
start
,
delCount
);
self
.
queryTypes
.
splice
(
start
,
delCount
);
}
}
...
...
Write
Preview
Supports
Markdown
0%
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment